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: */