Merge pull request #25280 from Kumataro:fix25274

imgcodecs: jpeg: re-support to read CMYK Jpeg #25280

Close #25274 
OpenCV Extra: https://github.com/opencv/opencv_extra/pull/1163

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
pull/25299/head
Kumataro 8 months ago committed by GitHub
parent b758897c29
commit 912cf2a028
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 56
      modules/imgcodecs/src/grfmt_jpeg.cpp
  2. 38
      modules/imgcodecs/test/test_jpeg.cpp

@ -402,16 +402,12 @@ int my_jpeg_load_dht (struct jpeg_decompress_struct *info, unsigned char *dht,
bool JpegDecoder::readData( Mat& img )
{
volatile bool result = false;
size_t step = img.step;
bool color = img.channels() > 1;
const bool color = img.channels() > 1;
if( m_state && m_width && m_height )
{
jpeg_decompress_struct* cinfo = &((JpegState*)m_state)->cinfo;
JpegErrorMgr* jerr = &((JpegState*)m_state)->jerr;
#ifndef JCS_EXTENSIONS
JSAMPARRAY buffer = 0;
#endif
if( setjmp( jerr->setjmp_buffer ) == 0 )
{
@ -431,29 +427,30 @@ bool JpegDecoder::readData( Mat& img )
}
#endif
#ifdef JCS_EXTENSIONS
// See https://github.com/opencv/opencv/issues/25274
// Conversion CMYK->BGR is not supported in libjpeg-turbo.
// So supporting both directly and indirectly is necessary.
bool doDirectRead = false;
if( color )
{
if( cinfo->num_components != 4 )
{
#ifdef JCS_EXTENSIONS
cinfo->out_color_space = JCS_EXT_BGR;
cinfo->out_color_components = 3;
}
else
{
cinfo->out_color_space = JCS_GRAYSCALE;
cinfo->out_color_components = 1;
}
doDirectRead = true; // BGR -> BGR
#else
if( color )
{
if( cinfo->num_components != 4 )
{
cinfo->out_color_space = JCS_RGB;
cinfo->out_color_components = 3;
doDirectRead = false; // RGB -> BGR
#endif
}
else
{
cinfo->out_color_space = JCS_CMYK;
cinfo->out_color_components = 4;
doDirectRead = false; // CMYK -> BGR
}
}
else
@ -462,14 +459,15 @@ bool JpegDecoder::readData( Mat& img )
{
cinfo->out_color_space = JCS_GRAYSCALE;
cinfo->out_color_components = 1;
doDirectRead = true; // GRAY -> GRAY
}
else
{
cinfo->out_color_space = JCS_CMYK;
cinfo->out_color_components = 4;
doDirectRead = false; // CMYK -> GRAY
}
}
#endif
// Check for Exif marker APP1
jpeg_saved_marker_ptr exif_marker = NULL;
@ -496,18 +494,24 @@ bool JpegDecoder::readData( Mat& img )
jpeg_start_decompress( cinfo );
#ifndef JCS_EXTENSIONS
buffer = (*cinfo->mem->alloc_sarray)((j_common_ptr)cinfo,
if( doDirectRead)
{
for( int iy = 0 ; iy < m_height; iy ++ )
{
uchar* data = img.ptr<uchar>(iy);
jpeg_read_scanlines( cinfo, &data, 1 );
}
}
else
{
JSAMPARRAY buffer = (*cinfo->mem->alloc_sarray)((j_common_ptr)cinfo,
JPOOL_IMAGE, m_width*4, 1 );
#endif
uchar* data = img.ptr();
for( ; m_height--; data += step )
for( int iy = 0 ; iy < m_height; iy ++ )
{
#ifdef JCS_EXTENSIONS
jpeg_read_scanlines( cinfo, &data, 1 );
#else
uchar* data = img.ptr<uchar>(iy);
jpeg_read_scanlines( cinfo, buffer, 1 );
if( color )
{
if( cinfo->out_color_components == 3 )
@ -522,7 +526,7 @@ bool JpegDecoder::readData( Mat& img )
else
icvCvt_CMYK2Gray_8u_C4C1R( buffer[0], 0, data, 0, Size(m_width,1) );
}
#endif
}
}
result = true;

@ -178,6 +178,44 @@ TEST(Imgcodecs_Jpeg, encode_decode_rst_jpeg)
EXPECT_EQ(0, remove(output_normal.c_str()));
}
// See https://github.com/opencv/opencv/issues/25274
typedef testing::TestWithParam<int> Imgcodecs_Jpeg_decode_cmyk;
TEST_P(Imgcodecs_Jpeg_decode_cmyk, regression25274)
{
const int imread_flag = GetParam();
/*
* "test_1_c4.jpg" is CMYK-JPEG.
* $ convert test_1_c3.jpg -colorspace CMYK test_1_c4.jpg
* $ identify test_1_c4.jpg
* test_1_c4.jpg JPEG 480x640 480x640+0+0 8-bit CMYK 11240B 0.000u 0:00.000
*/
cvtest::TS& ts = *cvtest::TS::ptr();
string rgb_filename = string(ts.get_data_path()) + "readwrite/test_1_c3.jpg";
cv::Mat rgb_img = cv::imread(rgb_filename, imread_flag);
ASSERT_FALSE(rgb_img.empty());
string cmyk_filename = string(ts.get_data_path()) + "readwrite/test_1_c4.jpg";
cv::Mat cmyk_img = cv::imread(cmyk_filename, imread_flag);
ASSERT_FALSE(cmyk_img.empty());
EXPECT_EQ(rgb_img.size(), cmyk_img.size());
EXPECT_EQ(rgb_img.type(), cmyk_img.type());
// Jpeg is lossy compression.
// There may be small differences in decoding results by environments.
// -> 255 * 1% = 2.55 .
EXPECT_EQ(3, cvtest::norm(rgb_img, cmyk_img, NORM_INF));
}
INSTANTIATE_TEST_CASE_P( /* nothing */,
Imgcodecs_Jpeg_decode_cmyk,
testing::Values(cv::IMREAD_COLOR,
cv::IMREAD_GRAYSCALE,
cv::IMREAD_ANYCOLOR));
//==================================================================================================
static const uint32_t default_sampling_factor = static_cast<uint32_t>(0x221111);

Loading…
Cancel
Save