PngWriter.C

Go to the documentation of this file.
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: */
Generated on Sun May 8 08:41:16 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3