Merge pull request #3575 from Ashod:multipage_imread

pull/3593/head
Vadim Pisarevsky 10 years ago
commit 390f17d1e0
  1. 10
      modules/imgcodecs/include/opencv2/imgcodecs.hpp
  2. 3
      modules/imgcodecs/src/grfmt_base.hpp
  3. 19
      modules/imgcodecs/src/grfmt_tiff.cpp
  4. 1
      modules/imgcodecs/src/grfmt_tiff.hpp
  5. 93
      modules/imgcodecs/src/loadsave.cpp
  6. 102
      modules/imgcodecs/test/test_grfmt.cpp

@ -134,6 +134,16 @@ returns an empty matrix ( Mat::data==NULL ). Currently, the following file forma
*/
CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR );
/** @brief Loads a multi-page image from a file. (see imread for details.)
@param filename Name of file to be loaded.
@param flags Flags specifying the color type of a loaded image (see imread).
Defaults to IMREAD_ANYCOLOR, as each page may be different.
@param mats A vector of Mat objects holding each page, if more than one.
*/
CV_EXPORTS_W bool imreadmulti(const String& filename, std::vector<Mat>& mats, int flags = IMREAD_ANYCOLOR);
/** @brief Saves an image to a specified file.
@param filename Name of the file.

@ -70,6 +70,9 @@ public:
virtual bool readHeader() = 0;
virtual bool readData( Mat& img ) = 0;
/// Called after readData to advance to the next page, if any.
virtual bool nextPage() { return false; }
virtual size_t signatureLength() const;
virtual bool checkSignature( const String& signature ) const;
virtual ImageDecoder newDecoder() const;

@ -118,10 +118,13 @@ bool TiffDecoder::readHeader()
{
bool result = false;
close();
// TIFFOpen() mode flags are different to fopen(). A 'b' in mode "rb" has no effect when reading.
// http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html
TIFF* tif = TIFFOpen( m_filename.c_str(), "r" );
TIFF* tif = static_cast<TIFF*>(m_tif);
if (!m_tif)
{
// TIFFOpen() mode flags are different to fopen(). A 'b' in mode "rb" has no effect when reading.
// http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html
tif = TIFFOpen(m_filename.c_str(), "r");
}
if( tif )
{
@ -182,6 +185,13 @@ bool TiffDecoder::readHeader()
return result;
}
bool TiffDecoder::nextPage()
{
// Prepare the next page, if any.
return m_tif &&
TIFFReadDirectory(static_cast<TIFF*>(m_tif)) &&
readHeader();
}
bool TiffDecoder::readData( Mat& img )
{
@ -413,7 +423,6 @@ bool TiffDecoder::readData( Mat& img )
}
}
close();
return result;
}

@ -100,6 +100,7 @@ public:
bool readHeader();
bool readData( Mat& img );
void close();
bool nextPage();
size_t signatureLength() const;
bool checkSignature( const String& signature ) const;

@ -320,6 +320,84 @@ imread_( const String& filename, int flags, int hdrtype, Mat* mat=0 )
hdrtype == LOAD_IMAGE ? (void*)image : (void*)mat;
}
/**
* Read an image into memory and return the information
*
* @param[in] filename File to load
* @param[in] flags Flags
* @param[in] mats Reference to C++ vector<Mat> object to hold the images
*
*/
static bool
imreadmulti_(const String& filename, int flags, std::vector<Mat>& mats)
{
/// Search for the relevant decoder to handle the imagery
ImageDecoder decoder;
#ifdef HAVE_GDAL
if ((flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL){
decoder = GdalDecoder().newDecoder();
}
else{
#endif
decoder = findDecoder(filename);
#ifdef HAVE_GDAL
}
#endif
/// if no decoder was found, return nothing.
if (!decoder){
return 0;
}
/// set the filename in the driver
decoder->setSource(filename);
// read the header to make sure it succeeds
if (!decoder->readHeader())
return 0;
for (;;)
{
// grab the decoded type
int type = decoder->type();
if (flags != -1)
{
if ((flags & CV_LOAD_IMAGE_ANYDEPTH) == 0)
type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type));
if ((flags & CV_LOAD_IMAGE_COLOR) != 0 ||
((flags & CV_LOAD_IMAGE_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.
CvSize size;
size.width = decoder->width();
size.height = decoder->height();
Mat mat;
mat.create(size.height, size.width, type);
// read the image data
if (!decoder->readData(mat))
{
break;
}
mats.push_back(mat);
if (!decoder->nextPage())
{
break;
}
}
return !mats.empty();
}
/**
* Read an image
*
@ -340,6 +418,21 @@ Mat imread( const String& filename, int flags )
return img;
}
/**
* Read a multi-page image
*
* This function merely calls the actual implementation above and returns itself.
*
* @param[in] filename File to load
* @param[in] mats Reference to C++ vector<Mat> object to hold the images
* @param[in] flags Flags you wish to set.
*
*/
bool imreadmulti(const String& filename, std::vector<Mat>& mats, int flags)
{
return imreadmulti_(filename, flags, mats);
}
static bool imwrite_( const String& filename, const Mat& image,
const std::vector<int>& params, bool flipv )
{

@ -45,6 +45,68 @@
using namespace cv;
using namespace std;
static
bool mats_equal(const Mat& lhs, const Mat& rhs)
{
if (lhs.channels() != rhs.channels() ||
lhs.depth() != rhs.depth() ||
lhs.size().height != rhs.size().height ||
lhs.size().width != rhs.size().width)
{
return false;
}
Mat diff = (lhs != rhs);
const Scalar s = sum(diff);
for (int i = 0; i < s.channels; ++i)
{
if (s[i] != 0)
{
return false;
}
}
return true;
}
static
bool imread_compare(const string& filepath, int flags = IMREAD_COLOR)
{
vector<Mat> pages;
if (!imreadmulti(filepath, pages, flags) ||
pages.empty())
{
return false;
}
const Mat single = imread(filepath, flags);
return mats_equal(single, pages[0]);
}
TEST(Imgcodecs_imread, regression)
{
const char* const filenames[] =
{
"color_palette_alpha.png",
"multipage.tif",
"rle.hdr",
"ordinary.bmp",
"rle8.bmp",
"test_1_c1.jpg"
};
const string folder = string(cvtest::TS::ptr()->get_data_path()) + "/readwrite/";
for (size_t i = 0; i < sizeof(filenames) / sizeof(filenames[0]); ++i)
{
ASSERT_TRUE(imread_compare(folder + string(filenames[i]), IMREAD_UNCHANGED));
ASSERT_TRUE(imread_compare(folder + string(filenames[i]), IMREAD_GRAYSCALE));
ASSERT_TRUE(imread_compare(folder + string(filenames[i]), IMREAD_COLOR));
ASSERT_TRUE(imread_compare(folder + string(filenames[i]), IMREAD_ANYDEPTH));
ASSERT_TRUE(imread_compare(folder + string(filenames[i]), IMREAD_ANYCOLOR));
ASSERT_TRUE(imread_compare(folder + string(filenames[i]), IMREAD_LOAD_GDAL));
}
}
class CV_GrfmtWriteBigImageTest : public cvtest::BaseTest
{
@ -591,6 +653,46 @@ TEST(Imgcodecs_Tiff, decode_tile_remainder)
CV_GrfmtReadTifTiledWithNotFullTiles test; test.safe_run();
}
class CV_GrfmtReadTifMultiPage : public cvtest::BaseTest
{
private:
void compare(int flags)
{
const string folder = string(cvtest::TS::ptr()->get_data_path()) + "/readwrite/";
const int page_count = 6;
vector<Mat> pages;
bool res = imreadmulti(folder + "multipage.tif", pages, flags);
ASSERT_TRUE(res == true);
ASSERT_TRUE(pages.size() == page_count);
for (int i = 0; i < page_count; i++)
{
char buffer[256];
sprintf(buffer, "%smultipage_p%d.tif", folder.c_str(), i + 1);
const string filepath(buffer);
const Mat page = imread(filepath, flags);
ASSERT_TRUE(mats_equal(page, pages[i]));
}
}
public:
void run(int)
{
compare(IMREAD_UNCHANGED);
compare(IMREAD_GRAYSCALE);
compare(IMREAD_COLOR);
compare(IMREAD_ANYDEPTH);
compare(IMREAD_ANYCOLOR);
compare(IMREAD_LOAD_GDAL);
}
};
TEST(Imgcodecs_Tiff, decode_multipage)
{
CV_GrfmtReadTifMultiPage test; test.safe_run();
}
#endif
#ifdef HAVE_WEBP

Loading…
Cancel
Save