nareshmlx/opencv-pr huggingface dataset:
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -54,6 +54,8 @@
 #include <cerrno>
 #include <opencv2/core/utils/logger.hpp>
 #include <opencv2/core/utils/configuration.private.hpp>
+#include <opencv2/imgcodecs.hpp>
+
 
 
 /****************************************************************************************\
@@ -658,57 +660,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
```

Review comment:
We need this because if user wants to do std::vector<ImageCollection>. Renaming to `init` might be better
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -54,6 +54,8 @@
 #include <cerrno>
 #include <opencv2/core/utils/logger.hpp>
 #include <opencv2/core/utils/configuration.private.hpp>
+#include <opencv2/imgcodecs.hpp>
+
 
 
 /****************************************************************************************\
@@ -658,57 +660,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
```

Review comment:
This returns the number of pages in image. I think i should find a new, better name for this one. So we can return the current page Size(width, height).
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API
**Review Line:** 1051

**Code Changes:**
```diff
+    m_decoder->setSource(filename);
+    CV_Assert(m_decoder->readHeader());
+
+    // count the pages of the image collection
+    size_t count = 1;
+    while(m_decoder->nextPage()) count++;
+
+    m_size = count;
+    m_pages.resize(m_size);
+    // Reinitialize the decoder because we advanced to the last page while counting the pages of the image
+#ifdef HAVE_GDAL
```

Review comment:
nextPage() call is a problem. During initialization it may be fine, but it's also used to increment iterator. We need some other solution. Is it possible to extract all position-dependent information from the decoder and move it into iterator of ImageCollection?
**File:** modules/imgcodecs/test/test_read_write.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -303,4 +303,181 @@ TEST(Imgcodecs_Image, write_umat)
     EXPECT_EQ(0, remove(dst_name.c_str()));
 }
 
+TEST(Imgcodecs_Image, multipage_collection_size)
+{
+    const string root = cvtest::TS::ptr()->get_data_path();
+    const string filename = root + "readwrite/multipage.tif";
+
+    ImageCollection collection(filename, IMREAD_ANYCOLOR);
```

Review comment:
when all pages are the same, it's quite bad test. We need something different. I would create a multi-page image with different pages, then stored each page as individual file and then compared each page of multi-page image with the same page read as individual image, e.g.: ``` ImageCollection imcol("multi-page.tiff"); int i = 0; for (auto& page : imcol) { double diff = norm(page, imread(format("multi-page-%d.tiff", i), NORM_INF); EXPECT_EQ(diff, 0.); i++; } ```
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API
**Review Line:** 1051

**Code Changes:**
```diff
+    m_decoder->setSource(filename);
+    CV_Assert(m_decoder->readHeader());
+
+    // count the pages of the image collection
+    size_t count = 1;
+    while(m_decoder->nextPage()) count++;
+
+    m_size = count;
+    m_pages.resize(m_size);
+    // Reinitialize the decoder because we advanced to the last page while counting the pages of the image
+#ifdef HAVE_GDAL
```

Review comment:
This `nextPage` call is required to find the ImageCollection size. About extracting all position dependent information, I need to pass `m_decoder` and `this` to the iterator in order to do that. So, i am not sure if this is a good idea because operator[] and at() also decodes pages. `this` is required for `operator*` decoding. So it can put pages inside the `m_vector`. I think we need to discuss this.
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API
**Review Line:** 1029

**Code Changes:**
```diff
+
+ImageCollection::Impl::Impl(std::string const& filename, int flags) {
+    this->init(filename, flags);
+}
+
+void ImageCollection::Impl::init(String const& filename, int flags) {
+    m_filename = filename;
+    m_flags = flags;
+
+#ifdef HAVE_GDAL
+    if (m_flags != IMREAD_UNCHANGED && (m_flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) {
```

Review comment:
init() should close the already opened collection, if any
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API
**Review Line:** 1223

**Code Changes:**
```diff
+    return &m_pCollection->getImpl()->operator[](m_curr);
+}
+
+ImageCollection::iterator& ImageCollection::iterator::operator++() {
+    if(m_pCollection->pImpl->currentIndex() == m_curr) {
+        m_pCollection->pImpl->advance();
+    }
+    m_curr++;
+    return *this;
+}
+
```

Review comment:
do we need to call advance() here? Can operator [] take care of advancing internal pointer?
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -54,6 +54,8 @@
 #include <cerrno>
 #include <opencv2/core/utils/logger.hpp>
 #include <opencv2/core/utils/configuration.private.hpp>
+#include <opencv2/imgcodecs.hpp>
+
 
 
 /****************************************************************************************\
@@ -658,57 +660,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
```

Review comment:
Out-of-range access check is required here.
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -54,6 +54,8 @@
 #include <cerrno>
 #include <opencv2/core/utils/logger.hpp>
 #include <opencv2/core/utils/configuration.private.hpp>
+#include <opencv2/imgcodecs.hpp>
+
 
 
 /****************************************************************************************\
@@ -658,57 +660,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
```

Review comment:
Most probably `m_decoder->readHeader()`. is redundant here. `nextPage()` calls `readHeader()` after switch to the new page internally. If Also range for `m_current` check will be useful.
**File:** modules/imgcodecs/test/test_read_write.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API
**Review Line:** 439

**Code Changes:**
```diff
+        cv::Mat ithPage = imread(page_files[index]);
+        EXPECT_FALSE(ithPage.empty());
+        double diff = cv::norm(i, ithPage, NORM_INF);
+        EXPECT_EQ(0., diff);
+        ++index;
+    }
+    EXPECT_EQ(index, collection.size());
+
+    index = 0;
+    for(auto &&i: collection)
+    {
```

Review comment:
It makes sense to check index after the loop to ensure that all pages has been read.
**File:** modules/imgcodecs/test/test_read_write.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -303,4 +303,181 @@ TEST(Imgcodecs_Image, write_umat)
     EXPECT_EQ(0, remove(dst_name.c_str()));
 }
 
+TEST(Imgcodecs_Image, multipage_collection_size)
+{
+    const string root = cvtest::TS::ptr()->get_data_path();
+    const string filename = root + "readwrite/multipage.tif";
+
+    ImageCollection collection(filename, IMREAD_ANYCOLOR);
```

Review comment:
I propose to extract `imread` as separate line and check it it's result is not empty.
**File:** modules/imgcodecs/test/test_read_write.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API
**Review Line:** 399

**Code Changes:**
```diff
+
+    ImageCollection collection(filename, IMREAD_ANYCOLOR);
+    EXPECT_EQ((size_t)6, collection.size());
+
+    // backward decoding -> 5,4,3,2,1,0
+    for(int i = (int)collection.size() - 1; i >= 0; --i)
+    {
+        cv::Mat ithPage = imread(page_files[i]);
+        EXPECT_FALSE(ithPage.empty());
+        double diff = cv::norm(collection[i], ithPage, NORM_INF);
+        EXPECT_EQ(diff, 0.);
```

Review comment:
Need `collection.size()` check against constant.
**File:** modules/imgcodecs/test/test_read_write.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -303,4 +303,181 @@ TEST(Imgcodecs_Image, write_umat)
     EXPECT_EQ(0, remove(dst_name.c_str()));
 }
 
+TEST(Imgcodecs_Image, multipage_collection_size)
+{
+    const string root = cvtest::TS::ptr()->get_data_path();
+    const string filename = root + "readwrite/multipage.tif";
+
+    ImageCollection collection(filename, IMREAD_ANYCOLOR);
```

Review comment:
I propose to extract imread as separate line and check it it's result is not empty.
**File:** modules/imgcodecs/include/opencv2/imgcodecs.hpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -332,6 +332,51 @@ CV_EXPORTS_W bool haveImageReader( const String& filename );
  */
 CV_EXPORTS_W bool haveImageWriter( const String& filename );
 
+/** @brief To read Multi Page images on demand
+
+The ImageCollection class provides iterator API to read multi page images on demand. Create iterator
+to the collection of the images and iterate over the collection. Decode the necessary page with operator*.
+
+The performance of page decoding is O(1) if collection is increment sequentially. If the user wants to access random page,
```

Review comment:
O(n) is not fully true. For TIFF `TIFFReadDirectory() and readHeader()` are called, but not full decoding pipeline.
**File:** modules/imgcodecs/include/opencv2/imgcodecs.hpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API
**Review Line:** 344

**Code Changes:**
```diff
+
+The performance of page decoding is O(1) if collection is increment sequentially. If the user wants to access random page,
+then the time Complexity is O(n) because the collection has to be reinitialized every time in order to go to the correct page.
+However, the intermediate pages are not decoded during the process, so typically it's quite fast.
+This is required because multipage codecs does not support going backwards.
+After decoding the one page, it is stored inside the collection cache. Hence, trying to get Mat object from already decoded page is O(1).
+If you need memory, you can use .releaseCache() method to release cached index.
+The space complexity is O(n) if all pages are decoded into memory. The user is able to decode and release images on demand.
+*/
+class CV_EXPORTS ImageCollection {
+public:
```

Review comment:
I propose to make caching optional. It slows down some cases a bit, but may dramatically reduce memory consumption.
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -54,6 +54,8 @@
 #include <cerrno>
 #include <opencv2/core/utils/logger.hpp>
 #include <opencv2/core/utils/configuration.private.hpp>
+#include <opencv2/imgcodecs.hpp>
+
 
 
 /****************************************************************************************\
@@ -658,57 +660,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
```

Review comment:
I propose to rename the method to `ReleaseCache()`. Other OpenCV classes that contain `release()` method finalize the algorithm, but not clean cache. Object usage after `release()` is not expected.
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -54,6 +54,8 @@
 #include <cerrno>
 #include <opencv2/core/utils/logger.hpp>
 #include <opencv2/core/utils/configuration.private.hpp>
+#include <opencv2/imgcodecs.hpp>
+
 
 
 /****************************************************************************************\
@@ -658,57 +660,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
```

Review comment:
I inspired from cv::Mat. The intention of the both call is almost the same. But i am fine with the renaming of the method name. releaseCache()
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -54,6 +54,8 @@
 #include <cerrno>
 #include <opencv2/core/utils/logger.hpp>
 #include <opencv2/core/utils/configuration.private.hpp>
+#include <opencv2/imgcodecs.hpp>
+
 
 
 /****************************************************************************************\
@@ -658,57 +660,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
```

Review comment:
range is already checked at the external method `ImageCollection::at()` method
**File:** modules/imgcodecs/include/opencv2/imgcodecs.hpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -332,6 +332,51 @@ CV_EXPORTS_W bool haveImageReader( const String& filename );
  */
 CV_EXPORTS_W bool haveImageWriter( const String& filename );
 
+/** @brief To read Multi Page images on demand
+
+The ImageCollection class provides iterator API to read multi page images on demand. Create iterator
+to the collection of the images and iterate over the collection. Decode the necessary page with operator*.
+
+The performance of page decoding is O(1) if collection is increment sequentially. If the user wants to access random page,
```

Review comment:
how about "Complexity is O(n) because the collection has to be reinitialized every time in order to go to the correct page. However, the intermediate pages are not decoded during the process, so typically it's quite fast"?
**File:** modules/imgcodecs/include/opencv2/imgcodecs.hpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API
**Review Line:** 344

**Code Changes:**
```diff
+
+The performance of page decoding is O(1) if collection is increment sequentially. If the user wants to access random page,
+then the time Complexity is O(n) because the collection has to be reinitialized every time in order to go to the correct page.
+However, the intermediate pages are not decoded during the process, so typically it's quite fast.
+This is required because multipage codecs does not support going backwards.
+After decoding the one page, it is stored inside the collection cache. Hence, trying to get Mat object from already decoded page is O(1).
+If you need memory, you can use .releaseCache() method to release cached index.
+The space complexity is O(n) if all pages are decoded into memory. The user is able to decode and release images on demand.
+*/
+class CV_EXPORTS ImageCollection {
+public:
```

Review comment:
Here is the example. Suppose the scenario is user wants to read images sequentially then discard them. ```cpp ImageCollection collection("some.tiff"); int index = 0; for(auto& page: collection) { // process page here collection.release(index); // or releaseCache as you proposed index++; } ``` So the total memory usage here is actually `collection.size() * sizeof(cv::Mat (empty)) + sizeof(current page)`. If user release every image after processing, there are no extra memory usage.
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -54,6 +54,8 @@
 #include <cerrno>
 #include <opencv2/core/utils/logger.hpp>
 #include <opencv2/core/utils/configuration.private.hpp>
+#include <opencv2/imgcodecs.hpp>
+
 
 
 /****************************************************************************************\
@@ -658,57 +660,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
```

Review comment:
There are 2 methods in the ImageCollection to read images. `.at()` method and `operator[]` operator. As in the STD containers, `.at` method does out-of-range access check but operator[] does not. There is no need to check here.
**File:** modules/imgcodecs/test/test_read_write.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -303,4 +303,181 @@ TEST(Imgcodecs_Image, write_umat)
     EXPECT_EQ(0, remove(dst_name.c_str()));
 }
 
+TEST(Imgcodecs_Image, multipage_collection_size)
+{
+    const string root = cvtest::TS::ptr()->get_data_path();
+    const string filename = root + "readwrite/multipage.tif";
+
+    ImageCollection collection(filename, IMREAD_ANYCOLOR);
```

Review comment:
it make sense to use `size_t` for `index` No need to add type conversion to checks.
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -54,6 +54,8 @@
 #include <cerrno>
 #include <opencv2/core/utils/logger.hpp>
 #include <opencv2/core/utils/configuration.private.hpp>
+#include <opencv2/imgcodecs.hpp>
+
 
 
 /****************************************************************************************\
@@ -658,57 +660,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
```

Review comment:
`at()` calls `operator[]`, but not vise versa. In case if index is -1 you'll get out of bound access for std::vector. For index, let say 1e+6 the next loop will be iterated 1e+6 times: `for(int i = 0; i != index && advance(); ++i) {}`. I propose to move the check from `at()` to `operator[]` or have it in both methods. In `std::vector<>` is reasonable to get rid of range check for performance reason. Range check is heavier than actual work. But it's not true for image codecs. Also it could be a security issue.
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API
**Review Line:** 1166

**Code Changes:**
```diff
+    CV_Assert(index >= 0 && size_t(index) < m_size);
+    return operator[](index);
+}
+
+Mat& ImageCollection::Impl::operator[](int index) {
+    if(m_pages.at(index).empty()) {
+        // We can't go backward in multi images. If the page is not in vector yet,
+        // go back to first page and advance until the desired page and read it into memory
+        if(m_current != index) {
+            reset();
+            for(int i = 0; i != index && advance(); ++i) {}
```

Review comment:
OpenCV 4.x relies on C++11 as default C++ standard. `at` was added in C++20. Let's use manual range check + [] to presume compatibility. Details: https://en.cppreference.com/w/cpp/container/vector/at
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -54,6 +54,8 @@
 #include <cerrno>
 #include <opencv2/core/utils/logger.hpp>
 #include <opencv2/core/utils/configuration.private.hpp>
+#include <opencv2/imgcodecs.hpp>
+
 
 
 /****************************************************************************************\
@@ -658,57 +660,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
```

Review comment:
OpenCV uses `CV_Assert` for such checks.
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -54,6 +54,8 @@
 #include <cerrno>
 #include <opencv2/core/utils/logger.hpp>
 #include <opencv2/core/utils/configuration.private.hpp>
+#include <opencv2/imgcodecs.hpp>
+
 
 
 /****************************************************************************************\
@@ -658,57 +660,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
```

Review comment:
The same CV_Assert();
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API
**Review Line:** 1166

**Code Changes:**
```diff
+    CV_Assert(index >= 0 && size_t(index) < m_size);
+    return operator[](index);
+}
+
+Mat& ImageCollection::Impl::operator[](int index) {
+    if(m_pages.at(index).empty()) {
+        // We can't go backward in multi images. If the page is not in vector yet,
+        // go back to first page and advance until the desired page and read it into memory
+        if(m_current != index) {
+            reset();
+            for(int i = 0; i != index && advance(); ++i) {}
```

Review comment:
`at` method exists in C++11. In C++20, they added constexpr.
**File:** modules/imgcodecs/src/loadsave.cpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API
**Review Line:** 1166

**Code Changes:**
```diff
+    CV_Assert(index >= 0 && size_t(index) < m_size);
+    return operator[](index);
+}
+
+Mat& ImageCollection::Impl::operator[](int index) {
+    if(m_pages.at(index).empty()) {
+        // We can't go backward in multi images. If the page is not in vector yet,
+        // go back to first page and advance until the desired page and read it into memory
+        if(m_current != index) {
+            reset();
+            for(int i = 0; i != index && advance(); ++i) {}
```

Review comment:
Sorry, my fault.
**File:** modules/imgcodecs/include/opencv2/imgcodecs.hpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -332,6 +332,51 @@ CV_EXPORTS_W bool haveImageReader( const String& filename );
  */
 CV_EXPORTS_W bool haveImageWriter( const String& filename );
 
+/** @brief To read Multi Page images on demand
+
+The ImageCollection class provides iterator API to read multi page images on demand. Create iterator
+to the collection of the images and iterate over the collection. Decode the necessary page with operator*.
+
+The performance of page decoding is O(1) if collection is increment sequentially. If the user wants to access random page,
```

Review comment:
Reference as return value destroys the idea with cache. `resetCache` calls `release` for all images, including the image user has reference to. It's not what expected. Previous version relied on reference counting inside of `cv::Mat` and worked well. I propose to restore it back.
**File:** modules/imgcodecs/include/opencv2/imgcodecs.hpp
**Change Type:** modified
**Context:** PR #22128: [GSoC 2022] Multipage Image Decoder API

**Code Changes:**
```diff
@@ -332,6 +332,51 @@ CV_EXPORTS_W bool haveImageReader( const String& filename );
  */
 CV_EXPORTS_W bool haveImageWriter( const String& filename );
 
+/** @brief To read Multi Page images on demand
+
+The ImageCollection class provides iterator API to read multi page images on demand. Create iterator
+to the collection of the images and iterate over the collection. Decode the necessary page with operator*.
+
+The performance of page decoding is O(1) if collection is increment sequentially. If the user wants to access random page,
```

Review comment:
Actually, `releaseCache` only calls `release` method on the given index page. For example, suppose you decode all pages into cache, ```cpp ImageCollection col("filename.txt"); for(auto& i: coll){ *i; // Decode all pages so they can go to cache coll.releaseCache(2); // only release page 3 from the cache. auto mat = coll.at(1); // No decoding happens because page 2 is still at the cache ``` With returning reference, now it is user responsibility to not call `releaseCache` on the index that he is referring to. What do you think? And why do you think `releaseCache` releases for all images? I have never mentioned that and noone gave me feedback about that. Should I make that method to release for all images?