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
This commit is contained in:
zihaomu 2024-07-03 15:58:25 +08:00 committed by GitHub
parent a7fd9446cf
commit 934e6899f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 408 additions and 95 deletions

View File

@ -68,7 +68,8 @@ namespace cv
enum ImreadModes {
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_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_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.
@ -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_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_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
@ -268,7 +270,7 @@ Currently, the following file formats are supported:
@param filename Name of file to be loaded.
@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.
@ -279,7 +281,7 @@ This is an overloaded member function, provided for convenience. It differs from
@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.
*/
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.

View File

@ -27,6 +27,23 @@ PERF_TEST(JPEG, Decode)
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)
{
String filename = getDataPath("stitching/boat1.jpg");

View File

@ -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<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)
{
String filename = getDataPath("perf/2560x1600.png");

View File

@ -33,7 +33,7 @@ struct 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->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;
}

View File

@ -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();

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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
{

View File

@ -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;

View File

@ -350,7 +350,7 @@ opj_cparameters setupEncoderParameters(const std::vector<int>& 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<const OPJ_INT32*>;
@ -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<const OPJ_INT32*>;
@ -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<const OPJ_INT32*>;
@ -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

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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) );

View File

@ -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<const ushort *>(buffer[row_info.row_num]), 0,
reinterpret_cast<ushort *>(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<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;
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<const ushort *>(buffer[row_info.row_num]), 0,
reinterpret_cast<ushort *>(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<const ushort *>(img.data), step,
reinterpret_cast<ushort *>(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));
}

View File

@ -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 );

View File

@ -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<ushort>(img_y + i, x), 0,
Size(tile_width, 1));
if (m_use_rgb)
memcpy(buffer16, img.ptr<ushort>(img_y + i, x), tile_width * sizeof(ushort));
else
icvCvt_RGB2BGR_16u_C3R(buffer16, 0,
img.ptr<ushort>(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<ushort>(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;
}

View File

@ -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)

View File

@ -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<Mat>& mats, int star
count = std::numeric_limits<int>::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<Mat>& 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<int>::max();
}

View File

@ -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 )

View File

@ -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 );

View File

@ -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

View File

@ -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
}

View File

@ -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);

View File

@ -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<uint8_t> jpegNormal;
ASSERT_NO_THROW(cv::imencode(".jpg", src, jpegNormal));

View File

@ -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, 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
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<Vec3b>(0, 0), 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));
}
/**

View File

@ -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 << ")";
}

View File

@ -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()));
}

View File

@ -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<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)
{
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
};

View File

@ -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);