From b77c74b6fc79f6dca2da0fb5694ba57037310caf Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Tue, 4 Jun 2024 14:44:39 +0300 Subject: [PATCH] imgproc: fixed imread with output image argument, minor refactoring, fixes in HDR --- modules/imgcodecs/src/grfmt_hdr.cpp | 16 +++- modules/imgcodecs/src/loadsave.cpp | 97 +++++++--------------- modules/imgcodecs/test/test_png.cpp | 13 --- modules/imgcodecs/test/test_read_write.cpp | 29 +++++++ 4 files changed, 72 insertions(+), 83 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_hdr.cpp b/modules/imgcodecs/src/grfmt_hdr.cpp index 6e2f565e32..3a18b5bbeb 100644 --- a/modules/imgcodecs/src/grfmt_hdr.cpp +++ b/modules/imgcodecs/src/grfmt_hdr.cpp @@ -93,10 +93,18 @@ bool HdrDecoder::readData(Mat& _img) RGBE_ReadPixels_RLE(file, const_cast(img.ptr()), img.cols, img.rows); fclose(file); file = NULL; - if(_img.depth() == img.depth()) { - img.convertTo(_img, _img.type()); - } else { - img.convertTo(_img, _img.type(), 255); + // NOTE: 'img' has type CV32FC3 + switch (_img.depth()) + { + case CV_8U: img.convertTo(img, _img.depth(), 255); break; + case CV_32F: break; + default: CV_Error(Error::StsError, "Wrong expected image depth, allowed: CV_8U and CV_32F"); + } + switch (_img.channels()) + { + case 1: cvtColor(img, _img, COLOR_BGR2GRAY); break; + case 3: img.copyTo(_img); break; + default: CV_Error(Error::StsError, "Wrong expected image channels, allowed: 1 and 3"); } return true; } diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 2586fc1fa4..ec4760b879 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -81,6 +81,22 @@ static Size validateInputImageSize(const Size& size) } +static inline int calcType(int type, int flags) +{ + if( (flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && flags != IMREAD_UNCHANGED ) + { + if( (flags & IMREAD_ANYDEPTH) == 0 ) + type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type)); + + if( (flags & IMREAD_COLOR) != 0 || + ((flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1) ) + type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3); + else + type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1); + } + return type; +} + namespace { class ByteStreamBuffer: public std::streambuf @@ -328,7 +344,7 @@ static ImageEncoder findEncoder( const String& _ext ) } -static void ExifTransform(int orientation, Mat& img) +static void ExifTransform(int orientation, OutputArray img) { switch( orientation ) { @@ -365,7 +381,7 @@ static void ExifTransform(int orientation, Mat& img) } } -static void ApplyExifOrientation(ExifEntry_t orientationTag, Mat& img) +static void ApplyExifOrientation(ExifEntry_t orientationTag, OutputArray img) { int orientation = IMAGE_ORIENTATION_TL; @@ -385,7 +401,7 @@ static void ApplyExifOrientation(ExifEntry_t orientationTag, Mat& img) * */ static bool -imread_( const String& filename, int flags, Mat& mat ) +imread_( const String& filename, int flags, OutputArray mat ) { /// Search for the relevant decoder to handle the imagery ImageDecoder decoder; @@ -444,18 +460,7 @@ imread_( const String& filename, int flags, Mat& mat ) Size size = validateInputImageSize(Size(decoder->width(), decoder->height())); // grab the decoded type - int type = decoder->type(); - if( (flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && flags != IMREAD_UNCHANGED ) - { - if( (flags & IMREAD_ANYDEPTH) == 0 ) - type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type)); - - if( (flags & IMREAD_COLOR) != 0 || - ((flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1) ) - type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3); - else - type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1); - } + const int type = calcType(decoder->type(), flags); if (mat.empty()) { @@ -469,11 +474,16 @@ imread_( const String& filename, int flags, Mat& mat ) } // read the image data + Mat real_mat = mat.getMat(); + const void * original_ptr = real_mat.data; bool success = false; try { - if (decoder->readData(mat)) + if (decoder->readData(real_mat)) + { + CV_CheckTrue(original_ptr == real_mat.data, "Internal imread issue"); success = true; + } } catch (const cv::Exception& e) { @@ -567,18 +577,7 @@ imreadmulti_(const String& filename, int flags, std::vector& mats, int star while (current < count) { // grab the decoded type - int type = decoder->type(); - if ((flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && flags != IMREAD_UNCHANGED) - { - if ((flags & IMREAD_ANYDEPTH) == 0) - type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type)); - - if ((flags & IMREAD_COLOR) != 0 || - ((flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1)) - type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3); - else - type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1); - } + const int type = calcType(decoder->type(), flags); // established the required input image size Size size = validateInputImageSize(Size(decoder->width(), decoder->height())); @@ -645,10 +644,8 @@ void imread( const String& filename, OutputArray dst, int flags ) { CV_TRACE_FUNCTION(); - Mat img = dst.getMat(); - /// load the data - imread_(filename, flags, img); + imread_(filename, flags, dst); } /** @@ -884,18 +881,7 @@ imdecode_( const Mat& buf, int flags, Mat& mat ) // established the required input image size Size size = validateInputImageSize(Size(decoder->width(), decoder->height())); - int type = decoder->type(); - if( (flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && flags != IMREAD_UNCHANGED ) - { - if( (flags & IMREAD_ANYDEPTH) == 0 ) - type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type)); - - if( (flags & IMREAD_COLOR) != 0 || - ((flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1) ) - type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3); - else - type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1); - } + const int type = calcType(decoder->type(), flags); mat.create( size.height, size.width, type ); @@ -1046,18 +1032,7 @@ imdecodemulti_(const Mat& buf, int flags, std::vector& mats, int start, int while (current < count) { // grab the decoded type - int type = decoder->type(); - if ((flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && flags != IMREAD_UNCHANGED) - { - if ((flags & IMREAD_ANYDEPTH) == 0) - type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type)); - - if ((flags & IMREAD_COLOR) != 0 || - ((flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1)) - type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3); - else - type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1); - } + const int type = calcType(decoder->type(), flags); // established the required input image size Size size = validateInputImageSize(Size(decoder->width(), decoder->height())); @@ -1316,17 +1291,7 @@ bool ImageCollection::Impl::readHeader() { // readHeader must be called before calling this method Mat ImageCollection::Impl::readData() { - int type = m_decoder->type(); - if ((m_flags & IMREAD_LOAD_GDAL) != IMREAD_LOAD_GDAL && m_flags != IMREAD_UNCHANGED) { - if ((m_flags & IMREAD_ANYDEPTH) == 0) - type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type)); - - if ((m_flags & IMREAD_COLOR) != 0 || - ((m_flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1)) - type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3); - else - type = CV_MAKETYPE(CV_MAT_DEPTH(type), 1); - } + const int type = calcType(m_decoder->type(), m_flags); // established the required input image size Size size = validateInputImageSize(Size(m_width, m_height)); diff --git a/modules/imgcodecs/test/test_png.cpp b/modules/imgcodecs/test/test_png.cpp index 655c59430d..cdc7da39b2 100644 --- a/modules/imgcodecs/test/test_png.cpp +++ b/modules/imgcodecs/test/test_png.cpp @@ -7,19 +7,6 @@ namespace opencv_test { namespace { #if defined(HAVE_PNG) || defined(HAVE_SPNG) -TEST(Imgcodecs_Png, imread_passing_mat) -{ - const string root = cvtest::TS::ptr()->get_data_path(); - const string imgName = root + "../cv/shared/lena.png"; - - Mat ref = imread(imgName); - Mat img(ref.size(), ref.type()); - void* ptr = img.data; - imread(imgName, img); - EXPECT_EQ(cv::norm(ref, img, NORM_INF), 0); - EXPECT_EQ(img.data, ptr); -} - TEST(Imgcodecs_Png, write_big) { const string root = cvtest::TS::ptr()->get_data_path(); diff --git a/modules/imgcodecs/test/test_read_write.cpp b/modules/imgcodecs/test/test_read_write.cpp index 39c02ca95c..824688b366 100644 --- a/modules/imgcodecs/test/test_read_write.cpp +++ b/modules/imgcodecs/test/test_read_write.cpp @@ -282,6 +282,35 @@ TEST(Imgcodecs_Image, regression_9376) EXPECT_EQ(32, m.rows); } +TEST(Imgcodecs_Image, imread_overload) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string imgName = findDataFile("../highgui/readwrite/ordinary.bmp"); + + Mat ref = imread(imgName); + ASSERT_FALSE(ref.empty()); + { + Mat img(ref.size(), ref.type(), Scalar::all(0)); // existing image + void * ptr = img.data; + imread(imgName, img); + ASSERT_FALSE(img.empty()); + EXPECT_EQ(cv::norm(ref, img, NORM_INF), 0); + EXPECT_EQ(img.data, ptr); // no reallocation + } + { + Mat img; // empty image + imread(imgName, img); + ASSERT_FALSE(img.empty()); + EXPECT_EQ(cv::norm(ref, img, NORM_INF), 0); + } + { + UMat img; // empty UMat + imread(imgName, img); + ASSERT_FALSE(img.empty()); + EXPECT_EQ(cv::norm(ref, img, NORM_INF), 0); + } +} + //================================================================================================== TEST(Imgcodecs_Image, write_umat)