From 934e6899f87badc50145c2cef5941d8422ac8e0f Mon Sep 17 00:00:00 2001 From: zihaomu Date: Wed, 3 Jul 2024 15:58:25 +0800 Subject: [PATCH] Merge pull request #25809 from zihaomu:imread_rgb_flag imgcodecs: Add rgb flag for imread and imdecode #25809 Try to `imread` images by RGB to save R-B swapping costs. ## How to use it? ``` img_rgb = cv2.imread("PATH", IMREAD_COLOR_RGB) # OpenCV decode the image by RGB format. ``` ## TODO - [x] Fix the broken code - [x] Add imread rgb test - [x] Speed test of rgb mode. ## Performance test | file name | IMREAD_COLOR | IMREAD_COLOR_RGB | | --------- | ------ | --------- | | jpg01 | 284 ms | 277 ms | | jpg02 | 376 ms | 366 ms | | png01 | 62 ms | 60 ms | | Png02 | 97 ms | 94 ms | Test with [image_test.zip](https://github.com/user-attachments/files/15982949/image_test.zip) ```.cpp string img_path = "/Users/mzh/work/data/image_test/png02.png"; int loop = 20; TickMeter t; double t0 = 10000; for (int i = 0; i < loop; i++) { t.reset(); t.start(); img_bgr = imread(img_path, IMREAD_COLOR); t.stop(); if (t.getTimeMilli() < t0) t0 = t.getTimeMilli(); } std::cout<<"bgr time = "< file_buf((size_t)len); + EXPECT_EQ(len, (long)fread(&file_buf[0], 1, (size_t)len, f)); + fclose(f); f = NULL; + + TEST_CYCLE() imdecode(file_buf, IMREAD_COLOR_RGB); + + SANITY_CHECK_NOTHING(); +} + PERF_TEST(JPEG, Encode) { String filename = getDataPath("stitching/boat1.jpg"); diff --git a/modules/imgcodecs/perf/perf_png.cpp b/modules/imgcodecs/perf/perf_png.cpp index 1af4780882..5bbef590bd 100644 --- a/modules/imgcodecs/perf/perf_png.cpp +++ b/modules/imgcodecs/perf/perf_png.cpp @@ -30,6 +30,23 @@ PERF_TEST(PNG, decode) SANITY_CHECK_NOTHING(); } +PERF_TEST(PNG, decode_rgb) +{ + String filename = getDataPath("perf/2560x1600.png"); + + FILE *f = fopen(filename.c_str(), "rb"); + fseek(f, 0, SEEK_END); + long len = ftell(f); + fseek(f, 0, SEEK_SET); + vector file_buf((size_t)len); + EXPECT_EQ(len, (long)fread(&file_buf[0], 1, (size_t)len, f)); + fclose(f); f = NULL; + + TEST_CYCLE() imdecode(file_buf, IMREAD_COLOR_RGB); + + SANITY_CHECK_NOTHING(); +} + PERF_TEST(PNG, encode) { String filename = getDataPath("perf/2560x1600.png"); diff --git a/modules/imgcodecs/src/grfmt_avif.cpp b/modules/imgcodecs/src/grfmt_avif.cpp index 4b39ada60a..d71843c3f0 100644 --- a/modules/imgcodecs/src/grfmt_avif.cpp +++ b/modules/imgcodecs/src/grfmt_avif.cpp @@ -33,7 +33,7 @@ struct AvifImageDeleter { using AvifImageUniquePtr = std::unique_ptr; -avifResult CopyToMat(const avifImage *image, int channels, Mat *mat) { +avifResult CopyToMat(const avifImage *image, int channels, bool useRGB , Mat *mat) { CV_Assert((int)image->height == mat->rows); CV_Assert((int)image->width == mat->cols); if (channels == 1) { @@ -53,7 +53,10 @@ avifResult CopyToMat(const avifImage *image, int channels, Mat *mat) { avifRGBImage rgba; avifRGBImageSetDefaults(&rgba, image); if (channels == 3) { - rgba.format = AVIF_RGB_FORMAT_BGR; + if (useRGB) + rgba.format = AVIF_RGB_FORMAT_RGB; + else + rgba.format = AVIF_RGB_FORMAT_BGR; } else { CV_Assert(channels == 4); rgba.format = AVIF_RGB_FORMAT_BGRA; @@ -227,7 +230,7 @@ bool AvifDecoder::readData(Mat &img) { is_first_image_ = false; } - if (CopyToMat(decoder_->image, channels_, &read_img) != AVIF_RESULT_OK) { + if (CopyToMat(decoder_->image, channels_, m_use_rgb, &read_img) != AVIF_RESULT_OK) { CV_Error(Error::StsInternal, "Cannot convert from AVIF to Mat"); return false; } diff --git a/modules/imgcodecs/src/grfmt_base.cpp b/modules/imgcodecs/src/grfmt_base.cpp index 88f1c04f30..da343941bd 100644 --- a/modules/imgcodecs/src/grfmt_base.cpp +++ b/modules/imgcodecs/src/grfmt_base.cpp @@ -53,6 +53,7 @@ BaseImageDecoder::BaseImageDecoder() m_type = -1; m_buf_supported = false; m_scale_denom = 1; + m_use_rgb = false; } @@ -94,6 +95,11 @@ int BaseImageDecoder::setScale( const int& scale_denom ) return temp; } +void BaseImageDecoder::setRGB(bool useRGB) +{ + m_use_rgb = useRGB; +} + ImageDecoder BaseImageDecoder::newDecoder() const { return ImageDecoder(); diff --git a/modules/imgcodecs/src/grfmt_base.hpp b/modules/imgcodecs/src/grfmt_base.hpp index 816bef98fb..0d98c51ae2 100644 --- a/modules/imgcodecs/src/grfmt_base.hpp +++ b/modules/imgcodecs/src/grfmt_base.hpp @@ -73,6 +73,8 @@ public: virtual bool readHeader() = 0; virtual bool readData( Mat& img ) = 0; + virtual void setRGB(bool useRGB); + /// Called after readData to advance to the next page, if any. virtual bool nextPage() { return false; } @@ -89,6 +91,7 @@ protected: String m_signature; Mat m_buf; bool m_buf_supported; + bool m_use_rgb; // flag of decode image as RGB order instead of BGR. ExifReader m_exif; }; diff --git a/modules/imgcodecs/src/grfmt_bmp.cpp b/modules/imgcodecs/src/grfmt_bmp.cpp index 9c76597908..91ef23cc3f 100644 --- a/modules/imgcodecs/src/grfmt_bmp.cpp +++ b/modules/imgcodecs/src/grfmt_bmp.cpp @@ -544,6 +544,11 @@ decode_rle8_bad: ; throw; } + if (m_use_rgb && color && img.channels() == 3) + { + cv::cvtColor(img, img, cv::COLOR_BGR2RGB); + } + return result; } diff --git a/modules/imgcodecs/src/grfmt_exr.cpp b/modules/imgcodecs/src/grfmt_exr.cpp index 65a0e5e03b..d48e067edd 100644 --- a/modules/imgcodecs/src/grfmt_exr.cpp +++ b/modules/imgcodecs/src/grfmt_exr.cpp @@ -373,18 +373,35 @@ bool ExrDecoder::readData( Mat& img ) if( m_iscolor ) { - if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) ) - UpSample( data, channelstoread, step / xstep, m_blue->xSampling, m_blue->ySampling ); - if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) - UpSample( data + xstep, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling ); - if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) - UpSample( data + 2 * xstep, channelstoread, step / xstep, m_red->xSampling, m_red->ySampling ); + if (m_use_rgb) + { + if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) + UpSample( data, channelstoread, step / xstep, m_red->xSampling, m_red->ySampling ); + if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) + UpSample( data + xstep, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling ); + if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) ) + UpSample( data + 2 * xstep, channelstoread, step / xstep, m_blue->xSampling, m_blue->ySampling ); + } + else + { + if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) ) + UpSample( data, channelstoread, step / xstep, m_blue->xSampling, m_blue->ySampling ); + if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) + UpSample( data + xstep, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling ); + if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) + UpSample( data + 2 * xstep, channelstoread, step / xstep, m_red->xSampling, m_red->ySampling ); + } } else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) UpSample( data, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling ); if( chromatorgb ) - ChromaToBGR( (float *)data, m_height, channelstoread, step / xstep ); + { + if (m_use_rgb) + ChromaToRGB( (float *)data, m_height, channelstoread, step / xstep ); + else + ChromaToBGR( (float *)data, m_height, channelstoread, step / xstep ); + } } else { @@ -406,7 +423,12 @@ bool ExrDecoder::readData( Mat& img ) else { if( chromatorgb ) - ChromaToBGR( (float *)buffer, 1, defaultchannels, step ); + { + if (m_use_rgb) + ChromaToRGB( (float *)buffer, 1, defaultchannels, step ); + else + ChromaToBGR( (float *)buffer, 1, defaultchannels, step ); + } if( m_type == FLOAT ) { @@ -430,12 +452,24 @@ bool ExrDecoder::readData( Mat& img ) } if( color ) { - if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) ) - UpSampleY( data, defaultchannels, step / xstep, m_blue->ySampling ); - if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) - UpSampleY( data + xstep, defaultchannels, step / xstep, m_green->ySampling ); - if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) - UpSampleY( data + 2 * xstep, defaultchannels, step / xstep, m_red->ySampling ); + if (m_use_rgb) + { + if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) + UpSampleY( data, defaultchannels, step / xstep, m_red->ySampling ); + if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) + UpSampleY( data + xstep, defaultchannels, step / xstep, m_green->ySampling ); + if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) ) + UpSampleY( data + 2 * xstep, defaultchannels, step / xstep, m_blue->ySampling ); + } + else + { + if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) ) + UpSampleY( data, defaultchannels, step / xstep, m_blue->ySampling ); + if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) + UpSampleY( data + xstep, defaultchannels, step / xstep, m_green->ySampling ); + if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) + UpSampleY( data + 2 * xstep, defaultchannels, step / xstep, m_red->ySampling ); + } } else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) UpSampleY( data, 1, step / xstep, m_green->ySampling ); @@ -558,6 +592,47 @@ void ExrDecoder::ChromaToBGR( float *data, int numlines, int xstep, int ystep ) } } +void ExrDecoder::ChromaToRGB(float *data, int numlines, int xstep, int ystep) +{ + for( int y = 0; y < numlines; y++ ) + { + for( int x = 0; x < m_width; x++ ) + { + double b, Y, r; + if( m_type == FLOAT ) + { + b = data[y * ystep + x * xstep]; + Y = data[y * ystep + x * xstep + 1]; + r = data[y * ystep + x * xstep + 2]; + } + else + { + b = ((unsigned *)data)[y * ystep + x * xstep]; + Y = ((unsigned *)data)[y * ystep + x * xstep + 1]; + r = ((unsigned *)data)[y * ystep + x * xstep + 2]; + } + r = (r + 1) * Y; + b = (b + 1) * Y; + Y = (Y - b * m_chroma.blue[1] - r * m_chroma.red[1]) / m_chroma.green[1]; + + if( m_type == FLOAT ) + { + data[y * ystep + x * xstep] = (float)r; + data[y * ystep + x * xstep + 1] = (float)Y; + data[y * ystep + x * xstep + 2] = (float)b; + } + else + { + int t = cvRound(r); + ((unsigned *)data)[y * ystep + x * xstep + 0] = (unsigned)MAX(t, 0); + t = cvRound(Y); + ((unsigned *)data)[y * ystep + x * xstep + 1] = (unsigned)MAX(t, 0); + t = cvRound(b); + ((unsigned *)data)[y * ystep + x * xstep + 2] = (unsigned)MAX(t, 0); + } + } + } +} /** // convert one row to gray diff --git a/modules/imgcodecs/src/grfmt_exr.hpp b/modules/imgcodecs/src/grfmt_exr.hpp index a86874d228..48ca09acd8 100644 --- a/modules/imgcodecs/src/grfmt_exr.hpp +++ b/modules/imgcodecs/src/grfmt_exr.hpp @@ -83,6 +83,7 @@ protected: void UpSampleX( float *data, int xstep, int xsample ); void UpSampleY( uchar *data, int xstep, int ystep, int ysample ); void ChromaToBGR( float *data, int numlines, int xstep, int ystep ); + void ChromaToRGB( float *data, int numlines, int xstep, int ystep ); void RGBToGray( float *in, float *out ); InputFile *m_file; diff --git a/modules/imgcodecs/src/grfmt_gdal.cpp b/modules/imgcodecs/src/grfmt_gdal.cpp index 17581b576a..ff059338cf 100644 --- a/modules/imgcodecs/src/grfmt_gdal.cpp +++ b/modules/imgcodecs/src/grfmt_gdal.cpp @@ -397,13 +397,13 @@ bool GdalDecoder::readData( Mat& img ){ case GCI_PaletteIndex: case GCI_GrayIndex: case GCI_BlueBand: - color = 0; + color = m_use_rgb ? 2 : 0; break; case GCI_GreenBand: color = 1; break; case GCI_RedBand: - color = 2; + color = m_use_rgb ? 0 : 2; break; case GCI_AlphaBand: color = 3; diff --git a/modules/imgcodecs/src/grfmt_hdr.cpp b/modules/imgcodecs/src/grfmt_hdr.cpp index c9fec94aa3..3039626121 100644 --- a/modules/imgcodecs/src/grfmt_hdr.cpp +++ b/modules/imgcodecs/src/grfmt_hdr.cpp @@ -106,7 +106,13 @@ bool HdrDecoder::readData(Mat& _img) switch (_img.channels()) { case 1: cvtColor(img, _img, COLOR_BGR2GRAY); break; - case 3: img.copyTo(_img); break; + case 3: + // TODO, try to modify RGBE_ReadPixels_RLE to load rgb data directly. + if (m_use_rgb) + cv::cvtColor(img, _img, cv::COLOR_BGR2RGB); + else + img.copyTo(_img); + break; default: CV_Error(Error::StsError, "Wrong expected image channels, allowed: 1 and 3"); } return true; diff --git a/modules/imgcodecs/src/grfmt_jpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg.cpp index 4e3b1df48d..98019cc48a 100644 --- a/modules/imgcodecs/src/grfmt_jpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg.cpp @@ -437,13 +437,13 @@ bool JpegDecoder::readData( Mat& img ) if( cinfo->num_components != 4 ) { #ifdef JCS_EXTENSIONS - cinfo->out_color_space = JCS_EXT_BGR; + cinfo->out_color_space = m_use_rgb ? JCS_EXT_RGB : JCS_EXT_BGR; cinfo->out_color_components = 3; doDirectRead = true; // BGR -> BGR #else cinfo->out_color_space = JCS_RGB; cinfo->out_color_components = 3; - doDirectRead = false; // RGB -> BGR + doDirectRead = m_use_rgb ? true : false; // RGB -> BGR #endif } else @@ -514,10 +514,20 @@ bool JpegDecoder::readData( Mat& img ) if( color ) { - if( cinfo->out_color_components == 3 ) - icvCvt_RGB2BGR_8u_C3R( buffer[0], 0, data, 0, Size(m_width,1) ); + if (m_use_rgb) + { + if( cinfo->out_color_components == 3 ) + icvCvt_BGR2RGB_8u_C3R( buffer[0], 0, data, 0, Size(m_width,1) ); + else + icvCvt_CMYK2RGB_8u_C4C3R( buffer[0], 0, data, 0, Size(m_width,1) ); + } else - icvCvt_CMYK2BGR_8u_C4C3R( buffer[0], 0, data, 0, Size(m_width,1) ); + { + if( cinfo->out_color_components == 3 ) + icvCvt_RGB2BGR_8u_C3R( buffer[0], 0, data, 0, Size(m_width,1) ); + else + icvCvt_CMYK2BGR_8u_C4C3R( buffer[0], 0, data, 0, Size(m_width,1) ); + } } else { diff --git a/modules/imgcodecs/src/grfmt_jpeg2000.cpp b/modules/imgcodecs/src/grfmt_jpeg2000.cpp index 0f80d89c8d..9b8680ac1f 100644 --- a/modules/imgcodecs/src/grfmt_jpeg2000.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg2000.cpp @@ -286,11 +286,12 @@ bool Jpeg2KDecoder::readData( Mat& img ) { int ncmpts; int cmptlut[3]; + int swap_rb = m_use_rgb ? 0 : 2; if( color ) { - cmptlut[0] = jas_image_getcmptbytype( image, JAS_IMAGE_CT_RGB_B ); - cmptlut[1] = jas_image_getcmptbytype( image, JAS_IMAGE_CT_RGB_G ); - cmptlut[2] = jas_image_getcmptbytype( image, JAS_IMAGE_CT_RGB_R ); + cmptlut[0] = jas_image_getcmptbytype( image, swap_rb ); + cmptlut[1] = jas_image_getcmptbytype( image, 1 ); + cmptlut[2] = jas_image_getcmptbytype( image, swap_rb^2 ); if( cmptlut[0] < 0 || cmptlut[1] < 0 || cmptlut[2] < 0 ) result = false; ncmpts = 3; diff --git a/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp index c5b1a292cc..c05c0bca0e 100644 --- a/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp @@ -350,7 +350,7 @@ opj_cparameters setupEncoderParameters(const std::vector& params) return parameters; } -bool decodeSRGBData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shift) +bool decodeSRGBData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shift, bool use_rgb) { using ImageComponents = std::vector; @@ -377,8 +377,9 @@ bool decodeSRGBData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shift) if (inChannels >= 3) { + int swap_rb = use_rgb ? 0 : 2; // Assume RGB (+ alpha) for 3 channels -> BGR - ImageComponents incomps { inImg.comps[2].data, inImg.comps[1].data, inImg.comps[0].data }; + ImageComponents incomps { inImg.comps[swap_rb].data, inImg.comps[1].data, inImg.comps[swap_rb^2].data }; // Assume RGBA for 4 channels -> BGRA if (outChannels > 3) { @@ -393,7 +394,7 @@ bool decodeSRGBData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shift) return false; } -bool decodeGrayscaleData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shift) +bool decodeGrayscaleData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shift, bool) { using ImageComponents = std::vector; @@ -411,7 +412,7 @@ bool decodeGrayscaleData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shif return false; } -bool decodeSYCCData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shift) +bool decodeSYCCData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shift, bool use_rgb) { using ImageComponents = std::vector; @@ -426,7 +427,10 @@ bool decodeSYCCData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shift) if (outChannels == 3 && inChannels >= 3) { copyToMat(ImageComponents { inImg.comps[0].data, inImg.comps[1].data, inImg.comps[2].data }, outImg, shift); - cvtColor(outImg, outImg, COLOR_YUV2BGR); + if (use_rgb) + cvtColor(outImg, outImg, COLOR_YUV2RGB); + else + cvtColor(outImg, outImg, COLOR_YUV2BGR); return true; } @@ -585,7 +589,7 @@ bool Jpeg2KOpjDecoderBase::readHeader() bool Jpeg2KOpjDecoderBase::readData( Mat& img ) { - using DecodeFunc = bool(*)(const opj_image_t&, cv::Mat&, uint8_t shift); + using DecodeFunc = bool(*)(const opj_image_t&, cv::Mat&, uint8_t shift, bool use_rgb); if (!opj_decode(codec_.get(), stream_.get(), image_.get())) { @@ -647,7 +651,7 @@ bool Jpeg2KOpjDecoderBase::readData( Mat& img ) CV_Assert(comp.data && "OpenJPEG2000: missing component data (unsupported / broken input)"); } - return decode(*image_, img, shift); + return decode(*image_, img, shift, m_use_rgb); } } // namespace detail diff --git a/modules/imgcodecs/src/grfmt_pam.cpp b/modules/imgcodecs/src/grfmt_pam.cpp index 979a31ca91..2c15ab244c 100644 --- a/modules/imgcodecs/src/grfmt_pam.cpp +++ b/modules/imgcodecs/src/grfmt_pam.cpp @@ -90,7 +90,7 @@ const static struct pam_header_field fields[] = { #define PAM_FIELDS_NO (sizeof (fields) / sizeof ((fields)[0])) typedef bool (*cvtFunc) (void *src, void *target, int width, int target_channels, - int target_depth); + int target_depth, bool use_rgb); struct channel_layout { uint rchan, gchan, bchan, graychan; @@ -108,7 +108,7 @@ struct pam_format { }; static bool rgb_convert (void *src, void *target, int width, int target_channels, - int target_depth); + int target_depth, bool use_rgb); const static struct pam_format formats[] = { {IMWRITE_PAM_FORMAT_NULL, "", NULL, {0, 0, 0, 0} }, @@ -125,19 +125,25 @@ const static struct pam_format formats[] = { */ static bool -rgb_convert (void *src, void *target, int width, int target_channels, int target_depth) +rgb_convert (void *src, void *target, int width, int target_channels, int target_depth, bool use_rgb) { bool ret = false; if (target_channels == 3) { switch (target_depth) { case CV_8U: - icvCvt_RGB2BGR_8u_C3R( (uchar*) src, 0, (uchar*) target, 0, - Size(width,1) ); + if (use_rgb) + memcpy(target, src, sizeof(uchar) * width); + else + icvCvt_RGB2BGR_8u_C3R( (uchar*) src, 0, (uchar*) target, 0, + Size(width,1) ); ret = true; break; case CV_16U: - icvCvt_RGB2BGR_16u_C3R( (ushort *)src, 0, (ushort *)target, 0, - Size(width,1) ); + if (use_rgb) + memcpy(target, src, sizeof(ushort) * width); + else + icvCvt_RGB2BGR_16u_C3R( (ushort *)src, 0, (ushort *)target, 0, + Size(width,1) ); ret = true; break; default: @@ -169,7 +175,7 @@ rgb_convert (void *src, void *target, int width, int target_channels, int target static void basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_size, - int src_width, void *target, int target_channels, int target_depth) + int src_width, void *target, int target_channels, int target_depth, bool use_rgb) { switch (target_depth) { case CV_8U: @@ -182,11 +188,18 @@ basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_ d[0] = d[1] = d[2] = s[layout->graychan]; break; case 3: - for( ; s < end; d += 3, s += src_sampe_size ) { - d[0] = s[layout->bchan]; - d[1] = s[layout->gchan]; - d[2] = s[layout->rchan]; - } + if (use_rgb) + for( ; s < end; d += 3, s += src_sampe_size ) { + d[0] = s[layout->rchan]; + d[1] = s[layout->gchan]; + d[2] = s[layout->bchan]; + } + else + for( ; s < end; d += 3, s += src_sampe_size ) { + d[0] = s[layout->bchan]; + d[1] = s[layout->gchan]; + d[2] = s[layout->rchan]; + } break; default: CV_Error(Error::StsInternal, ""); @@ -203,11 +216,18 @@ basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_ d[0] = d[1] = d[2] = s[layout->graychan]; break; case 3: - for( ; s < end; d += 3, s += src_sampe_size ) { - d[0] = s[layout->bchan]; - d[1] = s[layout->gchan]; - d[2] = s[layout->rchan]; - } + if (use_rgb) + for( ; s < end; d += 3, s += src_sampe_size ) { + d[0] = s[layout->rchan]; + d[1] = s[layout->gchan]; + d[2] = s[layout->bchan]; + } + else + for( ; s < end; d += 3, s += src_sampe_size ) { + d[0] = s[layout->bchan]; + d[1] = s[layout->gchan]; + d[2] = s[layout->rchan]; + } break; default: CV_Error(Error::StsInternal, ""); @@ -610,18 +630,18 @@ bool PAMDecoder::readData(Mat& img) bool funcout = false; if (fmt->cvt_func) funcout = fmt->cvt_func (src, data, m_width, target_channels, - img.depth()); + img.depth(), m_use_rgb); /* fall back to default if there is no conversion function or it * can't handle the specified characteristics */ if (!funcout) basic_conversion (src, &fmt->layout, m_channels, - m_width, data, target_channels, img.depth()); + m_width, data, target_channels, img.depth(), m_use_rgb); /* default to selecting the first available channels */ } else { basic_conversion (src, &layout, m_channels, - m_width, data, target_channels, img.depth()); + m_width, data, target_channels, img.depth(), m_use_rgb); } } } diff --git a/modules/imgcodecs/src/grfmt_pfm.cpp b/modules/imgcodecs/src/grfmt_pfm.cpp index addae34b4f..b213d18fde 100644 --- a/modules/imgcodecs/src/grfmt_pfm.cpp +++ b/modules/imgcodecs/src/grfmt_pfm.cpp @@ -142,7 +142,7 @@ bool PFMDecoder::readData(Mat& mat) } } - if (buffer.channels() == 3) { + if (buffer.channels() == 3 && !m_use_rgb) { cv::cvtColor(buffer, buffer, cv::COLOR_BGR2RGB); } diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index aca73bd000..726c8b90b7 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -261,7 +261,7 @@ bool PngDecoder::readData( Mat& img ) png_set_gray_1_2_4_to_8( png_ptr ); #endif - if( (m_color_type & PNG_COLOR_MASK_COLOR) && color ) + if( (m_color_type & PNG_COLOR_MASK_COLOR) && color && !m_use_rgb) png_set_bgr( png_ptr ); // convert RGB to BGR else if( color ) png_set_gray_to_rgb( png_ptr ); // Gray->RGB diff --git a/modules/imgcodecs/src/grfmt_pxm.cpp b/modules/imgcodecs/src/grfmt_pxm.cpp index 76290c43de..d2ce60c743 100644 --- a/modules/imgcodecs/src/grfmt_pxm.cpp +++ b/modules/imgcodecs/src/grfmt_pxm.cpp @@ -340,7 +340,9 @@ bool PxMDecoder::readData( Mat& img ) { if( color ) { - if( img.depth() == CV_8U ) + if (m_use_rgb) + memcpy(data, src, m_width * CV_ELEM_SIZE(img.type())); + else if( img.depth() == CV_8U ) icvCvt_RGB2BGR_8u_C3R( src, 0, data, 0, Size(m_width,1) ); else icvCvt_RGB2BGR_16u_C3R( (ushort *)src, 0, (ushort *)data, 0, Size(m_width,1) ); diff --git a/modules/imgcodecs/src/grfmt_spng.cpp b/modules/imgcodecs/src/grfmt_spng.cpp index fa15bd46c7..59b2decc6e 100644 --- a/modules/imgcodecs/src/grfmt_spng.cpp +++ b/modules/imgcodecs/src/grfmt_spng.cpp @@ -381,14 +381,14 @@ bool SPngDecoder::readData(Mat &img) break; ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width); - if (ihdr.interlace_method == 0) + if (ihdr.interlace_method == 0 && !m_use_rgb) { icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast(buffer[row_info.row_num]), 0, reinterpret_cast(buffer[row_info.row_num]), 0, Size(m_width, 1)); } } while (ret == SPNG_OK); - if (ihdr.interlace_method) + if (ihdr.interlace_method && !m_use_rgb) { icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast(img.data), step * 2, reinterpret_cast(img.data), step * 2, Size(m_width, m_height)); } @@ -402,12 +402,12 @@ bool SPngDecoder::readData(Mat &img) break; ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width); - if (ihdr.interlace_method == 0) + if (ihdr.interlace_method == 0 && !m_use_rgb) { icvCvt_RGBA2BGRA_8u_C4R(buffer[row_info.row_num], 0, buffer[row_info.row_num], 0, Size(m_width, 1)); } } while (ret == SPNG_OK); - if (ihdr.interlace_method) + if (ihdr.interlace_method && !m_use_rgb) { icvCvt_RGBA2BGRA_8u_C4R(img.data, step, img.data, step, Size(m_width, m_height)); } @@ -421,13 +421,13 @@ bool SPngDecoder::readData(Mat &img) break; ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width); - if (ihdr.interlace_method == 0) + if (ihdr.interlace_method == 0 && !m_use_rgb) { icvCvt_RGB2BGR_16u_C3R(reinterpret_cast(buffer[row_info.row_num]), 0, reinterpret_cast(buffer[row_info.row_num]), 0, Size(m_width, 1)); } } while (ret == SPNG_OK); - if (ihdr.interlace_method) + if (ihdr.interlace_method && !m_use_rgb) { icvCvt_RGB2BGR_16u_C3R(reinterpret_cast(img.data), step, reinterpret_cast(img.data), step, Size(m_width, m_height)); @@ -442,12 +442,12 @@ bool SPngDecoder::readData(Mat &img) break; ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width); - if (ihdr.interlace_method == 0) + if (ihdr.interlace_method == 0 && !m_use_rgb) { icvCvt_RGB2BGR_8u_C3R(buffer[row_info.row_num], 0, buffer[row_info.row_num], 0, Size(m_width, 1)); } } while (ret == SPNG_OK); - if (ihdr.interlace_method) + if (ihdr.interlace_method && !m_use_rgb) { icvCvt_RGB2BGR_8u_C3R(img.data, step, img.data, step, Size(m_width, m_height)); } diff --git a/modules/imgcodecs/src/grfmt_sunras.cpp b/modules/imgcodecs/src/grfmt_sunras.cpp index 31c0286248..798f295376 100644 --- a/modules/imgcodecs/src/grfmt_sunras.cpp +++ b/modules/imgcodecs/src/grfmt_sunras.cpp @@ -342,7 +342,7 @@ bad_decoding_end: if( color ) { - if( m_type == RAS_FORMAT_RGB ) + if( m_type == RAS_FORMAT_RGB || m_use_rgb) icvCvt_RGB2BGR_8u_C3R(src, 0, data, 0, Size(m_width,1) ); else memcpy(data, src, std::min(step, (size_t)src_pitch)); @@ -365,7 +365,7 @@ bad_decoding_end: if( color ) icvCvt_BGRA2BGR_8u_C4C3R( src + 4, 0, data, 0, Size(m_width,1), - m_type == RAS_FORMAT_RGB ? 2 : 0 ); + (m_type == RAS_FORMAT_RGB || m_use_rgb) ? 2 : 0 ); else icvCvt_BGRA2Gray_8u_C4C1R( src + 4, 0, data, 0, Size(m_width,1), m_type == RAS_FORMAT_RGB ? 2 : 0 ); diff --git a/modules/imgcodecs/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp index 3890df96bd..41607006e2 100644 --- a/modules/imgcodecs/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -865,9 +865,14 @@ bool TiffDecoder::readData( Mat& img ) break; case MAKE_FLAG( 3, 3 ): // RGB to BGR - icvCvt_BGR2RGB_8u_C3R( bstart, 0, - img_line_buffer, 0, - Size(tile_width, 1) ); + if (m_use_rgb) + memcpy( (void*) img_line_buffer, + (void*) bstart, + tile_width * sizeof(uchar) ); + else + icvCvt_BGR2RGB_8u_C3R( bstart, 0, + img_line_buffer, 0, + Size(tile_width, 1) ); break; case MAKE_FLAG( 4, 1 ): // RGBA to GRAY @@ -879,7 +884,7 @@ bool TiffDecoder::readData( Mat& img ) case MAKE_FLAG( 4, 3 ): // RGBA to BGR icvCvt_BGRA2BGR_8u_C4C3R( bstart, 0, img_line_buffer, 0, - Size(tile_width, 1), 2 ); + Size(tile_width, 1), m_use_rgb ? 0 : 2); break; case MAKE_FLAG( 4, 4 ): // RGBA to BGRA @@ -909,7 +914,7 @@ bool TiffDecoder::readData( Mat& img ) CV_CheckEQ(wanted_channels, 3, "TIFF-8bpp: BGR/BGRA images are supported only"); icvCvt_BGRA2BGR_8u_C4C3R(bstart + i*tile_width0*4, 0, img.ptr(img_y + tile_height - i - 1, x), 0, - Size(tile_width, 1), 2); + Size(tile_width, 1), m_use_rgb ? 0 : 2); } } else @@ -972,9 +977,12 @@ bool TiffDecoder::readData( Mat& img ) else if (ncn == 3) { CV_CheckEQ(wanted_channels, 3, ""); - icvCvt_RGB2BGR_16u_C3R(buffer16, 0, - img.ptr(img_y + i, x), 0, - Size(tile_width, 1)); + if (m_use_rgb) + memcpy(buffer16, img.ptr(img_y + i, x), tile_width * sizeof(ushort)); + else + icvCvt_RGB2BGR_16u_C3R(buffer16, 0, + img.ptr(img_y + i, x), 0, + Size(tile_width, 1)); } else if (ncn == 4) { @@ -989,7 +997,7 @@ bool TiffDecoder::readData( Mat& img ) CV_CheckEQ(wanted_channels, 3, "TIFF-16bpp: BGR/BGRA images are supported only"); icvCvt_BGRA2BGR_16u_C4C3R(buffer16, 0, img.ptr(img_y + i, x), 0, - Size(tile_width, 1), 2); + Size(tile_width, 1), m_use_rgb ? 0 : 2); } } else @@ -1032,7 +1040,7 @@ bool TiffDecoder::readData( Mat& img ) Mat m_tile(Size(tile_width0, tile_height0), CV_MAKETYPE((dst_bpp == 32) ? (depth == CV_32S ? CV_32S : CV_32F) : CV_64F, ncn), src_buffer); Rect roi_tile(0, 0, tile_width, tile_height); Rect roi_img(x, img_y, tile_width, tile_height); - if (!m_hdr && ncn == 3) + if (!m_hdr && ncn == 3 && !m_use_rgb) extend_cvtColor(m_tile(roi_tile), img(roi_img), COLOR_RGB2BGR); else if (!m_hdr && ncn == 4) extend_cvtColor(m_tile(roi_tile), img(roi_img), COLOR_RGBA2BGRA); @@ -1060,7 +1068,10 @@ bool TiffDecoder::readData( Mat& img ) if (m_hdr && depth >= CV_32F) { CV_Assert(photometric == PHOTOMETRIC_LOGLUV); - cvtColor(img, img, COLOR_XYZ2BGR); + if (m_use_rgb) + cvtColor(img, img, COLOR_XYZ2RGB); + else + cvtColor(img, img, COLOR_XYZ2BGR); } return true; } diff --git a/modules/imgcodecs/src/grfmt_webp.cpp b/modules/imgcodecs/src/grfmt_webp.cpp index 6f5cdfb6ab..ca54effe86 100644 --- a/modules/imgcodecs/src/grfmt_webp.cpp +++ b/modules/imgcodecs/src/grfmt_webp.cpp @@ -184,14 +184,22 @@ bool WebPDecoder::readData(Mat &img) if (channels == 3) { CV_CheckTypeEQ(read_img.type(), CV_8UC3, ""); - res_ptr = WebPDecodeBGRInto(data.ptr(), data.total(), out_data, - (int)out_data_size, (int)read_img.step); + if (m_use_rgb) + res_ptr = WebPDecodeRGBInto(data.ptr(), data.total(), out_data, + (int)out_data_size, (int)read_img.step); + else + res_ptr = WebPDecodeBGRInto(data.ptr(), data.total(), out_data, + (int)out_data_size, (int)read_img.step); } else if (channels == 4) { CV_CheckTypeEQ(read_img.type(), CV_8UC4, ""); - res_ptr = WebPDecodeBGRAInto(data.ptr(), data.total(), out_data, - (int)out_data_size, (int)read_img.step); + if (m_use_rgb) + res_ptr = WebPDecodeRGBAInto(data.ptr(), data.total(), out_data, + (int)out_data_size, (int)read_img.step); + else + res_ptr = WebPDecodeBGRAInto(data.ptr(), data.total(), out_data, + (int)out_data_size, (int)read_img.step); } if (res_ptr != out_data) diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index ec4760b879..354f2a4b34 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -88,7 +88,7 @@ static inline int calcType(int type, int flags) if( (flags & IMREAD_ANYDEPTH) == 0 ) type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type)); - if( (flags & IMREAD_COLOR) != 0 || + if( (flags & IMREAD_COLOR) != 0 || (flags & IMREAD_COLOR_RGB) != 0 || ((flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1) ) type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3); else @@ -432,6 +432,12 @@ imread_( const String& filename, int flags, OutputArray mat ) scale_denom = 8; } + // Try to decode image by RGB instead of BGR. + if (flags & IMREAD_COLOR_RGB && flags != IMREAD_UNCHANGED) + { + decoder->setRGB(true); + } + /// set the scale_denom in the driver decoder->setScale( scale_denom ); @@ -542,6 +548,9 @@ imreadmulti_(const String& filename, int flags, std::vector& mats, int star count = std::numeric_limits::max(); } + if (flags & IMREAD_COLOR_RGB && flags != IMREAD_UNCHANGED) + decoder->setRGB(true); + /// set the filename in the driver decoder->setSource(filename); @@ -829,6 +838,12 @@ imdecode_( const Mat& buf, int flags, Mat& mat ) scale_denom = 8; } + // Try to decode image by RGB instead of BGR. + if (flags & IMREAD_COLOR_RGB && flags != IMREAD_UNCHANGED) + { + decoder->setRGB(true); + } + /// set the scale_denom in the driver decoder->setScale( scale_denom ); @@ -965,6 +980,12 @@ imdecodemulti_(const Mat& buf, int flags, std::vector& mats, int start, int if (!decoder) return 0; + // Try to decode image by RGB instead of BGR. + if (flags & IMREAD_COLOR_RGB && flags != IMREAD_UNCHANGED) + { + decoder->setRGB(true); + } + if (count < 0) { count = std::numeric_limits::max(); } diff --git a/modules/imgcodecs/src/utils.cpp b/modules/imgcodecs/src/utils.cpp index 0962ebea62..41fd9f5041 100644 --- a/modules/imgcodecs/src/utils.cpp +++ b/modules/imgcodecs/src/utils.cpp @@ -352,6 +352,25 @@ void icvCvt_CMYK2BGR_8u_C4C3R( const uchar* cmyk, int cmyk_step, } } +void icvCvt_CMYK2RGB_8u_C4C3R( const uchar* cmyk, int cmyk_step, + uchar* rgb, int rgb_step, Size size ) +{ + int i; + for( ; size.height--; ) + { + for( i = 0; i < size.width; i++, rgb += 3, cmyk += 4 ) + { + int c = cmyk[0], m = cmyk[1], y = cmyk[2], k = cmyk[3]; + c = k - ((255 - c)*k>>8); + m = k - ((255 - m)*k>>8); + y = k - ((255 - y)*k>>8); + rgb[0] = (uchar)c; rgb[1] = (uchar)m; rgb[2] = (uchar)y; + } + rgb += rgb_step - size.width*3; + cmyk += cmyk_step - size.width*4; + } +} + void icvCvt_CMYK2Gray_8u_C4C1R( const uchar* cmyk, int cmyk_step, uchar* gray, int gray_step, Size size ) diff --git a/modules/imgcodecs/src/utils.hpp b/modules/imgcodecs/src/utils.hpp index 43eb907f76..2a9451422e 100644 --- a/modules/imgcodecs/src/utils.hpp +++ b/modules/imgcodecs/src/utils.hpp @@ -115,6 +115,8 @@ void icvCvt_BGR5652BGR_8u_C2C3R( const uchar* bgr565, int bgr565_step, uchar* bgr, int bgr_step, Size size ); void icvCvt_CMYK2BGR_8u_C4C3R( const uchar* cmyk, int cmyk_step, uchar* bgr, int bgr_step, Size size ); +void icvCvt_CMYK2RGB_8u_C4C3R( const uchar* cmyk, int cmyk_step, + uchar* rgb, int rgb_step, Size size ); void icvCvt_CMYK2Gray_8u_C4C1R( const uchar* ycck, int ycck_step, uchar* gray, int gray_step, Size size ); diff --git a/modules/imgcodecs/test/test_avif.cpp b/modules/imgcodecs/test/test_avif.cpp index 72b7f54fea..227c69556d 100644 --- a/modules/imgcodecs/test/test_avif.cpp +++ b/modules/imgcodecs/test/test_avif.cpp @@ -150,7 +150,7 @@ INSTANTIATE_TEST_CASE_P( ::testing::ValuesIn({1, 3, 4}), ::testing::ValuesIn({0, 50, 100}), ::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE, - IMREAD_COLOR}))); + IMREAD_COLOR, IMREAD_COLOR_RGB}))); class Imgcodecs_Avif_Image_EncodeDecodeSuite : public Imgcodecs_Avif_Image_RoundTripSuite {}; @@ -183,7 +183,7 @@ INSTANTIATE_TEST_CASE_P( ::testing::ValuesIn({1, 3, 4}), ::testing::ValuesIn({0, 50, 100}), ::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE, - IMREAD_COLOR}))); + IMREAD_COLOR, IMREAD_COLOR_RGB}))); //////////////////////////////////////////////////////////////////////////////// @@ -311,7 +311,7 @@ INSTANTIATE_TEST_CASE_P( ::testing::Combine(::testing::ValuesIn({8, 10, 12}), ::testing::ValuesIn({1, 3}), ::testing::ValuesIn({50}), ::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE, - IMREAD_COLOR}))); + IMREAD_COLOR, IMREAD_COLOR_RGB}))); class Imgcodecs_Avif_Animation_WriteDecodeSuite : public Imgcodecs_Avif_Animation_RoundTripSuite {}; @@ -347,7 +347,7 @@ INSTANTIATE_TEST_CASE_P( ::testing::Combine(::testing::ValuesIn({8, 10, 12}), ::testing::ValuesIn({1, 3}), ::testing::ValuesIn({50}), ::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE, - IMREAD_COLOR}))); + IMREAD_COLOR, IMREAD_COLOR_RGB}))); } // namespace } // namespace opencv_test diff --git a/modules/imgcodecs/test/test_exr.impl.hpp b/modules/imgcodecs/test/test_exr.impl.hpp index 2418d9d817..c8cda11a63 100644 --- a/modules/imgcodecs/test/test_exr.impl.hpp +++ b/modules/imgcodecs/test/test_exr.impl.hpp @@ -192,6 +192,15 @@ TEST(Imgcodecs_EXR, read_YC_changeDepth) ASSERT_FALSE(img.empty()); ASSERT_EQ(CV_8UC3, img.type()); + const Mat img_rgb = cv::imread(filenameInput, IMREAD_COLOR_RGB); + + ASSERT_FALSE(img_rgb.empty()); + ASSERT_EQ(CV_8UC3, img_rgb.type()); + + cvtColor(img_rgb, img_rgb, COLOR_RGB2BGR); + + EXPECT_TRUE(cvtest::norm(img, img_rgb, NORM_INF) == 0); + // Cannot test writing, EXR encoder doesn't support 8U depth } diff --git a/modules/imgcodecs/test/test_grfmt.cpp b/modules/imgcodecs/test/test_grfmt.cpp index 1e0bf47b47..947e560c81 100644 --- a/modules/imgcodecs/test/test_grfmt.cpp +++ b/modules/imgcodecs/test/test_grfmt.cpp @@ -108,6 +108,7 @@ const int basic_modes[] = IMREAD_UNCHANGED, IMREAD_GRAYSCALE, IMREAD_COLOR, + IMREAD_COLOR_RGB, IMREAD_ANYDEPTH, IMREAD_ANYCOLOR }; @@ -356,6 +357,10 @@ TEST(Imgcodecs_Bmp, rgba_scale) ASSERT_FALSE(img.empty()); ASSERT_EQ(CV_8UC3, img.type()); + img = cv::imread(filenameInput, IMREAD_COLOR_RGB); + ASSERT_FALSE(img.empty()); + ASSERT_EQ(CV_8UC3, img.type()); + data = img.ptr(); ASSERT_EQ(data[0], 255); ASSERT_EQ(data[1], 255); diff --git a/modules/imgcodecs/test/test_jpeg.cpp b/modules/imgcodecs/test/test_jpeg.cpp index ee9da01aa7..503848068a 100644 --- a/modules/imgcodecs/test/test_jpeg.cpp +++ b/modules/imgcodecs/test/test_jpeg.cpp @@ -217,6 +217,7 @@ TEST_P(Imgcodecs_Jpeg_decode_cmyk, regression25274) INSTANTIATE_TEST_CASE_P( /* nothing */, Imgcodecs_Jpeg_decode_cmyk, testing::Values(cv::IMREAD_COLOR, + cv::IMREAD_COLOR_RGB, cv::IMREAD_GRAYSCALE, cv::IMREAD_ANYCOLOR)); @@ -327,6 +328,13 @@ TEST_P(Imgcodecs_Jpeg_encode_withLumaChromaQuality, basic) cv::Mat src = imread(fname, cv::IMREAD_COLOR); ASSERT_FALSE(src.empty()); + // Add imread RGB test + cv::Mat src_rgb = imread(fname, cv::IMREAD_COLOR_RGB); + ASSERT_FALSE(src_rgb.empty()); + + cvtColor(src_rgb, src_rgb, COLOR_RGB2BGR); + EXPECT_TRUE(cvtest::norm(src, src_rgb, NORM_INF) == 0); + std::vector jpegNormal; ASSERT_NO_THROW(cv::imencode(".jpg", src, jpegNormal)); diff --git a/modules/imgcodecs/test/test_png.cpp b/modules/imgcodecs/test/test_png.cpp index cdc7da39b2..13aca2e396 100644 --- a/modules/imgcodecs/test/test_png.cpp +++ b/modules/imgcodecs/test/test_png.cpp @@ -83,6 +83,14 @@ TEST(Imgcodecs_Png, read_color_palette_with_alpha) EXPECT_EQ(img.at(0, 0), Vec3b(0, 0, 255)); EXPECT_EQ(img.at(0, 1), Vec3b(0, 0, 255)); + img = imread(root + "readwrite/color_palette_alpha.png", IMREAD_COLOR_RGB); + ASSERT_FALSE(img.empty()); + ASSERT_TRUE(img.channels() == 3); + + // pixel is red in RGB + EXPECT_EQ(img.at(0, 0), Vec3b(255, 0, 0)); + EXPECT_EQ(img.at(0, 1), Vec3b(255, 0, 0)); + // Fourth Test : Read PNG without alpha, imread flag 1 img = imread(root + "readwrite/color_palette_no_alpha.png", IMREAD_COLOR); ASSERT_FALSE(img.empty()); @@ -91,6 +99,14 @@ TEST(Imgcodecs_Png, read_color_palette_with_alpha) // pixel is red in BGR EXPECT_EQ(img.at(0, 0), Vec3b(0, 0, 255)); EXPECT_EQ(img.at(0, 1), Vec3b(0, 0, 255)); + + img = imread(root + "readwrite/color_palette_no_alpha.png", IMREAD_COLOR_RGB); + ASSERT_FALSE(img.empty()); + ASSERT_TRUE(img.channels() == 3); + + // pixel is red in RGB + EXPECT_EQ(img.at(0, 0), Vec3b(255, 0, 0)); + EXPECT_EQ(img.at(0, 1), Vec3b(255, 0, 0)); } /** diff --git a/modules/imgcodecs/test/test_precomp.hpp b/modules/imgcodecs/test/test_precomp.hpp index 3bd4221dae..fab6606859 100644 --- a/modules/imgcodecs/test/test_precomp.hpp +++ b/modules/imgcodecs/test/test_precomp.hpp @@ -51,6 +51,11 @@ void PrintTo(const ImreadModes& val, std::ostream* os) v &= ~IMREAD_IGNORE_ORIENTATION; *os << "IMREAD_IGNORE_ORIENTATION" << (v == 0 ? "" : " | "); } + if ((v & IMREAD_COLOR_RGB) != 0) + { + v &= ~IMREAD_COLOR_RGB; + *os << "IMREAD_COLOR_RGB" << (v == 0 ? "" : " | "); + } switch (v) { case IMREAD_UNCHANGED: return; @@ -66,6 +71,7 @@ void PrintTo(const ImreadModes& val, std::ostream* os) case IMREAD_REDUCED_GRAYSCALE_8: // fallthru case IMREAD_REDUCED_COLOR_8: *os << "REDUCED_8"; return; case IMREAD_IGNORE_ORIENTATION: return; + case IMREAD_COLOR_RGB: return; } // don't use "default:" to emit compiler warnings *os << "IMREAD_UNKNOWN(" << (int)v << ")"; } diff --git a/modules/imgcodecs/test/test_read_write.cpp b/modules/imgcodecs/test/test_read_write.cpp index 824688b366..255f819a9a 100644 --- a/modules/imgcodecs/test/test_read_write.cpp +++ b/modules/imgcodecs/test/test_read_write.cpp @@ -196,9 +196,19 @@ void test_image_io(const Mat& image, const std::string& fname, const std::string Mat buf_loaded = imdecode(Mat(buf), imreadFlag); EXPECT_FALSE(buf_loaded.empty()); + if (imreadFlag & IMREAD_COLOR_RGB && imreadFlag != -1) + { + cvtColor(buf_loaded, buf_loaded, COLOR_RGB2BGR); + } + Mat loaded = imread(fname, imreadFlag); EXPECT_FALSE(loaded.empty()); + if (imreadFlag & IMREAD_COLOR_RGB && imreadFlag != -1) + { + cvtColor(loaded, loaded, COLOR_RGB2BGR); + } + EXPECT_EQ(0, cv::norm(loaded, buf_loaded, NORM_INF)) << "imread() and imdecode() calls must provide the same result (bit-exact)"; double psnr = cvtest::PSNR(loaded, image); @@ -238,6 +248,7 @@ TEST_P(Imgcodecs_Image, read_write_BGR) Mat image = generateTestImageBGR(); EXPECT_NO_THROW(test_image_io(image, fname, ext, IMREAD_COLOR, psnrThreshold)); + EXPECT_NO_THROW(test_image_io(image, fname, ext, IMREAD_COLOR_RGB, psnrThreshold)); EXPECT_EQ(0, remove(fname.c_str())); } diff --git a/modules/imgcodecs/test/test_tiff.cpp b/modules/imgcodecs/test/test_tiff.cpp index ee40c54b59..fb607bf18f 100644 --- a/modules/imgcodecs/test/test_tiff.cpp +++ b/modules/imgcodecs/test/test_tiff.cpp @@ -53,7 +53,7 @@ enum ImreadMixModes { IMREAD_MIX_UNCHANGED = IMREAD_UNCHANGED , IMREAD_MIX_GRAYSCALE = IMREAD_GRAYSCALE , - IMREAD_MIX_COLOR = IMREAD_COLOR , + IMREAD_MIX_COLOR = IMREAD_COLOR | IMREAD_COLOR_RGB , IMREAD_MIX_GRAYSCALE_ANYDEPTH = IMREAD_GRAYSCALE | IMREAD_ANYDEPTH , IMREAD_MIX_GRAYSCALE_ANYCOLOR = IMREAD_GRAYSCALE | IMREAD_ANYCOLOR, IMREAD_MIX_GRAYSCALE_ANYDEPTH_ANYCOLOR = IMREAD_GRAYSCALE | IMREAD_ANYDEPTH | IMREAD_ANYCOLOR, @@ -125,7 +125,7 @@ TEST_P(Imgcodecs_Tiff_decode_Huge, regression) case IMREAD_GRAYSCALE | IMREAD_ANYCOLOR | IMREAD_ANYDEPTH: ncn = (ncn == 1)?1:3; break; - case IMREAD_COLOR: + case IMREAD_COLOR | IMREAD_COLOR_RGB: ncn = 3; depth = 1; break; @@ -818,6 +818,24 @@ TEST(Imgcodecs_Tiff, read_palette_color_image) ASSERT_EQ(CV_8UC3, img.type()); } +TEST(Imgcodecs_Tiff, read_palette_color_image_rgb_and_bgr) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filenameInput = root + "readwrite/test_palette_color_image.tif"; + + Mat img_rgb, img_bgr; + ASSERT_NO_THROW(img_rgb = cv::imread(filenameInput, IMREAD_COLOR_RGB)); + ASSERT_NO_THROW(img_bgr = cv::imread(filenameInput, IMREAD_COLOR_BGR)); + ASSERT_FALSE(img_rgb.empty()); + ASSERT_EQ(CV_8UC3, img_rgb.type()); + + ASSERT_FALSE(img_bgr.empty()); + ASSERT_EQ(CV_8UC3, img_bgr.type()); + + EXPECT_EQ(img_rgb.at(32, 24), Vec3b(255, 0, 0)); + EXPECT_EQ(img_bgr.at(32, 24), Vec3b(0, 0, 255)); +} + TEST(Imgcodecs_Tiff, read_4_bit_palette_color_image) { const string root = cvtest::TS::ptr()->get_data_path(); @@ -1066,6 +1084,7 @@ const int all_modes[] = IMREAD_UNCHANGED, IMREAD_GRAYSCALE, IMREAD_COLOR, + IMREAD_COLOR_RGB, IMREAD_ANYDEPTH, IMREAD_ANYCOLOR }; diff --git a/modules/imgcodecs/test/test_webp.cpp b/modules/imgcodecs/test/test_webp.cpp index 1f2cad7d89..3647714039 100644 --- a/modules/imgcodecs/test/test_webp.cpp +++ b/modules/imgcodecs/test/test_webp.cpp @@ -51,6 +51,12 @@ TEST(Imgcodecs_WebP, encode_decode_lossless_webp) ASSERT_FALSE(decode.empty()); EXPECT_TRUE(cvtest::norm(decode, img_webp, NORM_INF) == 0); + cv::Mat decode_rgb = cv::imdecode(buf, IMREAD_COLOR_RGB); + ASSERT_FALSE(decode_rgb.empty()); + + cvtColor(decode_rgb, decode_rgb, COLOR_RGB2BGR); + EXPECT_TRUE(cvtest::norm(decode_rgb, img_webp, NORM_INF) == 0); + ASSERT_FALSE(img_webp.empty()); EXPECT_TRUE(cvtest::norm(img, img_webp, NORM_INF) == 0);