Boost logo

Boost Users :

From: John C. Femiani (john.femiani_at_[hidden])
Date: 2008-08-14 05:26:07


I found a bug in GIL, and I think I have a patch for it. Basically the
tiff reading code in GIL fails to handle alpha channels properly.

Basically an RGBA tiff can be read into an RGB image, causing garbage.
The source of the problem seems to be that TIFFTAG_PHOTOMETRIC is the
only thing used to decide which pixel format to read. One should also
read TIFFTAG_SAMPLESPERPIXEL to see if there are any extra fields, and
TIFFTAG_EXTRASAMPLES to figure out what they mean. (I did not do all
that in this little patch).

On a related note:

1) Is GIL updating the boost/SVN repository? It seems to have been a
long time (6 months) since the last change (removing tabs)
2) What is the status of the new_io library (Christian Henning was
working on it I think).
I think that this is the best hope for dealing with the IO issues I tend
to have with GIL so far (it does not read RGBA jpegs either, it can not
handle palettes, certain kinds of tiff compression, etc.)

--John

Index: png_io_private.hpp
===================================================================
--- png_io_private.hpp (revision 48136)
+++ png_io_private.hpp (working copy)
@@ -291,6 +291,9 @@
             default: io_error("png_reader_color_convert::apply(): unknown combination of color type and bit depth");
             }
             break;
+ case PNG_COLOR_TYPE_PALETTE:
+ io_error("png_reader_color_convert::apply(): cannot read png images with a pallette (yet)");
+
         default: io_error("png_reader_color_convert::apply(): unknown color type");
         }
         png_read_end(_png_ptr,NULL);
Index: tiff_dynamic_io.hpp
===================================================================
--- tiff_dynamic_io.hpp (revision 48136)
+++ tiff_dynamic_io.hpp (working copy)
@@ -56,13 +56,32 @@
 class tiff_type_format_checker {
     int _bit_depth;
     int _color_type;
+ unsigned short _samples_per_pixel;
 public:
- tiff_type_format_checker(int bit_depth_in,int color_type_in) :
- _bit_depth(bit_depth_in),_color_type(color_type_in) {}
+ tiff_type_format_checker( int bit_depth_in
+ , int color_type_in
+ , unsigned short samples_per_pixel_in
+ )
+ : _bit_depth(bit_depth_in)
+ , _color_type(color_type_in)
+ , _samples_per_pixel(samples_per_pixel_in)
+ {}
     template <typename Image>
     bool apply() {
- return tiff_read_support<typename Image::view_t>::bit_depth==_bit_depth &&
- tiff_read_support<typename Image::view_t>::color_type==_color_type;
+ typedef tiff_read_support<typename Image::view_t> traits;
+
+ int my_samples_per_pixel = size<typename Image::value_type>();
+
+ bool same_bit_depth = traits::bit_depth == _bit_depth;
+ bool same_color_type = traits::color_type == _color_type;
+ bool same_samples_per_pixel = my_samples_per_pixel == _samples_per_pixel;
+
+ bool result = same_bit_depth && same_color_type;
+
+ if (_samples_per_pixel)
+ result = result && same_samples_per_pixel;
+
+ return result;
     }
 };
 
@@ -77,13 +96,17 @@
 
     template <typename Images>
     void read_image(any_image<Images>& im) {
- int width,height;
- unsigned short bps,photometric;
- TIFFGetField(_tp,TIFFTAG_IMAGEWIDTH,&width);
- TIFFGetField(_tp,TIFFTAG_IMAGELENGTH,&height);
- TIFFGetField(_tp,TIFFTAG_BITSPERSAMPLE,&bps);
- TIFFGetField(_tp,TIFFTAG_PHOTOMETRIC,&photometric);
- if (!construct_matched(im,tiff_type_format_checker(bps,photometric))) {
+ int width;
+ int height;
+ unsigned short bps=1;
+ unsigned short photometric = 1;
+ unsigned short samples_per_pixel = 0;
+ TIFFGetField(_tp, TIFFTAG_IMAGEWIDTH, &width);
+ TIFFGetField(_tp, TIFFTAG_IMAGELENGTH, &height);
+ TIFFGetField(_tp, TIFFTAG_BITSPERSAMPLE,&bps);
+ TIFFGetField(_tp, TIFFTAG_PHOTOMETRIC, &photometric);
+ TIFFGetField(_tp, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
+ if (!construct_matched(im, tiff_type_format_checker(bps,photometric, samples_per_pixel))) {
             io_error("tiff_reader_dynamic::read_image(): no matching image type between those of the given any_image and that of the file");
         } else {
             im.recreate(width,height);
Index: tiff_io.hpp
===================================================================
--- tiff_io.hpp (revision 48136)
+++ tiff_io.hpp (working copy)
@@ -1,6 +1,6 @@
 /*
     Copyright 2005-2007 Adobe Systems Incorporated
-
+
     Use, modification and distribution are subject to the Boost Software License,
     Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
     http://www.boost.org/LICENSE_1_0.txt).
@@ -51,6 +51,12 @@
     BOOST_STATIC_CONSTANT(int,color_type=PHOTOMETRIC_RGB);
 };
 template <>
+struct tiff_read_support_private<bits8,rgba_t> {
+ BOOST_STATIC_CONSTANT(bool,is_supported=true);
+ BOOST_STATIC_CONSTANT(int,bit_depth=8);
+ BOOST_STATIC_CONSTANT(int,color_type=PHOTOMETRIC_RGB);
+};
+template <>
 struct tiff_read_support_private<bits16,gray_t> {
     BOOST_STATIC_CONSTANT(bool,is_supported=true);
     BOOST_STATIC_CONSTANT(int,bit_depth=16);
@@ -94,6 +100,12 @@
     BOOST_STATIC_CONSTANT(int,color_type=PHOTOMETRIC_RGB);
 };
 template <>
+struct tiff_write_support_private<bits8,rgba_t> {
+ BOOST_STATIC_CONSTANT(bool,is_supported=true);
+ BOOST_STATIC_CONSTANT(int,bit_depth=8);
+ BOOST_STATIC_CONSTANT(int,color_type=PHOTOMETRIC_RGB);
+};
+template <>
 struct tiff_write_support_private<bits16,gray_t> {
     BOOST_STATIC_CONSTANT(bool,is_supported=true);
     BOOST_STATIC_CONSTANT(int,bit_depth=16);
@@ -129,16 +141,18 @@
     ~tiff_reader() { TIFFClose(_tp); }
     template <typename View>
     void apply(const View& view) {
- unsigned short bps,photometric;
+ unsigned short bps,spp, photometric;
         point2<std::ptrdiff_t> dims=get_dimensions();
         io_error_if(TIFFGetField(_tp,TIFFTAG_BITSPERSAMPLE,&bps)!=1);
         io_error_if(TIFFGetField(_tp,TIFFTAG_PHOTOMETRIC,&photometric)!=1);
+ io_error_if(TIFFGetField(_tp,TIFFTAG_SAMPLESPERPIXEL,&spp)!=1);
         io_error_if(dims!=view.dimensions(),
                     "tiff_read_view: input view size does not match TIFF file size");
         io_error_if(tiff_read_support_private<typename channel_type<View>::type,
                                               typename color_space_type<View>::type>::bit_depth!=bps ||
                     tiff_read_support_private<typename channel_type<View>::type,
- typename color_space_type<View>::type>::color_type!=photometric,
+ typename color_space_type<View>::type>::color_type!=photometric ||
+ mpl::size<typename color_space_type<View>::type>::value != spp,
                     "tiff_read_view: input view type is incompatible with the image type");
         std::size_t element_size=sizeof(pixel<typename channel_type<View>::type,
                                               layout<typename color_space_type<View>::type> >);
@@ -166,14 +180,14 @@
 };
 
 // This code will be simplified...
-template <typename CC>
+template <typename CC>
 class tiff_reader_color_convert : public tiff_reader {
 private:
     CC _cc;
 public:
- tiff_reader_color_convert(const char* filename) :
+ tiff_reader_color_convert(const char* filename) :
         tiff_reader(filename) {}
- tiff_reader_color_convert(const char* filename,CC cc_in) :
+ tiff_reader_color_convert(const char* filename,CC cc_in) :
         tiff_reader(filename),_cc(cc_in) {}
     template <typename View>
     void apply(const View& view) {
@@ -273,7 +287,7 @@
         default: {
             // reads an image in incompatible format via TIFFReadRGBAImage
             rgba8_image_t rgbaImg(dims);
- io_error_if(!TIFFReadRGBAImage(_tp, dims.x, dims.y, (uint32*)&gil::view(rgbaImg)(0,0), 0),
+ io_error_if(!TIFFReadRGBAImage(_tp, dims.x, dims.y, (uint32*)&gil::view(rgbaImg)(0,0), 0),
                 "tiff_reader_color_convert::unsupported image format");
             copy_and_convert_pixels(flipped_up_down_view(const_view(rgbaImg)), view, _cc);
         }
@@ -353,7 +367,7 @@
 /// \ingroup TIFF_IO
 /// \brief Loads the image specified by the given tiff image file name into the given view.
 /// Triggers a compile assert if the view color space and channel depth are not supported by the TIFF library or by the I/O extension.
-/// Throws std::ios_base::failure if the file is not a valid TIFF file, or if its color space or channel depth are not
+/// 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) {
@@ -372,7 +386,7 @@
 /// \ingroup TIFF_IO
 /// \brief Allocates a new image whose dimensions are determined by the given tiff image file, and loads the pixels into it.
 /// Triggers a compile assert if the image color space or channel depth are not supported by the TIFF library or by the I/O extension.
-/// Throws std::ios_base::failure if the file is not a valid TIFF file, or if its color space or channel depth are not
+/// 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) {


/******************************************************************************
 * __LICENCE_BEGIN__
 *
 * __LICENCE_END__
 *****************************************************************************/

///@file
///@brief Test that RGBA images are detected by the TIFF reader.
///
///@author john.femiani_at_[hidden]
///
///

#include <boost/test/unit_test.hpp>

#include <boost/gil/gil_all.hpp>
#include <boost/gil/extension/io/tiff_io.hpp>

namespace gil = boost::gil;

BOOST_AUTO_TEST_CASE(test_tiff_read_rgba8_image){
    gil::rgba8_image_t rgb;
    ::boost::gil::tiff_read_image("images/rgba.tif", rgb);

    BOOST_CHECK_MESSAGE(rgb.width() == 86,
                        "rgb.width() == 86 failed: Was " << rgb.width());
    BOOST_CHECK_MESSAGE(rgb.height() == 64,
                        "rgb.height() == 64 failed: Was " << rgb.width());

    for (int y = 0; y < rgb.height(); ++y){
        for (int x = 0; x < rgb.width(); ++x){
             gil::rgba8_pixel_t pix = view(rgb)(x,y);
             BOOST_REQUIRE_MESSAGE(pix == gil::rgba8_pixel_t(128,128,128,255),
                                   "pix!=128,128,128,255 found pix=" <<
                                   (int)pix[0] << "," <<
                                   (int)pix[1] << "," <<
                                   (int)pix[2] << "," <<
                                   (int)pix[3] <<
                                   " at x="<<x<<", y=" << y << ".");
         }
    }

}

BOOST_AUTO_TEST_CASE(test_tiff_read_rgb8_image_fails_on_rgba){

    try {
        gil::rgb8_image_t rgb;
        ::boost::gil::tiff_read_image("images/rgba.tif", rgb);
        BOOST_FAIL("Reading an rgb image from an rgba tif should fail.");
    } catch (std::ios_base::failure& err ){
        //Good, we should have an io error!
    }
}




Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net