@@ -265,7 +265,18 @@ bool glow::isPngFormat(const std::string &filename) {
265265 return isPngHdrSignature (header);
266266}
267267
268- std::tuple<size_t , size_t , bool > glow::getPngInfo (const char *filename) {
268+ bool glow::isPpmFormat (const std::string &filename) {
269+ // Open file and test for it being a PPM.
270+ char magic[2 ];
271+ FILE *fp = fopen (filename.c_str (), " rb" );
272+ CHECK (fp) << " Can't open image file with name: " << filename;
273+ CHECK_EQ (fread (magic, sizeof (char ), sizeof (magic), fp), sizeof (magic))
274+ << " Failed to read magic number from file: " << filename;
275+ fclose (fp);
276+ return magic[0 ] == ' P' && (magic[1 ] == ' 5' || magic[1 ] == ' 6' );
277+ }
278+
279+ std::tuple<dim_t , dim_t , bool > glow::getPngInfo (const char *filename) {
269280 // open file and test for it being a png.
270281 FILE *fp = fopen (filename, " rb" );
271282 CHECK (fp) << " Can't open image file with name: " << filename;
@@ -302,6 +313,61 @@ std::tuple<size_t, size_t, bool> glow::getPngInfo(const char *filename) {
302313 return std::make_tuple (height, width, isGray);
303314}
304315
316+ static void skipSpacePPM (FILE *fp) {
317+ int c = getc (fp);
318+ while (c != EOF) {
319+ if (c == ' #' ) {
320+ // Skip comment line.
321+ do {
322+ c = getc (fp);
323+ } while (c != EOF && c != ' \n ' );
324+ } else if (!isspace (c)) {
325+ ungetc (c, fp);
326+ break ;
327+ }
328+ c = getc (fp);
329+ }
330+ }
331+
332+ std::tuple<dim_t , dim_t , bool > glow::getPpmInfo (FILE *fp,
333+ const char *filename) {
334+ // Open file and test for it being a PPM.
335+ char magic[2 ];
336+ CHECK_EQ (fread (magic, sizeof (char ), sizeof (magic), fp), sizeof (magic))
337+ << " Failed to read magic number from file: " << filename;
338+ CHECK (magic[0 ] == ' P' && (magic[1 ] == ' 5' || magic[1 ] == ' 6' ))
339+ << filename << " is not a PPM image" ;
340+
341+ // Gray-scale or color is determined by magic number.
342+ bool isGray = magic[1 ] == ' 5' ;
343+
344+ // Read dimensions and color depth.
345+ int32_t height, width, depth;
346+ skipSpacePPM (fp);
347+ CHECK_EQ (fscanf (fp, " %d" , &width), 1 )
348+ << " Can't read width from: " << filename;
349+ skipSpacePPM (fp);
350+ CHECK_EQ (fscanf (fp, " %d" , &height), 1 )
351+ << " Can't read height from: " << filename;
352+ skipSpacePPM (fp);
353+ CHECK_EQ (fscanf (fp, " %d" , &depth), 1 )
354+ << " Can't read color depth from: " << filename;
355+ CHECK_EQ (depth, 255 ) << " Unsupported color depth " << depth
356+ << " in file: " << filename;
357+
358+ return std::make_tuple (dim_t (height), dim_t (width), isGray);
359+ }
360+
361+ std::tuple<dim_t , dim_t , bool > glow::getPpmInfo (const char *filename) {
362+ bool isGray;
363+ dim_t width, height;
364+ FILE *fp = fopen (filename, " rb" );
365+ CHECK (fp) << " Can't open image file with name: " << filename;
366+ std::tie (height, width, isGray) = getPpmInfo (fp, filename);
367+ fclose (fp);
368+ return std::make_tuple (height, width, isGray);
369+ }
370+
305371bool glow::readPngImage (Tensor *T, const char *filename,
306372 std::pair<float , float > range,
307373 llvm::ArrayRef<float > mean,
@@ -415,6 +481,50 @@ bool glow::readPngImage(Tensor *T, const char *filename,
415481 return false ;
416482}
417483
484+ bool glow::readPpmImage (Tensor *T, const char *filename,
485+ std::pair<float , float > range,
486+ llvm::ArrayRef<float > mean,
487+ llvm::ArrayRef<float > stddev) {
488+ bool isGray;
489+ dim_t height, width;
490+ FILE *fp = fopen (filename, " rb" );
491+ if (!fp) {
492+ return true ;
493+ }
494+
495+ // Get PPM info.
496+ std::tie (height, width, isGray) = getPpmInfo (fp, filename);
497+ const dim_t numChannels = isGray ? 1 : 3 ;
498+ T->reset (ElemKind::FloatTy, {height, width, numChannels});
499+
500+ // Skip a single byte of space.
501+ fgetc (fp);
502+
503+ // Read pixels and do pre-processing.
504+ auto H = T->getHandle <>();
505+ float scale = ((range.second - range.first ) / 255.0 );
506+ float bias = range.first ;
507+ unsigned char *buf =
508+ (unsigned char *)malloc (width * numChannels * sizeof (unsigned char ));
509+ for (dim_t h = 0 ; h < height; h++) {
510+ if (fread (buf, width * numChannels, 1 , fp) != 1 ) {
511+ free (buf);
512+ fclose (fp);
513+ return true ;
514+ }
515+ for (dim_t w = 0 ; w < width; w++) {
516+ for (dim_t c = 0 ; c < numChannels; c++) {
517+ float val = float (buf[w * numChannels + c]);
518+ H.at ({h, w, c}) = ((val - mean[c]) / stddev[c]) * scale + bias;
519+ }
520+ }
521+ }
522+
523+ free (buf);
524+ fclose (fp);
525+ return false ;
526+ }
527+
418528bool glow::writePngImage (Tensor *T, const char *filename,
419529 std::pair<float , float > range,
420530 llvm::ArrayRef<float > mean,
@@ -509,25 +619,25 @@ bool glow::writePngImage(Tensor *T, const char *filename,
509619 return false ;
510620}
511621
512- Tensor glow::readPngImageAndPreprocess (llvm::StringRef filename,
513- ImageNormalizationMode imageNormMode,
514- ImageChannelOrder imageChannelOrder,
515- ImageLayout imageLayout,
516- llvm::ArrayRef<float > mean,
517- llvm::ArrayRef<float > stddev) {
622+ Tensor glow::readPngPpmImageAndPreprocess (llvm::StringRef filename,
623+ ImageNormalizationMode imageNormMode,
624+ ImageChannelOrder imageChannelOrder,
625+ ImageLayout imageLayout,
626+ llvm::ArrayRef<float > mean,
627+ llvm::ArrayRef<float > stddev) {
518628 Tensor imageData;
519- readPngImageAndPreprocess (imageData, filename, imageNormMode,
520- imageChannelOrder, imageLayout, mean, stddev);
629+ readPngPpmImageAndPreprocess (imageData, filename, imageNormMode,
630+ imageChannelOrder, imageLayout, mean, stddev);
521631 return imageData;
522632}
523633
524- void glow::readPngImageAndPreprocess (Tensor &imageData,
525- llvm::StringRef filename,
526- ImageNormalizationMode imageNormMode,
527- ImageChannelOrder imageChannelOrder,
528- ImageLayout imageLayout,
529- llvm::ArrayRef<float > mean,
530- llvm::ArrayRef<float > stddev) {
634+ void glow::readPngPpmImageAndPreprocess (Tensor &imageData,
635+ llvm::StringRef filename,
636+ ImageNormalizationMode imageNormMode,
637+ ImageChannelOrder imageChannelOrder,
638+ ImageLayout imageLayout,
639+ llvm::ArrayRef<float > mean,
640+ llvm::ArrayRef<float > stddev) {
531641
532642 // PNG images are RGB, so shuffle mean and stddev values to be in RGB order
533643 // as well, prior applying them to input image.
@@ -538,15 +648,20 @@ void glow::readPngImageAndPreprocess(Tensor &imageData,
538648 std::reverse (stddevRGB.begin (), stddevRGB.end ());
539649 }
540650
651+ bool isPNG = isPngFormat (filename.data ());
652+ CHECK (isPNG || isPpmFormat (filename.data ())) << " Unrecognized image format" ;
541653 auto range = normModeToRange (imageNormMode);
542- bool loadSuccess =
543- !readPngImage (&imageData, filename.data (), range, meanRGB, stddevRGB);
654+ bool loadSuccess = isPNG ? !readPngImage (&imageData, filename.data (), range,
655+ meanRGB, stddevRGB)
656+ : !readPpmImage (&imageData, filename.data (), range,
657+ meanRGB, stddevRGB);
658+
544659 CHECK (loadSuccess) " Error reading input image from file: " << filename.str ();
545660 dim_t imgHeight = imageData.dims ()[0 ];
546661 dim_t imgWidth = imageData.dims ()[1 ];
547662 dim_t numChannels = imageData.dims ()[2 ];
548663
549- // PNG images are NHWC and RGB. Convert if needed.
664+ // PNG/PPM images are NHWC and RGB. Convert if needed.
550665 // Convert to requested channel ordering.
551666 if (imageChannelOrder == ImageChannelOrder::BGR) {
552667 Tensor swizzled (imageData.getType ());
@@ -569,8 +684,8 @@ void glow::readPngImageAndPreprocess(Tensor &imageData,
569684 }
570685}
571686
572- // / Entry point for the PNG images loader.
573- void glow::readPngImagesAndPreprocess (
687+ // / Entry point for the PNG/PPM images loader.
688+ void glow::readPngPpmImagesAndPreprocess (
574689 Tensor &inputImageData, const llvm::ArrayRef<std::string> &filenames,
575690 ImageNormalizationMode imageNormMode, ImageChannelOrder imageChannelOrder,
576691 ImageLayout imageLayout, llvm::ArrayRef<float > meanRef,
@@ -584,7 +699,11 @@ void glow::readPngImagesAndPreprocess(
584699 dim_t imgHeight;
585700 dim_t imgWidth;
586701 bool isGray;
587- std::tie (imgHeight, imgWidth, isGray) = getPngInfo (filenames[0 ].c_str ());
702+ bool isPNG = isPngFormat (filenames[0 ]);
703+ CHECK (isPNG || isPpmFormat (filenames[0 ])) << " Unrecognized image format" ;
704+ std::tie (imgHeight, imgWidth, isGray) =
705+ isPNG ? getPngInfo (filenames[0 ].c_str ())
706+ : getPpmInfo (filenames[0 ].c_str ());
588707 const dim_t numChannels = isGray ? 1 : 3 ;
589708
590709 // Assign mean and stddev for input normalization.
@@ -632,8 +751,8 @@ void glow::readPngImagesAndPreprocess(
632751 // Read images into local tensors and add to batch.
633752 for (size_t n = 0 ; n < filenames.size (); n++) {
634753 Tensor localCopy;
635- readPngImageAndPreprocess (localCopy, filenames[n], imageNormMode,
636- imageChannelOrder, imageLayout, mean, stddev);
754+ readPngPpmImageAndPreprocess (localCopy, filenames[n], imageNormMode,
755+ imageChannelOrder, imageLayout, mean, stddev);
637756 DCHECK (std::equal (localCopy.dims ().begin (), localCopy.dims ().end (),
638757 inputImageData.dims ().begin () + 1 ))
639758 << " All images must have the same dimensions" ;
@@ -688,10 +807,10 @@ void glow::loadImagesAndPreprocess(
688807 auto inputImageData = inputImageDataList[i];
689808 // All files for an input must be of the same type, thus will just check
690809 // the first one.
691- if (isPngFormat (filenames[0 ])) {
692- readPngImagesAndPreprocess (*inputImageData, filenames, imageNormMode[i],
693- imageChannelOrderOpt [i], imageLayoutOpt [i],
694- meanValuesOpt[i], stddevValuesOpt[i]);
810+ if (isPngFormat (filenames[0 ]) || isPpmFormat (filenames[ 0 ]) ) {
811+ readPngPpmImagesAndPreprocess (
812+ *inputImageData, filenames, imageNormMode [i], imageChannelOrderOpt [i],
813+ imageLayoutOpt[i], meanValuesOpt[i], stddevValuesOpt[i]);
695814 } else if (isNumpyNpyFormat (filenames[0 ])) {
696815 loadNumpyImagesAndPreprocess (filenames, *inputImageData, imageNormMode[i],
697816 imageLayoutOpt[i], inputLayoutOpt[i],
0 commit comments