ImageWriter.cpp
Go to the documentation of this file.
00001 #include "puma2config.h"
00002 #include "ImageWriter.h"
00003 #include "PumaException.h"
00004 #include "PumaMessages.h"
00005 
00006 #include <cstdio>
00007 #include <cerrno>
00008 #include <cstring>
00009 
00010 using namespace std;
00011 using namespace puma2;
00012 
00013 #ifdef HAVE_IMAGEMAGICK
00014 #  include <Magick++/Image.h>
00015 static bool writeImageGrayMagick(const GrayLevelImage8 &img, string filename);
00016 static bool writeImageGrayMagick(const GrayLevelImage16 &img, string filename);
00017 static bool writeImageColorMagick(const ColorImageRGB8 &img, string filename);
00018 static bool writeImageColorMagick(const ColorImageRGBa8 &img, string filename);
00019 #endif
00020 
00021 // forward declarations
00022 static bool writeImageGrayBuiltinPNM(const GrayLevelImage8 &img, string &filename);
00023 static bool writeImageColorBuiltinPNM(const ColorImageRGB8 &img, string &filename);
00024 #define USE_BUILTIN_PNM_WRITER  1
00025 
00026 
00027 bool ImageWriter::writeImage(const GrayLevelImage8 &img, string filename)
00028 {
00029   bool success = false;
00030   
00031 #ifdef USE_BUILTIN_PNM_WRITER
00032   int slen = filename.length();
00033   if ((filename.compare(slen-4, 4, ".pbm") == 0)
00034    || (filename.compare(slen-4, 4, ".pgm") == 0)
00035    || (filename.compare(slen-4, 4, ".ppm") == 0)
00036    || (filename.compare(slen-4, 4, ".pnm") == 0)) {
00037     success = writeImageGrayBuiltinPNM(img, filename);
00038   }
00039 #endif
00040 
00041 #ifdef HAVE_IMAGEMAGICK
00042   if (! success) {
00043     success = writeImageGrayMagick(img, filename);
00044   }
00045 #endif
00046   if (! success) {
00047     string msg ("failed to write file.");
00048     throw PumaException(PumaException::intolerable, msg);
00049     success = false;
00050   }
00051   return success;
00052 }
00053 
00054 bool ImageWriter::writeImage(const GrayLevelImage16 &img, string filename)
00055 {
00056   bool success = false;
00057   
00058 #ifdef HAVE_IMAGEMAGICK
00059   if (! success) {
00060     success = writeImageGrayMagick(img, filename);
00061   }
00062 #endif
00063   if (! success) {
00064     string msg ("failed to write file.");
00065     throw PumaException(PumaException::intolerable, msg);
00066     success = false;
00067   }
00068   return success;
00069 }
00070 
00071 bool ImageWriter::writeImage(const ColorImageRGB8 &img, string filename)
00072 {
00073   bool success = false;
00074   
00075 #ifdef USE_BUILTIN_PNM_WRITER
00076   int slen = filename.length();
00077   if ((filename.compare(slen-4, 4, ".pbm") == 0)
00078    || (filename.compare(slen-4, 4, ".pgm") == 0)
00079    || (filename.compare(slen-4, 4, ".ppm") == 0)
00080    || (filename.compare(slen-4, 4, ".pnm") == 0)) {
00081     success = writeImageColorBuiltinPNM(img, filename);
00082   }
00083 #endif
00084 
00085 #ifdef HAVE_IMAGEMAGICK
00086   if (! success) {
00087     success = writeImageColorMagick(img, filename);
00088   }
00089 #endif
00090   if (! success) {
00091     string msg ("failed to write file.");
00092     throw PumaException(PumaException::intolerable, msg);
00093     success = false;
00094   }
00095   return success;
00096 }
00097 
00098 bool ImageWriter::writeImage(const ColorImageRGBa8 &img, string filename)
00099 {
00100   bool success = false;
00101   
00102 #ifdef USE_BUILTIN_PNM_WRITER_NOAPLHAINPNM
00103   int slen = filename.length();
00104   if ((filename.compare(slen-4, 4, ".pbm") == 0)
00105    || (filename.compare(slen-4, 4, ".pgm") == 0)
00106    || (filename.compare(slen-4, 4, ".ppm") == 0)
00107    || (filename.compare(slen-4, 4, ".pnm") == 0)) {
00108     success = writeImageColorBuiltinPNM(img, filename);
00109   }
00110 #endif
00111 
00112 #ifdef HAVE_IMAGEMAGICK
00113   if (! success) {
00114     success = writeImageColorMagick(img, filename);
00115   }
00116 #endif
00117   if (! success) {
00118     string msg ("failed to write file.");
00119     throw PumaException(PumaException::intolerable, msg);
00120     success = false;
00121   }
00122   return success;
00123 }
00124 
00125 // --------------------------------------------------------
00126 // writer for PNM files (no libraries, builtin code)
00127 // --------------------------------------------------------
00128 
00129 static bool writeImageGrayBuiltinPNM(const GrayLevelImage8 &img, string &filename)
00130 {   
00131   FILE *filePointer = fopen (filename.c_str(), "wb"); // "b" for binary mode on non-Unix
00132   if (filePointer == NULL) {
00133     string msg ("cannot write to file: ");
00134     msg += filename;
00135     msg += strerror(errno);
00136     throw PumaException(PumaException::intolerable, msg);
00137     return false;
00138   }
00139   
00140   bool success = true;
00141   int wid = img.getWidth();
00142   int hig = img.getHeight();
00143   char outputMode = filename[filename.length()-2];
00144   int valMax = int(img.getValueRangeMaximum());
00145   int depth = 255;
00146   int denominator = 1;
00147   int bytesPerRow, bytesWritten = 0;
00148   if (valMax < 255) {
00149     depth = valMax;
00150     assert (valMax > 0);        // this could fail for float or double images
00151   }
00152   else {
00153     denominator = valMax / 255;
00154     assert (valMax == 255); // > 255 can't happen for type 'byte' in GrayLevelImage8
00155   }
00156   switch (outputMode) {
00157     case 'g':
00158     case 'G':
00159     case 'n':
00160     case 'N':
00161       {
00162         fprintf (filePointer, "P5\n%d %d\n%d\n", wid, hig, depth);
00163         // data must be scaled down here for valMax > 255 (that is for 16bit etc.) !!
00164         // that is all values devided by denominator !
00165         const GrayLevelImage8::ElementType **ptr = img.unsafeRowPointerArray();
00166         for (int y = 0; y < hig; y++) {
00167           bytesWritten += fwrite (ptr[y], 1, wid, filePointer);
00168         }
00169       }
00170       break;
00171     
00172     case 'p':
00173     case 'P':
00174       {
00175         fprintf (filePointer, "P6\n%d %d\n%d\n", wid, hig, depth);
00176         const GrayLevelImage8::ElementType **ptr = img.unsafeRowPointerArray();
00177         for (int y = 0; y < hig; y++) {
00178           const GrayLevelImage8::ElementType *linePtr = ptr[y];
00179           for (int x = 0; x < wid; x++) {
00180             // data must be scaled down here for valMax > 255 !!
00181             byte val = *linePtr++  / denominator;
00182             putc(val, filePointer);
00183             putc(val, filePointer);
00184             putc(val, filePointer);
00185           }
00186           bytesWritten += 3 * wid;
00187         }
00188       }
00189       break;
00190     
00191     case 'b':
00192     case 'B':
00193       {
00194         fprintf (filePointer, "P4\n%d %d\n", wid, hig);
00195         denominator = (valMax + 1) / 2; // so "/ denom" yields [0 | 1], threshold 50%
00196         bytesPerRow = (wid + 7) / 8;
00197         byte bitRow[bytesPerRow+10];
00198         const GrayLevelImage8::ElementType **ptr = img.unsafeRowPointerArray();
00199         for (int y = 0; y < hig; y++) {
00200           const GrayLevelImage8::ElementType *linePtr = ptr[y];
00201           byte *bitPtr = bitRow;
00202           byte curByte = 0;
00203           int x;
00204           for (x = 0; x < wid; x++) {
00205             // data must be scaled down here for valMax > 1 !!
00206             curByte <<= 1;
00207             int val = (unsigned(*linePtr++)  / denominator);
00208             curByte |= (val > 0) ? 0 : 1;
00209             if ((x & 0x07) == 7) {
00210               *bitPtr++ = curByte;
00211               curByte = 0;
00212             }
00213           }
00214           if ((x & 0x07) != 0) { // ship remaining bits in curByte
00215             curByte <<= (8 - (x & 0x07));
00216             *bitPtr++ = curByte;
00217           }
00218           bytesWritten += fwrite (bitRow, 1, bytesPerRow, filePointer);
00219         }
00220       }
00221       break;
00222       
00223     default:
00224       // TODO PumaMessage (msgImportant, "file is no recognized PNM file");
00225       success = false;
00226       break;
00227   }
00228   fclose (filePointer);
00229   return success;
00230 }
00231 
00232 static bool writeImageColorBuiltinPNM(const ColorImageRGB8 &img, string &filename)
00233 {   
00234   FILE *filePointer = fopen (filename.c_str(), "wb"); // "b" for binary mode on non-Unix
00235   if (filePointer == NULL) {
00236     string msg ("cannot write to file: ");
00237     msg += filename;
00238     msg += strerror(errno);
00239     throw PumaException(PumaException::intolerable, msg);
00240     return false;
00241   }
00242   
00243   bool success = true;
00244   int wid = img.getWidth();
00245   int hig = img.getHeight();
00246   char outputMode = filename[filename.length()-2];
00247   int valMax = int(img.getValueRangeMaximum());
00248   int depth = 255;
00249   int denominator = 1;
00250   int bytesWritten = 0;
00251   if (valMax < 255) {
00252     depth = valMax;
00253     assert (valMax > 0);        // this could fail for float or double images
00254   }
00255   else {
00256     denominator = valMax / 255;
00257     assert (valMax == 255); // > 255 can't happen for type 'byte' in GrayLevelImage8
00258   }
00259   switch (outputMode) {
00260     case 'g':
00261     case 'G':
00262       {
00263         denominator *= 1000;    // 299 + 587 + 114 == 1000
00264         fprintf (filePointer, "P5\n%d %d\n%d\n", wid, hig, depth);
00265         // data must be scaled down here for valMax > 255 (that is for 16bit etc.) !!
00266         // that is all values devided by denominator !
00267         const ColorImageRGB8::PixelType **ptr = img.unsafeRowPointerArray();
00268         for (int y = 0; y < hig; y++) {
00269           const ColorImageRGB8::ElementType *linePtr = (ColorImageRGB8::ElementType*)(ptr[y]);
00270           for (int x = 0; x < wid; x++) {
00271             int val = 299 * *linePtr++;
00272             val += 587 * *linePtr++;
00273             val += 114 * *linePtr++;
00274             putc(val / denominator, filePointer);
00275           }
00276           bytesWritten += wid;
00277         }
00278       }
00279       break;
00280     
00281     case 'p':
00282     case 'P':
00283     case 'n':
00284     case 'N':
00285       {
00286         fprintf (filePointer, "P6\n%d %d\n%d\n", wid, hig, depth);
00287         const ColorImageRGB8::PixelType **ptr = img.unsafeRowPointerArray();
00288         for (int y = 0; y < hig; y++) {
00289           // data should be scaled down here for valMax > 255 (that is for 16bit etc.) !!
00290           // that is all values devided by denominator !
00291           bytesWritten += fwrite (ptr[y], 1, wid * 3, filePointer);
00292         }
00293       }
00294       break;
00295     
00296     case 'b':
00297     case 'B':
00298       {
00299         fprintf (filePointer, "P4\n%d %d\n", wid, hig);
00300         denominator = (valMax + 1) / 2; // so "/ denom" yields [0 | 1], threshold 50%
00301         denominator *= 1000;    // 299 + 587 + 114 == 1000
00302         const ColorImageRGB8::PixelType **ptr = img.unsafeRowPointerArray();
00303         for (int y = 0; y < hig; y++) {
00304           const ColorImageRGB8::ElementType *linePtr = (ColorImageRGB8::ElementType*)(ptr[y]);
00305           int bytesPerRow = (wid + 7) / 8;
00306           byte bitRow[bytesPerRow];
00307           byte *bitPtr = bitRow;
00308           byte curByte = 0;
00309           int x;
00310           for (x = 0; x < wid; x++) {
00311             // data must be scaled down here for valMax > 1 !!
00312             curByte <<= 1;
00313             int val = 299 * *linePtr++;
00314             val += 587 * *linePtr++;
00315             val += 114 * *linePtr++;
00316             curByte |= ((val  / denominator) > 0) ? 0 : 1;
00317             if ((x & 0x07) == 7) {
00318               *bitPtr++ = curByte;
00319               curByte = 0;
00320             }
00321           }
00322           if ((x & 0x07) != 0) { // ship remaining bits in curByte
00323             curByte <<= (8 - (x & 0x07));
00324             *bitPtr++ = curByte;
00325           }
00326           bytesWritten += fwrite (bitRow, 1, bytesPerRow, filePointer);
00327         }
00328       }
00329       break;
00330     
00331     default:
00332       // TODO PumaMessage (msgImportant, "file is no recognized PNM file");
00333       success = false;
00334       break;
00335   }
00336   fclose (filePointer);
00337   return success;
00338 }
00339 
00340 // --------------------------------------------------------
00341 // writer for other image files (using libMagick++ from www.imagemagick.org)
00342 // --------------------------------------------------------
00343 
00344 #ifdef HAVE_IMAGEMAGICK
00345 
00346 static bool writeImageGrayMagick(const GrayLevelImage8 &img, string filename)
00347 {
00348   Magick::Image *magickImage;
00349   const GrayLevelImage8::PixelType *byteDataPointer = img.unsafeRowPointerArray()[0];
00350   byte *copiedData = NULL;
00351 
00352   if (img.isSubImage()) {
00353     // very ugly: no way to tell ImageMagick about subimages.
00354         // must make a copy of it so ImageMagick can work on it. :-(
00355         
00356         const int lineWid = img.getWidth() * sizeof(GrayLevelImage8::PixelType);
00357         copiedData = new byte[img.getHeight() * lineWid];
00358         for (int y = 0; y < img.getHeight(); y ++) {
00359           memcpy (&(copiedData[y*lineWid]), img.unsafeRowPointerArray()[y], lineWid);
00360         }
00361         byteDataPointer = (GrayLevelImage8::PixelType*)copiedData;
00362   }
00363   try {
00364     magickImage = new Magick::Image(
00365         img.getWidth(),
00366         img.getHeight(),
00367         "I",
00368         Magick::CharPixel,
00369         byteDataPointer         //      &(img.sample(0,0,0))
00370         );
00371   }
00372 //   catch( Magick::Warning &warning_ ) {
00373 //     cout << "Caught Image Magic Warning: " << warning_.what() << endl;
00374 //   }
00375   catch( Magick::Exception &error_ ) {
00376     string msg ("Caught Image Magic Exception: ");
00377     msg += error_.what();
00378     throw PumaException(PumaException::intolerable, msg);
00379   }
00380 
00381   // chose "RAW" for PNM images. Magick++ does this if compressionType
00382   // is set to anything else but "NoCompression". 
00383   int slen = filename.length();
00384   if ((filename.compare(slen-4, 4, ".pbm") == 0)
00385    || (filename.compare(slen-4, 4, ".pgm") == 0)
00386    || (filename.compare(slen-4, 4, ".ppm") == 0)
00387    || (filename.compare(slen-4, 4, ".pnm") == 0))
00388     magickImage->compressType(Magick::ZipCompression);
00389 
00390   int bitDepth = sizeof(img[0][0]) * 8;
00391   if (0 /* depth() really used by ImageMagick? I doubt it */ ) {
00392     unsigned int valMax = int(img.getValueRangeMaximum());
00393     while (valMax > 0) {
00394       bitDepth++;
00395       valMax >>= 1;
00396     }
00397   }
00398   magickImage->compressType(Magick::ZipCompression); // try allways to compress
00399   magickImage->depth(bitDepth);
00400   magickImage->write(filename);
00401   if (copiedData)
00402     delete [] copiedData;
00403   delete magickImage;
00404   return true;
00405 }
00406 
00407 static bool writeImageGrayMagick(const GrayLevelImage16 &img, string filename)
00408 {
00409   Magick::Image *magickImage;
00410   const GrayLevelImage16::PixelType *byteDataPointer = img.unsafeRowPointerArray()[0];
00411   byte *copiedData = NULL;
00412 
00413   if (img.isSubImage()) {
00414     // very ugly: no way to tell ImageMagick about subimages.
00415         // must make a copy of it so ImageMagick can work on it. :-(
00416         
00417         const int lineWid = img.getWidth() * sizeof(GrayLevelImage16::PixelType);
00418         copiedData = new byte[img.getHeight() * lineWid];
00419         for (int y = 0; y < img.getHeight(); y ++) {
00420           memcpy (&(copiedData[y*lineWid]), img.unsafeRowPointerArray()[y], lineWid);
00421         }
00422         byteDataPointer = (GrayLevelImage16::PixelType*)copiedData;
00423   }
00424 
00425   try {
00426     magickImage = new Magick::Image(
00427         img.getWidth(),
00428         img.getHeight(),
00429         "I",
00430         Magick::ShortPixel,
00431         byteDataPointer //      &(img.sample(0,0,0))
00432         );
00433   }
00434 //   catch( Magick::Warning &warning_ ) {
00435 //     cout << "Caught Image Magic Warning: " << warning_.what() << endl;
00436 //   }
00437   catch( Magick::Exception &error_ ) {
00438     string msg ("Caught Image Magic Exception: ");
00439     msg += error_.what();
00440     throw PumaException(PumaException::intolerable, msg);
00441   }
00442 
00443   // chose "RAW" for PNM images. Magick++ does this if compressionType
00444   // is set to anything else but "NoCompression". 
00445   int slen = filename.length();
00446   if ((filename.compare(slen-4, 4, ".pbm") == 0)
00447    || (filename.compare(slen-4, 4, ".pgm") == 0)
00448    || (filename.compare(slen-4, 4, ".ppm") == 0)
00449    || (filename.compare(slen-4, 4, ".pnm") == 0))
00450     magickImage->compressType(Magick::ZipCompression);
00451 
00452   int bitDepth = sizeof(img[0][0]) * 8;
00453   if (0 /* depth() really used by ImageMagick? I doubt it */ ) {
00454     unsigned int valMax = int(img.getValueRangeMaximum());
00455     while (valMax > 0) {
00456       bitDepth++;
00457       valMax >>= 1;
00458     }
00459   }
00460   magickImage->compressType(Magick::ZipCompression); // try allways to compress
00461   magickImage->depth(bitDepth);
00462   magickImage->write(filename);
00463   if (copiedData)
00464     delete [] copiedData;
00465   delete magickImage;
00466   return true;
00467 }
00468 
00469 static bool writeImageColorMagick(const ColorImageRGB8 &img, string filename)
00470 {
00471   Magick::Image *magickImage;
00472   const ColorImageRGB8::PixelType *byteDataPointer = img.unsafeRowPointerArray()[0];
00473   byte *copiedData = NULL;
00474 
00475   if (img.isSubImage()) {
00476     // very ugly: no way to tell ImageMagick about subimages.
00477         // must make a copy of it so ImageMagick can work on it. :-(
00478         
00479         const int lineWid = img.getWidth() * sizeof(ColorImageRGB8::PixelType);
00480         copiedData = new byte[img.getHeight() * lineWid];
00481         for (int y = 0; y < img.getHeight(); y ++) {
00482           memcpy (&(copiedData[y*lineWid]), img.unsafeRowPointerArray()[y], lineWid);
00483         }
00484         byteDataPointer = (ColorImageRGB8::PixelType*)copiedData;
00485   }
00486 
00487   try {
00488     magickImage = new Magick::Image(
00489         img.getWidth(),
00490         img.getHeight(),
00491         "RGB",
00492         Magick::CharPixel,
00493         byteDataPointer //      &(img.sample(0,0,0))
00494       );
00495   }
00496 //   catch( Magick::Warning &warning_ ) {
00497 //     cout << "Caught Image Magic Warning: " << warning_.what() << endl;
00498 //   }
00499   catch( Magick::Exception &error_ ) {
00500     string msg ("Caught Image Magic Exception: ");
00501     msg += error_.what();
00502     throw PumaException(PumaException::intolerable, msg);
00503   }
00504 
00505   // chose "RAW" for PNM images. Magick++ does this if compressionType
00506   // is set to anything else but "NoCompression". 
00507   int slen = filename.length();
00508   if ((filename.compare(slen-4, 4, ".pbm") == 0)
00509    || (filename.compare(slen-4, 4, ".pgm") == 0)
00510    || (filename.compare(slen-4, 4, ".ppm") == 0)
00511    || (filename.compare(slen-4, 4, ".pnm") == 0))
00512     magickImage->compressType(Magick::ZipCompression);
00513 
00514   magickImage->compressType(Magick::ZipCompression); // try allways to compress
00515   magickImage->depth(8);
00516   magickImage->write(filename);
00517   if (copiedData)
00518     delete [] copiedData;
00519   delete magickImage;
00520   return true;
00521 }
00522 
00523 static bool writeImageColorMagick(const ColorImageRGBa8 &img, string filename)
00524 {
00525   Magick::Image *magickImage;
00526   const ColorImageRGBa8::PixelType *byteDataPointer = img.unsafeRowPointerArray()[0];
00527   byte *copiedData = NULL;
00528 
00529   if (img.isSubImage()) {
00530     // very ugly: no way to tell ImageMagick about subimages.
00531         // must make a copy of it so ImageMagick can work on it. :-(
00532         
00533         const int lineWid = img.getWidth() * sizeof(ColorImageRGBa8::PixelType);
00534         copiedData = new byte[img.getHeight() * lineWid];
00535         for (int y = 0; y < img.getHeight(); y ++) {
00536           memcpy (&(copiedData[y*lineWid]), img.unsafeRowPointerArray()[y], lineWid);
00537         }
00538         byteDataPointer = (ColorImageRGBa8::PixelType*)copiedData;
00539   }
00540 
00541   try {
00542     magickImage = new Magick::Image(
00543         img.getWidth(),
00544         img.getHeight(),
00545         "RGBA",
00546         Magick::CharPixel,
00547         byteDataPointer //      &(img.sample(0,0,0))
00548       );
00549   }
00550 //   catch( Magick::Warning &warning_ ) {
00551 //     cout << "Caught Image Magic Warning: " << warning_.what() << endl;
00552 //   }
00553   catch( Magick::Exception &error_ ) {
00554     string msg ("Caught Image Magic Exception: ");
00555     msg += error_.what();
00556     throw PumaException(PumaException::intolerable, msg);
00557   }
00558 
00559   // chose "RAW" for PNM images. Magick++ does this if compressionType
00560   // is set to anything else but "NoCompression". 
00561   int slen = filename.length();
00562   if ((filename.compare(slen-4, 4, ".pbm") == 0)
00563    || (filename.compare(slen-4, 4, ".pgm") == 0)
00564    || (filename.compare(slen-4, 4, ".ppm") == 0)
00565    || (filename.compare(slen-4, 4, ".pnm") == 0))
00566     magickImage->compressType(Magick::ZipCompression);
00567 
00568   magickImage->compressType(Magick::ZipCompression); // try allways to compress
00569   magickImage->depth(8);
00570   magickImage->write(filename);
00571   if (copiedData)
00572     delete [] copiedData;
00573   delete magickImage;
00574   return true;
00575 }
00576 #endif  /* HAVE_IMAGEMAGICK */


obj_rec_gui
Author(s): AGAS/agas@uni-koblenz.de
autogenerated on Mon Oct 6 2014 02:53:43