From e9e6b1e22c1a966a81aca1217b16a51fe7311b3b Mon Sep 17 00:00:00 2001 From: Marek Kochanczyk Date: Mon, 30 Oct 2023 09:58:08 +0100 Subject: [PATCH] Merge pull request #24405 from kochanczyk:4.x Extend the signature of imdecodemulti() #24405 (Edited after addressing Reviewers' comments.) Add an argument to `imdecodemulti()` to enable optional selection of pages of multi-page images. Be default, all pages are decoded. If used, the additional argument may specify a continuous selection of pages to decode. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [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] I agree to contribute to the project under Apache 2 License. - [X] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake --- .../imgcodecs/include/opencv2/imgcodecs.hpp | 3 +- modules/imgcodecs/src/loadsave.cpp | 13 +++- modules/imgcodecs/test/test_tiff.cpp | 67 +++++++++++++++++-- 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 2d3bc4e6f2..89bd6e1c1b 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -337,8 +337,9 @@ See cv::imreadmulti for the list of supported formats and flags description. @param buf Input array or vector of bytes. @param flags The same flags as in cv::imread, see cv::ImreadModes. @param mats A vector of Mat objects holding each page, if more than one. +@param range A continuous selection of pages. */ -CV_EXPORTS_W bool imdecodemulti(InputArray buf, int flags, CV_OUT std::vector& mats); +CV_EXPORTS_W bool imdecodemulti(InputArray buf, int flags, CV_OUT std::vector& mats, const cv::Range& range = Range::all()); /** @brief Encodes an image into a memory buffer. diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 4ce490610e..734f7b516c 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -1095,12 +1095,21 @@ imdecodemulti_(const Mat& buf, int flags, std::vector& mats, int start, int return !mats.empty(); } -bool imdecodemulti(InputArray _buf, int flags, CV_OUT std::vector& mats) +bool imdecodemulti(InputArray _buf, int flags, CV_OUT std::vector& mats, const Range& range) { CV_TRACE_FUNCTION(); Mat buf = _buf.getMat(); - return imdecodemulti_(buf, flags, mats, 0, -1); + if (range == Range::all()) + { + return imdecodemulti_(buf, flags, mats, 0, -1); + } + else + { + CV_CheckGE(range.start, 0, "Range start cannot be negative."); + CV_CheckGT(range.size(), 0, "Range cannot be empty."); + return imdecodemulti_(buf, flags, mats, range.start, range.size()); + } } bool imencode( const String& ext, InputArray _image, diff --git a/modules/imgcodecs/test/test_tiff.cpp b/modules/imgcodecs/test/test_tiff.cpp index 82da0cdf42..db27a305e5 100644 --- a/modules/imgcodecs/test/test_tiff.cpp +++ b/modules/imgcodecs/test/test_tiff.cpp @@ -965,7 +965,7 @@ TEST_P(Imgcodecs_Tiff_Modes, decode_multipage) } } -TEST_P(Imgcodecs_Tiff_Modes, decode_multipage_use_memory_buffer) +TEST_P(Imgcodecs_Tiff_Modes, decode_multipage_use_memory_buffer_all_pages) { const int mode = GetParam(); const string root = cvtest::TS::ptr()->get_data_path(); @@ -984,13 +984,14 @@ TEST_P(Imgcodecs_Tiff_Modes, decode_multipage_use_memory_buffer) FILE* fp = fopen(filename.c_str(), "rb"); ASSERT_TRUE(fp != NULL); fseek(fp, 0, SEEK_END); - long pos = ftell(fp); - - std::vector buf; - buf.resize((size_t)pos); + const size_t file_size = ftell(fp); fseek(fp, 0, SEEK_SET); - buf.resize(fread(&buf[0], 1, buf.size(), fp)); + + std::vector buf(file_size); + const size_t actual_read = fread(&buf[0], 1, file_size, fp); fclose(fp); + ASSERT_EQ(file_size, actual_read); + ASSERT_EQ(file_size, static_cast(buf.size())); bool res = imdecodemulti(buf, mode, pages); ASSERT_TRUE(res == true); @@ -1002,6 +1003,60 @@ TEST_P(Imgcodecs_Tiff_Modes, decode_multipage_use_memory_buffer) } } +TEST_P(Imgcodecs_Tiff_Modes, decode_multipage_use_memory_buffer_selected_pages) +{ + const int mode = GetParam(); + const string root = cvtest::TS::ptr()->get_data_path(); + const string filename = root + "readwrite/multipage.tif"; + const string page_files[] = { + "readwrite/multipage_p1.tif", + "readwrite/multipage_p2.tif", + "readwrite/multipage_p3.tif", + "readwrite/multipage_p4.tif", + "readwrite/multipage_p5.tif", + "readwrite/multipage_p6.tif" + }; + const size_t page_count = sizeof(page_files) / sizeof(page_files[0]); + + FILE* fp = fopen(filename.c_str(), "rb"); + ASSERT_TRUE(fp != NULL); + fseek(fp, 0, SEEK_END); + const size_t file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + std::vector buf(file_size); + const size_t actual_read = fread(&buf[0], 1, file_size, fp); + fclose(fp); + ASSERT_EQ(file_size, actual_read); + ASSERT_EQ(file_size, static_cast(buf.size())); + + const Range range(1, page_count - 1); + ASSERT_GE(range.size(), 1); + + vector middle_pages_from_imread; + for (int page_i = range.start; page_i < range.end; page_i++) + { + const Mat page = imread(root + page_files[page_i], mode); + middle_pages_from_imread.push_back(page); + } + ASSERT_EQ( + static_cast(range.size()), + static_cast(middle_pages_from_imread.size()) + ); + + vector middle_pages_from_imdecodemulti; + const bool res = imdecodemulti(buf, mode, middle_pages_from_imdecodemulti, range); + ASSERT_TRUE(res == true); + EXPECT_EQ(middle_pages_from_imread.size(), middle_pages_from_imdecodemulti.size()); + + for (int i = 0, e = range.size(); i < e; i++) + { + EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), + middle_pages_from_imread[i], + middle_pages_from_imdecodemulti[i]); + } +} + const int all_modes[] = { IMREAD_UNCHANGED,