1 /************************************************************************/
2 /*                                                                      */
3 /*               Copyright 2002 by Gunnar Kedenburg                     */
4 /*       Cognitive Systems Group, University of Hamburg, Germany        */
5 /*                                                                      */
6 /*    This file is part of the VIGRA computer vision library.           */
7 /*    ( Version 1.5.0, Dec 07 2006 )                                    */
8 /*    The VIGRA Website is                                              */
9 /*        http://kogs-www.informatik.uni-hamburg.de/~koethe/vigra/      */
10 /*    Please direct questions, bug reports, and contributions to        */
11 /*        koethe@informatik.uni-hamburg.de          or                  */
12 /*        vigra@kogs1.informatik.uni-hamburg.de                         */
13 /*                                                                      */
14 /*    Permission is hereby granted, free of charge, to any person       */
15 /*    obtaining a copy of this software and associated documentation    */
16 /*    files (the "Software"), to deal in the Software without           */
17 /*    restriction, including without limitation the rights to use,      */
18 /*    copy, modify, merge, publish, distribute, sublicense, and/or      */
19 /*    sell copies of the Software, and to permit persons to whom the    */
20 /*    Software is furnished to do so, subject to the following          */
21 /*    conditions:                                                       */
22 /*                                                                      */
23 /*    The above copyright notice and this permission notice shall be    */
24 /*    included in all copies or substantial portions of the             */
25 /*    Software.                                                         */
26 /*                                                                      */
27 /*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
28 /*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
29 /*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
30 /*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
31 /*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
32 /*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
33 /*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
34 /*    OTHER DEALINGS IN THE SOFTWARE.                                   */
35 /*                                                                      */
36 /************************************************************************/
37 /* Modifications by Pablo d'Angelo
38  * updated to vigra 1.4 by Douglas Wilkins
39  * as of 18 Febuary 2006:
40  *  - Added UINT16 and UINT32 pixel types.
41  *  - Added support for obtaining extra bands beyond RGB.
42  *  - Added support for a position field that indicates the start of this
43  *    image relative to some global origin.
44  *  - Added support for x and y resolution fields.
45  *  - Added support for ICC profiles
46  */
47 
48 #include <iostream>
49 #include <algorithm>
50 #include <iterator>
51 #include <string>
52 #include <sstream>
53 #include <vector>
54 #include <iterator>
55 #include <sys/types.h>
56 #include <errno.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include "vigra/imageinfo.hxx"
61 #include "codecmanager.hxx"
62 
63 #if defined(_WIN32)
64 #  include "vigra/windows.h"
65 #else
66 #  include <dirent.h>
67 #endif
68 
69 namespace vigra
70 {
71 
72 namespace detail
73 {
74 
75 struct NumberCompare
76 {
77     bool operator()(std::string const & l, std::string const & r) const
78     {
79         return atoi(l.c_str()) < atoi(r.c_str());
80     }
81 };
82 
83 } // namespace detail
84 
85 
86 // find filenames matching the pattern "<path>/base[0-9]+ext"
87 #ifdef _WIN32
88 VIGRA_EXPORT void findImageSequence(const std::string &name_base,
89                        const std::string &name_ext,
90                        std::vector<std::string> & numbers)
91 {
92     // find out how many images we have
93     BOOL            fFinished;
94     HANDLE          hList;
95     TCHAR           szDir[MAX_PATH+1];
96     WIN32_FIND_DATA FileData;
97 
98     std::string base, path;
99 
100 	// on Windows, both '/' and '\' are valid path separators
101 	// note: std::basic_string.rfind() may return 'unsigned int', so explicitely cast to 'int'
102 	int split = std::max(static_cast<int>(name_base.rfind('/')), static_cast<int>(name_base.rfind('\\')));
103 	if(split == static_cast<int>(std::string::npos))
104     {
105         path = ".";
106         base = name_base;
107     }
108     else
109     {
110         for(int i=0; i<split; ++i)
111         {
112             if(name_base[i] == '/')
113                 path += '\\';
114             else
115                 path += name_base[i];
116         }
117         base.append(name_base, split+1, name_base.size() - split - 1);
118     }
119 
120     std::vector<std::string> result;
121     char numbuf[21], extbuf[1024];
122     std::string pattern = base + "%20[0-9]%1023s";
123 
124     // Get the proper directory path
125     sprintf(szDir, "%s\\%s*%s", path.c_str(), base.c_str(), name_ext.c_str());
126 
127     // Get the first file
128     hList = FindFirstFile(szDir, &FileData);
129     if (hList == INVALID_HANDLE_VALUE)
130     {
131         std::string message("importVolume(): No files matching '");
132         message = message + szDir + "'.";
133         vigra_fail(message.c_str());
134     }
135     else
136     {
137         // Traverse through the directory structure
138         fFinished = FALSE;
139         while (!fFinished)
140         {
141             if(sscanf(FileData.cFileName, pattern.c_str(), numbuf, extbuf) == 2)
142             {
143                 if(strcmp(name_ext.c_str(), extbuf) == 0)
144                     result.push_back(std::string(numbuf));
145             }
146             if (!FindNextFile(hList, &FileData))
147             {
148                 if (GetLastError() == ERROR_NO_MORE_FILES)
149                 {
150                     fFinished = TRUE;
151                 }
152             }
153         }
154     }
155 
156     FindClose(hList);
157 
158     std::sort(result.begin(), result.end(), detail::NumberCompare());
159     numbers.swap(result);
160 }
161 
162 #else // _WIN32
163 
164 void findImageSequence(const std::string &name_base,
165                        const std::string &name_ext,
166                        std::vector<std::string> & numbers)
167 {
168     // find out how many images we have
169     std::string base, path;
170     int split = name_base.rfind('/');
171     if(split == -1)
172     {
173         path = ".";
174         base = name_base;
175     }
176     else
177     {
178         path.append(name_base, 0, split);
179         base.append(name_base, split+1, name_base.size() - split - 1);
180     }
181 
182     DIR * dir = opendir(path.c_str());
183     if(!dir)
184     {
185         std::string message("importVolume(): Unable to open directory '");
186         message = message + path + "'.";
187         vigra_fail(message.c_str());
188     }
189 
190     std::vector<std::string> result;
191     dirent * dp;
192     errno = 0;
193     char numbuf[21], extbuf[1024];
194     std::string pattern = base + "%20[0-9]%1023s";
195     while ((dp = readdir(dir)) != NULL)
196     {
197         if(sscanf(dp->d_name, pattern.c_str(), numbuf, extbuf) == 2)
198         {
199             if(strcmp(name_ext.c_str(), extbuf) == 0)
200                 result.push_back(std::string(numbuf));
201         }
202     }
203 
204     closedir(dir);
205 
206     vigra_precondition(errno == 0,
207           "importVolume(): I/O error while searching for images.");
208 
209     std::sort(result.begin(), result.end(), detail::NumberCompare());
210     numbers.swap(result);
211 }
212 
213 #endif // _WIN32
214 
215 // build a string from a sequence.
216 #if defined(_MSC_VER) && (_MSC_VER < 1300)
217 template <class iterator>
218 std::string stringify (const iterator &start, const iterator &end)
219 {
220     return stringifyImpl(start, end, *start);
221 }
222 
223 template <class iterator, class Value>
224 std::string stringifyImpl (const iterator &start, const iterator &end, Value const &)
225 {
226     std::ostringstream out;
227     // do not place a space character after the last sequence element.
228     std::copy (start, end - 1,
229                std::ostream_iterator <Value> (out, " "));
230     out << *(end-1);
231     return out.str ();
232 }
233 
234 #else
235 
236 template <class iterator>
237 std::string stringify (const iterator &start, const iterator &end)
238 {
239     typedef typename std::iterator_traits<iterator>::value_type value_type;
240     std::ostringstream out;
241     // do not place a space character after the last sequence element.
242     std::copy (start, end - 1,
243                std::ostream_iterator <value_type> (out, " "));
244     out << *(end-1);
245     return out.str ();
246 }
247 
248 #endif // _MSC_VER < 1300
249 
250 void validate_filetype( std::string filetype )
251 {
252     vigra_precondition( codecManager().fileTypeSupported(filetype),
253                         "given file type is not supported" );
254 }
255 
256 std::string impexListFormats()
257 {
258     std::vector<std::string> ft = codecManager().supportedFileTypes();
259     return stringify( ft.begin(), ft.end() );
260 }
261 
262 std::string impexListExtensions()
263 {
264     std::vector<std::string> ft = codecManager().supportedFileExtensions();
265     return stringify( ft.begin(), ft.end() );
266 }
267 
268 bool isImage(char const * filename)
269 {
270     return CodecManager::manager().getFileTypeByMagicString(filename) != "";
271 }
272 
273 // class ImageExportInfo
274 
275 ImageExportInfo::ImageExportInfo( const char * filename )
276     : m_filename(filename),
277       m_x_res(0), m_y_res(0)
278 {}
279 
280 ImageExportInfo::~ImageExportInfo()
281 {
282 }
283 
284 ImageExportInfo & ImageExportInfo::setFileType( const char * filetype )
285 {
286     m_filetype = filetype;
287     return *this;
288 }
289 
290 ImageExportInfo & ImageExportInfo::setCompression( const char * comp )
291 {
292     m_comp = comp;
293     return *this;
294 }
295 
296 const char * ImageExportInfo::getFileName() const
297 {
298     return m_filename.c_str();
299 }
300 
301 const char * ImageExportInfo::getFileType() const
302 {
303     return m_filetype.c_str();
304 }
305 
306 ImageExportInfo & ImageExportInfo::setPixelType( const char * s )
307 {
308     m_pixeltype = s;
309     return *this;
310 }
311 
312 const char * ImageExportInfo::getPixelType() const
313 {
314     return m_pixeltype.c_str();
315 }
316 
317 const char * ImageExportInfo::getCompression() const
318 {
319     return m_comp.c_str();
320 }
321 
322 float ImageExportInfo::getXResolution() const
323 {
324     return m_x_res;
325 }
326 
327 float ImageExportInfo::getYResolution() const
328 {
329     return m_y_res;
330 }
331 
332 ImageExportInfo & ImageExportInfo::setXResolution( float val )
333 {
334     m_x_res = val;
335     return *this;
336 }
337 
338 ImageExportInfo & ImageExportInfo::setYResolution( float val )
339 {
340     m_y_res = val;
341     return *this;
342 }
343 
344 ImageExportInfo & ImageExportInfo::setPosition(const vigra::Diff2D & pos)
345 {
346     m_pos = pos;
347     return *this;
348 }
349 
350 vigra::Size2D ImageExportInfo::getCanvasSize() const
351 {
352     return m_canvas_size ;
353 }
354 
355 
356 ImageExportInfo & ImageExportInfo::setCanvasSize(const Size2D & size)
357 {
358     //std::cerr << "ImageExportInfo: setting canvas size: " << size << std::endl;
359     m_canvas_size = size;
360     return *this;
361 }
362 
363 
364 vigra::Diff2D ImageExportInfo::getPosition() const
365 {
366     return m_pos;
367 }
368 
369 const ImageExportInfo::ICCProfile & ImageExportInfo::getICCProfile() const
370 {
371     return m_icc_profile;
372 }
373 
374 ImageExportInfo & ImageExportInfo::setICCProfile(
375     const ImageExportInfo::ICCProfile &profile)
376 {
377     m_icc_profile = profile;
378     return *this;
379 }
380 
381 // return an encoder for a given ImageExportInfo object
382 std::auto_ptr<Encoder> encoder( const ImageExportInfo & info )
383 {
384     std::auto_ptr<Encoder> enc;
385 
386     std::string filetype = info.getFileType();
387     if ( filetype != "" ) {
388         validate_filetype(filetype);
389         std::auto_ptr<Encoder> enc2
390             = getEncoder( std::string( info.getFileName() ), filetype );
391         enc = enc2;
392     } else {
393         std::auto_ptr<Encoder> enc2
394             = getEncoder( std::string( info.getFileName() ) );
395         enc = enc2;
396     }
397 
398     std::string comp = info.getCompression();
399     if ( comp != "" ) {
400 
401         // check for JPEG compression
402         int quality = -1;
403         std::istringstream compstream(comp.c_str());
404         compstream >> quality;
405 
406         // FIXME: dangelo: This code might lead to strange effects (setting an invalid compression mode),
407         // if other formats also support a numerical compression parameter.
408         if ( quality != -1 ) {
409             enc->setCompressionType( "JPEG", quality );
410         } else {
411             // leave any other compression type to the codec
412             enc->setCompressionType(comp);
413         }
414     }
415 
416     std::string pixel_type = info.getPixelType();
417     if ( pixel_type != "" ) {
418         if(!isPixelTypeSupported( enc->getFileType(), pixel_type ))
419         {
420             std::string msg("exportImage(): file type ");
421             msg += enc->getFileType() + " does not support requested pixel type "
422                                       + pixel_type + ".";
423             vigra_precondition(false, msg.c_str());
424         }
425         enc->setPixelType(pixel_type);
426     }
427 
428     // set other properties
429     enc->setXResolution(info.getXResolution());
430     enc->setYResolution(info.getYResolution());
431     enc->setPosition(info.getPosition());
432     enc->setCanvasSize(info.getCanvasSize());
433 
434     if ( info.getICCProfile().size() > 0 ) {
435         enc->setICCProfile(info.getICCProfile());
436     }
437 
438     return enc;
439 }
440 
441 // class ImageImportInfo
442 
443 ImageImportInfo::ImageImportInfo( const char * filename )
444     : m_filename(filename)
445 {
446     std::auto_ptr<Decoder> decoder = getDecoder(m_filename);
447 
448     m_filetype = decoder->getFileType();
449     m_pixeltype = decoder->getPixelType();
450     m_width = decoder->getWidth();
451     m_height = decoder->getHeight();
452     m_num_bands = decoder->getNumBands();
453     m_num_extra_bands = decoder->getNumExtraBands();
454     m_pos = decoder->getPosition();
455     m_canvas_size = decoder->getCanvasSize();
456 
457     m_icc_profile = decoder->getICCProfile();
458 
459     decoder->abort(); // there probably is no better way than this
460 }
461 
462 ImageImportInfo::~ImageImportInfo() {
463 }
464 
465 const char * ImageImportInfo::getFileName() const
466 {
467     return m_filename.c_str();
468 }
469 
470 const char * ImageImportInfo::getFileType() const
471 {
472     return m_filetype.c_str();
473 }
474 
475 const char * ImageImportInfo::getPixelType() const
476 {
477     return m_pixeltype.c_str();
478 }
479 
480 ImageImportInfo::PixelType ImageImportInfo::pixelType() const
481 {
482    const std::string pixeltype=getPixelType();
483    if (pixeltype == "UINT8")
484      return UINT8;
485    if (pixeltype == "INT16")
486      return INT16;
487    if (pixeltype == "UINT16")
488      return UINT16;
489    if (pixeltype == "INT32")
490      return INT32;
491    if (pixeltype == "UINT32")
492      return UINT32;
493    if (pixeltype == "FLOAT")
494      return FLOAT;
495    if (pixeltype == "DOUBLE")
496      return DOUBLE;
497    vigra_fail( "internal error: unknown pixel type" );
498    return ImageImportInfo::PixelType();
499 }
500 
501 int ImageImportInfo::width() const
502 {
503     return m_width;
504 }
505 
506 int ImageImportInfo::height() const
507 {
508     return m_height;
509 }
510 
511 int ImageImportInfo::numBands() const
512 {
513     return m_num_bands;
514 }
515 
516 int ImageImportInfo::numExtraBands() const
517 {
518     return m_num_extra_bands;
519 }
520 
521 Size2D ImageImportInfo::size() const
522 {
523     return Size2D( m_width, m_height );
524 }
525 
526 bool ImageImportInfo::isGrayscale() const
527 {
528     return m_num_bands == 1;
529 }
530 
531 bool ImageImportInfo::isColor() const
532 {
533     return (m_num_bands - m_num_extra_bands) == 3;
534 }
535 
536 bool ImageImportInfo::isByte() const
537 {
538     return m_pixeltype == "UINT8";
539 }
540 
541 Diff2D ImageImportInfo::getPosition() const
542 {
543     return m_pos;
544 }
545 
546 Size2D ImageImportInfo::getCanvasSize() const
547 {
548     return m_canvas_size;
549 }
550 
551 
552 float ImageImportInfo::getXResolution() const
553 {
554     return m_x_res;
555 }
556 
557 float ImageImportInfo::getYResolution() const
558 {
559     return m_y_res;
560 }
561 
562 const ImageImportInfo::ICCProfile & ImageImportInfo::getICCProfile() const
563 {
564     return m_icc_profile;
565 }
566 
567 // return a decoder for a given ImageImportInfo object
568 std::auto_ptr<Decoder> decoder( const ImageImportInfo & info )
569 {
570     std::string filetype = info.getFileType();
571     validate_filetype(filetype);
572     return getDecoder( std::string( info.getFileName() ), filetype );
573 }
574 
575 } // namespace vigra
576