PnmParser.C

Go to the documentation of this file.
00001 /*!@file Raster/PnmParser.C Parse pbm/pgm/ppm image files. */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the //
00005 // University of Southern California (USC) and the iLab at USC.         //
00006 // See http://iLab.usc.edu for information about this project.          //
00007 // //////////////////////////////////////////////////////////////////// //
00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00010 // in Visual Environments, and Applications'' by Christof Koch and      //
00011 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00012 // pending; application number 09/912,225 filed July 23, 2001; see      //
00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00014 // //////////////////////////////////////////////////////////////////// //
00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00016 //                                                                      //
00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00018 // redistribute it and/or modify it under the terms of the GNU General  //
00019 // Public License as published by the Free Software Foundation; either  //
00020 // version 2 of the License, or (at your option) any later version.     //
00021 //                                                                      //
00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00025 // PURPOSE.  See the GNU General Public License for more details.       //
00026 //                                                                      //
00027 // You should have received a copy of the GNU General Public License    //
00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00030 // Boston, MA 02111-1307 USA.                                           //
00031 // //////////////////////////////////////////////////////////////////// //
00032 //
00033 // Primary maintainer for this file: Rob Peters <rjpeters@klab.caltech.edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Raster/PnmParser.C $
00035 // $Id: PnmParser.C 8790 2007-09-28 22:24:10Z rjpeters $
00036 //
00037 
00038 #include "Raster/PnmParser.H"
00039 
00040 #include "Util/Assert.H"
00041 #include "Image/Image.H"
00042 #include "Image/Pixels.H"
00043 #include "Raster/GenericFrame.H"
00044 #include "Util/Assert.H"
00045 #include "Util/FileUtil.H"
00046 #include "Util/log.H"
00047 #include "rutz/shared_ptr.h"
00048 
00049 #include <cctype>
00050 #include <istream>
00051 #include <limits>
00052 #include <string>
00053 
00054 // ######################################################################
00055 struct PnmParser::Rep
00056 {
00057   Rep(const std::string& fname_) :
00058     fname(fname_),
00059     owned_strm(openMaybeCompressedFile(fname_)),
00060     strm(0),
00061     mode(-1),
00062     w(-1),
00063     h(-1),
00064     maxGrey(1),
00065     comments("")
00066   {
00067     ASSERT(owned_strm.get() != 0);
00068     strm = owned_strm.get();
00069   }
00070 
00071   Rep(std::istream& strm_) :
00072     fname("(anonymous stream)"),
00073     owned_strm(),
00074     strm(&strm_),
00075     mode(-1),
00076     w(-1),
00077     h(-1),
00078     maxGrey(1),
00079     comments("")
00080   {}
00081 
00082   void init();
00083 
00084   /// Read the actual pixels out of the file, for 1-byte-per-value images
00085   void readPixels8(byte* space, int nvals);
00086 
00087   /// Read the actual pixels out of the file, for 2-bytes-per-value images
00088   void readPixels16(uint16* space, int nvals);
00089 
00090   /// Parse a 1-bit black+white raw or ascii image file into an 8-bit image.
00091   /** The underlying file <i>must</i> be a black_white file (mode 1 or
00092       4). */
00093   Image<byte> parseBW();
00094 
00095   /// Parse an 8-bit grayscale image in either raw or ascii mode.
00096   /** The underlying file <i>must</i> be a grayscale file (mode 2 or 5). */
00097   Image<byte> parseGrayU8();
00098 
00099   /// Parse a 16-bit grayscale image in either raw or ascii mode.
00100   /** The underlying file <i>must</i> be a grayscale file (mode 2 or 5). */
00101   Image<uint16> parseGrayU16();
00102 
00103   /// Parse an 8-bit rgb image in either raw or ascii mode
00104   /** The underlying file <i>must</i> be a RGB file (mode 3 or 6). */
00105   Image<PixRGB<byte> > parseRgbU8();
00106 
00107   /// Parse a 16-bit rgb image in either raw or ascii mode
00108   /** The underlying file <i>must</i> be a RGB file (mode 3 or 6). */
00109   Image<PixRGB<uint16> > parseRgbU16();
00110 
00111   std::string fname;
00112   rutz::shared_ptr<std::istream> owned_strm;
00113   std::istream* strm;
00114   int mode;
00115   int w, h;
00116   int maxGrey;
00117   std::string comments;
00118 };
00119 
00120 // ######################################################################
00121 void PnmParser::Rep::init()
00122 {
00123   int c = this->strm->get();
00124   if (c != 'P')
00125     LFATAL("Missing magic number in pnm file '%s'"
00126            "(got '%c' [%d], expected '%c' [%d]).",
00127            fname.c_str(), c, c, 'P', 'P');
00128 
00129   *(this->strm) >> this->mode;
00130 
00131   *(this->strm) >> std::ws;
00132   // copy and concatenate optional comment line(s) starting with '#'
00133   // into comments string
00134 
00135   std::string temp;
00136 
00137   while ( this->strm->peek() == '#' )
00138     {
00139       //get the full line
00140       std::getline( *(this->strm), temp, '\n');
00141       this->comments += temp;
00142 
00143       //This is what we did before we were interested in the comments
00144       //this->strm->ignore(std::numeric_limits<int>::max(), '\n');
00145     }
00146 
00147   *(this->strm) >> this->w >> this->h;
00148 
00149   if (this->mode != 1 && this->mode != 4)
00150     {
00151       *(this->strm) >> this->maxGrey;
00152     }
00153 
00154   // read one more character of whitespace from the stream after maxGrey
00155   c = this->strm->get();
00156   if ( !isspace(c) )
00157     LFATAL("Missing whitespace after maxGrey in pbm file '%s'.",
00158            fname.c_str());
00159 }
00160 
00161 // ######################################################################
00162 void PnmParser::Rep::readPixels8(byte* p, int nvals)
00163 {
00164   ASSERT(this->mode == 2 || this->mode == 3 || this->mode == 5 || this->mode == 6);
00165 
00166   ASSERT(this->maxGrey > 0);
00167   ASSERT(this->maxGrey <= 255);
00168 
00169   const double scale = 255.0/double(this->maxGrey);
00170 
00171   if (this->mode == 2 || this->mode == 3)
00172     {
00173       // Parse ascii:
00174       int position = 0;
00175       int val = 0;
00176 
00177       while (this->strm->peek() != EOF && position < nvals)
00178         {
00179           *(this->strm) >> val;
00180           p[position++] = static_cast<byte>(val * scale);
00181         }
00182     }
00183   else
00184     {
00185       // Parse raw:
00186       this->strm->read(reinterpret_cast<char*>(p), nvals);
00187 
00188       if (this->maxGrey != 255)
00189         for (int i = 0; i < nvals; ++i)
00190           {
00191             if (p[i] > this->maxGrey)
00192               {
00193                 LERROR("%s: value %d was %d but maxgrey "
00194                        "is claimed to be %d",
00195                        this->fname.c_str(), i, p[i], this->maxGrey);
00196                 p[i] = this->maxGrey;
00197               }
00198             p[i] = byte(p[i] * scale);
00199           }
00200     }
00201 }
00202 
00203 // ######################################################################
00204 void PnmParser::Rep::readPixels16(uint16* p, int nvals)
00205 {
00206   ASSERT(this->mode == 2 || this->mode == 3 || this->mode == 5 || this->mode == 6);
00207 
00208   ASSERT(this->maxGrey >= 256);
00209   ASSERT(this->maxGrey <= 65535);
00210 
00211   const double scale = 65535.0/double(this->maxGrey);
00212 
00213   if (this->mode == 2 || this->mode == 3)
00214     {
00215       // Parse ascii:
00216       int position = 0;
00217       int val = 0;
00218 
00219       while (this->strm->peek() != EOF && position < nvals)
00220         {
00221           *(this->strm) >> val;
00222           p[position++] = static_cast<uint16>(val * scale);
00223         }
00224     }
00225   else
00226     {
00227       // Parse raw:
00228       this->strm->read(reinterpret_cast<char*>(p), nvals * 2);
00229 
00230       union { byte b[2]; uint16 u16; } u;
00231       u.b[0] = 0; u.b[1] = 1;
00232 
00233       if (u.u16 != 1) // need to swap bytes
00234         {
00235           for (int i = 0; i < nvals; ++i)
00236             p[i] = ((p[i] & 0xff00) >> 8) | ((p[i] & 0x00ff) << 8);
00237         }
00238 
00239       if (this->maxGrey != 65535)
00240         for (int i = 0; i < nvals; ++i)
00241           {
00242             if (p[i] > this->maxGrey)
00243               {
00244                 LERROR("%s: value %d was %d but maxgrey "
00245                        "is claimed to be %d",
00246                        this->fname.c_str(), i, p[i], this->maxGrey);
00247                 p[i] = this->maxGrey;
00248               }
00249             p[i] = uint16(p[i] * scale);
00250           }
00251     }
00252 }
00253 
00254 // ######################################################################
00255 Image<byte> PnmParser::Rep::parseBW()
00256 {
00257   ASSERT(this->mode == 1 || this->mode == 4);
00258 
00259   Image<byte> img(this->w, this->h, NO_INIT);
00260 
00261   if (this->mode == 1)
00262     {
00263       Image<byte>::iterator itr = img.beginw(), stop = img.endw();
00264 
00265       while (itr != stop)
00266         {
00267           const int c = this->strm->get();
00268           if (c == EOF)
00269             LFATAL("while parsing %s: premature EOF before pixel #%d",
00270                    this->fname.c_str(), int(stop - itr));
00271 
00272           if (isspace(c))
00273             continue;
00274 
00275           if (c == '0') // "0" ==> white pixel
00276             *itr++ = 255;
00277           else if (c == '1') // "1" ==> black pixel
00278             *itr++ = 0;
00279           else
00280             LFATAL("while parsing %s: "
00281                    "invalid pixel value '%c' at pixel #%d",
00282                    this->fname.c_str(), c, int(stop - itr));
00283         }
00284     }
00285   else if (this->mode == 4)
00286     {
00287       Image<byte>::iterator itr = img.beginw(), stop = img.endw();
00288 
00289       int c = 0;
00290       int pos = 7;
00291 
00292       while (itr != stop)
00293         {
00294           if (pos == 7)
00295             {
00296               c = this->strm->get();
00297               if (c == EOF)
00298                 LFATAL("while parsing %s: premature EOF before pixel #%d",
00299                        this->fname.c_str(), int(stop - itr));
00300             }
00301 
00302           if (c & (1 << pos))
00303             *itr++ = 0;
00304           else
00305             *itr++ = 255;
00306 
00307           if (pos == 0) pos = 7;
00308           else --pos;
00309         }
00310     }
00311 
00312   return img;
00313 }
00314 
00315 // ######################################################################
00316 Image<byte> PnmParser::Rep::parseGrayU8()
00317 {
00318   ASSERT(this->mode == 2 || this->mode == 5);
00319 
00320   Image<byte> img(this->w, this->h, NO_INIT);
00321   this->readPixels8(img.getArrayPtr(), img.getSize());
00322   return img;
00323 }
00324 
00325 // ######################################################################
00326 Image<uint16> PnmParser::Rep::parseGrayU16()
00327 {
00328   ASSERT(this->mode == 2 || this->mode == 5);
00329 
00330   Image<uint16> img(this->w, this->h, NO_INIT);
00331   this->readPixels16(img.getArrayPtr(), img.getSize());
00332   return img;
00333 }
00334 
00335 // ######################################################################
00336 Image<PixRGB<byte> > PnmParser::Rep::parseRgbU8()
00337 {
00338   ASSERT(this->mode == 3 || this->mode == 6);
00339 
00340   Image<PixRGB<byte> > img(this->w, this->h, NO_INIT);
00341   this->readPixels8(reinterpret_cast<byte*>(img.getArrayPtr()), img.getSize()*3);
00342   return img;
00343 }
00344 
00345 // ######################################################################
00346 Image<PixRGB<uint16> > PnmParser::Rep::parseRgbU16()
00347 {
00348   ASSERT(this->mode == 3 || this->mode == 6);
00349 
00350   Image<PixRGB<uint16> > img(this->w, this->h, NO_INIT);
00351   this->readPixels16(reinterpret_cast<uint16*>(img.getArrayPtr()), img.getSize()*3);
00352   return img;
00353 }
00354 
00355 // ######################################################################
00356 PnmParser::PnmParser(const char* fname) :
00357   rep(new Rep(fname))
00358 {
00359   rep->init();
00360 }
00361 
00362 // ######################################################################
00363 PnmParser::PnmParser(std::istream& strm) :
00364   rep(new Rep(strm))
00365 {
00366   rep->init();
00367 }
00368 
00369 // ######################################################################
00370 PnmParser::~PnmParser()
00371 {
00372   delete rep;
00373 }
00374 
00375 // ######################################################################
00376 GenericFrameSpec PnmParser::getFrameSpec() const
00377 {
00378   GenericFrameSpec result;
00379 
00380   switch (rep->mode)
00381     {
00382     case 1: case 4: // 1-bit bitmap file, but we parse it into an 8-bit image:
00383       result.nativeType = GenericFrame::GRAY_U8;
00384       break;
00385 
00386     case 2: case 5: // 8-bit grayscale
00387       result.nativeType =
00388         rep->maxGrey <= 255
00389         ? GenericFrame::GRAY_U8
00390         : GenericFrame::GRAY_F32;
00391       break;
00392 
00393     case 3: case 6: // 24-bit RGB
00394       result.nativeType =
00395         rep->maxGrey <= 255
00396         ? GenericFrame::RGB_U8
00397         : GenericFrame::RGB_F32;
00398       break;
00399 
00400     default:
00401       LFATAL("invalid PNM mode '%d'", rep->mode);
00402     }
00403 
00404   result.videoFormat = VIDFMT_AUTO;
00405   result.videoByteSwap = false;
00406   result.dims = Dims(rep->w, rep->h);
00407   result.floatFlags = 0;
00408 
00409   return result;
00410 }
00411 
00412 // ######################################################################
00413 std::string PnmParser::getComments() const
00414 { return rep->comments; }
00415 
00416 // ######################################################################
00417 uint PnmParser::getTagCount() const
00418 { return 0; }
00419 
00420 // ######################################################################
00421 bool PnmParser::getTag(uint tag, std::string &name, std::string &value) const
00422 { return false; }
00423 
00424 // ######################################################################
00425 GenericFrame PnmParser::getFrame()
00426 {
00427   switch (rep->mode)
00428     {
00429     case 1: case 4: // black+white
00430       return GenericFrame(rep->parseBW());
00431       break;
00432 
00433     case 2: case 5: // grayscale
00434       if (rep->maxGrey <= 255)
00435         return GenericFrame(rep->parseGrayU8());
00436       else
00437         {
00438           const Image<uint16> gray16(rep->parseGrayU16());
00439           Image<float> ret(gray16.getDims(), NO_INIT);
00440           Image<uint16>::const_iterator sptr = gray16.begin();
00441           Image<float>::iterator fptr = ret.beginw();
00442           Image<float>::iterator const stop = ret.endw();
00443           // scale the result so its range is still [0.0,256.0[, just
00444           // as if the input image file had been 8-bit, but now with
00445           // extra precision:
00446           while (fptr != stop)
00447             *fptr++ = *sptr++ / 256.0f;
00448           return GenericFrame(ret, /* floatFlags = */ 0);
00449         }
00450       break;
00451 
00452     case 3: case 6: // rgb
00453       if (rep->maxGrey <= 255)
00454         return GenericFrame(rep->parseRgbU8());
00455       else
00456         {
00457           const Image<PixRGB<uint16> > rgb16(rep->parseRgbU16());
00458           Image<PixRGB<float> > ret(rgb16.getDims(), NO_INIT);
00459           Image<PixRGB<uint16> >::const_iterator sptr = rgb16.begin();
00460           Image<PixRGB<float> >::iterator fptr = ret.beginw();
00461           Image<PixRGB<float> >::iterator const stop = ret.endw();
00462           // scale the result so its range is still [0.0,256.0[, just
00463           // as if the input image file had been 8-bit, but now with
00464           // extra precision:
00465           while (fptr != stop)
00466             *fptr++ = PixRGB<float>(*sptr++) / 256.0f;
00467           return GenericFrame(ret, /* floatFlags = */ 0);
00468         }
00469       break;
00470     }
00471 
00472   LFATAL("invalid PNM mode '%d'", rep->mode);
00473   /* can't happen */ return GenericFrame();
00474 }
00475 
00476 // ######################################################################
00477 /* So things look consistent in everyone's emacs... */
00478 /* Local Variables: */
00479 /* indent-tabs-mode: nil */
00480 /* End: */
Generated on Sun May 8 08:05:34 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3