mirror of
https://github.com/opencv/opencv.git
synced 2025-06-09 02:23:23 +08:00
Merge pull request #22128 from ocpalo:multipage_img_decoder
[GSoC 2022] Multipage Image Decoder API
This commit is contained in:
commit
04ebedb6f0
@ -332,6 +332,51 @@ CV_EXPORTS_W bool haveImageReader( const String& filename );
|
|||||||
*/
|
*/
|
||||||
CV_EXPORTS_W bool haveImageWriter( 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,
|
||||||
|
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:
|
||||||
|
struct CV_EXPORTS iterator {
|
||||||
|
iterator(ImageCollection* col);
|
||||||
|
iterator(ImageCollection* col, int end);
|
||||||
|
Mat& operator*();
|
||||||
|
Mat* operator->();
|
||||||
|
iterator& operator++();
|
||||||
|
iterator operator++(int);
|
||||||
|
friend bool operator== (const iterator& a, const iterator& b) { return a.m_curr == b.m_curr; };
|
||||||
|
friend bool operator!= (const iterator& a, const iterator& b) { return a.m_curr != b.m_curr; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
ImageCollection* m_pCollection;
|
||||||
|
int m_curr;
|
||||||
|
};
|
||||||
|
|
||||||
|
ImageCollection();
|
||||||
|
ImageCollection(const String& filename, int flags);
|
||||||
|
void init(const String& img, int flags);
|
||||||
|
size_t size() const;
|
||||||
|
const Mat& at(int index);
|
||||||
|
const Mat& operator[](int index);
|
||||||
|
void releaseCache(int index);
|
||||||
|
iterator begin();
|
||||||
|
iterator end();
|
||||||
|
|
||||||
|
class Impl;
|
||||||
|
Ptr<Impl> getImpl();
|
||||||
|
protected:
|
||||||
|
Ptr<Impl> pImpl;
|
||||||
|
};
|
||||||
|
|
||||||
//! @} imgcodecs
|
//! @} imgcodecs
|
||||||
|
|
||||||
|
@ -54,6 +54,8 @@
|
|||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <opencv2/core/utils/logger.hpp>
|
#include <opencv2/core/utils/logger.hpp>
|
||||||
#include <opencv2/core/utils/configuration.private.hpp>
|
#include <opencv2/core/utils/configuration.private.hpp>
|
||||||
|
#include <opencv2/imgcodecs.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************\
|
/****************************************************************************************\
|
||||||
@ -661,57 +663,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
|
|||||||
static
|
static
|
||||||
size_t imcount_(const String& filename, int flags)
|
size_t imcount_(const String& filename, int flags)
|
||||||
{
|
{
|
||||||
/// Search for the relevant decoder to handle the imagery
|
try{
|
||||||
ImageDecoder decoder;
|
ImageCollection collection(filename, flags);
|
||||||
|
return collection.size();
|
||||||
#ifdef HAVE_GDAL
|
} catch(cv::Exception const& e) {
|
||||||
if (flags != IMREAD_UNCHANGED && (flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) {
|
// Reading header or finding decoder for the filename is failed
|
||||||
decoder = GdalDecoder().newDecoder();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
#else
|
|
||||||
CV_UNUSED(flags);
|
|
||||||
#endif
|
|
||||||
decoder = findDecoder(filename);
|
|
||||||
#ifdef HAVE_GDAL
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// if no decoder was found, return nothing.
|
|
||||||
if (!decoder) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
/// set the filename in the driver
|
|
||||||
decoder->setSource(filename);
|
|
||||||
|
|
||||||
// read the header to make sure it succeeds
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// read the header to make sure it succeeds
|
|
||||||
if (!decoder->readHeader())
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
catch (const cv::Exception& e)
|
|
||||||
{
|
|
||||||
std::cerr << "imcount_('" << filename << "'): can't read header: " << e.what() << std::endl << std::flush;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
std::cerr << "imcount_('" << filename << "'): can't read header: unknown exception" << std::endl << std::flush;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t result = 1;
|
|
||||||
|
|
||||||
|
|
||||||
while (decoder->nextPage())
|
|
||||||
{
|
|
||||||
++result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t imcount(const String& filename, int flags)
|
size_t imcount(const String& filename, int flags)
|
||||||
@ -1035,6 +994,247 @@ bool haveImageWriter( const String& filename )
|
|||||||
return !encoder.empty();
|
return !encoder.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ImageCollection::Impl {
|
||||||
|
public:
|
||||||
|
Impl() = default;
|
||||||
|
Impl(const std::string& filename, int flags);
|
||||||
|
void init(String const& filename, int flags);
|
||||||
|
size_t size() const;
|
||||||
|
Mat& at(int index);
|
||||||
|
Mat& operator[](int index);
|
||||||
|
void releaseCache(int index);
|
||||||
|
ImageCollection::iterator begin(ImageCollection* ptr);
|
||||||
|
ImageCollection::iterator end(ImageCollection* ptr);
|
||||||
|
Mat read();
|
||||||
|
int width() const;
|
||||||
|
int height() const;
|
||||||
|
bool readHeader();
|
||||||
|
Mat readData();
|
||||||
|
bool advance();
|
||||||
|
int currentIndex() const;
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
String m_filename;
|
||||||
|
int m_flags{};
|
||||||
|
std::size_t m_size{};
|
||||||
|
int m_width{};
|
||||||
|
int m_height{};
|
||||||
|
int m_current{};
|
||||||
|
std::vector<cv::Mat> m_pages;
|
||||||
|
ImageDecoder m_decoder;
|
||||||
|
};
|
||||||
|
|
||||||
|
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) {
|
||||||
|
m_decoder = GdalDecoder().newDecoder();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#endif
|
||||||
|
m_decoder = findDecoder(filename);
|
||||||
|
#ifdef HAVE_GDAL
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
CV_Assert(m_decoder);
|
||||||
|
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
|
||||||
|
if (m_flags != IMREAD_UNCHANGED && (m_flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) {
|
||||||
|
m_decoder = GdalDecoder().newDecoder();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#endif
|
||||||
|
m_decoder = findDecoder(m_filename);
|
||||||
|
#ifdef HAVE_GDAL
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_decoder->setSource(m_filename);
|
||||||
|
m_decoder->readHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ImageCollection::Impl::size() const { return m_size; }
|
||||||
|
|
||||||
|
Mat ImageCollection::Impl::read() {
|
||||||
|
auto result = this->readHeader();
|
||||||
|
if(!result) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return this->readData();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImageCollection::Impl::width() const {
|
||||||
|
return m_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImageCollection::Impl::height() const {
|
||||||
|
return m_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImageCollection::Impl::readHeader() {
|
||||||
|
bool status = m_decoder->readHeader();
|
||||||
|
m_width = m_decoder->width();
|
||||||
|
m_height = m_decoder->height();
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// established the required input image size
|
||||||
|
Size size = validateInputImageSize(Size(m_width, m_height));
|
||||||
|
|
||||||
|
Mat mat(size.height, size.width, type);
|
||||||
|
bool success = false;
|
||||||
|
try {
|
||||||
|
if (m_decoder->readData(mat))
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
catch (const cv::Exception &e) {
|
||||||
|
std::cerr << "ImageCollection class: can't read data: " << e.what() << std::endl << std::flush;
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
std::cerr << "ImageCollection class:: can't read data: unknown exception" << std::endl << std::flush;
|
||||||
|
}
|
||||||
|
if (!success)
|
||||||
|
return cv::Mat();
|
||||||
|
|
||||||
|
if ((m_flags & IMREAD_IGNORE_ORIENTATION) == 0 && m_flags != IMREAD_UNCHANGED) {
|
||||||
|
ApplyExifOrientation(m_decoder->getExifTag(ORIENTATION), mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImageCollection::Impl::advance() { ++m_current; return m_decoder->nextPage(); }
|
||||||
|
|
||||||
|
int ImageCollection::Impl::currentIndex() const { return m_current; }
|
||||||
|
|
||||||
|
ImageCollection::iterator ImageCollection::Impl::begin(ImageCollection* ptr) { return ImageCollection::iterator(ptr); }
|
||||||
|
|
||||||
|
ImageCollection::iterator ImageCollection::Impl::end(ImageCollection* ptr) { return ImageCollection::iterator(ptr, this->size()); }
|
||||||
|
|
||||||
|
void ImageCollection::Impl::reset() {
|
||||||
|
m_current = 0;
|
||||||
|
#ifdef HAVE_GDAL
|
||||||
|
if (m_flags != IMREAD_UNCHANGED && (m_flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) {
|
||||||
|
m_decoder = GdalDecoder().newDecoder();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#endif
|
||||||
|
m_decoder = findDecoder(m_filename);
|
||||||
|
#ifdef HAVE_GDAL
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_decoder->setSource(m_filename);
|
||||||
|
m_decoder->readHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat& ImageCollection::Impl::at(int index) {
|
||||||
|
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) {}
|
||||||
|
}
|
||||||
|
m_pages[index] = read();
|
||||||
|
}
|
||||||
|
return m_pages[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageCollection::Impl::releaseCache(int index) {
|
||||||
|
CV_Assert(index >= 0 && size_t(index) < m_size);
|
||||||
|
m_pages[index].release();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ImageCollection API*/
|
||||||
|
|
||||||
|
ImageCollection::ImageCollection() : pImpl(new Impl()) {}
|
||||||
|
|
||||||
|
ImageCollection::ImageCollection(const std::string& filename, int flags) : pImpl(new Impl(filename, flags)) {}
|
||||||
|
|
||||||
|
void ImageCollection::init(const String& img, int flags) { pImpl->init(img, flags); }
|
||||||
|
|
||||||
|
size_t ImageCollection::size() const { return pImpl->size(); }
|
||||||
|
|
||||||
|
const Mat& ImageCollection::at(int index) { return pImpl->at(index); }
|
||||||
|
|
||||||
|
const Mat& ImageCollection::operator[](int index) { return pImpl->operator[](index); }
|
||||||
|
|
||||||
|
void ImageCollection::releaseCache(int index) { pImpl->releaseCache(index); }
|
||||||
|
|
||||||
|
Ptr<ImageCollection::Impl> ImageCollection::getImpl() { return pImpl; }
|
||||||
|
|
||||||
|
/* Iterator API */
|
||||||
|
|
||||||
|
ImageCollection::iterator ImageCollection::begin() { return pImpl->begin(this); }
|
||||||
|
|
||||||
|
ImageCollection::iterator ImageCollection::end() { return pImpl->end(this); }
|
||||||
|
|
||||||
|
ImageCollection::iterator::iterator(ImageCollection* col) : m_pCollection(col), m_curr(0) {}
|
||||||
|
|
||||||
|
ImageCollection::iterator::iterator(ImageCollection* col, int end) : m_pCollection(col), m_curr(end) {}
|
||||||
|
|
||||||
|
Mat& ImageCollection::iterator::operator*() {
|
||||||
|
CV_Assert(m_pCollection);
|
||||||
|
return m_pCollection->getImpl()->operator[](m_curr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat* ImageCollection::iterator::operator->() {
|
||||||
|
CV_Assert(m_pCollection);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageCollection::iterator ImageCollection::iterator::operator++(int) {
|
||||||
|
iterator tmp = *this;
|
||||||
|
++(*this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* End of file. */
|
/* End of file. */
|
||||||
|
@ -303,4 +303,181 @@ TEST(Imgcodecs_Image, write_umat)
|
|||||||
EXPECT_EQ(0, remove(dst_name.c_str()));
|
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);
|
||||||
|
EXPECT_EQ((std::size_t)6, collection.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Imgcodecs_Image, multipage_collection_read_pages_iterator)
|
||||||
|
{
|
||||||
|
const string root = cvtest::TS::ptr()->get_data_path();
|
||||||
|
const string filename = root + "readwrite/multipage.tif";
|
||||||
|
const string page_files[] = {
|
||||||
|
root + "readwrite/multipage_p1.tif",
|
||||||
|
root + "readwrite/multipage_p2.tif",
|
||||||
|
root + "readwrite/multipage_p3.tif",
|
||||||
|
root + "readwrite/multipage_p4.tif",
|
||||||
|
root + "readwrite/multipage_p5.tif",
|
||||||
|
root + "readwrite/multipage_p6.tif"
|
||||||
|
};
|
||||||
|
|
||||||
|
ImageCollection collection(filename, IMREAD_ANYCOLOR);
|
||||||
|
|
||||||
|
auto collectionBegin = collection.begin();
|
||||||
|
for(size_t i = 0; i < collection.size(); ++i, ++collectionBegin)
|
||||||
|
{
|
||||||
|
double diff = cv::norm(collectionBegin.operator*(), imread(page_files[i]), NORM_INF);
|
||||||
|
EXPECT_EQ(0., diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Imgcodecs_Image, multipage_collection_two_iterator)
|
||||||
|
{
|
||||||
|
const string root = cvtest::TS::ptr()->get_data_path();
|
||||||
|
const string filename = root + "readwrite/multipage.tif";
|
||||||
|
const string page_files[] = {
|
||||||
|
root + "readwrite/multipage_p1.tif",
|
||||||
|
root + "readwrite/multipage_p2.tif",
|
||||||
|
root + "readwrite/multipage_p3.tif",
|
||||||
|
root + "readwrite/multipage_p4.tif",
|
||||||
|
root + "readwrite/multipage_p5.tif",
|
||||||
|
root + "readwrite/multipage_p6.tif"
|
||||||
|
};
|
||||||
|
|
||||||
|
ImageCollection collection(filename, IMREAD_ANYCOLOR);
|
||||||
|
auto firstIter = collection.begin();
|
||||||
|
auto secondIter = collection.begin();
|
||||||
|
|
||||||
|
// Decode all odd pages then decode even pages -> 1, 0, 3, 2 ...
|
||||||
|
firstIter++;
|
||||||
|
for(size_t i = 1; i < collection.size(); i += 2, ++firstIter, ++firstIter, ++secondIter, ++secondIter) {
|
||||||
|
Mat mat = *firstIter;
|
||||||
|
double diff = cv::norm(mat, imread(page_files[i]), NORM_INF);
|
||||||
|
EXPECT_EQ(0., diff);
|
||||||
|
Mat evenMat = *secondIter;
|
||||||
|
diff = cv::norm(evenMat, imread(page_files[i-1]), NORM_INF);
|
||||||
|
EXPECT_EQ(0., diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Imgcodecs_Image, multipage_collection_operator_plusplus)
|
||||||
|
{
|
||||||
|
const string root = cvtest::TS::ptr()->get_data_path();
|
||||||
|
const string filename = root + "readwrite/multipage.tif";
|
||||||
|
|
||||||
|
// operator++ test
|
||||||
|
ImageCollection collection(filename, IMREAD_ANYCOLOR);
|
||||||
|
auto firstIter = collection.begin();
|
||||||
|
auto secondIter = firstIter++;
|
||||||
|
|
||||||
|
// firstIter points to second page, secondIter points to first page
|
||||||
|
double diff = cv::norm(*firstIter, *secondIter, NORM_INF);
|
||||||
|
EXPECT_NE(diff, 0.);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Imgcodecs_Image, multipage_collection_backward_decoding)
|
||||||
|
{
|
||||||
|
const string root = cvtest::TS::ptr()->get_data_path();
|
||||||
|
const string filename = root + "readwrite/multipage.tif";
|
||||||
|
const string page_files[] = {
|
||||||
|
root + "readwrite/multipage_p1.tif",
|
||||||
|
root + "readwrite/multipage_p2.tif",
|
||||||
|
root + "readwrite/multipage_p3.tif",
|
||||||
|
root + "readwrite/multipage_p4.tif",
|
||||||
|
root + "readwrite/multipage_p5.tif",
|
||||||
|
root + "readwrite/multipage_p6.tif"
|
||||||
|
};
|
||||||
|
|
||||||
|
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.);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < (int)collection.size(); ++i)
|
||||||
|
{
|
||||||
|
collection.releaseCache(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
double diff = cv::norm(collection[2], imread(page_files[2]), NORM_INF);
|
||||||
|
EXPECT_EQ(diff, 0.);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ImgCodecs, multipage_collection_decoding_range_based_for_loop_test)
|
||||||
|
{
|
||||||
|
const string root = cvtest::TS::ptr()->get_data_path();
|
||||||
|
const string filename = root + "readwrite/multipage.tif";
|
||||||
|
const string page_files[] = {
|
||||||
|
root + "readwrite/multipage_p1.tif",
|
||||||
|
root + "readwrite/multipage_p2.tif",
|
||||||
|
root + "readwrite/multipage_p3.tif",
|
||||||
|
root + "readwrite/multipage_p4.tif",
|
||||||
|
root + "readwrite/multipage_p5.tif",
|
||||||
|
root + "readwrite/multipage_p6.tif"
|
||||||
|
};
|
||||||
|
|
||||||
|
ImageCollection collection(filename, IMREAD_ANYCOLOR);
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
for(auto &i: collection)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ImgCodecs, multipage_collection_two_iterator_operatorpp)
|
||||||
|
{
|
||||||
|
const string root = cvtest::TS::ptr()->get_data_path();
|
||||||
|
const string filename = root + "readwrite/multipage.tif";
|
||||||
|
|
||||||
|
ImageCollection imcol(filename, IMREAD_ANYCOLOR);
|
||||||
|
|
||||||
|
auto it0 = imcol.begin(), it1 = it0, it2 = it0;
|
||||||
|
vector<Mat> img(6);
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
img[i] = *it0;
|
||||||
|
it0->release();
|
||||||
|
++it0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
++it2;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
auto img2 = *it2;
|
||||||
|
auto img1 = *it1;
|
||||||
|
++it2;
|
||||||
|
++it1;
|
||||||
|
EXPECT_TRUE(cv::norm(img2, img[i+3], NORM_INF) == 0);
|
||||||
|
EXPECT_TRUE(cv::norm(img1, img[i], NORM_INF) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
|
Loading…
Reference in New Issue
Block a user