libidx
/home/rex/ebltrunk/core/libidx/include/imageIO.hpp
00001 /***************************************************************************
00002  *   Copyright (C) 2008 by Yann LeCun and Pierre Sermanet *
00003  *   yann@cs.nyu.edu, pierre.sermanet@gmail.com *
00004  *   All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are met:
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Redistribution under a license not approved by the Open Source
00014  *       Initiative (http://www.opensource.org) must display the
00015  *       following acknowledgement in all advertising material:
00016  *        This product includes software developed at the Courant
00017  *        Institute of Mathematical Sciences (http://cims.nyu.edu).
00018  *     * The names of the authors may not be used to endorse or promote products
00019  *       derived from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
00022  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00023  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00024  * DISCLAIMED. IN NO EVENT SHALL ThE AUTHORS BE LIABLE FOR ANY
00025  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00026  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00028  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00029  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00030  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031  ***************************************************************************/
00032 
00033 #ifndef IMAGEIO_HPP_
00034 #define IMAGEIO_HPP_
00035 
00036 #include <math.h>
00037 #include <stdlib.h>
00038 
00039 #ifndef __NOSTL__
00040 #include <fstream>
00041 #include <cstdlib>
00042 #include <cstdio>
00043 #endif
00044 
00045 #ifndef __WINDOWS__
00046 #include <unistd.h>
00047 #endif
00048 
00049 #ifdef __MAGICKPP__
00050 #include <Magick++.h>
00051 #endif
00052 
00053 #include <errno.h>
00054 #include "config.h"
00055 #include "idxIO.h"
00056 #include "stl.h"
00057 
00058 using namespace std;
00059 
00060 namespace ebl {
00061 
00063   // I/O: helper functions
00064 
00065   template<class T>
00066   idx<T> image_read(const char *fname, idx<T> *out_, int attempts) {
00067     idx<ubyte> tmp;
00068 #ifdef __MAGICKPP__
00069     // we are under any platform, convert is not available but Magick++ is
00070     try {
00071       Magick::Image im(fname);
00072       tmp = idx<ubyte>(im.rows(), im.columns(), 3);
00073       im.write(0, 0, im.columns(), im.rows(), "RGB", Magick::CharPixel,
00074                tmp.idx_ptr());
00075       // TODO: handle grayscale?
00076     } catch(Magick::WarningCoder &warning) {
00077       // Process coder warning while loading file (e.g. TIFF warning)
00078       // Maybe the user will be interested in these warnings (or not).
00079       // If a warning is produced while loading an image, the image
00080       // can normally still be used (but not if the warning was about
00081       // something important!)
00082       eblthrow("Coder Warning: " << warning.what());
00083     } catch(Magick::Warning &warning) {
00084       // Handle any other Magick++ warning.
00085       eblthrow("Warning: " << warning.what());
00086     } catch(Magick::ErrorBlob &error) { 
00087       // Process Magick++ file open error
00088       eblthrow("Error: " << error.what());
00089     } catch(Magick::ErrorOption &error) { 
00090       // Process Magick++ file open error
00091       eblthrow("Error: " << error.what());
00092     }
00093 #else
00094 #if (defined(__IMAGEMAGICK__) && !defined(__NOIMAGEMAGICK__))
00095     // we are under linux or mac and convert is available
00096     string cmd;
00097     cmd << IMAGEMAGICK_CONVERT << " -compress lossless -depth 8 \"" 
00098         << fname << "\" PPM:-";
00099 #ifdef __WINDOWS__
00100     FILE* fp = POPEN(cmd.c_str(), "rb");
00101 #else
00102     FILE* fp = POPEN(cmd.c_str(), "r");
00103 #endif
00104     if (!fp) {
00105       std::string err;
00106       err << "conversion of image " << fname << " failed (errno: " 
00107           << errno << ", " << strerror(errno) << ")";
00108       if (attempts > 0) {
00109         cerr << "Warning: " << err << endl;
00110         cerr << "trying again... (remaining attempts: " << attempts << ")" << endl;
00111         return image_read(fname, out_, attempts - 1);
00112       } else
00113         eblthrow(err);
00114     }
00115     try {
00116       // read pnm image
00117       tmp = pnm_read(fp);
00118     } catch (eblexception &err) {
00119       if (PCLOSE(fp) != 0) {
00120         cerr << "Warning: pclose failed (errno: " << errno << ")" << endl;
00121       } 
00122       if (attempts > 0) {
00123         cerr << "Warning: " << err << endl;
00124         cerr << "trying again... (remaining attempts: " << attempts << ")" << endl;
00125         return image_read(fname, out_, attempts - 1);
00126       } else
00127         eblthrow(err);
00128     }
00129     if (PCLOSE(fp) != 0) {
00130       cerr << "Warning: pclose failed (errno: " << errno << ")" << endl;
00131     }
00132 #else
00133     // nor Magick++ nor convert are available, error
00134     eblerror("Nor ImageMagick's convert nor Magick++ are available, "
00135              << "please install");
00136 #endif /* __IMAGEMAGICK__ */
00137 #endif /* __MAGICK++__ */
00138     
00139     idxdim dims(tmp);
00140     idx<T> out;
00141     idx<T> *pout = &out;
00142     // allocate if not allocated
00143     if (!out_)
00144       out = idx<T>(dims);
00145     else
00146       pout = out_;
00147     // resize if necessary
00148     if (pout->get_idxdim() != dims)
00149       pout->resize(dims);
00150     // copy/cast
00151     idx_copy(tmp, *pout);
00152     return *pout;
00153   }
00154 
00156   // I/O: loading
00157 
00158   template<class T>
00159   void load_image(const char *fname, idx<T> &out) {
00160     // first try if the image is a mat file
00161     try {
00162       if (out.order() > 3 || out.order() < 2)
00163         eblerror("image has to be 2D or 3D");
00164       load_matrix<T>(out, fname);
00165       // channels are likely in dim 0 if size 1 or 3
00166       if (((out.dim(0) == 1) || (out.dim(0) == 3)) && (out.order() == 3))
00167         out = out.shift_dim(0, 2);
00168       return ;
00169     } catch(eblexception &e) {
00170       e = e;
00171       // not a mat file, try regular image
00172     }
00173     image_read(fname, &out);
00174   }
00175 
00176   template<class T>
00177   void load_image(const string &fname, idx<T> &out) {
00178     return load_image(fname.c_str(), out);
00179   }
00180   
00181   template<class T>
00182   idx<T> load_image(const char *fname) {
00183     // first try if the image is a mat file
00184     try {
00185       idx<T> m = load_matrix<T>(fname);
00186       if (m.order() > 3 || m.order() < 2)
00187         eblerror("image has to be 2D or 3D");
00188       // channels are likely in dim 0 if size 1 or 3
00189       if (((m.dim(0) == 1) || (m.dim(0) == 3)) && (m.order() == 3))
00190         m = m.shift_dim(0, 2);
00191       return m;
00192     } catch(eblexception &e) {
00193       e = e; // not a mat file, try regular image
00194     }
00195     return image_read<T>(fname);
00196   }
00197 
00198   template<class T>
00199   idx<T> load_image(const string &fname) {
00200     return load_image<T>(fname.c_str());
00201   }
00202 
00204   // I/O: saving
00205 
00206   template<class T>
00207   bool save_image_ppm(const string &fname, idx<T> &in) {
00208     return save_image_ppm(fname.c_str(), in);
00209   }
00210 
00211   template<class T>
00212   bool save_image_ppm(const char *fname, idx<T> &in) {
00213     // check order
00214     // TODO: support grayscale
00215     if (in.order() != 3) {
00216       cerr << "error: image order (" << in.order() << " not supported." << endl;
00217       return false;
00218     }
00219     // save as ppm
00220     FILE *fp = fopen(fname, "wb");
00221     if (!fp) {
00222       cerr << "error: failed to open file " << fname << endl;
00223       return false;
00224     }
00225     save_image_ppm(fp, in);
00226     fclose(fp);
00227     return true;
00228   }
00229 
00230   template<class T>
00231   bool save_image_ppm(FILE *fp, idx<T> &in) {
00232     // check order
00233     // TODO: support grayscale
00234     if (in.order() != 3) {
00235       cerr << "error: image order (" << in.order() << " not supported." << endl;
00236       return false;
00237     }
00238     if (!fp) {
00239       cerr << "error: NULL fp " << endl;
00240       return false;
00241     }
00242     fprintf(fp,"P6 %d %d 255\n", (int) in.dim(1), (int) in.dim(0));
00243     if (in.dim(2) == 3) {
00244       idx_bloop1(inn, in, T) {
00245         idx_bloop1(innn, inn, T) {
00246           fputc((ubyte) innn.get(0), fp);
00247           fputc((ubyte) innn.get(1), fp);
00248           fputc((ubyte) innn.get(2), fp);
00249         }
00250       }
00251     } else if (in.dim(2) == 1) {
00252       idx_bloop1(inn, in, T) {
00253         idx_bloop1(innn, inn, T) {
00254           fputc((ubyte) innn.get(0), fp);
00255           fputc((ubyte) innn.get(0), fp);
00256           fputc((ubyte) innn.get(0), fp);
00257         }
00258       }
00259     } else
00260       eblerror("Error saving image " << in <<
00261                ", pixel dimension not supported");
00262     fflush(fp);
00263     return true;
00264   }
00265 
00266   template<class T>
00267   bool save_image_jpg(const string &fname, idx<T> &in) {
00268     return save_image(fname, in, "JPG");
00269   }
00270 
00271   template<class T>
00272   bool save_image(const string &fname, idx<T> &in, const char *format) {
00273     if (!strcmp(format, "mat")) { // save as idx
00274       return save_matrix(in, fname);
00275     }
00276 #ifdef __MAGICKPP__
00277     // we are under any platform, convert is not available but Magick++ is
00278     try {
00279       Magick::Image im;
00280       idx<T> tmp = in;//.shift_dim(0, 1);
00281       idx<ubyte> tmp2(tmp.get_idxdim());
00282       idx_copy(tmp, tmp2);
00283       switch (tmp2.dim(2)) {
00284       case 1: // grayscale
00285         im.read(tmp2.dim(1), tmp2.dim(0), "I", Magick::CharPixel, tmp2.idx_ptr());
00286         im.type(Magick::GrayscaleType);
00287         break ;
00288       case 3: // RGB
00289         im.read(tmp2.dim(1), tmp2.dim(0), "RGB", Magick::CharPixel, tmp2.idx_ptr());
00290         im.type(Magick::TrueColorType);
00291         break ;
00292       default: // unsupported
00293         eblerror("unsupported channels size for saving: " << tmp2);
00294       }
00295       im.write(fname);
00296       // TODO: handle grayscale?
00297     } catch(Magick::WarningCoder &warning) {
00298       // Process coder warning while loading file (e.g. TIFF warning)
00299       // Maybe the user will be interested in these warnings (or not).
00300       // If a warning is produced while loading an image, the image
00301       // can normally still be used (but not if the warning was about
00302       // something important!)
00303       eblthrow("Coder Warning: " << warning.what());
00304     } catch(Magick::Warning &warning) {
00305       // Handle any other Magick++ warning.
00306       eblthrow("Warning: " << warning.what());
00307     } catch(Magick::ErrorBlob &error) { 
00308       // Process Magick++ file open error
00309       eblthrow("Error: " << error.what());
00310     } catch(Magick::ErrorOption &error) { 
00311       // Process Magick++ file open error
00312       eblthrow("Error: " << error.what());
00313     }
00314 #else
00315 #if (defined(__IMAGEMAGICK__) && !defined(__NOIMAGEMAGICK__))
00316     // we are under linux or mac and convert is available
00317     string cmd;
00318     cmd << IMAGEMAGICK_CONVERT << " PPM:- " << format << ":\"" << fname << "\"";
00319 #ifdef __WINDOWS__
00320     FILE* fp = POPEN(cmd.c_str(), "wb");
00321 #else
00322     FILE* fp = POPEN(cmd.c_str(), "w");
00323 #endif
00324     if (!fp) {
00325       cerr << "conversion of image " << fname << " failed (errno: " 
00326            << errno << ", " << strerror(errno) << ")";
00327       return false;
00328     }
00329     try {
00330       // read pnm image
00331       save_image_ppm(fp, in);
00332     } catch (eblexception &err) {
00333       if (PCLOSE(fp) != 0) {
00334         cerr << "Warning: pclose failed (errno: " << errno << ")" << endl;
00335       }
00336       return false;
00337     }
00338     if (PCLOSE(fp) != 0) {
00339       cerr << "Warning: pclose failed (errno: " << errno << ")" << endl;
00340       return false;
00341     }
00342 #else
00343     // nor Magick++ nor convert are available, error
00344     eblerror("Nor ImageMagick's convert nor Magick++ are available, "
00345              << "please install");
00346 #endif /* __IMAGEMAGICK__ */
00347 #endif /* __MAGICK++__ */
00348     return true;
00349   }
00350 
00351 } // end namespace ebl
00352 
00353 #endif /* IMAGE_HPP_ */