[GIL] [patch] read multipage TIFF files

Hello, the following patch (against the current trunk) allows to read any page of a multipage TIFF file, by optionally specifying the zero-based directory number. (The correct term for a TIFF page is "directory".) It adds a "dirnum" to the relevant functions, and uses the corresponding libtiff functionality. The "dirnum" default value is 0 for compatibility with present callers. Additionally, it defines the function tiff_get_directory_count to get the number of directories in the TIFF file. Please give me some feedback or consider applying the patch. This patch would close the corresponding feature request https://svn.boost.org/trac/boost/ticket/2373 . Credits go to Roland Richter, whose code the patch is based on. Best Regards, Robert Pollak Index: boost/gil/extension/io/tiff_io.hpp =================================================================== --- boost/gil/extension/io/tiff_io.hpp (revision 64065) +++ boost/gil/extension/io/tiff_io.hpp (working copy) @@ -122,9 +122,13 @@ protected: TIFF *_tp; public: - tiff_reader(const char* filename) { + tiff_reader(const char* filename,tdir_t dirnum=0) { io_error_if((_tp=TIFFOpen(filename,"r"))==NULL, "tiff_reader: fail to open file"); + if(dirnum>0) { + io_error_if(TIFFSetDirectory(_tp,dirnum)!=1, + "tiff_reader: fail to set directory"); + } } ~tiff_reader() { TIFFClose(_tp); } template <typename View> @@ -171,10 +175,10 @@ private: CC _cc; public: - tiff_reader_color_convert(const char* filename) : - tiff_reader(filename) {} - tiff_reader_color_convert(const char* filename,CC cc_in) : - tiff_reader(filename),_cc(cc_in) {} + tiff_reader_color_convert(const char* filename,tdir_t dirnum=0) : + tiff_reader(filename,dirnum) {} + tiff_reader_color_convert(const char* filename,CC cc_in,tdir_t dirnum=0) : + tiff_reader(filename,dirnum),_cc(cc_in) {} template <typename View> void apply(const View& view) { point2<std::ptrdiff_t> dims=get_dimensions(); @@ -336,18 +340,34 @@ }; /// \ingroup TIFF_IO +/// \brief Returns the number of directories in the TIFF file +inline int tiff_get_directory_count(const char* filename) { + TIFF *tif; + io_error_if((tif=TIFFOpen(filename,"r"))==NULL, + "tiff_get_count: fail to open file"); + + int dircount = 0; + do { + dircount++; + } while (TIFFReadDirectory(tif)); + + TIFFClose(tif); + return dircount; +} + +/// \ingroup TIFF_IO /// \brief Returns the width and height of the TIFF file at the specified location. /// Throws std::ios_base::failure if the location does not correspond to a valid TIFF file -inline point2<std::ptrdiff_t> tiff_read_dimensions(const char* filename) { - detail::tiff_reader m(filename); +inline point2<std::ptrdiff_t> tiff_read_dimensions(const char* filename,tdir_t dirnum=0) { + detail::tiff_reader m(filename,dirnum); return m.get_dimensions(); } /// \ingroup TIFF_IO /// \brief Returns the width and height of the TIFF file at the specified location. /// Throws std::ios_base::failure if the location does not correspond to a valid TIFF file -inline point2<std::ptrdiff_t> tiff_read_dimensions(const std::string& filename) { - return tiff_read_dimensions(filename.c_str()); +inline point2<std::ptrdiff_t> tiff_read_dimensions(const std::string& filename,tdir_t dirnum=0) { + return tiff_read_dimensions(filename.c_str(),dirnum); } /// \ingroup TIFF_IO @@ -356,17 +376,17 @@ /// Throws std::ios_base::failure if the file is not a valid TIFF file, or if its color space or channel depth are not /// compatible with the ones specified by View, or if its dimensions don't match the ones of the view. template <typename View> -inline void tiff_read_view(const char* filename,const View& view) { +inline void tiff_read_view(const char* filename,const View& view,tdir_t dirnum=0) { BOOST_STATIC_ASSERT(tiff_read_support<View>::is_supported); - detail::tiff_reader m(filename); + detail::tiff_reader m(filename,dirnum); m.apply(view); } /// \ingroup TIFF_IO /// \brief Loads the image specified by the given tiff image file name into the given view. template <typename View> -inline void tiff_read_view(const std::string& filename,const View& view) { - tiff_read_view(filename.c_str(),view); +inline void tiff_read_view(const std::string& filename,const View& view,tdir_t dirnum=0) { + tiff_read_view(filename.c_str(),view,dirnum); } /// \ingroup TIFF_IO @@ -375,25 +395,25 @@ /// Throws std::ios_base::failure if the file is not a valid TIFF file, or if its color space or channel depth are not /// compatible with the ones specified by Image template <typename Image> -void tiff_read_image(const char* filename,Image& im) { +void tiff_read_image(const char* filename,Image& im,tdir_t dirnum=0) { BOOST_STATIC_ASSERT(tiff_read_support<typename Image::view_t>::is_supported); - detail::tiff_reader m(filename); + detail::tiff_reader m(filename,dirnum); m.read_image(im); } /// \ingroup TIFF_IO /// \brief Allocates a new image whose dimensions are determined by the given tiff image file, and loads the pixels into it. template <typename Image> -inline void tiff_read_image(const std::string& filename,Image& im) { - tiff_read_image(filename.c_str(),im); +inline void tiff_read_image(const std::string& filename,Image& im,tdir_t dirnum=0) { + tiff_read_image(filename.c_str(),im,dirnum); } /// \ingroup TIFF_IO /// \brief Loads and color-converts the image specified by the given tiff image file name into the given view. /// Throws std::ios_base::failure if the file is not a valid TIFF file, or if its dimensions don't match the ones of the view. template <typename View,typename CC> -inline void tiff_read_and_convert_view(const char* filename,const View& view,CC cc) { - detail::tiff_reader_color_convert<CC> m(filename,cc); +inline void tiff_read_and_convert_view(const char* filename,const View& view,CC cc,tdir_t dirnum=0) { + detail::tiff_reader_color_convert<CC> m(filename,cc,dirnum); m.apply(view); } @@ -401,31 +421,31 @@ /// \brief Loads and color-converts the image specified by the given tiff image file name into the given view. /// Throws std::ios_base::failure if the file is not a valid TIFF file, or if its dimensions don't match the ones of the view. template <typename View> -inline void tiff_read_and_convert_view(const char* filename,const View& view) { - detail::tiff_reader_color_convert<default_color_converter> m(filename,default_color_converter()); +inline void tiff_read_and_convert_view(const char* filename,const View& view,tdir_t dirnum=0) { + detail::tiff_reader_color_convert<default_color_converter> m(filename,default_color_converter(),dirnum); m.apply(view); } /// \ingroup TIFF_IO /// \brief Loads and color-converts the image specified by the given tiff image file name into the given view. template <typename View,typename CC> -inline void tiff_read_and_convert_view(const std::string& filename,const View& view,CC cc) { - tiff_read_and_convert_view(filename.c_str(),view,cc); +inline void tiff_read_and_convert_view(const std::string& filename,const View& view,CC cc,tdir_t dirnum=0) { + tiff_read_and_convert_view(filename.c_str(),view,cc,dirnum); } /// \ingroup TIFF_IO /// \brief Loads and color-converts the image specified by the given tiff image file name into the given view. template <typename View> -inline void tiff_read_and_convert_view(const std::string& filename,const View& view) { - tiff_read_and_convert_view(filename.c_str(),view); +inline void tiff_read_and_convert_view(const std::string& filename,const View& view,tdir_t dirnum=0) { + tiff_read_and_convert_view(filename.c_str(),view,dirnum); } /// \ingroup TIFF_IO /// \brief Allocates a new image whose dimensions are determined by the given tiff image file, loads and color-converts the pixels into it. /// Throws std::ios_base::failure if the file is not a valid TIFF file template <typename Image,typename CC> -void tiff_read_and_convert_image(const char* filename,Image& im,CC cc) { - detail::tiff_reader_color_convert<CC> m(filename,cc); +void tiff_read_and_convert_image(const char* filename,Image& im,CC cc,tdir_t dirnum=0) { + detail::tiff_reader_color_convert<CC> m(filename,cc,dirnum); m.read_image(im); } @@ -433,23 +453,23 @@ /// \brief Allocates a new image whose dimensions are determined by the given tiff image file, loads and color-converts the pixels into it. /// Throws std::ios_base::failure if the file is not a valid TIFF file template <typename Image> -void tiff_read_and_convert_image(const char* filename,Image& im) { - detail::tiff_reader_color_convert<default_color_converter> m(filename,default_color_converter()); +void tiff_read_and_convert_image(const char* filename,Image& im,tdir_t dirnum=0) { + detail::tiff_reader_color_convert<default_color_converter> m(filename,default_color_converter(),dirnum); m.read_image(im); } /// \ingroup TIFF_IO /// \brief Allocates a new image whose dimensions are determined by the given tiff image file, loads and color-converts the pixels into it. template <typename Image,typename CC> -inline void tiff_read_and_convert_image(const std::string& filename,Image& im,CC cc) { - tiff_read_and_convert_image(filename.c_str(),im,cc); +inline void tiff_read_and_convert_image(const std::string& filename,Image& im,CC cc,tdir_t dirnum=0) { + tiff_read_and_convert_image(filename.c_str(),im,cc,dirnum); } /// \ingroup TIFF_IO /// \brief Allocates a new image whose dimensions are determined by the given tiff image file, loads and color-converts the pixels into it. template <typename Image> -inline void tiff_read_and_convert_image(const std::string& filename,Image& im) { - tiff_read_and_convert_image(filename.c_str(),im); +inline void tiff_read_and_convert_image(const std::string& filename,Image& im,tdir_t dirnum=0) { + tiff_read_and_convert_image(filename.c_str(),im,dirnum); } /// \ingroup TIFF_IO

Hi Robert, thanks for your submission. I gladly will update the code after some testing, of course. Could you send me your tiff_io.hpp. I have no idea how to apply a patch file on Windows. Unless someone can explain that to me. Do you have some sort of test suite with multi-page tiff files? I think I could even use this code in my gil::io rewrite. I have to check but of the top of my head multi-page cannot be read right now. Thanks again, Christian On Fri, Jul 16, 2010 at 4:12 AM, Robert Pollak <robert.pollak@jku.at> wrote:
Hello,
the following patch (against the current trunk) allows to read any page of a multipage TIFF file, by optionally specifying the zero-based directory number. (The correct term for a TIFF page is "directory".)
It adds a "dirnum" to the relevant functions, and uses the corresponding libtiff functionality. The "dirnum" default value is 0 for compatibility with present callers.
Additionally, it defines the function tiff_get_directory_count to get the number of directories in the TIFF file.
Please give me some feedback or consider applying the patch.
This patch would close the corresponding feature request https://svn.boost.org/trac/boost/ticket/2373 .
Credits go to Roland Richter, whose code the patch is based on.
Best Regards, Robert Pollak
Index: boost/gil/extension/io/tiff_io.hpp =================================================================== --- boost/gil/extension/io/tiff_io.hpp (revision 64065) +++ boost/gil/extension/io/tiff_io.hpp (working copy) @@ -122,9 +122,13 @@ protected: TIFF *_tp; public: - tiff_reader(const char* filename) { + tiff_reader(const char* filename,tdir_t dirnum=0) { io_error_if((_tp=TIFFOpen(filename,"r"))==NULL, "tiff_reader: fail to open file"); + if(dirnum>0) { + io_error_if(TIFFSetDirectory(_tp,dirnum)!=1, + "tiff_reader: fail to set directory"); + } } ~tiff_reader() { TIFFClose(_tp); } template <typename View> @@ -171,10 +175,10 @@ private: CC _cc; public: - tiff_reader_color_convert(const char* filename) : - tiff_reader(filename) {} - tiff_reader_color_convert(const char* filename,CC cc_in) : - tiff_reader(filename),_cc(cc_in) {} + tiff_reader_color_convert(const char* filename,tdir_t dirnum=0) : + tiff_reader(filename,dirnum) {} + tiff_reader_color_convert(const char* filename,CC cc_in,tdir_t dirnum=0) : + tiff_reader(filename,dirnum),_cc(cc_in) {} template <typename View> void apply(const View& view) { point2<std::ptrdiff_t> dims=get_dimensions(); @@ -336,18 +340,34 @@ };
/// \ingroup TIFF_IO +/// \brief Returns the number of directories in the TIFF file +inline int tiff_get_directory_count(const char* filename) { + TIFF *tif; + io_error_if((tif=TIFFOpen(filename,"r"))==NULL, + "tiff_get_count: fail to open file"); + + int dircount = 0; + do { + dircount++; + } while (TIFFReadDirectory(tif)); + + TIFFClose(tif); + return dircount; +} + +/// \ingroup TIFF_IO /// \brief Returns the width and height of the TIFF file at the specified location. /// Throws std::ios_base::failure if the location does not correspond to a valid TIFF file -inline point2<std::ptrdiff_t> tiff_read_dimensions(const char* filename) { - detail::tiff_reader m(filename); +inline point2<std::ptrdiff_t> tiff_read_dimensions(const char* filename,tdir_t dirnum=0) { + detail::tiff_reader m(filename,dirnum); return m.get_dimensions(); }
/// \ingroup TIFF_IO /// \brief Returns the width and height of the TIFF file at the specified location. /// Throws std::ios_base::failure if the location does not correspond to a valid TIFF file -inline point2<std::ptrdiff_t> tiff_read_dimensions(const std::string& filename) { - return tiff_read_dimensions(filename.c_str()); +inline point2<std::ptrdiff_t> tiff_read_dimensions(const std::string& filename,tdir_t dirnum=0) { + return tiff_read_dimensions(filename.c_str(),dirnum); }
/// \ingroup TIFF_IO @@ -356,17 +376,17 @@ /// Throws std::ios_base::failure if the file is not a valid TIFF file, or if its color space or channel depth are not /// compatible with the ones specified by View, or if its dimensions don't match the ones of the view. template <typename View> -inline void tiff_read_view(const char* filename,const View& view) { +inline void tiff_read_view(const char* filename,const View& view,tdir_t dirnum=0) { BOOST_STATIC_ASSERT(tiff_read_support<View>::is_supported); - detail::tiff_reader m(filename); + detail::tiff_reader m(filename,dirnum); m.apply(view); }
/// \ingroup TIFF_IO /// \brief Loads the image specified by the given tiff image file name into the given view. template <typename View> -inline void tiff_read_view(const std::string& filename,const View& view) { - tiff_read_view(filename.c_str(),view); +inline void tiff_read_view(const std::string& filename,const View& view,tdir_t dirnum=0) { + tiff_read_view(filename.c_str(),view,dirnum); }
/// \ingroup TIFF_IO @@ -375,25 +395,25 @@ /// Throws std::ios_base::failure if the file is not a valid TIFF file, or if its color space or channel depth are not /// compatible with the ones specified by Image template <typename Image> -void tiff_read_image(const char* filename,Image& im) { +void tiff_read_image(const char* filename,Image& im,tdir_t dirnum=0) { BOOST_STATIC_ASSERT(tiff_read_support<typename Image::view_t>::is_supported); - detail::tiff_reader m(filename); + detail::tiff_reader m(filename,dirnum); m.read_image(im); }
/// \ingroup TIFF_IO /// \brief Allocates a new image whose dimensions are determined by the given tiff image file, and loads the pixels into it. template <typename Image> -inline void tiff_read_image(const std::string& filename,Image& im) { - tiff_read_image(filename.c_str(),im); +inline void tiff_read_image(const std::string& filename,Image& im,tdir_t dirnum=0) { + tiff_read_image(filename.c_str(),im,dirnum); }
/// \ingroup TIFF_IO /// \brief Loads and color-converts the image specified by the given tiff image file name into the given view. /// Throws std::ios_base::failure if the file is not a valid TIFF file, or if its dimensions don't match the ones of the view. template <typename View,typename CC> -inline void tiff_read_and_convert_view(const char* filename,const View& view,CC cc) { - detail::tiff_reader_color_convert<CC> m(filename,cc); +inline void tiff_read_and_convert_view(const char* filename,const View& view,CC cc,tdir_t dirnum=0) { + detail::tiff_reader_color_convert<CC> m(filename,cc,dirnum); m.apply(view); }
@@ -401,31 +421,31 @@ /// \brief Loads and color-converts the image specified by the given tiff image file name into the given view. /// Throws std::ios_base::failure if the file is not a valid TIFF file, or if its dimensions don't match the ones of the view. template <typename View> -inline void tiff_read_and_convert_view(const char* filename,const View& view) { - detail::tiff_reader_color_convert<default_color_converter> m(filename,default_color_converter()); +inline void tiff_read_and_convert_view(const char* filename,const View& view,tdir_t dirnum=0) { + detail::tiff_reader_color_convert<default_color_converter> m(filename,default_color_converter(),dirnum); m.apply(view); }
/// \ingroup TIFF_IO /// \brief Loads and color-converts the image specified by the given tiff image file name into the given view. template <typename View,typename CC> -inline void tiff_read_and_convert_view(const std::string& filename,const View& view,CC cc) { - tiff_read_and_convert_view(filename.c_str(),view,cc); +inline void tiff_read_and_convert_view(const std::string& filename,const View& view,CC cc,tdir_t dirnum=0) { + tiff_read_and_convert_view(filename.c_str(),view,cc,dirnum); }
/// \ingroup TIFF_IO /// \brief Loads and color-converts the image specified by the given tiff image file name into the given view. template <typename View> -inline void tiff_read_and_convert_view(const std::string& filename,const View& view) { - tiff_read_and_convert_view(filename.c_str(),view); +inline void tiff_read_and_convert_view(const std::string& filename,const View& view,tdir_t dirnum=0) { + tiff_read_and_convert_view(filename.c_str(),view,dirnum); }
/// \ingroup TIFF_IO /// \brief Allocates a new image whose dimensions are determined by the given tiff image file, loads and color-converts the pixels into it. /// Throws std::ios_base::failure if the file is not a valid TIFF file template <typename Image,typename CC> -void tiff_read_and_convert_image(const char* filename,Image& im,CC cc) { - detail::tiff_reader_color_convert<CC> m(filename,cc); +void tiff_read_and_convert_image(const char* filename,Image& im,CC cc,tdir_t dirnum=0) { + detail::tiff_reader_color_convert<CC> m(filename,cc,dirnum); m.read_image(im); }
@@ -433,23 +453,23 @@ /// \brief Allocates a new image whose dimensions are determined by the given tiff image file, loads and color-converts the pixels into it. /// Throws std::ios_base::failure if the file is not a valid TIFF file template <typename Image> -void tiff_read_and_convert_image(const char* filename,Image& im) { - detail::tiff_reader_color_convert<default_color_converter> m(filename,default_color_converter()); +void tiff_read_and_convert_image(const char* filename,Image& im,tdir_t dirnum=0) { + detail::tiff_reader_color_convert<default_color_converter> m(filename,default_color_converter(),dirnum); m.read_image(im); }
/// \ingroup TIFF_IO /// \brief Allocates a new image whose dimensions are determined by the given tiff image file, loads and color-converts the pixels into it. template <typename Image,typename CC> -inline void tiff_read_and_convert_image(const std::string& filename,Image& im,CC cc) { - tiff_read_and_convert_image(filename.c_str(),im,cc); +inline void tiff_read_and_convert_image(const std::string& filename,Image& im,CC cc,tdir_t dirnum=0) { + tiff_read_and_convert_image(filename.c_str(),im,cc,dirnum); }
/// \ingroup TIFF_IO /// \brief Allocates a new image whose dimensions are determined by the given tiff image file, loads and color-converts the pixels into it. template <typename Image> -inline void tiff_read_and_convert_image(const std::string& filename,Image& im) { - tiff_read_and_convert_image(filename.c_str(),im); +inline void tiff_read_and_convert_image(const std::string& filename,Image& im,tdir_t dirnum=0) { + tiff_read_and_convert_image(filename.c_str(),im,dirnum); }
/// \ingroup TIFF_IO _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Hello Christian, you wrote:
I gladly will update the code after some testing, of course. Could you send me your tiff_io.hpp.
Thank you! I will send it per mail.
I have no idea how to apply a patch file on Windows. Unless someone can explain that to me.
I would use TortoiseSVN's "Apply patch ..." :)
Do you have some sort of test suite with multi-page tiff files?
Unfortunately not. We are just using the patch for years in an industry project.
I think I could even use this code in my gil::io rewrite. I have to check but of the top of my head multi-page cannot be read right now.
I must say that I was not aware about this rewrite, but we would certainly be happy to have it work with multi-page TIFF. Best regards, Robert

Hi Robert,
Thank you! I will send it per mail.
Thanks, I figured out ( with the help of Steven Watanabe ) how to apply your patch file.
I have no idea how to apply a patch file on Windows. Unless someone can explain that to me.
I would use TortoiseSVN's "Apply patch ..." :)
Tried that but didn't work since some white-spaces were introduced when I copied and paste your patch file from your email. Can you convince Tortoise to use the -l option for ignoring white-spaces?
Do you have some sort of test suite with multi-page tiff files?
Unfortunately not. We are just using the patch for years in an industry project.
Good to know.
I think I could even use this code in my gil::io rewrite. I have to check but of the top of my head multi-page cannot be read right now.
I must say that I was not aware about this rewrite, but we would certainly be happy to have it work with multi-page TIFF.
Let me know if you wanna know more about the new gil::io? Reading and writing tiff files has dramatically improved! Regards, Christian

Hi Christian,
Let me know if you wanna know more about the new gil::io? Reading and writing tiff files has dramatically improved!
I have found your announcement of it at http://lists.boost.org/Archives/boost/2008/04/135914.php . Why is tiff io speed improved, although you are still using libtiff? Robert

Hi Robert, On Thu, Jul 22, 2010 at 3:21 AM, Robert Pollak <robert.pollak@jku.at> wrote:
Hi Christian,
Let me know if you wanna know more about the new gil::io? Reading and writing tiff files has dramatically improved!
I have found your announcement of it at http://lists.boost.org/Archives/boost/2008/04/135914.php . Why is tiff io speed improved, although you are still using libtiff?
What I mean is that you can now read/write bit-aligned images or tiled images. You can use streams for reading/writing file from/into memory. You can do that for all supported image formats. Also, you can read subimages. Etc. Regards, Christian

Hi Christian, you wrote on 20.07.2010:
Thanks, I figured out ( with the help of Steven Watanabe ) how to apply your patch file.
I see you committed this to the trunk as "Enables reading multi-page tiff." on 2010-08-04 23:07:36 (Subversion revision 64600). Why was it never merged to the release branch? I was expecting this to be in the new Boost 1.45.0, which is tagged from the release branch. Best regards, Robert

Hi Robert, my bad, I apologize. I added the fix to the release branch. Regards, Christian On Mon, Nov 29, 2010 at 4:17 AM, Robert Pollak <robert.pollak@jku.at> wrote:
Hi Christian,
you wrote on 20.07.2010:
Thanks, I figured out ( with the help of Steven Watanabe ) how to apply your patch file.
I see you committed this to the trunk as "Enables reading multi-page tiff." on 2010-08-04 23:07:36 (Subversion revision 64600).
Why was it never merged to the release branch? I was expecting this to be in the new Boost 1.45.0, which is tagged from the release branch.
Best regards, Robert
participants (2)
-
Christian Henning
-
Robert Pollak