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<VecX>(row,col)[] to be (*image.ptr<VecX>(row,col))[] to correctly access VecX array elements, as ptr<VecX>() 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.
This commit is contained in:
gdkessler 2017-11-20 07:07:24 -05:00 committed by Alexander Alekhin
parent c7f1843584
commit 2674c6b5e0

View File

@ -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<Vec3b>(row,col)[channel] = newValue; }
else if( image.depth() == CV_16U ){ image.ptr<Vec3s>(row,col)[channel] = newValue; }
else if( image.depth() == CV_16S ){ image.ptr<Vec3s>(row,col)[channel] = newValue; }
else if( image.depth() == CV_32S ){ image.ptr<Vec3i>(row,col)[channel] = newValue; }
else if( image.depth() == CV_32F ){ image.ptr<Vec3f>(row,col)[channel] = newValue; }
else if( image.depth() == CV_64F ){ image.ptr<Vec3d>(row,col)[channel] = newValue; }
if( image.depth() == CV_8U ){ (*image.ptr<Vec3b>(row,col))[channel] = newValue; }
else if( image.depth() == CV_16U ){ (*image.ptr<Vec3s>(row,col))[channel] = newValue; }
else if( image.depth() == CV_16S ){ (*image.ptr<Vec3s>(row,col))[channel] = newValue; }
else if( image.depth() == CV_32S ){ (*image.ptr<Vec3i>(row,col))[channel] = newValue; }
else if( image.depth() == CV_32F ){ (*image.ptr<Vec3f>(row,col))[channel] = newValue; }
else if( image.depth() == CV_64F ){ (*image.ptr<Vec3d>(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<Vec3b>(row,col)[channel] = newValue; }
else if( image.depth() == CV_16U && channel < 4 ){ image.ptr<Vec3s>(row,col)[channel] = newValue; }
else if( image.depth() == CV_16S && channel < 4 ){ image.ptr<Vec3s>(row,col)[channel] = newValue; }
else if( image.depth() == CV_32S && channel < 4 ){ image.ptr<Vec3i>(row,col)[channel] = newValue; }
else if( image.depth() == CV_32F && channel < 4 ){ image.ptr<Vec3f>(row,col)[channel] = newValue; }
else if( image.depth() == CV_64F && channel < 4 ){ image.ptr<Vec3d>(row,col)[channel] = newValue; }
else if( image.depth() == CV_8U && channel < 4 ){ (*image.ptr<Vec3b>(row,col))[channel] = newValue; }
else if( image.depth() == CV_16U && channel < 4 ){ (*image.ptr<Vec3s>(row,col))[channel] = newValue; }
else if( image.depth() == CV_16S && channel < 4 ){ (*image.ptr<Vec3s>(row,col))[channel] = newValue; }
else if( image.depth() == CV_32S && channel < 4 ){ (*image.ptr<Vec3i>(row,col))[channel] = newValue; }
else if( image.depth() == CV_32F && channel < 4 ){ (*image.ptr<Vec3f>(row,col))[channel] = newValue; }
else if( image.depth() == CV_64F && channel < 4 ){ (*image.ptr<Vec3d>(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<Vec4b>(row,col)[channel] = newValue; }
else if( image.depth() == CV_16U ){ image.at<Vec4s>(row,col)[channel] = newValue; }
else if( image.depth() == CV_16S ){ image.at<Vec4s>(row,col)[channel] = newValue; }
else if( image.depth() == CV_32S ){ image.at<Vec4i>(row,col)[channel] = newValue; }
else if( image.depth() == CV_32F ){ image.at<Vec4f>(row,col)[channel] = newValue; }
else if( image.depth() == CV_64F ){ image.at<Vec4d>(row,col)[channel] = newValue; }
if( image.depth() == CV_8U ){ (*image.ptr<Vec4b>(row,col))[channel] = newValue; }
else if( image.depth() == CV_16U ){ (*image.ptr<Vec4s>(row,col))[channel] = newValue; }
else if( image.depth() == CV_16S ){ (*image.ptr<Vec4s>(row,col))[channel] = newValue; }
else if( image.depth() == CV_32S ){ (*image.ptr<Vec4i>(row,col))[channel] = newValue; }
else if( image.depth() == CV_32F ){ (*image.ptr<Vec4f>(row,col))[channel] = newValue; }
else if( image.depth() == CV_64F ){ (*image.ptr<Vec4d>(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 );
}
}
}