00001 /*!@file Raster/JpegParser.C Parser for jpeg image files */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the 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 at usc dot edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Raster/JpegParser.C $ 00035 // $Id: JpegParser.C 10439 2008-11-17 06:31:45Z itti $ 00036 // 00037 00038 #ifndef RASTER_JPEGPARSER_C_DEFINED 00039 #define RASTER_JPEGPARSER_C_DEFINED 00040 00041 #include "Raster/JpegParser.H" 00042 00043 #include "Raster/GenericFrame.H" 00044 #include "rutz/error.h" 00045 #include "rutz/sfmt.h" 00046 #include "rutz/trace.h" 00047 00048 #include <csetjmp> 00049 #include <cstdio> 00050 00051 #ifdef INVT_HAVE_LIBJPEG 00052 00053 // WARNING: jpeglib.h is braindead in a couple ways -- (1) it doesn't 00054 // have the extern "C" to be c++-friendly, like every other modern C 00055 // header does, so we have to provide the extern "C" ourselves, and 00056 // (2) it's not self-contained -- it will bomb out if we don't 00057 // explicitly #include <stdio.h> and <stdlib.h> before jpeglib.h; (3) 00058 // on darwin it defines HAVE_STDLIB_H which conflicts with our 00059 // definition: 00060 #include <stdlib.h> 00061 #include <stdio.h> 00062 #undef HAVE_STDLIB_H 00063 extern "C" 00064 { 00065 #include <jpeglib.h> 00066 } 00067 00068 namespace 00069 { 00070 struct jpeg_aux 00071 { 00072 FILE* infile; 00073 jmp_buf* jmp_state; 00074 }; 00075 00076 void cleanup(jpeg_decompress_struct* cinfo) 00077 { 00078 jpeg_aux* aux = static_cast<jpeg_aux*>(cinfo->client_data); 00079 if (aux->infile != 0) 00080 fclose(aux->infile); 00081 jpeg_destroy_decompress(cinfo); 00082 } 00083 00084 void jpeg_error_exit(j_common_ptr cinfo) 00085 { 00086 // Since we longjmp out of here, DON'T use any C++ objects that 00087 // need to have destructors run! 00088 00089 cinfo->err->output_message(cinfo); 00090 00091 jpeg_aux* aux = static_cast<jpeg_aux*>(cinfo->client_data); 00092 longjmp(*(aux->jmp_state), 1); 00093 } 00094 } 00095 00096 #define SETJMP_TRY(statement) \ 00097 do { \ 00098 if (setjmp(state) == 0) \ 00099 { \ 00100 statement; \ 00101 } \ 00102 else \ 00103 { \ 00104 LFATAL("%s failed for file %s", #statement, filename); \ 00105 } \ 00106 } while (0) 00107 00108 #endif 00109 00110 struct JpegParser::Impl 00111 { 00112 Impl(const char* fname); 00113 00114 Image<PixRGB<byte> > rgb; 00115 Image<byte> gray; 00116 }; 00117 00118 // ###################################################################### 00119 00120 JpegParser::Impl::Impl(const char* filename) 00121 { 00122 #ifndef INVT_HAVE_LIBJPEG 00123 LFATAL("jpeg support is disabled since libjpeg was not found at configure time"); 00124 #else 00125 GVX_TRACE(__PRETTY_FUNCTION__); 00126 00127 if (BITS_IN_JSAMPLE != 8) 00128 { 00129 throw rutz::error("jpeg library must be built for 8 bits-per-sample", 00130 SRC_POS); 00131 } 00132 00133 jmp_buf state; 00134 00135 // 1. Allocate and initialize the JPEG decompression object 00136 jpeg_decompress_struct cinfo; 00137 jpeg_error_mgr jerr; 00138 00139 jpeg_aux aux; 00140 aux.infile = 0; 00141 aux.jmp_state = &state; 00142 cinfo.client_data = static_cast<void*>(&aux); 00143 00144 cinfo.err = jpeg_std_error(&jerr); 00145 cinfo.err->error_exit = &jpeg_error_exit; 00146 00147 SETJMP_TRY(jpeg_create_decompress(&cinfo)); 00148 00149 // 2. Specify the source of the compressed data (i.e., the input file) 00150 aux.infile = fopen(filename, "rb"); 00151 00152 if (aux.infile == 0) 00153 { 00154 throw rutz::error(rutz::sfmt("couldn't open '%s' for reading", 00155 filename), SRC_POS); 00156 } 00157 00158 00159 SETJMP_TRY(jpeg_stdio_src(&cinfo, aux.infile)); 00160 00161 // 3. Call jpeg_read_header() to obtain image info 00162 SETJMP_TRY(jpeg_read_header(&cinfo, TRUE)); 00163 00164 // 4. Set parameters for decompression 00165 00166 // 5. Start decompression 00167 SETJMP_TRY(jpeg_start_decompress(&cinfo)); 00168 00169 // cinfo.output_width 00170 // cinfo.output_height 00171 // cinfo.out_color_components 00172 // cinfo.output_components 00173 00174 // 6. Read scanlines 00175 00176 switch (cinfo.output_components) 00177 { 00178 case 1: 00179 { 00180 this->gray = Image<byte>(cinfo.output_width, 00181 cinfo.output_height, 00182 NO_INIT); 00183 00184 while (cinfo.output_scanline < cinfo.output_height) 00185 { 00186 JSAMPLE* dest = 00187 this->gray.getArrayPtr() 00188 + cinfo.output_scanline * this->gray.getWidth(); 00189 00190 SETJMP_TRY(jpeg_read_scanlines(&cinfo, 00191 &dest, 00192 /* max lines */ 1)); 00193 } 00194 } 00195 break; 00196 case 3: 00197 { 00198 this->rgb = Image<PixRGB<byte> >(cinfo.output_width, 00199 cinfo.output_height, 00200 NO_INIT); 00201 00202 while (cinfo.output_scanline < cinfo.output_height) 00203 { 00204 JSAMPLE* dest = 00205 reinterpret_cast<JSAMPLE*> 00206 (this->rgb.getArrayPtr() 00207 + cinfo.output_scanline * this->rgb.getWidth()); 00208 00209 SETJMP_TRY(jpeg_read_scanlines(&cinfo, 00210 &dest, 00211 /* max lines */ 1)); 00212 } 00213 } 00214 break; 00215 default: 00216 LFATAL("invalid number of jpeg components (%d)", 00217 cinfo.output_components); 00218 break; 00219 } 00220 00221 // 7. finish decompression 00222 SETJMP_TRY(jpeg_finish_decompress(&cinfo)); 00223 00224 // 8. cleanup 00225 cleanup(&cinfo); 00226 #endif 00227 } 00228 00229 // ###################################################################### 00230 00231 JpegParser::JpegParser(const char* fname) 00232 : 00233 rep(new Impl(fname)) 00234 { 00235 ASSERT(rep->rgb.initialized() || rep->gray.initialized()); 00236 } 00237 00238 // ###################################################################### 00239 00240 JpegParser::~JpegParser() 00241 { 00242 delete rep; 00243 } 00244 00245 GenericFrameSpec JpegParser::getFrameSpec() const 00246 { 00247 GenericFrameSpec result; 00248 00249 if (rep->rgb.initialized()) 00250 { 00251 result.nativeType = GenericFrame::RGB_U8; 00252 result.dims = rep->rgb.getDims(); 00253 } 00254 else if (rep->gray.initialized()) 00255 { 00256 result.nativeType = GenericFrame::GRAY_U8; 00257 result.dims = rep->gray.getDims(); 00258 } 00259 else 00260 ASSERT(0); 00261 00262 result.videoFormat = VIDFMT_AUTO; 00263 result.videoByteSwap = false; 00264 result.floatFlags = 0; 00265 00266 return result; 00267 } 00268 00269 // ###################################################################### 00270 00271 std::string JpegParser::getComments() const 00272 { 00273 return std::string(); 00274 } 00275 00276 // ###################################################################### 00277 00278 uint JpegParser::getTagCount() const 00279 { 00280 return 0; 00281 } 00282 00283 // ###################################################################### 00284 00285 bool JpegParser::getTag(uint tag, std::string &name, std::string &value) const 00286 { 00287 return false; 00288 } 00289 00290 // ###################################################################### 00291 00292 GenericFrame JpegParser::getFrame() 00293 { 00294 if (rep->rgb.initialized()) 00295 return GenericFrame(rep->rgb); 00296 00297 // else... 00298 ASSERT(rep->gray.initialized()); 00299 return GenericFrame(rep->gray); 00300 } 00301 00302 // ###################################################################### 00303 /* So things look consistent in everyone's emacs... */ 00304 /* Local Variables: */ 00305 /* mode: c++ */ 00306 /* indent-tabs-mode: nil */ 00307 /* End: */ 00308 00309 #endif // RASTER_JPEGPARSER_C_DEFINED