From 2674c6b5e0d564071dd40f0364993e2abef294af Mon Sep 17 00:00:00 2001 From: gdkessler Date: Mon, 20 Nov 2017 07:07:24 -0500 Subject: [PATCH] Merge pull request #10093 from gdkessler/gdal_image_read_fix_10089 Fix GDAL image decoding color problems identified by issue #10089, by: (#10093) * Fix GDAL image decoding color problems identified by issue #10089, by: Fixing CV_8UC1 symbol, which should be CV_8UC3 for RGB GDAL color table images. Fixing image.ptr(row,col)[] to be (*image.ptr(row,col))[] to correctly access VecX array elements, as ptr() returns a pointer to the VecX, not the first element of VecX. This fixes the color problem with color table gif images, and avoids out-of-bounds memory access. Respecting the color identification of raster bands provided by the GDAL image driver, and produce BGR or BGRA images. Note that color bands of images using the HSL, CMY, CMYK, or YCbCr color space are ignored, rather than converting them to BGR. * When reading image files using the GDAL decoder, exit with an error if a color band is encountered that isn't used (eg. from CMYK or YCbCbr), rather than silently ignoring the band's data. --- modules/imgcodecs/src/grfmt_gdal.cpp | 66 +++++++++++++++++++--------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_gdal.cpp b/modules/imgcodecs/src/grfmt_gdal.cpp index 1a5a680a9b..c04e287711 100644 --- a/modules/imgcodecs/src/grfmt_gdal.cpp +++ b/modules/imgcodecs/src/grfmt_gdal.cpp @@ -78,7 +78,7 @@ int gdalPaletteInterpretation2OpenCV( GDALPaletteInterp const& paletteInterp, G /// RGB case GPI_RGB: - if( gdalType == GDT_Byte ){ return CV_8UC1; } + if( gdalType == GDT_Byte ){ return CV_8UC3; } if( gdalType == GDT_UInt16 ){ return CV_16UC3; } if( gdalType == GDT_Int16 ){ return CV_16SC3; } if( gdalType == GDT_UInt32 ){ return CV_32SC3; } @@ -256,35 +256,35 @@ void write_pixel( const double& pixelValue, // input: 3 channel, output: 3 channel else if( gdalChannels == 3 && image.channels() == 3 ){ - if( image.depth() == CV_8U ){ image.at(row,col)[channel] = newValue; } - else if( image.depth() == CV_16U ){ image.ptr(row,col)[channel] = newValue; } - else if( image.depth() == CV_16S ){ image.ptr(row,col)[channel] = newValue; } - else if( image.depth() == CV_32S ){ image.ptr(row,col)[channel] = newValue; } - else if( image.depth() == CV_32F ){ image.ptr(row,col)[channel] = newValue; } - else if( image.depth() == CV_64F ){ image.ptr(row,col)[channel] = newValue; } + if( image.depth() == CV_8U ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_16U ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_16S ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_32S ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_32F ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_64F ){ (*image.ptr(row,col))[channel] = newValue; } else{ throw std::runtime_error("Unknown image depth, gdal: 3, image: 3"); } } // input: 4 channel, output: 3 channel else if( gdalChannels == 4 && image.channels() == 3 ){ if( channel >= 4 ){ return; } - else if( image.depth() == CV_8U && channel < 4 ){ image.ptr(row,col)[channel] = newValue; } - else if( image.depth() == CV_16U && channel < 4 ){ image.ptr(row,col)[channel] = newValue; } - else if( image.depth() == CV_16S && channel < 4 ){ image.ptr(row,col)[channel] = newValue; } - else if( image.depth() == CV_32S && channel < 4 ){ image.ptr(row,col)[channel] = newValue; } - else if( image.depth() == CV_32F && channel < 4 ){ image.ptr(row,col)[channel] = newValue; } - else if( image.depth() == CV_64F && channel < 4 ){ image.ptr(row,col)[channel] = newValue; } + else if( image.depth() == CV_8U && channel < 4 ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_16U && channel < 4 ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_16S && channel < 4 ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_32S && channel < 4 ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_32F && channel < 4 ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_64F && channel < 4 ){ (*image.ptr(row,col))[channel] = newValue; } else{ throw std::runtime_error("Unknown image depth, gdal: 4, image: 3"); } } // input: 4 channel, output: 4 channel else if( gdalChannels == 4 && image.channels() == 4 ){ - if( image.depth() == CV_8U ){ image.at(row,col)[channel] = newValue; } - else if( image.depth() == CV_16U ){ image.at(row,col)[channel] = newValue; } - else if( image.depth() == CV_16S ){ image.at(row,col)[channel] = newValue; } - else if( image.depth() == CV_32S ){ image.at(row,col)[channel] = newValue; } - else if( image.depth() == CV_32F ){ image.at(row,col)[channel] = newValue; } - else if( image.depth() == CV_64F ){ image.at(row,col)[channel] = newValue; } + if( image.depth() == CV_8U ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_16U ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_16S ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_32S ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_32F ){ (*image.ptr(row,col))[channel] = newValue; } + else if( image.depth() == CV_64F ){ (*image.ptr(row,col))[channel] = newValue; } else{ throw std::runtime_error("Unknown image depth, gdal: 4, image: 4"); } } @@ -388,6 +388,30 @@ bool GdalDecoder::readData( Mat& img ){ // get the GDAL Band GDALRasterBand* band = m_dataset->GetRasterBand(c+1); + /* Map palette band and gray band to color index 0 and red, green, + blue, alpha bands to BGRA indexes. Note: ignoring HSL, CMY, + CMYK, and YCbCr color spaces, rather than converting them + to BGR. */ + int color = 0; + switch (band->GetColorInterpretation()) { + case GCI_PaletteIndex: + case GCI_GrayIndex: + case GCI_BlueBand: + color = 0; + break; + case GCI_GreenBand: + color = 1; + break; + case GCI_RedBand: + color = 2; + break; + case GCI_AlphaBand: + color = 3; + break; + default: + CV_ErrorNoReturn(cv::Error::StsError, "Invalid/unsupported mode"); + } + // make sure the image band has the same dimensions as the image if( band->GetXSize() != m_width || band->GetYSize() != m_height ){ return false; } @@ -410,10 +434,10 @@ bool GdalDecoder::readData( Mat& img ){ // set depending on image types // given boost, I would use enable_if to speed up. Avoid for now. if( hasColorTable == false ){ - write_pixel( scanline[x], gdalType, nChannels, img, y, x, c ); + write_pixel( scanline[x], gdalType, nChannels, img, y, x, color ); } else{ - write_ctable_pixel( scanline[x], gdalType, gdalColorTable, img, y, x, c ); + write_ctable_pixel( scanline[x], gdalType, gdalColorTable, img, y, x, color ); } } }