00001 /*!@file Raster/PngWriter.C Write PNG 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/PngWriter.C $ 00035 // $Id: PngWriter.C 14488 2011-02-10 23:44:13Z itti $ 00036 // 00037 00038 #ifdef INVT_HAVE_LIBPNG 00039 00040 #include "Raster/PngWriter.H" 00041 00042 #include "Image/Image.H" 00043 #include "Image/Normalize.H" 00044 #include "Image/Pixels.H" 00045 #include "Raster/GenericFrame.H" 00046 #include "rutz/fstring.h" 00047 #include "rutz/sfmt.h" 00048 #include "Util/FileUtil.H" 00049 00050 #include <cerrno> 00051 #include <cstdio> 00052 #include <cstdlib> 00053 #include <png.h> 00054 #include <sys/file.h> 00055 #include <unistd.h> 00056 #include <fcntl.h> 00057 00058 00059 namespace 00060 { 00061 rutz::fstring errnoMessage() 00062 { 00063 if (errno != 0) 00064 return rutz::sfmt("\n [errno %d: %s]", errno, strerror(errno)); 00065 00066 return rutz::fstring(); 00067 } 00068 00069 /// Helper class for PNG-writing functions. 00070 /** Essentially this is here to make proper error cleanup easier, 00071 e.g. in the presence of exceptions. */ 00072 class PngWriterHelper 00073 { 00074 public: 00075 PngWriterHelper(const char* filename); 00076 00077 ~PngWriterHelper(); 00078 00079 /// Return the error status given by ::close(). 00080 /** If possible, we want to check and report that error status; 00081 however if close() is being called from ~PngWriterHelper(), 00082 then it's already too late (we can't/shouldn't throw an 00083 exception from a destructor) and we'll have to just ignore the 00084 error. */ 00085 int close(); 00086 00087 /// Write an 8-bit grayscale PNG image file. 00088 void writeGray(const Image<byte>& img); 00089 00090 /// Write an 8-bit color PNG image file. 00091 void writeRGB(const Image<PixRGB<byte> >& img); 00092 00093 private: 00094 PngWriterHelper(const PngWriterHelper&); 00095 PngWriterHelper& operator=(const PngWriterHelper&); 00096 00097 void onError(const rutz::fstring& msg); 00098 00099 const rutz::fstring itsFilename; 00100 FILE* itsFile; 00101 png_structp itsPngPtr; 00102 png_infop itsInfoPtr; 00103 }; 00104 00105 PngWriterHelper::PngWriterHelper(const char* filename) 00106 : 00107 itsFilename(filename), 00108 itsFile(0), 00109 itsPngPtr(0), 00110 itsInfoPtr(0) 00111 { 00112 errno = 0; 00113 00114 itsFile = fopen(filename, "wb"); 00115 00116 if (itsFile == 0) 00117 { 00118 onError(rutz::sfmt("couldn't open png file '%s' for writing%s", 00119 filename, errnoMessage().c_str())); 00120 } 00121 00122 const int lockstat = flock(fileno(itsFile),LOCK_EX); 00123 00124 if (lockstat == EBADF) 00125 { 00126 onError(rutz::sfmt("couldn't open png file '%s' for writing%s", 00127 filename, errnoMessage().c_str())); 00128 } 00129 if (lockstat == EINTR) 00130 { 00131 onError(rutz::sfmt("Lock on png file '%s' interupted %s", 00132 filename, errnoMessage().c_str())); 00133 } 00134 00135 itsPngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); 00136 if (itsPngPtr == 0) 00137 { 00138 onError(rutz::sfmt("png_create_write_struct() failed for file '%s'%s", 00139 filename, errnoMessage().c_str())); 00140 } 00141 00142 itsInfoPtr = png_create_info_struct(itsPngPtr); 00143 if (itsInfoPtr == 0) 00144 { 00145 onError(rutz::sfmt("png_create_info_struct() failed for file '%s'%s", 00146 filename, errnoMessage().c_str())); 00147 } 00148 00149 png_init_io(itsPngPtr, itsFile); 00150 00151 png_set_compression_level(itsPngPtr, 9); 00152 } 00153 00154 PngWriterHelper::~PngWriterHelper() 00155 { 00156 const int status = this->close(); 00157 if (status != 0) 00158 // NOTE: Don't use PLFATAL here since then we'd end up with 00159 // double-error-reporting. 00160 PLERROR("Error during fclose() while writing %s", 00161 itsFilename.c_str()); 00162 } 00163 00164 int PngWriterHelper::close() 00165 { 00166 if (itsPngPtr != 0) 00167 { 00168 png_destroy_write_struct(itsPngPtr ? &itsPngPtr : 0, 00169 itsInfoPtr ? &itsInfoPtr : 0); 00170 00171 itsPngPtr = 0; 00172 itsInfoPtr = 0; 00173 } 00174 00175 if (itsFile != 0) 00176 { 00177 // force a FULL sync before we unlock this file 00178 // fclose will not wait for the file to get onto 00179 // the HD so we can still remove a lock on a file 00180 // being written. 00181 fsync(fileno(itsFile)); 00182 // copy since fclose may delete the reference before flock is called 00183 const int file = fileno(itsFile); 00184 const int status = fclose(itsFile); 00185 /*const int lockstat =*/ flock(file,LOCK_UN); 00186 itsFile = 0; 00187 return status; 00188 } 00189 00190 return 0; 00191 } 00192 00193 void PngWriterHelper::writeGray(const Image<byte>& img) 00194 { 00195 // run-time mmx PNG optimizations (nate) 00196 #if defined(HAVE_PNG_ASM_FLAGS) 00197 const png_uint_32 flags = png_get_asm_flags(itsPngPtr); 00198 const png_uint_32 mask = 00199 png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); 00200 png_set_asm_flags(itsPngPtr, flags | mask); 00201 #endif 00202 00203 png_set_IHDR(itsPngPtr, itsInfoPtr, 00204 img.getWidth(), img.getHeight(), 00205 8, /* bits per pixel element */ 00206 PNG_COLOR_TYPE_GRAY, 00207 PNG_INTERLACE_NONE, 00208 PNG_COMPRESSION_TYPE_DEFAULT, 00209 PNG_FILTER_TYPE_DEFAULT); 00210 00211 png_write_info(itsPngPtr, itsInfoPtr); 00212 00213 const int height = img.getHeight(); 00214 00215 png_bytep row_pointers[height]; 00216 00217 const byte* p = img.getArrayPtr(); 00218 00219 for (int i = 0; i < height; ++i) 00220 { 00221 row_pointers[i] = 00222 static_cast<png_bytep> 00223 (static_cast<void*> 00224 (const_cast<byte*>(p + i*img.getWidth()))); 00225 } 00226 00227 png_write_image(itsPngPtr, &row_pointers[0]); 00228 png_write_end(itsPngPtr, itsInfoPtr); 00229 00230 const int status = this->close(); 00231 if (status != 0) 00232 PLFATAL("Error during fclose() while writing %s", 00233 itsFilename.c_str()); 00234 } 00235 00236 void PngWriterHelper::writeRGB(const Image<PixRGB<byte> >& img) 00237 { 00238 00239 // run-time mmx PNG optimizations (nate) 00240 #if defined(HAVE_PNG_ASM_FLAGS) 00241 const png_uint_32 flags = png_get_asm_flags(itsPngPtr); 00242 const png_uint_32 mask = 00243 png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE); 00244 png_set_asm_flags(itsPngPtr, flags | mask); 00245 #endif 00246 00247 png_set_IHDR(itsPngPtr, itsInfoPtr, 00248 img.getWidth(), img.getHeight(), 00249 8, /* bits per pixel element */ 00250 PNG_COLOR_TYPE_RGB, 00251 PNG_INTERLACE_NONE, 00252 PNG_COMPRESSION_TYPE_DEFAULT, 00253 PNG_FILTER_TYPE_DEFAULT); 00254 00255 png_write_info(itsPngPtr, itsInfoPtr); 00256 00257 const int height = img.getHeight(); 00258 00259 png_bytep row_pointers[height]; 00260 00261 const PixRGB<byte>* p = img.getArrayPtr(); 00262 00263 for (int i = 0; i < height; ++i) 00264 { 00265 row_pointers[i] = 00266 static_cast<png_bytep> 00267 (static_cast<void*> 00268 (const_cast<PixRGB<byte>*>(p + i*img.getWidth()))); 00269 } 00270 00271 png_write_image(itsPngPtr, &row_pointers[0]); 00272 png_write_end(itsPngPtr, itsInfoPtr); 00273 00274 const int status = this->close(); 00275 if (status != 0) 00276 PLFATAL("Error during fclose() while writing %s", 00277 itsFilename.c_str()); 00278 } 00279 00280 void PngWriterHelper::onError(const rutz::fstring& msg) 00281 { 00282 const int status = this->close(); 00283 if (status != 0) 00284 // NOTE: Don't use PLFATAL here since then we'd end up with 00285 // double-error-reporting. 00286 PLERROR("Error during fclose() while writing %s", 00287 itsFilename.c_str()); 00288 00289 LFATAL("%s", msg.c_str()); 00290 } 00291 00292 } // end anonymous namespace 00293 00294 PngWriter::PngWriter() {} 00295 00296 PngWriter::~PngWriter() {} 00297 00298 std::string PngWriter::writeFrame(const GenericFrame& image, 00299 const std::string& fname) 00300 { 00301 switch (image.nativeType()) 00302 { 00303 case GenericFrame::NONE: 00304 LFATAL("cannot write an empty image to '%s'", fname.c_str()); 00305 break; 00306 00307 case GenericFrame::RGB_U8: 00308 case GenericFrame::RGBD: 00309 PngWriter::writeRGB(image.asRgbU8(), fname); 00310 break; 00311 00312 case GenericFrame::RGB_U16: 00313 PngWriter::writeRGB(image.asRgbU16(), fname); 00314 break; 00315 00316 case GenericFrame::RGB_F32: 00317 PngWriter::writeRGB(image.asRgbU8(), fname); 00318 break; 00319 00320 case GenericFrame::GRAY_U8: 00321 PngWriter::writeGray(image.asGrayU8(), fname); 00322 break; 00323 00324 case GenericFrame::GRAY_U16: 00325 PngWriter::writeGray(image.asGrayU16(), fname); 00326 break; 00327 00328 case GenericFrame::GRAY_F32: 00329 if ((image.floatFlags() & FLOAT_NORM_PRESERVE) 00330 && !(image.floatFlags() & FLOAT_NORM_0_255)) 00331 { 00332 // the user wanted to preserve the full floating-point 00333 // precision without normalizing to 0..255; however for png 00334 // output we don't have any way to accomodate that request 00335 // so let's just go ahead and normalize to 0..255 so that 00336 // the output will be viewable: 00337 const GenericFrame image2(image.asGrayF32(), 00338 image.floatFlags() | FLOAT_NORM_0_255); 00339 PngWriter::writeGray(image2.asGrayU8(), fname); 00340 } 00341 else 00342 PngWriter::writeGray(image.asGrayU8(), fname); 00343 break; 00344 00345 case GenericFrame::VIDEO: 00346 PngWriter::writeRGB(image.asRgbU8(), fname); 00347 break; 00348 } 00349 00350 return fname; 00351 } 00352 00353 void PngWriter::writeRGB(const Image<PixRGB<byte> >& image, 00354 const std::string& fname) 00355 { 00356 PngWriterHelper writer(fname.c_str()); 00357 writer.writeRGB(image); 00358 } 00359 00360 void PngWriter::writeGray(const Image<byte>& image, 00361 const std::string& fname) 00362 { 00363 PngWriterHelper writer(fname.c_str()); 00364 writer.writeGray(image); 00365 } 00366 00367 #endif // INVT_HAVE_LIBPNG 00368 00369 // ###################################################################### 00370 /* So things look consistent in everyone's emacs... */ 00371 /* Local Variables: */ 00372 /* mode: c++ */ 00373 /* indent-tabs-mode: nil */ 00374 /* End: */