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 = "<<t0<<std::endl;

t0 = 10000;
for (int i = 0; i < loop; i++)
{
    t.reset();
    t.start();
    img_rgb = imread(img_path, IMREAD_COLOR_RGB);
    t.stop();
    if (t.getTimeMilli() < t0) t0 = t.getTimeMilli();
}
std::cout<<"rgb time = "<<t0<<std::endl;
``` 
### 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
- [ ] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
pull/25857/head
zihaomu 5 months ago committed by GitHub
parent a7fd9446cf
commit 934e6899f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 10
      modules/imgcodecs/include/opencv2/imgcodecs.hpp
  2. 17
      modules/imgcodecs/perf/perf_jpeg.cpp
  3. 17
      modules/imgcodecs/perf/perf_png.cpp
  4. 7
      modules/imgcodecs/src/grfmt_avif.cpp
  5. 6
      modules/imgcodecs/src/grfmt_base.cpp
  6. 3
      modules/imgcodecs/src/grfmt_base.hpp
  7. 5
      modules/imgcodecs/src/grfmt_bmp.cpp
  8. 75
      modules/imgcodecs/src/grfmt_exr.cpp
  9. 1
      modules/imgcodecs/src/grfmt_exr.hpp
  10. 4
      modules/imgcodecs/src/grfmt_gdal.cpp
  11. 8
      modules/imgcodecs/src/grfmt_hdr.cpp
  12. 14
      modules/imgcodecs/src/grfmt_jpeg.cpp
  13. 7
      modules/imgcodecs/src/grfmt_jpeg2000.cpp
  14. 16
      modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp
  15. 34
      modules/imgcodecs/src/grfmt_pam.cpp
  16. 2
      modules/imgcodecs/src/grfmt_pfm.cpp
  17. 2
      modules/imgcodecs/src/grfmt_png.cpp
  18. 4
      modules/imgcodecs/src/grfmt_pxm.cpp
  19. 16
      modules/imgcodecs/src/grfmt_spng.cpp
  20. 4
      modules/imgcodecs/src/grfmt_sunras.cpp
  21. 19
      modules/imgcodecs/src/grfmt_tiff.cpp
  22. 8
      modules/imgcodecs/src/grfmt_webp.cpp
  23. 23
      modules/imgcodecs/src/loadsave.cpp
  24. 19
      modules/imgcodecs/src/utils.cpp
  25. 2
      modules/imgcodecs/src/utils.hpp
  26. 8
      modules/imgcodecs/test/test_avif.cpp
  27. 9
      modules/imgcodecs/test/test_exr.impl.hpp
  28. 5
      modules/imgcodecs/test/test_grfmt.cpp
  29. 8
      modules/imgcodecs/test/test_jpeg.cpp
  30. 16
      modules/imgcodecs/test/test_png.cpp
  31. 6
      modules/imgcodecs/test/test_precomp.hpp
  32. 11
      modules/imgcodecs/test/test_read_write.cpp
  33. 23
      modules/imgcodecs/test/test_tiff.cpp
  34. 6
      modules/imgcodecs/test/test_webp.cpp

@ -68,7 +68,8 @@ namespace cv
enum ImreadModes { enum ImreadModes {
IMREAD_UNCHANGED = -1, //!< If set, return the loaded image as is (with alpha channel, otherwise it gets cropped). Ignore EXIF orientation. IMREAD_UNCHANGED = -1, //!< If set, return the loaded image as is (with alpha channel, otherwise it gets cropped). Ignore EXIF orientation.
IMREAD_GRAYSCALE = 0, //!< If set, always convert image to the single channel grayscale image (codec internal conversion). IMREAD_GRAYSCALE = 0, //!< If set, always convert image to the single channel grayscale image (codec internal conversion).
IMREAD_COLOR = 1, //!< If set, always convert image to the 3 channel BGR color image. IMREAD_COLOR_BGR = 1, //!< If set, always convert image to the 3 channel BGR color image.
IMREAD_COLOR = 1, //!< Same as IMREAD_COLOR_BGR.
IMREAD_ANYDEPTH = 2, //!< If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit. IMREAD_ANYDEPTH = 2, //!< If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit.
IMREAD_ANYCOLOR = 4, //!< If set, the image is read in any possible color format. IMREAD_ANYCOLOR = 4, //!< If set, the image is read in any possible color format.
IMREAD_LOAD_GDAL = 8, //!< If set, use the gdal driver for loading the image. IMREAD_LOAD_GDAL = 8, //!< If set, use the gdal driver for loading the image.
@ -78,7 +79,8 @@ enum ImreadModes {
IMREAD_REDUCED_COLOR_4 = 33, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/4. IMREAD_REDUCED_COLOR_4 = 33, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/4.
IMREAD_REDUCED_GRAYSCALE_8 = 64, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/8. IMREAD_REDUCED_GRAYSCALE_8 = 64, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/8.
IMREAD_REDUCED_COLOR_8 = 65, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/8. IMREAD_REDUCED_COLOR_8 = 65, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/8.
IMREAD_IGNORE_ORIENTATION = 128 //!< If set, do not rotate the image according to EXIF's orientation flag. IMREAD_IGNORE_ORIENTATION = 128, //!< If set, do not rotate the image according to EXIF's orientation flag.
IMREAD_COLOR_RGB = 256, //!< If set, always convert image to the 3 channel RGB color image.
}; };
//! Imwrite flags //! Imwrite flags
@ -268,7 +270,7 @@ Currently, the following file formats are supported:
@param filename Name of file to be loaded. @param filename Name of file to be loaded.
@param flags Flag that can take values of cv::ImreadModes @param flags Flag that can take values of cv::ImreadModes
*/ */
CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR ); CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR_BGR );
/** @brief Loads an image from a file. /** @brief Loads an image from a file.
@ -279,7 +281,7 @@ This is an overloaded member function, provided for convenience. It differs from
@note @note
The image passing through the img parameter can be pre-allocated. The memory is reused if the shape and the type match with the load image. The image passing through the img parameter can be pre-allocated. The memory is reused if the shape and the type match with the load image.
*/ */
CV_EXPORTS_W void imread( const String& filename, OutputArray dst, int flags = IMREAD_COLOR ); CV_EXPORTS_W void imread( const String& filename, OutputArray dst, int flags = IMREAD_COLOR_BGR );
/** @brief Loads a multi-page image from a file. /** @brief Loads a multi-page image from a file.

@ -27,6 +27,23 @@ PERF_TEST(JPEG, Decode)
SANITY_CHECK_NOTHING(); SANITY_CHECK_NOTHING();
} }
PERF_TEST(JPEG, Decode_rgb)
{
String filename = getDataPath("stitching/boat1.jpg");
FILE *f = fopen(filename.c_str(), "rb");
fseek(f, 0, SEEK_END);
long len = ftell(f);
fseek(f, 0, SEEK_SET);
vector<uchar> 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) PERF_TEST(JPEG, Encode)
{ {
String filename = getDataPath("stitching/boat1.jpg"); String filename = getDataPath("stitching/boat1.jpg");

@ -30,6 +30,23 @@ PERF_TEST(PNG, decode)
SANITY_CHECK_NOTHING(); 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<uchar> 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) PERF_TEST(PNG, encode)
{ {
String filename = getDataPath("perf/2560x1600.png"); String filename = getDataPath("perf/2560x1600.png");

@ -33,7 +33,7 @@ struct AvifImageDeleter {
using AvifImageUniquePtr = std::unique_ptr<avifImage, AvifImageDeleter>; using AvifImageUniquePtr = std::unique_ptr<avifImage, AvifImageDeleter>;
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->height == mat->rows);
CV_Assert((int)image->width == mat->cols); CV_Assert((int)image->width == mat->cols);
if (channels == 1) { if (channels == 1) {
@ -53,6 +53,9 @@ avifResult CopyToMat(const avifImage *image, int channels, Mat *mat) {
avifRGBImage rgba; avifRGBImage rgba;
avifRGBImageSetDefaults(&rgba, image); avifRGBImageSetDefaults(&rgba, image);
if (channels == 3) { if (channels == 3) {
if (useRGB)
rgba.format = AVIF_RGB_FORMAT_RGB;
else
rgba.format = AVIF_RGB_FORMAT_BGR; rgba.format = AVIF_RGB_FORMAT_BGR;
} else { } else {
CV_Assert(channels == 4); CV_Assert(channels == 4);
@ -227,7 +230,7 @@ bool AvifDecoder::readData(Mat &img) {
is_first_image_ = false; 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"); CV_Error(Error::StsInternal, "Cannot convert from AVIF to Mat");
return false; return false;
} }

@ -53,6 +53,7 @@ BaseImageDecoder::BaseImageDecoder()
m_type = -1; m_type = -1;
m_buf_supported = false; m_buf_supported = false;
m_scale_denom = 1; m_scale_denom = 1;
m_use_rgb = false;
} }
@ -94,6 +95,11 @@ int BaseImageDecoder::setScale( const int& scale_denom )
return temp; return temp;
} }
void BaseImageDecoder::setRGB(bool useRGB)
{
m_use_rgb = useRGB;
}
ImageDecoder BaseImageDecoder::newDecoder() const ImageDecoder BaseImageDecoder::newDecoder() const
{ {
return ImageDecoder(); return ImageDecoder();

@ -73,6 +73,8 @@ public:
virtual bool readHeader() = 0; virtual bool readHeader() = 0;
virtual bool readData( Mat& img ) = 0; virtual bool readData( Mat& img ) = 0;
virtual void setRGB(bool useRGB);
/// Called after readData to advance to the next page, if any. /// Called after readData to advance to the next page, if any.
virtual bool nextPage() { return false; } virtual bool nextPage() { return false; }
@ -89,6 +91,7 @@ protected:
String m_signature; String m_signature;
Mat m_buf; Mat m_buf;
bool m_buf_supported; bool m_buf_supported;
bool m_use_rgb; // flag of decode image as RGB order instead of BGR.
ExifReader m_exif; ExifReader m_exif;
}; };

@ -544,6 +544,11 @@ decode_rle8_bad: ;
throw; throw;
} }
if (m_use_rgb && color && img.channels() == 3)
{
cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
}
return result; return result;
} }

@ -372,6 +372,17 @@ bool ExrDecoder::readData( Mat& img )
m_file->readPixels( m_datawindow.min.y, m_datawindow.max.y ); m_file->readPixels( m_datawindow.min.y, m_datawindow.max.y );
if( m_iscolor ) if( m_iscolor )
{
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) ) if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) )
UpSample( data, channelstoread, step / xstep, m_blue->xSampling, m_blue->ySampling ); UpSample( data, channelstoread, step / xstep, m_blue->xSampling, m_blue->ySampling );
@ -380,12 +391,18 @@ bool ExrDecoder::readData( Mat& img )
if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) )
UpSample( data + 2 * xstep, channelstoread, step / xstep, m_red->xSampling, m_red->ySampling ); 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) ) else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
UpSample( data, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling ); UpSample( data, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling );
if( chromatorgb ) if( chromatorgb )
{
if (m_use_rgb)
ChromaToRGB( (float *)data, m_height, channelstoread, step / xstep );
else
ChromaToBGR( (float *)data, m_height, channelstoread, step / xstep ); ChromaToBGR( (float *)data, m_height, channelstoread, step / xstep );
} }
}
else else
{ {
uchar *out = data; uchar *out = data;
@ -406,7 +423,12 @@ bool ExrDecoder::readData( Mat& img )
else else
{ {
if( chromatorgb ) if( chromatorgb )
{
if (m_use_rgb)
ChromaToRGB( (float *)buffer, 1, defaultchannels, step );
else
ChromaToBGR( (float *)buffer, 1, defaultchannels, step ); ChromaToBGR( (float *)buffer, 1, defaultchannels, step );
}
if( m_type == FLOAT ) if( m_type == FLOAT )
{ {
@ -429,6 +451,17 @@ bool ExrDecoder::readData( Mat& img )
out += step; out += step;
} }
if( color ) if( color )
{
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) ) if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) )
UpSampleY( data, defaultchannels, step / xstep, m_blue->ySampling ); UpSampleY( data, defaultchannels, step / xstep, m_blue->ySampling );
@ -437,6 +470,7 @@ bool ExrDecoder::readData( Mat& img )
if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) )
UpSampleY( data + 2 * xstep, defaultchannels, step / xstep, m_red->ySampling ); UpSampleY( data + 2 * xstep, defaultchannels, step / xstep, m_red->ySampling );
} }
}
else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
UpSampleY( data, 1, step / xstep, m_green->ySampling ); 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 // convert one row to gray

@ -83,6 +83,7 @@ protected:
void UpSampleX( float *data, int xstep, int xsample ); void UpSampleX( float *data, int xstep, int xsample );
void UpSampleY( uchar *data, int xstep, int ystep, int ysample ); void UpSampleY( uchar *data, int xstep, int ystep, int ysample );
void ChromaToBGR( float *data, int numlines, int xstep, int ystep ); 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 ); void RGBToGray( float *in, float *out );
InputFile *m_file; InputFile *m_file;

@ -397,13 +397,13 @@ bool GdalDecoder::readData( Mat& img ){
case GCI_PaletteIndex: case GCI_PaletteIndex:
case GCI_GrayIndex: case GCI_GrayIndex:
case GCI_BlueBand: case GCI_BlueBand:
color = 0; color = m_use_rgb ? 2 : 0;
break; break;
case GCI_GreenBand: case GCI_GreenBand:
color = 1; color = 1;
break; break;
case GCI_RedBand: case GCI_RedBand:
color = 2; color = m_use_rgb ? 0 : 2;
break; break;
case GCI_AlphaBand: case GCI_AlphaBand:
color = 3; color = 3;

@ -106,7 +106,13 @@ bool HdrDecoder::readData(Mat& _img)
switch (_img.channels()) switch (_img.channels())
{ {
case 1: cvtColor(img, _img, COLOR_BGR2GRAY); break; 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"); default: CV_Error(Error::StsError, "Wrong expected image channels, allowed: 1 and 3");
} }
return true; return true;

@ -437,13 +437,13 @@ bool JpegDecoder::readData( Mat& img )
if( cinfo->num_components != 4 ) if( cinfo->num_components != 4 )
{ {
#ifdef JCS_EXTENSIONS #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; cinfo->out_color_components = 3;
doDirectRead = true; // BGR -> BGR doDirectRead = true; // BGR -> BGR
#else #else
cinfo->out_color_space = JCS_RGB; cinfo->out_color_space = JCS_RGB;
cinfo->out_color_components = 3; cinfo->out_color_components = 3;
doDirectRead = false; // RGB -> BGR doDirectRead = m_use_rgb ? true : false; // RGB -> BGR
#endif #endif
} }
else else
@ -513,12 +513,22 @@ bool JpegDecoder::readData( Mat& img )
jpeg_read_scanlines( cinfo, buffer, 1 ); jpeg_read_scanlines( cinfo, buffer, 1 );
if( color ) if( color )
{
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
{ {
if( cinfo->out_color_components == 3 ) if( cinfo->out_color_components == 3 )
icvCvt_RGB2BGR_8u_C3R( buffer[0], 0, data, 0, Size(m_width,1) ); icvCvt_RGB2BGR_8u_C3R( buffer[0], 0, data, 0, Size(m_width,1) );
else else
icvCvt_CMYK2BGR_8u_C4C3R( buffer[0], 0, data, 0, Size(m_width,1) ); icvCvt_CMYK2BGR_8u_C4C3R( buffer[0], 0, data, 0, Size(m_width,1) );
} }
}
else else
{ {
if( cinfo->out_color_components == 1 ) if( cinfo->out_color_components == 1 )

@ -286,11 +286,12 @@ bool Jpeg2KDecoder::readData( Mat& img )
{ {
int ncmpts; int ncmpts;
int cmptlut[3]; int cmptlut[3];
int swap_rb = m_use_rgb ? 0 : 2;
if( color ) if( color )
{ {
cmptlut[0] = jas_image_getcmptbytype( image, JAS_IMAGE_CT_RGB_B ); cmptlut[0] = jas_image_getcmptbytype( image, swap_rb );
cmptlut[1] = jas_image_getcmptbytype( image, JAS_IMAGE_CT_RGB_G ); cmptlut[1] = jas_image_getcmptbytype( image, 1 );
cmptlut[2] = jas_image_getcmptbytype( image, JAS_IMAGE_CT_RGB_R ); cmptlut[2] = jas_image_getcmptbytype( image, swap_rb^2 );
if( cmptlut[0] < 0 || cmptlut[1] < 0 || cmptlut[2] < 0 ) if( cmptlut[0] < 0 || cmptlut[1] < 0 || cmptlut[2] < 0 )
result = false; result = false;
ncmpts = 3; ncmpts = 3;

@ -350,7 +350,7 @@ opj_cparameters setupEncoderParameters(const std::vector<int>& params)
return parameters; 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<const OPJ_INT32*>; using ImageComponents = std::vector<const OPJ_INT32*>;
@ -377,8 +377,9 @@ bool decodeSRGBData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shift)
if (inChannels >= 3) if (inChannels >= 3)
{ {
int swap_rb = use_rgb ? 0 : 2;
// Assume RGB (+ alpha) for 3 channels -> BGR // 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 // Assume RGBA for 4 channels -> BGRA
if (outChannels > 3) if (outChannels > 3)
{ {
@ -393,7 +394,7 @@ bool decodeSRGBData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shift)
return false; 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<const OPJ_INT32*>; using ImageComponents = std::vector<const OPJ_INT32*>;
@ -411,7 +412,7 @@ bool decodeGrayscaleData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shif
return false; 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<const OPJ_INT32*>; using ImageComponents = std::vector<const OPJ_INT32*>;
@ -426,6 +427,9 @@ bool decodeSYCCData(const opj_image_t& inImg, cv::Mat& outImg, uint8_t shift)
if (outChannels == 3 && inChannels >= 3) { if (outChannels == 3 && inChannels >= 3) {
copyToMat(ImageComponents { inImg.comps[0].data, inImg.comps[1].data, inImg.comps[2].data }, copyToMat(ImageComponents { inImg.comps[0].data, inImg.comps[1].data, inImg.comps[2].data },
outImg, shift); outImg, shift);
if (use_rgb)
cvtColor(outImg, outImg, COLOR_YUV2RGB);
else
cvtColor(outImg, outImg, COLOR_YUV2BGR); cvtColor(outImg, outImg, COLOR_YUV2BGR);
return true; return true;
} }
@ -585,7 +589,7 @@ bool Jpeg2KOpjDecoderBase::readHeader()
bool Jpeg2KOpjDecoderBase::readData( Mat& img ) 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())) 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)"); 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 } // namespace detail

@ -90,7 +90,7 @@ const static struct pam_header_field fields[] = {
#define PAM_FIELDS_NO (sizeof (fields) / sizeof ((fields)[0])) #define PAM_FIELDS_NO (sizeof (fields) / sizeof ((fields)[0]))
typedef bool (*cvtFunc) (void *src, void *target, int width, int target_channels, typedef bool (*cvtFunc) (void *src, void *target, int width, int target_channels,
int target_depth); int target_depth, bool use_rgb);
struct channel_layout { struct channel_layout {
uint rchan, gchan, bchan, graychan; 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, 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[] = { const static struct pam_format formats[] = {
{IMWRITE_PAM_FORMAT_NULL, "", NULL, {0, 0, 0, 0} }, {IMWRITE_PAM_FORMAT_NULL, "", NULL, {0, 0, 0, 0} },
@ -125,17 +125,23 @@ const static struct pam_format formats[] = {
*/ */
static bool 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; bool ret = false;
if (target_channels == 3) { if (target_channels == 3) {
switch (target_depth) { switch (target_depth) {
case CV_8U: case CV_8U:
if (use_rgb)
memcpy(target, src, sizeof(uchar) * width);
else
icvCvt_RGB2BGR_8u_C3R( (uchar*) src, 0, (uchar*) target, 0, icvCvt_RGB2BGR_8u_C3R( (uchar*) src, 0, (uchar*) target, 0,
Size(width,1) ); Size(width,1) );
ret = true; ret = true;
break; break;
case CV_16U: case CV_16U:
if (use_rgb)
memcpy(target, src, sizeof(ushort) * width);
else
icvCvt_RGB2BGR_16u_C3R( (ushort *)src, 0, (ushort *)target, 0, icvCvt_RGB2BGR_16u_C3R( (ushort *)src, 0, (ushort *)target, 0,
Size(width,1) ); Size(width,1) );
ret = true; ret = true;
@ -169,7 +175,7 @@ rgb_convert (void *src, void *target, int width, int target_channels, int target
static void static void
basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_size, 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) { switch (target_depth) {
case CV_8U: case CV_8U:
@ -182,6 +188,13 @@ basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_
d[0] = d[1] = d[2] = s[layout->graychan]; d[0] = d[1] = d[2] = s[layout->graychan];
break; break;
case 3: case 3:
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 ) { for( ; s < end; d += 3, s += src_sampe_size ) {
d[0] = s[layout->bchan]; d[0] = s[layout->bchan];
d[1] = s[layout->gchan]; d[1] = s[layout->gchan];
@ -203,6 +216,13 @@ basic_conversion (void *src, const struct channel_layout *layout, int src_sampe_
d[0] = d[1] = d[2] = s[layout->graychan]; d[0] = d[1] = d[2] = s[layout->graychan];
break; break;
case 3: case 3:
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 ) { for( ; s < end; d += 3, s += src_sampe_size ) {
d[0] = s[layout->bchan]; d[0] = s[layout->bchan];
d[1] = s[layout->gchan]; d[1] = s[layout->gchan];
@ -610,18 +630,18 @@ bool PAMDecoder::readData(Mat& img)
bool funcout = false; bool funcout = false;
if (fmt->cvt_func) if (fmt->cvt_func)
funcout = fmt->cvt_func (src, data, m_width, target_channels, 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 /* fall back to default if there is no conversion function or it
* can't handle the specified characteristics * can't handle the specified characteristics
*/ */
if (!funcout) if (!funcout)
basic_conversion (src, &fmt->layout, m_channels, 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 */ /* default to selecting the first available channels */
} else { } else {
basic_conversion (src, &layout, m_channels, basic_conversion (src, &layout, m_channels,
m_width, data, target_channels, img.depth()); m_width, data, target_channels, img.depth(), m_use_rgb);
} }
} }
} }

@ -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); cv::cvtColor(buffer, buffer, cv::COLOR_BGR2RGB);
} }

@ -261,7 +261,7 @@ bool PngDecoder::readData( Mat& img )
png_set_gray_1_2_4_to_8( png_ptr ); png_set_gray_1_2_4_to_8( png_ptr );
#endif #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 png_set_bgr( png_ptr ); // convert RGB to BGR
else if( color ) else if( color )
png_set_gray_to_rgb( png_ptr ); // Gray->RGB png_set_gray_to_rgb( png_ptr ); // Gray->RGB

@ -340,7 +340,9 @@ bool PxMDecoder::readData( Mat& img )
{ {
if( color ) 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) ); icvCvt_RGB2BGR_8u_C3R( src, 0, data, 0, Size(m_width,1) );
else else
icvCvt_RGB2BGR_16u_C3R( (ushort *)src, 0, (ushort *)data, 0, Size(m_width,1) ); icvCvt_RGB2BGR_16u_C3R( (ushort *)src, 0, (ushort *)data, 0, Size(m_width,1) );

@ -381,14 +381,14 @@ bool SPngDecoder::readData(Mat &img)
break; break;
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width); 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<const ushort *>(buffer[row_info.row_num]), 0, icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast<const ushort *>(buffer[row_info.row_num]), 0,
reinterpret_cast<ushort *>(buffer[row_info.row_num]), 0, reinterpret_cast<ushort *>(buffer[row_info.row_num]), 0,
Size(m_width, 1)); Size(m_width, 1));
} }
} while (ret == SPNG_OK); } while (ret == SPNG_OK);
if (ihdr.interlace_method) if (ihdr.interlace_method && !m_use_rgb)
{ {
icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast<const ushort *>(img.data), step * 2, reinterpret_cast<ushort *>(img.data), step * 2, Size(m_width, m_height)); icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast<const ushort *>(img.data), step * 2, reinterpret_cast<ushort *>(img.data), step * 2, Size(m_width, m_height));
} }
@ -402,12 +402,12 @@ bool SPngDecoder::readData(Mat &img)
break; break;
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width); 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)); icvCvt_RGBA2BGRA_8u_C4R(buffer[row_info.row_num], 0, buffer[row_info.row_num], 0, Size(m_width, 1));
} }
} while (ret == SPNG_OK); } 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)); 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; break;
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width); 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<const ushort *>(buffer[row_info.row_num]), 0, icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort *>(buffer[row_info.row_num]), 0,
reinterpret_cast<ushort *>(buffer[row_info.row_num]), 0, Size(m_width, 1)); reinterpret_cast<ushort *>(buffer[row_info.row_num]), 0, Size(m_width, 1));
} }
} while (ret == SPNG_OK); } while (ret == SPNG_OK);
if (ihdr.interlace_method) if (ihdr.interlace_method && !m_use_rgb)
{ {
icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort *>(img.data), step, icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort *>(img.data), step,
reinterpret_cast<ushort *>(img.data), step, Size(m_width, m_height)); reinterpret_cast<ushort *>(img.data), step, Size(m_width, m_height));
@ -442,12 +442,12 @@ bool SPngDecoder::readData(Mat &img)
break; break;
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width); 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)); icvCvt_RGB2BGR_8u_C3R(buffer[row_info.row_num], 0, buffer[row_info.row_num], 0, Size(m_width, 1));
} }
} while (ret == SPNG_OK); } 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)); icvCvt_RGB2BGR_8u_C3R(img.data, step, img.data, step, Size(m_width, m_height));
} }

@ -342,7 +342,7 @@ bad_decoding_end:
if( color ) 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) ); icvCvt_RGB2BGR_8u_C3R(src, 0, data, 0, Size(m_width,1) );
else else
memcpy(data, src, std::min(step, (size_t)src_pitch)); memcpy(data, src, std::min(step, (size_t)src_pitch));
@ -365,7 +365,7 @@ bad_decoding_end:
if( color ) if( color )
icvCvt_BGRA2BGR_8u_C4C3R( src + 4, 0, data, 0, Size(m_width,1), 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 else
icvCvt_BGRA2Gray_8u_C4C1R( src + 4, 0, data, 0, Size(m_width,1), icvCvt_BGRA2Gray_8u_C4C1R( src + 4, 0, data, 0, Size(m_width,1),
m_type == RAS_FORMAT_RGB ? 2 : 0 ); m_type == RAS_FORMAT_RGB ? 2 : 0 );

@ -865,6 +865,11 @@ bool TiffDecoder::readData( Mat& img )
break; break;
case MAKE_FLAG( 3, 3 ): // RGB to BGR case MAKE_FLAG( 3, 3 ): // RGB to BGR
if (m_use_rgb)
memcpy( (void*) img_line_buffer,
(void*) bstart,
tile_width * sizeof(uchar) );
else
icvCvt_BGR2RGB_8u_C3R( bstart, 0, icvCvt_BGR2RGB_8u_C3R( bstart, 0,
img_line_buffer, 0, img_line_buffer, 0,
Size(tile_width, 1) ); Size(tile_width, 1) );
@ -879,7 +884,7 @@ bool TiffDecoder::readData( Mat& img )
case MAKE_FLAG( 4, 3 ): // RGBA to BGR case MAKE_FLAG( 4, 3 ): // RGBA to BGR
icvCvt_BGRA2BGR_8u_C4C3R( bstart, 0, icvCvt_BGRA2BGR_8u_C4C3R( bstart, 0,
img_line_buffer, 0, img_line_buffer, 0,
Size(tile_width, 1), 2 ); Size(tile_width, 1), m_use_rgb ? 0 : 2);
break; break;
case MAKE_FLAG( 4, 4 ): // RGBA to BGRA 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"); CV_CheckEQ(wanted_channels, 3, "TIFF-8bpp: BGR/BGRA images are supported only");
icvCvt_BGRA2BGR_8u_C4C3R(bstart + i*tile_width0*4, 0, icvCvt_BGRA2BGR_8u_C4C3R(bstart + i*tile_width0*4, 0,
img.ptr(img_y + tile_height - i - 1, x), 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 else
@ -972,6 +977,9 @@ bool TiffDecoder::readData( Mat& img )
else if (ncn == 3) else if (ncn == 3)
{ {
CV_CheckEQ(wanted_channels, 3, ""); CV_CheckEQ(wanted_channels, 3, "");
if (m_use_rgb)
memcpy(buffer16, img.ptr<ushort>(img_y + i, x), tile_width * sizeof(ushort));
else
icvCvt_RGB2BGR_16u_C3R(buffer16, 0, icvCvt_RGB2BGR_16u_C3R(buffer16, 0,
img.ptr<ushort>(img_y + i, x), 0, img.ptr<ushort>(img_y + i, x), 0,
Size(tile_width, 1)); Size(tile_width, 1));
@ -989,7 +997,7 @@ bool TiffDecoder::readData( Mat& img )
CV_CheckEQ(wanted_channels, 3, "TIFF-16bpp: BGR/BGRA images are supported only"); CV_CheckEQ(wanted_channels, 3, "TIFF-16bpp: BGR/BGRA images are supported only");
icvCvt_BGRA2BGR_16u_C4C3R(buffer16, 0, icvCvt_BGRA2BGR_16u_C4C3R(buffer16, 0,
img.ptr<ushort>(img_y + i, x), 0, img.ptr<ushort>(img_y + i, x), 0,
Size(tile_width, 1), 2); Size(tile_width, 1), m_use_rgb ? 0 : 2);
} }
} }
else 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); 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_tile(0, 0, tile_width, tile_height);
Rect roi_img(x, img_y, 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); extend_cvtColor(m_tile(roi_tile), img(roi_img), COLOR_RGB2BGR);
else if (!m_hdr && ncn == 4) else if (!m_hdr && ncn == 4)
extend_cvtColor(m_tile(roi_tile), img(roi_img), COLOR_RGBA2BGRA); extend_cvtColor(m_tile(roi_tile), img(roi_img), COLOR_RGBA2BGRA);
@ -1060,6 +1068,9 @@ bool TiffDecoder::readData( Mat& img )
if (m_hdr && depth >= CV_32F) if (m_hdr && depth >= CV_32F)
{ {
CV_Assert(photometric == PHOTOMETRIC_LOGLUV); CV_Assert(photometric == PHOTOMETRIC_LOGLUV);
if (m_use_rgb)
cvtColor(img, img, COLOR_XYZ2RGB);
else
cvtColor(img, img, COLOR_XYZ2BGR); cvtColor(img, img, COLOR_XYZ2BGR);
} }
return true; return true;

@ -184,12 +184,20 @@ bool WebPDecoder::readData(Mat &img)
if (channels == 3) if (channels == 3)
{ {
CV_CheckTypeEQ(read_img.type(), CV_8UC3, ""); CV_CheckTypeEQ(read_img.type(), CV_8UC3, "");
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, res_ptr = WebPDecodeBGRInto(data.ptr(), data.total(), out_data,
(int)out_data_size, (int)read_img.step); (int)out_data_size, (int)read_img.step);
} }
else if (channels == 4) else if (channels == 4)
{ {
CV_CheckTypeEQ(read_img.type(), CV_8UC4, ""); CV_CheckTypeEQ(read_img.type(), CV_8UC4, "");
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, res_ptr = WebPDecodeBGRAInto(data.ptr(), data.total(), out_data,
(int)out_data_size, (int)read_img.step); (int)out_data_size, (int)read_img.step);
} }

@ -88,7 +88,7 @@ static inline int calcType(int type, int flags)
if( (flags & IMREAD_ANYDEPTH) == 0 ) if( (flags & IMREAD_ANYDEPTH) == 0 )
type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type)); 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) ) ((flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1) )
type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3); type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3);
else else
@ -432,6 +432,12 @@ imread_( const String& filename, int flags, OutputArray mat )
scale_denom = 8; 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 /// set the scale_denom in the driver
decoder->setScale( scale_denom ); decoder->setScale( scale_denom );
@ -542,6 +548,9 @@ imreadmulti_(const String& filename, int flags, std::vector<Mat>& mats, int star
count = std::numeric_limits<int>::max(); count = std::numeric_limits<int>::max();
} }
if (flags & IMREAD_COLOR_RGB && flags != IMREAD_UNCHANGED)
decoder->setRGB(true);
/// set the filename in the driver /// set the filename in the driver
decoder->setSource(filename); decoder->setSource(filename);
@ -829,6 +838,12 @@ imdecode_( const Mat& buf, int flags, Mat& mat )
scale_denom = 8; 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 /// set the scale_denom in the driver
decoder->setScale( scale_denom ); decoder->setScale( scale_denom );
@ -965,6 +980,12 @@ imdecodemulti_(const Mat& buf, int flags, std::vector<Mat>& mats, int start, int
if (!decoder) if (!decoder)
return 0; 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) { if (count < 0) {
count = std::numeric_limits<int>::max(); count = std::numeric_limits<int>::max();
} }

@ -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, void icvCvt_CMYK2Gray_8u_C4C1R( const uchar* cmyk, int cmyk_step,
uchar* gray, int gray_step, Size size ) uchar* gray, int gray_step, Size size )

@ -115,6 +115,8 @@ void icvCvt_BGR5652BGR_8u_C2C3R( const uchar* bgr565, int bgr565_step,
uchar* bgr, int bgr_step, Size size ); uchar* bgr, int bgr_step, Size size );
void icvCvt_CMYK2BGR_8u_C4C3R( const uchar* cmyk, int cmyk_step, void icvCvt_CMYK2BGR_8u_C4C3R( const uchar* cmyk, int cmyk_step,
uchar* bgr, int bgr_step, Size size ); 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, void icvCvt_CMYK2Gray_8u_C4C1R( const uchar* ycck, int ycck_step,
uchar* gray, int gray_step, Size size ); uchar* gray, int gray_step, Size size );

@ -150,7 +150,7 @@ INSTANTIATE_TEST_CASE_P(
::testing::ValuesIn({1, 3, 4}), ::testing::ValuesIn({1, 3, 4}),
::testing::ValuesIn({0, 50, 100}), ::testing::ValuesIn({0, 50, 100}),
::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE, ::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE,
IMREAD_COLOR}))); IMREAD_COLOR, IMREAD_COLOR_RGB})));
class Imgcodecs_Avif_Image_EncodeDecodeSuite class Imgcodecs_Avif_Image_EncodeDecodeSuite
: public Imgcodecs_Avif_Image_RoundTripSuite {}; : public Imgcodecs_Avif_Image_RoundTripSuite {};
@ -183,7 +183,7 @@ INSTANTIATE_TEST_CASE_P(
::testing::ValuesIn({1, 3, 4}), ::testing::ValuesIn({1, 3, 4}),
::testing::ValuesIn({0, 50, 100}), ::testing::ValuesIn({0, 50, 100}),
::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE, ::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::Combine(::testing::ValuesIn({8, 10, 12}),
::testing::ValuesIn({1, 3}), ::testing::ValuesIn({50}), ::testing::ValuesIn({1, 3}), ::testing::ValuesIn({50}),
::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE, ::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE,
IMREAD_COLOR}))); IMREAD_COLOR, IMREAD_COLOR_RGB})));
class Imgcodecs_Avif_Animation_WriteDecodeSuite class Imgcodecs_Avif_Animation_WriteDecodeSuite
: public Imgcodecs_Avif_Animation_RoundTripSuite {}; : public Imgcodecs_Avif_Animation_RoundTripSuite {};
@ -347,7 +347,7 @@ INSTANTIATE_TEST_CASE_P(
::testing::Combine(::testing::ValuesIn({8, 10, 12}), ::testing::Combine(::testing::ValuesIn({8, 10, 12}),
::testing::ValuesIn({1, 3}), ::testing::ValuesIn({50}), ::testing::ValuesIn({1, 3}), ::testing::ValuesIn({50}),
::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE, ::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE,
IMREAD_COLOR}))); IMREAD_COLOR, IMREAD_COLOR_RGB})));
} // namespace } // namespace
} // namespace opencv_test } // namespace opencv_test

@ -192,6 +192,15 @@ TEST(Imgcodecs_EXR, read_YC_changeDepth)
ASSERT_FALSE(img.empty()); ASSERT_FALSE(img.empty());
ASSERT_EQ(CV_8UC3, img.type()); 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 // Cannot test writing, EXR encoder doesn't support 8U depth
} }

@ -108,6 +108,7 @@ const int basic_modes[] =
IMREAD_UNCHANGED, IMREAD_UNCHANGED,
IMREAD_GRAYSCALE, IMREAD_GRAYSCALE,
IMREAD_COLOR, IMREAD_COLOR,
IMREAD_COLOR_RGB,
IMREAD_ANYDEPTH, IMREAD_ANYDEPTH,
IMREAD_ANYCOLOR IMREAD_ANYCOLOR
}; };
@ -356,6 +357,10 @@ TEST(Imgcodecs_Bmp, rgba_scale)
ASSERT_FALSE(img.empty()); ASSERT_FALSE(img.empty());
ASSERT_EQ(CV_8UC3, img.type()); 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(); data = img.ptr();
ASSERT_EQ(data[0], 255); ASSERT_EQ(data[0], 255);
ASSERT_EQ(data[1], 255); ASSERT_EQ(data[1], 255);

@ -217,6 +217,7 @@ TEST_P(Imgcodecs_Jpeg_decode_cmyk, regression25274)
INSTANTIATE_TEST_CASE_P( /* nothing */, INSTANTIATE_TEST_CASE_P( /* nothing */,
Imgcodecs_Jpeg_decode_cmyk, Imgcodecs_Jpeg_decode_cmyk,
testing::Values(cv::IMREAD_COLOR, testing::Values(cv::IMREAD_COLOR,
cv::IMREAD_COLOR_RGB,
cv::IMREAD_GRAYSCALE, cv::IMREAD_GRAYSCALE,
cv::IMREAD_ANYCOLOR)); cv::IMREAD_ANYCOLOR));
@ -327,6 +328,13 @@ TEST_P(Imgcodecs_Jpeg_encode_withLumaChromaQuality, basic)
cv::Mat src = imread(fname, cv::IMREAD_COLOR); cv::Mat src = imread(fname, cv::IMREAD_COLOR);
ASSERT_FALSE(src.empty()); 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<uint8_t> jpegNormal; std::vector<uint8_t> jpegNormal;
ASSERT_NO_THROW(cv::imencode(".jpg", src, jpegNormal)); ASSERT_NO_THROW(cv::imencode(".jpg", src, jpegNormal));

@ -83,6 +83,14 @@ TEST(Imgcodecs_Png, read_color_palette_with_alpha)
EXPECT_EQ(img.at<Vec3b>(0, 0), Vec3b(0, 0, 255)); EXPECT_EQ(img.at<Vec3b>(0, 0), Vec3b(0, 0, 255));
EXPECT_EQ(img.at<Vec3b>(0, 1), Vec3b(0, 0, 255)); EXPECT_EQ(img.at<Vec3b>(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<Vec3b>(0, 0), Vec3b(255, 0, 0));
EXPECT_EQ(img.at<Vec3b>(0, 1), Vec3b(255, 0, 0));
// Fourth Test : Read PNG without alpha, imread flag 1 // Fourth Test : Read PNG without alpha, imread flag 1
img = imread(root + "readwrite/color_palette_no_alpha.png", IMREAD_COLOR); img = imread(root + "readwrite/color_palette_no_alpha.png", IMREAD_COLOR);
ASSERT_FALSE(img.empty()); ASSERT_FALSE(img.empty());
@ -91,6 +99,14 @@ TEST(Imgcodecs_Png, read_color_palette_with_alpha)
// pixel is red in BGR // pixel is red in BGR
EXPECT_EQ(img.at<Vec3b>(0, 0), Vec3b(0, 0, 255)); EXPECT_EQ(img.at<Vec3b>(0, 0), Vec3b(0, 0, 255));
EXPECT_EQ(img.at<Vec3b>(0, 1), Vec3b(0, 0, 255)); EXPECT_EQ(img.at<Vec3b>(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<Vec3b>(0, 0), Vec3b(255, 0, 0));
EXPECT_EQ(img.at<Vec3b>(0, 1), Vec3b(255, 0, 0));
} }
/** /**

@ -51,6 +51,11 @@ void PrintTo(const ImreadModes& val, std::ostream* os)
v &= ~IMREAD_IGNORE_ORIENTATION; v &= ~IMREAD_IGNORE_ORIENTATION;
*os << "IMREAD_IGNORE_ORIENTATION" << (v == 0 ? "" : " | "); *os << "IMREAD_IGNORE_ORIENTATION" << (v == 0 ? "" : " | ");
} }
if ((v & IMREAD_COLOR_RGB) != 0)
{
v &= ~IMREAD_COLOR_RGB;
*os << "IMREAD_COLOR_RGB" << (v == 0 ? "" : " | ");
}
switch (v) switch (v)
{ {
case IMREAD_UNCHANGED: return; 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_GRAYSCALE_8: // fallthru
case IMREAD_REDUCED_COLOR_8: *os << "REDUCED_8"; return; case IMREAD_REDUCED_COLOR_8: *os << "REDUCED_8"; return;
case IMREAD_IGNORE_ORIENTATION: return; case IMREAD_IGNORE_ORIENTATION: return;
case IMREAD_COLOR_RGB: return;
} // don't use "default:" to emit compiler warnings } // don't use "default:" to emit compiler warnings
*os << "IMREAD_UNKNOWN(" << (int)v << ")"; *os << "IMREAD_UNKNOWN(" << (int)v << ")";
} }

@ -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); Mat buf_loaded = imdecode(Mat(buf), imreadFlag);
EXPECT_FALSE(buf_loaded.empty()); EXPECT_FALSE(buf_loaded.empty());
if (imreadFlag & IMREAD_COLOR_RGB && imreadFlag != -1)
{
cvtColor(buf_loaded, buf_loaded, COLOR_RGB2BGR);
}
Mat loaded = imread(fname, imreadFlag); Mat loaded = imread(fname, imreadFlag);
EXPECT_FALSE(loaded.empty()); 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)"; 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); double psnr = cvtest::PSNR(loaded, image);
@ -238,6 +248,7 @@ TEST_P(Imgcodecs_Image, read_write_BGR)
Mat image = generateTestImageBGR(); 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, psnrThreshold));
EXPECT_NO_THROW(test_image_io(image, fname, ext, IMREAD_COLOR_RGB, psnrThreshold));
EXPECT_EQ(0, remove(fname.c_str())); EXPECT_EQ(0, remove(fname.c_str()));
} }

@ -53,7 +53,7 @@ enum ImreadMixModes
{ {
IMREAD_MIX_UNCHANGED = IMREAD_UNCHANGED , IMREAD_MIX_UNCHANGED = IMREAD_UNCHANGED ,
IMREAD_MIX_GRAYSCALE = IMREAD_GRAYSCALE , 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_ANYDEPTH = IMREAD_GRAYSCALE | IMREAD_ANYDEPTH ,
IMREAD_MIX_GRAYSCALE_ANYCOLOR = IMREAD_GRAYSCALE | IMREAD_ANYCOLOR, IMREAD_MIX_GRAYSCALE_ANYCOLOR = IMREAD_GRAYSCALE | IMREAD_ANYCOLOR,
IMREAD_MIX_GRAYSCALE_ANYDEPTH_ANYCOLOR = IMREAD_GRAYSCALE | IMREAD_ANYDEPTH | 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: case IMREAD_GRAYSCALE | IMREAD_ANYCOLOR | IMREAD_ANYDEPTH:
ncn = (ncn == 1)?1:3; ncn = (ncn == 1)?1:3;
break; break;
case IMREAD_COLOR: case IMREAD_COLOR | IMREAD_COLOR_RGB:
ncn = 3; ncn = 3;
depth = 1; depth = 1;
break; break;
@ -818,6 +818,24 @@ TEST(Imgcodecs_Tiff, read_palette_color_image)
ASSERT_EQ(CV_8UC3, img.type()); 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<Vec3b>(32, 24), Vec3b(255, 0, 0));
EXPECT_EQ(img_bgr.at<Vec3b>(32, 24), Vec3b(0, 0, 255));
}
TEST(Imgcodecs_Tiff, read_4_bit_palette_color_image) TEST(Imgcodecs_Tiff, read_4_bit_palette_color_image)
{ {
const string root = cvtest::TS::ptr()->get_data_path(); const string root = cvtest::TS::ptr()->get_data_path();
@ -1066,6 +1084,7 @@ const int all_modes[] =
IMREAD_UNCHANGED, IMREAD_UNCHANGED,
IMREAD_GRAYSCALE, IMREAD_GRAYSCALE,
IMREAD_COLOR, IMREAD_COLOR,
IMREAD_COLOR_RGB,
IMREAD_ANYDEPTH, IMREAD_ANYDEPTH,
IMREAD_ANYCOLOR IMREAD_ANYCOLOR
}; };

@ -51,6 +51,12 @@ TEST(Imgcodecs_WebP, encode_decode_lossless_webp)
ASSERT_FALSE(decode.empty()); ASSERT_FALSE(decode.empty());
EXPECT_TRUE(cvtest::norm(decode, img_webp, NORM_INF) == 0); 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()); ASSERT_FALSE(img_webp.empty());
EXPECT_TRUE(cvtest::norm(img, img_webp, NORM_INF) == 0); EXPECT_TRUE(cvtest::norm(img, img_webp, NORM_INF) == 0);

Loading…
Cancel
Save