diff --git a/bob/io/image/__init__.py b/bob/io/image/__init__.py index 5416a3296e6331c4333f6091b45ff277d5658d49..4081dc29e1b42c72793615681ea5150b0dae4021 100644 --- a/bob/io/image/__init__.py +++ b/bob/io/image/__init__.py @@ -17,6 +17,12 @@ def get_config(): import bob.extension return bob.extension.get_config(__name__, version.externals) +# fix imghdr's jpeg detection to use the first two bytes (according to https://en.wikipedia.org/wiki/List_of_file_signatures) +import imghdr +def _test_jpeg(h, f): + if h.startswith('\xff\xd8'): + return "jpeg" +imghdr.tests.append(_test_jpeg) def load(filename, extension=None): """load(filename) -> image @@ -48,8 +54,10 @@ def load(filename, extension=None): f = bob.io.base.File(filename, 'r') else: if extension == 'auto': - import imghdr - extension = "." + imghdr.what(filename) + extension = imghdr.what(filename) + if extension is None: + raise IOError("Could not detect the image type of file %s" % filename) + extension = "." + extension f = bob.io.base.File(filename, 'r', extension) return f.read() diff --git a/bob/io/image/cpp/png.cpp b/bob/io/image/cpp/png.cpp index e2ad1a75b8f1c9dbd642c7c7ce19bc979bd9137c..75e244b8791a115e2e835ba05b8c21922b747ad5 100644 --- a/bob/io/image/cpp/png.cpp +++ b/bob/io/image/cpp/png.cpp @@ -99,13 +99,7 @@ static void im_peek(const std::string& path, bob::io::base::array::typeinfo& inf // Set depth and number of dimensions info.dtype = (bit_depth <= 8 ? bob::io::base::array::t_uint8 : bob::io::base::array::t_uint16); - if(color_type == PNG_COLOR_TYPE_GRAY) - info.nd = 2; - else if (color_type == PNG_COLOR_TYPE_RGB || PNG_COLOR_TYPE_RGB_ALPHA || PNG_COLOR_TYPE_PALETTE) - info.nd = 3; - else {// Unsupported color type - throw std::runtime_error("PNG: codec does not support images with color spaces different than GRAY, RGB, RGBA or Indexed colors (Palette)"); - } + info.nd = color_type & PNG_COLOR_MASK_COLOR ? 3 : 2; if(info.nd == 2) { info.shape[0] = height; @@ -149,18 +143,18 @@ void im_load_gray(png_structp png_ptr, bob::io::base::array::interface& b) } template <typename T> static -void imbuffer_to_rgb(const size_t size, const T* im, T* r, T* g, T* b, const size_t number_of_elements) +void imbuffer_to_rgb(const size_t size, const T* im, T* r, T* g, T* b) { for(size_t k=0; k<size; ++k) { - r[k] = im[number_of_elements*k]; - g[k] = im[number_of_elements*k +1]; - b[k] = im[number_of_elements*k +2]; + *r++ = *im++; + *g++ = *im++; + *b++ = *im++; } } template <typename T> static -void im_load_color(png_structp png_ptr, bob::io::base::array::interface& b, bool has_alpha) +void im_load_color(png_structp png_ptr, bob::io::base::array::interface& b) { const bob::io::base::array::typeinfo& info = b.type(); const size_t height = info.shape[1]; @@ -168,10 +162,8 @@ void im_load_color(png_structp png_ptr, bob::io::base::array::interface& b, bool const size_t frame_size = height * width; const size_t row_color_stride = width; - const size_t number_of_elements = has_alpha ? 4 : 3; - // Allocate array to contains a row of RGB-like pixels - boost::shared_array<T> row(new T[number_of_elements*width]); + boost::shared_array<T> row(new T[3*width]); png_bytep row_pointer = reinterpret_cast<png_bytep>(row.get()); #ifdef PNG_READ_INTERLACING_SUPPORTED @@ -195,7 +187,7 @@ void im_load_color(png_structp png_ptr, bob::io::base::array::interface& b, bool for(size_t y=0; y<height; ++y) { png_read_row(png_ptr, row_pointer, NULL); - imbuffer_to_rgb(row_color_stride, reinterpret_cast<T*>(row_pointer), element_r, element_g, element_b, number_of_elements); + imbuffer_to_rgb(row_color_stride, reinterpret_cast<T*>(row_pointer), element_r, element_g, element_b); element_r += row_color_stride; element_g += row_color_stride; element_b += row_color_stride; @@ -256,20 +248,28 @@ static void im_load(const std::string& filename, bob::io::base::array::interface png_set_expand_gray_1_2_4_to_8(png_ptr); else if(color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); - // We currently only support grayscale and rgb images - if(color_type != PNG_COLOR_TYPE_GRAY && - color_type != PNG_COLOR_TYPE_RGB && - color_type != PNG_COLOR_TYPE_PALETTE && - color_type != PNG_COLOR_TYPE_RGB_ALPHA) { - throw std::runtime_error("PNG: codec does not support images with color spaces different than GRAY, RGB, RGBA or Indexed colors (Palette)"); + // skip the alpha channel + if ((color_type & PNG_COLOR_MASK_ALPHA) || png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_strip_alpha(png_ptr); + + // Check color type + switch (color_type){ + // I think that should be all, but in case a new version comes up with a different color space... + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_GRAY_ALPHA: + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_PALETTE: + case PNG_COLOR_TYPE_RGB_ALPHA: + break; + default: + throw std::runtime_error("PNG: codec does not support images with color spaces different than GRAY, GRAY+alpha, RGB, RGB+alpha or Indexed colors (Palette)"); } // 7. Read content const bob::io::base::array::typeinfo& info = b.type(); - bool has_alpha = (color_type & PNG_COLOR_MASK_ALPHA) == PNG_COLOR_MASK_ALPHA; if(info.dtype == bob::io::base::array::t_uint8) { if(info.nd == 2) im_load_gray<uint8_t>(png_ptr, b); - else if( info.nd == 3) im_load_color<uint8_t>(png_ptr, b, has_alpha); + else if(info.nd == 3) im_load_color<uint8_t>(png_ptr, b); else { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); boost::format m("the image in file `%s' has a number of dimensions for which this png codec has no support for: %s"); @@ -279,7 +279,7 @@ static void im_load(const std::string& filename, bob::io::base::array::interface } else if(info.dtype == bob::io::base::array::t_uint16) { if(info.nd == 2) im_load_gray<uint16_t>(png_ptr, b); - else if( info.nd == 3) im_load_color<uint16_t>(png_ptr, b, has_alpha); + else if( info.nd == 3) im_load_color<uint16_t>(png_ptr, b); else { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); boost::format m("the image in file `%s' has a number of dimensions for which this png codec has no support for: %s"); @@ -328,9 +328,9 @@ void rgb_to_imbuffer(const size_t size, const T* r, const T* g, const T* b, T* i { for (size_t k=0; k<size; ++k) { - im[3*k] = r[k]; - im[3*k+1] = g[k]; - im[3*k+2] = b[k]; + *im++ = *r++; + *im++ = *g++; + *im++ = *b++; } } diff --git a/bob/io/image/data/img_gray_alpha.png b/bob/io/image/data/img_gray_alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..7643ce025d21ec8385a93836180167d3ce62167e Binary files /dev/null and b/bob/io/image/data/img_gray_alpha.png differ diff --git a/bob/io/image/data/img_indexed_color_alpha.png b/bob/io/image/data/img_indexed_color_alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..defabb829d44bcd28b8939afbc64c1b3f9143342 Binary files /dev/null and b/bob/io/image/data/img_indexed_color_alpha.png differ diff --git a/bob/io/image/data/img_trns.png b/bob/io/image/data/img_trns.png new file mode 100644 index 0000000000000000000000000000000000000000..7c16aab16960149a4762610eb8c6827c0814600d Binary files /dev/null and b/bob/io/image/data/img_trns.png differ diff --git a/bob/io/image/test.py b/bob/io/image/test.py index c53e85f877b020da5daf8b6fc80d99f64d0e47ad..52c8032b4ef329023357c1d83ada50a019506d86 100644 --- a/bob/io/image/test.py +++ b/bob/io/image/test.py @@ -16,26 +16,48 @@ import nose # These are some global parameters for the test. PNG_INDEXED_COLOR = test_utils.datafile('img_indexed_color.png', __name__) +PNG_INDEXED_COLOR_ALPHA = test_utils.datafile('img_indexed_color_alpha.png', __name__) PNG_RGBA_COLOR = test_utils.datafile('img_rgba_color.png', __name__) +PNG_GRAY_ALPHA = test_utils.datafile('img_gray_alpha.png', __name__) +PNG_tRNS = test_utils.datafile('img_trns.png', __name__) def test_png_indexed_color(): - # Read an indexed color PNG image, and compared with hardcoded values img = load(PNG_INDEXED_COLOR) assert img.shape == (3, 22, 32) assert img[0, 0, 0] == 255 assert img[0, 17, 17] == 117 - def test_png_rgba_color(): - # Read an indexed color PNG image, and compared with hardcoded values img = load(PNG_RGBA_COLOR) assert img.shape == (3, 22, 32) assert img[0, 0, 0] == 255 assert img[0, 17, 17] == 117 +def test_png_indexed_color_alpha(): + # Read an indexed color+alpha PNG image, and compared with hardcoded values + img = load(PNG_INDEXED_COLOR_ALPHA) + assert img.shape == (3,22,32) + assert img[0,0,0] == 255 + assert img[0,17,17] == 117 + +def test_png_indexed_trns(): + # Read an tRNS PNG image (without alpha), and compared with hardcoded values + img = load(PNG_tRNS) + assert img.shape == (3,22,32) + assert img[0,0,0] == 255 + assert img[0,17,17] == 117 + +def test_png_gray_alpha(): + # Read a gray+alpha PNG image, and compared with hardcoded values + img = load(PNG_GRAY_ALPHA) + assert img.shape == (22,32) + assert img[0,0] == 255 + assert img[17,17] == 51 + + def transcode(filename):