mirror of
https://github.com/opencv/opencv.git
synced 2026-07-31 00:03:03 +04:00
Merge branch 4.x
This commit is contained in:
@@ -24,6 +24,12 @@ if(HAVE_WEBP)
|
||||
list(APPEND GRFMT_LIBS ${WEBP_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(HAVE_SPNG)
|
||||
add_definitions(${SPNG_DEFINITIONS})
|
||||
ocv_include_directories(${SPNG_INCLUDE_DIR})
|
||||
list(APPEND GRFMT_LIBS ${SPNG_LIBRARY})
|
||||
endif()
|
||||
|
||||
if(HAVE_PNG)
|
||||
add_definitions(${PNG_DEFINITIONS})
|
||||
ocv_include_directories(${PNG_INCLUDE_DIR})
|
||||
@@ -67,7 +73,7 @@ if(HAVE_OPENEXR)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR)
|
||||
if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR OR HAVE_SPNG)
|
||||
ocv_include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
list(APPEND GRFMT_LIBS ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
@@ -179,7 +185,7 @@ endif()
|
||||
if(TARGET opencv_test_imgcodecs AND HAVE_OPENEXR AND "$ENV{OPENCV_IO_ENABLE_OPENEXR}")
|
||||
ocv_target_compile_definitions(opencv_test_imgcodecs PRIVATE OPENCV_IMGCODECS_ENABLE_OPENEXR_TESTS=1)
|
||||
endif()
|
||||
if(TARGET opencv_test_imgcodecs AND HAVE_PNG AND NOT (PNG_VERSION VERSION_LESS "1.6.31"))
|
||||
if(TARGET opencv_test_imgcodecs AND ((HAVE_PNG AND NOT (PNG_VERSION VERSION_LESS "1.6.31")) OR HAVE_SPNG))
|
||||
# details: https://github.com/glennrp/libpng/commit/68cb0aaee3de6371b81a4613476d9b33e43e95b1
|
||||
ocv_target_compile_definitions(opencv_test_imgcodecs PRIVATE OPENCV_IMGCODECS_PNG_WITH_EXIF=1)
|
||||
endif()
|
||||
|
||||
@@ -97,7 +97,9 @@ enum ImwriteFlags {
|
||||
IMWRITE_PXM_BINARY = 32, //!< For PPM, PGM, or PBM, it can be a binary format flag, 0 or 1. Default value is 1.
|
||||
IMWRITE_EXR_TYPE = (3 << 4) + 0, /* 48 */ //!< override EXR storage type (FLOAT (FP32) is default)
|
||||
IMWRITE_EXR_COMPRESSION = (3 << 4) + 1, /* 49 */ //!< override EXR compression type (ZIP_COMPRESSION = 3 is default)
|
||||
IMWRITE_EXR_DWA_COMPRESSION_LEVEL = (3 << 4) + 2, /* 50 */ //!< override EXR DWA compression level (45 is default)
|
||||
IMWRITE_WEBP_QUALITY = 64, //!< For WEBP, it can be a quality from 1 to 100 (the higher is the better). By default (without any parameter) and for quality above 100 the lossless compression is used.
|
||||
IMWRITE_HDR_COMPRESSION = (5 << 4) + 0, /* 80 */ //!< specify HDR compression
|
||||
IMWRITE_PAM_TUPLETYPE = 128,//!< For PAM, sets the TUPLETYPE field to the corresponding string value that is defined for the format
|
||||
IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set; see libtiff documentation for valid values
|
||||
IMWRITE_TIFF_XDPI = 257,//!< For TIFF, use to specify the X direction DPI
|
||||
@@ -160,6 +162,12 @@ enum ImwritePAMFlags {
|
||||
IMWRITE_PAM_FORMAT_RGB_ALPHA = 5
|
||||
};
|
||||
|
||||
//! Imwrite HDR specific values for IMWRITE_HDR_COMPRESSION parameter key
|
||||
enum ImwriteHDRCompressionFlags {
|
||||
IMWRITE_HDR_COMPRESSION_NONE = 0,
|
||||
IMWRITE_HDR_COMPRESSION_RLE = 1
|
||||
};
|
||||
|
||||
//! @} imgcodecs_flags
|
||||
|
||||
/** @brief Loads an image from a file.
|
||||
@@ -306,6 +314,20 @@ reallocations when the function is called repeatedly for images of the same size
|
||||
*/
|
||||
CV_EXPORTS Mat imdecode( InputArray buf, int flags, Mat* dst);
|
||||
|
||||
/** @brief Reads a multi-page image from a buffer in memory.
|
||||
|
||||
The function imdecodemulti reads a multi-page image from the specified buffer in the memory. If the buffer is too short or
|
||||
contains invalid data, the function returns false.
|
||||
|
||||
See cv::imreadmulti for the list of supported formats and flags description.
|
||||
|
||||
@note In the case of color images, the decoded images will have the channels stored in **B G R** order.
|
||||
@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.
|
||||
*/
|
||||
CV_EXPORTS_W bool imdecodemulti(InputArray buf, int flags, CV_OUT std::vector<Mat>& mats);
|
||||
|
||||
/** @brief Encodes an image into a memory buffer.
|
||||
|
||||
The function imencode compresses the image and stores it in the memory buffer that is resized to fit the
|
||||
@@ -332,6 +354,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,
|
||||
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
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
using namespace perf;
|
||||
|
||||
PERF_TEST(JPEG, Decode)
|
||||
{
|
||||
String filename = getDataPath("stitching/boat1.jpg");
|
||||
|
||||
FILE *f = fopen(filename.c_str(), "rb");
|
||||
fseek(f, 0, SEEK_END);
|
||||
long len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
vector<uchar> file_buf((size_t)len);
|
||||
EXPECT_EQ(len, (long)fread(&file_buf[0], 1, (size_t)len, f));
|
||||
fclose(f); f = NULL;
|
||||
|
||||
TEST_CYCLE() imdecode(file_buf, IMREAD_UNCHANGED);
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST(JPEG, Encode)
|
||||
{
|
||||
String filename = getDataPath("stitching/boat1.jpg");
|
||||
cv::Mat src = imread(filename);
|
||||
|
||||
vector<uchar> buf;
|
||||
TEST_CYCLE() imencode(".jpg", src, buf);
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,41 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
using namespace perf;
|
||||
|
||||
typedef perf::TestBaseWithParam<std::string> PNG;
|
||||
|
||||
PERF_TEST(PNG, decode)
|
||||
{
|
||||
String filename = getDataPath("perf/2560x1600.png");
|
||||
|
||||
FILE *f = fopen(filename.c_str(), "rb");
|
||||
fseek(f, 0, SEEK_END);
|
||||
long len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
vector<uchar> file_buf((size_t)len);
|
||||
EXPECT_EQ(len, (long)fread(&file_buf[0], 1, (size_t)len, f));
|
||||
fclose(f); f = NULL;
|
||||
|
||||
TEST_CYCLE() imdecode(file_buf, IMREAD_UNCHANGED);
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST(PNG, encode)
|
||||
{
|
||||
String filename = getDataPath("perf/2560x1600.png");
|
||||
cv::Mat src = imread(filename);
|
||||
|
||||
vector<uchar> buf;
|
||||
TEST_CYCLE() imencode(".png", src, buf);
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -691,6 +691,14 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& params )
|
||||
CV_Error(Error::StsBadArg, "IMWRITE_EXR_COMPRESSION is invalid or not supported");
|
||||
}
|
||||
}
|
||||
if (params[i] == IMWRITE_EXR_DWA_COMPRESSION_LEVEL)
|
||||
{
|
||||
#if OPENEXR_VERSION_MAJOR >= 3
|
||||
header.dwaCompressionLevel() = params[i + 1];
|
||||
#else
|
||||
CV_LOG_ONCE_WARNING(NULL, "Setting `IMWRITE_EXR_DWA_COMPRESSION_LEVEL` not supported in OpenEXR version " + std::to_string(OPENEXR_VERSION_MAJOR) + " (version 3 is required)");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if( channels == 3 || channels == 4 )
|
||||
|
||||
@@ -141,14 +141,28 @@ bool HdrEncoder::write( const Mat& input_img, const std::vector<int>& params )
|
||||
if(img.depth() != CV_32F) {
|
||||
img.convertTo(img, CV_32FC3, 1/255.0f);
|
||||
}
|
||||
CV_Assert(params.empty() || params[0] == HDR_NONE || params[0] == HDR_RLE);
|
||||
|
||||
int compression = IMWRITE_HDR_COMPRESSION_RLE;
|
||||
for (size_t i = 0; i + 1 < params.size(); i += 2)
|
||||
{
|
||||
switch (params[i])
|
||||
{
|
||||
case IMWRITE_HDR_COMPRESSION:
|
||||
compression = params[i + 1];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
CV_Check(compression, compression == IMWRITE_HDR_COMPRESSION_NONE || compression == IMWRITE_HDR_COMPRESSION_RLE, "");
|
||||
|
||||
FILE *fout = fopen(m_filename.c_str(), "wb");
|
||||
if(!fout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RGBE_WriteHeader(fout, img.cols, img.rows, NULL);
|
||||
if(params.empty() || params[0] == HDR_RLE) {
|
||||
if (compression == IMWRITE_HDR_COMPRESSION_RLE) {
|
||||
RGBE_WritePixels_RLE(fout, const_cast<float*>(img.ptr<float>()), img.cols, img.rows);
|
||||
} else {
|
||||
RGBE_WritePixels(fout, const_cast<float*>(img.ptr<float>()), img.cols * img.rows);
|
||||
|
||||
@@ -50,12 +50,6 @@
|
||||
namespace cv
|
||||
{
|
||||
|
||||
enum HdrCompression
|
||||
{
|
||||
HDR_NONE = 0,
|
||||
HDR_RLE = 1
|
||||
};
|
||||
|
||||
// Radiance rgbe (.hdr) reader
|
||||
class HdrDecoder CV_FINAL : public BaseImageDecoder
|
||||
{
|
||||
|
||||
@@ -0,0 +1,754 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#ifdef HAVE_SPNG
|
||||
|
||||
/****************************************************************************************\
|
||||
This part of the file implements PNG codec on base of libspng library,
|
||||
in particular, this code is based on example.c from libspng
|
||||
(see 3rdparty/libspng/LICENSE for copyright notice)
|
||||
\****************************************************************************************/
|
||||
|
||||
#ifndef _LFS64_LARGEFILE
|
||||
#define _LFS64_LARGEFILE 0
|
||||
#endif
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 0
|
||||
#endif
|
||||
|
||||
#include <spng.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "grfmt_spng.hpp"
|
||||
|
||||
/*
|
||||
* libspng does not support RGB -> Gray conversion. In order to decode colorful images as grayscale
|
||||
* we need conversion functions. In the previous png implementation(grfmt_png), the author was set
|
||||
* to particular values for rgb coefficients. OpenCV icvCvt_BGR2Gray function values does not match
|
||||
* with these values. (png_set_rgb_to_gray( png_ptr, 1, 0.299, 0.587 );) For this codec implementation,
|
||||
* slightly modified versions are implemented in the below of this page.
|
||||
*/
|
||||
void spngCvt_BGR2Gray_8u_C3C1R(const uchar *bgr, int bgr_step,
|
||||
uchar *gray, int gray_step,
|
||||
cv::Size size, int _swap_rb);
|
||||
|
||||
void spngCvt_BGRA2Gray_8u_C4C1R(const uchar *bgra, int rgba_step,
|
||||
uchar *gray, int gray_step,
|
||||
cv::Size size, int _swap_rb);
|
||||
|
||||
void spngCvt_BGRA2Gray_16u_CnC1R(const ushort *bgr, int bgr_step,
|
||||
ushort *gray, int gray_step,
|
||||
cv::Size size, int ncn, int _swap_rb);
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
/////////////////////// SPngDecoder ///////////////////
|
||||
|
||||
SPngDecoder::SPngDecoder()
|
||||
{
|
||||
m_signature = "\x89\x50\x4e\x47\xd\xa\x1a\xa";
|
||||
m_color_type = 0;
|
||||
m_ctx = 0;
|
||||
m_f = 0;
|
||||
m_buf_supported = true;
|
||||
m_buf_pos = 0;
|
||||
m_bit_depth = 0;
|
||||
}
|
||||
|
||||
SPngDecoder::~SPngDecoder()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
ImageDecoder SPngDecoder::newDecoder() const
|
||||
{
|
||||
return makePtr<SPngDecoder>();
|
||||
}
|
||||
|
||||
void SPngDecoder::close()
|
||||
{
|
||||
if (m_f)
|
||||
{
|
||||
fclose(m_f);
|
||||
m_f = 0;
|
||||
}
|
||||
|
||||
if (m_ctx)
|
||||
{
|
||||
struct spng_ctx *ctx = (struct spng_ctx *)m_ctx;
|
||||
spng_ctx_free(ctx);
|
||||
m_ctx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int SPngDecoder::readDataFromBuf(void *sp_ctx, void *user, void *dst, size_t size)
|
||||
{
|
||||
/*
|
||||
* typedef int spng_read_fn(spng_ctx *ctx, void *user, void *dest, size_t length)
|
||||
* Type definition for callback passed to spng_set_png_stream() for decoders.
|
||||
* A read callback function should copy length bytes to dest and return 0 or SPNG_IO_EOF/SPNG_IO_ERROR on error.
|
||||
*/
|
||||
CV_UNUSED(sp_ctx);
|
||||
SPngDecoder *decoder = (SPngDecoder *)(user);
|
||||
CV_Assert(decoder);
|
||||
|
||||
const Mat &buf = decoder->m_buf;
|
||||
if (decoder->m_buf_pos + size > buf.cols * buf.rows * buf.elemSize())
|
||||
{
|
||||
return SPNG_IO_ERROR;
|
||||
}
|
||||
memcpy(dst, decoder->m_buf.ptr() + decoder->m_buf_pos, size);
|
||||
decoder->m_buf_pos += size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SPngDecoder::readHeader()
|
||||
{
|
||||
volatile bool result = false;
|
||||
close();
|
||||
|
||||
spng_ctx *ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32);
|
||||
|
||||
if (!ctx)
|
||||
{
|
||||
spng_ctx_free(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ctx = ctx;
|
||||
|
||||
if (!m_buf.empty())
|
||||
spng_set_png_stream((struct spng_ctx *)m_ctx, (spng_rw_fn *)readDataFromBuf, this);
|
||||
else
|
||||
{
|
||||
m_f = fopen(m_filename.c_str(), "rb");
|
||||
if (m_f)
|
||||
{
|
||||
spng_set_png_file(ctx, m_f);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_buf.empty() || m_f)
|
||||
{
|
||||
struct spng_ihdr ihdr;
|
||||
int ret = spng_get_ihdr(ctx, &ihdr);
|
||||
|
||||
if (ret == SPNG_OK)
|
||||
{
|
||||
m_width = static_cast<int>(ihdr.width);
|
||||
m_height = static_cast<int>(ihdr.height);
|
||||
m_color_type = ihdr.color_type;
|
||||
m_bit_depth = ihdr.bit_depth;
|
||||
|
||||
if (ihdr.bit_depth <= 8 || ihdr.bit_depth == 16)
|
||||
{
|
||||
int num_trans;
|
||||
switch (ihdr.color_type)
|
||||
{
|
||||
case SPNG_COLOR_TYPE_TRUECOLOR:
|
||||
case SPNG_COLOR_TYPE_INDEXED:
|
||||
struct spng_trns trns;
|
||||
num_trans = !spng_get_trns(ctx, &trns);
|
||||
if (num_trans > 0)
|
||||
m_type = CV_8UC4;
|
||||
else
|
||||
m_type = CV_8UC3;
|
||||
break;
|
||||
case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA:
|
||||
case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA:
|
||||
m_type = CV_8UC4;
|
||||
break;
|
||||
default:
|
||||
m_type = CV_8UC1;
|
||||
}
|
||||
if (ihdr.bit_depth == 16)
|
||||
m_type = CV_MAKETYPE(CV_16U, CV_MAT_CN(m_type));
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SPngDecoder::readData(Mat &img)
|
||||
{
|
||||
volatile bool result = false;
|
||||
bool color = img.channels() > 1;
|
||||
|
||||
struct spng_ctx *png_ptr = (struct spng_ctx *)m_ctx;
|
||||
|
||||
if (m_ctx && m_width && m_height)
|
||||
{
|
||||
int fmt = SPNG_FMT_PNG;
|
||||
|
||||
struct spng_trns trns;
|
||||
int have_trns = spng_get_trns((struct spng_ctx *)m_ctx, &trns);
|
||||
|
||||
int decode_flags = 0;
|
||||
if (have_trns == SPNG_OK)
|
||||
{
|
||||
decode_flags = SPNG_DECODE_TRNS;
|
||||
}
|
||||
if (img.channels() == 4)
|
||||
{
|
||||
if (m_color_type == SPNG_COLOR_TYPE_TRUECOLOR ||
|
||||
m_color_type == SPNG_COLOR_TYPE_INDEXED ||
|
||||
m_color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA)
|
||||
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8;
|
||||
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE)
|
||||
fmt = m_bit_depth == 16 ? SPNG_FMT_GA16 : SPNG_FMT_GA8;
|
||||
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA)
|
||||
{
|
||||
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8;
|
||||
}
|
||||
else
|
||||
fmt = SPNG_FMT_RGBA8;
|
||||
}
|
||||
if (img.channels() == 3)
|
||||
{
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
if ((m_color_type == SPNG_COLOR_TYPE_GRAYSCALE || m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA) &&
|
||||
m_bit_depth == 16)
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
else if (m_bit_depth == 16)
|
||||
fmt = SPNG_FMT_PNG;
|
||||
}
|
||||
else if (img.channels() == 1)
|
||||
{
|
||||
if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE && m_bit_depth <= 8)
|
||||
fmt = SPNG_FMT_G8;
|
||||
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE && m_bit_depth == 16)
|
||||
{
|
||||
if (img.depth() == CV_8U || img.depth() == CV_8S)
|
||||
{
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt = SPNG_FMT_PNG;
|
||||
}
|
||||
}
|
||||
else if (m_color_type == SPNG_COLOR_TYPE_INDEXED ||
|
||||
m_color_type == SPNG_COLOR_TYPE_TRUECOLOR)
|
||||
{
|
||||
if (img.depth() == CV_8U || img.depth() == CV_8S)
|
||||
{
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGB8;
|
||||
}
|
||||
}
|
||||
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA || fmt == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA)
|
||||
{
|
||||
if (img.depth() == CV_8U || img.depth() == CV_8S)
|
||||
{
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8;
|
||||
}
|
||||
}
|
||||
else
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
}
|
||||
|
||||
size_t image_width, image_size = 0;
|
||||
int ret = spng_decoded_image_size(png_ptr, fmt, &image_size);
|
||||
struct spng_ihdr ihdr;
|
||||
spng_get_ihdr(png_ptr, &ihdr);
|
||||
|
||||
if (ret == SPNG_OK)
|
||||
{
|
||||
image_width = image_size / m_height;
|
||||
|
||||
ret = spng_decode_image(png_ptr, nullptr, 0, fmt, SPNG_DECODE_PROGRESSIVE | decode_flags);
|
||||
if (ret == SPNG_OK)
|
||||
{
|
||||
struct spng_row_info row_info{};
|
||||
|
||||
// If user wants to read image as grayscale(IMREAD_GRAYSCALE), but image format is not
|
||||
// decode image then convert to grayscale
|
||||
if (!color && (fmt == SPNG_FMT_RGB8 || fmt == SPNG_FMT_RGBA8 || fmt == SPNG_FMT_RGBA16))
|
||||
{
|
||||
if (ihdr.interlace_method == 0)
|
||||
{
|
||||
AutoBuffer<unsigned char> buffer;
|
||||
buffer.allocate(image_width);
|
||||
if (fmt == SPNG_FMT_RGB8)
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
|
||||
spngCvt_BGR2Gray_8u_C3C1R(
|
||||
buffer.data(),
|
||||
0,
|
||||
img.data + row_info.row_num * img.step,
|
||||
0, Size(m_width, 1), 2);
|
||||
} while (ret == SPNG_OK);
|
||||
}
|
||||
else if (fmt == SPNG_FMT_RGBA8)
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
|
||||
spngCvt_BGRA2Gray_8u_C4C1R(
|
||||
buffer.data(),
|
||||
0,
|
||||
img.data + row_info.row_num * img.step,
|
||||
0, Size(m_width, 1), 2);
|
||||
} while (ret == SPNG_OK);
|
||||
}
|
||||
else if (fmt == SPNG_FMT_RGBA16)
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
|
||||
spngCvt_BGRA2Gray_16u_CnC1R(
|
||||
reinterpret_cast<const ushort *>(buffer.data()), 0,
|
||||
reinterpret_cast<ushort *>(img.data + row_info.row_num * img.step),
|
||||
0, Size(m_width, 1),
|
||||
4, 2);
|
||||
} while (ret == SPNG_OK);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoBuffer<unsigned char> imageBuffer(image_size);
|
||||
ret = spng_decode_image(png_ptr, imageBuffer.data(), image_size, fmt, 0);
|
||||
int step = m_width * img.channels();
|
||||
if (fmt == SPNG_FMT_RGB8)
|
||||
{
|
||||
spngCvt_BGR2Gray_8u_C3C1R(
|
||||
imageBuffer.data(),
|
||||
step,
|
||||
img.data,
|
||||
step, Size(m_width, m_height), 2);
|
||||
}
|
||||
else if (fmt == SPNG_FMT_RGBA8)
|
||||
{
|
||||
spngCvt_BGRA2Gray_8u_C4C1R(
|
||||
imageBuffer.data(),
|
||||
step,
|
||||
img.data,
|
||||
step, Size(m_width, m_height), 2);
|
||||
}
|
||||
else if (fmt == SPNG_FMT_RGBA16)
|
||||
{
|
||||
spngCvt_BGRA2Gray_16u_CnC1R(
|
||||
reinterpret_cast<const ushort *>(imageBuffer.data()), step / 3,
|
||||
reinterpret_cast<ushort *>(img.data),
|
||||
step / 3, Size(m_width, m_height),
|
||||
4, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (color)
|
||||
{ // RGB -> BGR, convert row by row if png is non-interlaced, otherwise convert image as one
|
||||
int step = m_width * img.channels();
|
||||
AutoBuffer<uchar *> _buffer(m_height);
|
||||
uchar **buffer = _buffer.data();
|
||||
for (int y = 0; y < m_height; y++)
|
||||
{
|
||||
buffer[y] = img.data + y * img.step;
|
||||
}
|
||||
if (img.channels() == 4 && m_bit_depth == 16)
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
|
||||
if (ihdr.interlace_method == 0)
|
||||
{
|
||||
icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast<const ushort *>(buffer[row_info.row_num]), 0,
|
||||
reinterpret_cast<ushort *>(buffer[row_info.row_num]), 0,
|
||||
Size(m_width, 1));
|
||||
}
|
||||
} while (ret == SPNG_OK);
|
||||
if (ihdr.interlace_method)
|
||||
{
|
||||
icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast<const ushort *>(img.data), step * 2, reinterpret_cast<ushort *>(img.data), step * 2, Size(m_width, m_height));
|
||||
}
|
||||
}
|
||||
else if (img.channels() == 4)
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
|
||||
if (ihdr.interlace_method == 0)
|
||||
{
|
||||
icvCvt_RGBA2BGRA_8u_C4R(buffer[row_info.row_num], 0, buffer[row_info.row_num], 0, Size(m_width, 1));
|
||||
}
|
||||
} while (ret == SPNG_OK);
|
||||
if (ihdr.interlace_method)
|
||||
{
|
||||
icvCvt_RGBA2BGRA_8u_C4R(img.data, step, img.data, step, Size(m_width, m_height));
|
||||
}
|
||||
}
|
||||
else if (fmt == SPNG_FMT_PNG)
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
|
||||
if (ihdr.interlace_method == 0)
|
||||
{
|
||||
icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort *>(buffer[row_info.row_num]), 0,
|
||||
reinterpret_cast<ushort *>(buffer[row_info.row_num]), 0, Size(m_width, 1));
|
||||
}
|
||||
} while (ret == SPNG_OK);
|
||||
if (ihdr.interlace_method)
|
||||
{
|
||||
icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort *>(img.data), step,
|
||||
reinterpret_cast<ushort *>(img.data), step, Size(m_width, m_height));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
|
||||
if (ihdr.interlace_method == 0)
|
||||
{
|
||||
icvCvt_RGB2BGR_8u_C3R(buffer[row_info.row_num], 0, buffer[row_info.row_num], 0, Size(m_width, 1));
|
||||
}
|
||||
} while (ret == SPNG_OK);
|
||||
if (ihdr.interlace_method)
|
||||
{
|
||||
icvCvt_RGB2BGR_8u_C3R(img.data, step, img.data, step, Size(m_width, m_height));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, img.data + row_info.row_num * image_width, image_width);
|
||||
} while (ret == SPNG_OK);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == SPNG_EOI)
|
||||
{
|
||||
ret = spng_decode_chunks(png_ptr);
|
||||
if(ret == SPNG_OK) result = true;
|
||||
struct spng_exif exif_s{};
|
||||
ret = spng_get_exif(png_ptr, &exif_s);
|
||||
if (ret == SPNG_OK)
|
||||
{
|
||||
if (exif_s.data && exif_s.length > 0)
|
||||
{
|
||||
result = m_exif.parseExif((unsigned char *)exif_s.data, exif_s.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////// SPngEncoder ///////////////////
|
||||
|
||||
SPngEncoder::SPngEncoder()
|
||||
{
|
||||
m_description = "Portable Network Graphics files (*.png)";
|
||||
m_buf_supported = true;
|
||||
}
|
||||
|
||||
SPngEncoder::~SPngEncoder()
|
||||
{
|
||||
}
|
||||
|
||||
bool SPngEncoder::isFormatSupported(int depth) const
|
||||
{
|
||||
return depth == CV_8U || depth == CV_16U;
|
||||
}
|
||||
|
||||
ImageEncoder SPngEncoder::newEncoder() const
|
||||
{
|
||||
return makePtr<SPngEncoder>();
|
||||
}
|
||||
|
||||
int SPngEncoder::writeDataToBuf(void *ctx, void *user, void *dst_src, size_t length)
|
||||
{
|
||||
CV_UNUSED(ctx);
|
||||
SPngEncoder *encoder = (SPngEncoder *)(user);
|
||||
CV_Assert(encoder && encoder->m_buf);
|
||||
size_t cursz = encoder->m_buf->size();
|
||||
encoder->m_buf->resize(cursz + length);
|
||||
memcpy(&(*encoder->m_buf)[cursz], dst_src, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SPngEncoder::write(const Mat &img, const std::vector<int> ¶ms)
|
||||
{
|
||||
int fmt;
|
||||
spng_ctx *ctx = spng_ctx_new(SPNG_CTX_ENCODER);
|
||||
FILE *volatile f = 0;
|
||||
int width = img.cols, height = img.rows;
|
||||
int depth = img.depth(), channels = img.channels();
|
||||
volatile bool result = false;
|
||||
|
||||
if (depth != CV_8U && depth != CV_16U)
|
||||
return false;
|
||||
|
||||
if (ctx)
|
||||
{
|
||||
struct spng_ihdr ihdr = {};
|
||||
ihdr.height = height;
|
||||
ihdr.width = width;
|
||||
int compression_level = -1; // Invalid value to allow setting 0-9 as valid
|
||||
int compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy
|
||||
bool isBilevel = false;
|
||||
|
||||
for (size_t i = 0; i < params.size(); i += 2)
|
||||
{
|
||||
if (params[i] == IMWRITE_PNG_COMPRESSION)
|
||||
{
|
||||
compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy
|
||||
compression_level = params[i + 1];
|
||||
compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION);
|
||||
}
|
||||
if (params[i] == IMWRITE_PNG_STRATEGY)
|
||||
{
|
||||
compression_strategy = params[i + 1];
|
||||
compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED);
|
||||
}
|
||||
if (params[i] == IMWRITE_PNG_BILEVEL)
|
||||
{
|
||||
isBilevel = params[i + 1] != 0;
|
||||
}
|
||||
}
|
||||
fmt = channels == 1 ? SPNG_COLOR_TYPE_GRAYSCALE : channels == 3 ? SPNG_COLOR_TYPE_TRUECOLOR
|
||||
: SPNG_COLOR_TYPE_TRUECOLOR_ALPHA;
|
||||
|
||||
ihdr.bit_depth = depth == CV_8U ? isBilevel ? 1 : 8 : 16;
|
||||
ihdr.color_type = fmt;
|
||||
ihdr.interlace_method = SPNG_INTERLACE_NONE;
|
||||
ihdr.filter_method = SPNG_FILTER_NONE;
|
||||
ihdr.compression_method = 0;
|
||||
spng_set_ihdr(ctx, &ihdr);
|
||||
|
||||
if (m_buf)
|
||||
{
|
||||
spng_set_png_stream(ctx, (spng_rw_fn *)writeDataToBuf, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
f = fopen(m_filename.c_str(), "wb");
|
||||
if (f)
|
||||
spng_set_png_file(ctx, f);
|
||||
}
|
||||
|
||||
if (m_buf || f)
|
||||
{
|
||||
if (compression_level >= 0)
|
||||
{
|
||||
spng_set_option(ctx, SPNG_IMG_COMPRESSION_LEVEL, compression_level);
|
||||
}
|
||||
else
|
||||
{
|
||||
spng_set_option(ctx, SPNG_FILTER_CHOICE, SPNG_FILTER_CHOICE_SUB);
|
||||
spng_set_option(ctx, SPNG_IMG_COMPRESSION_LEVEL, Z_BEST_SPEED);
|
||||
}
|
||||
spng_set_option(ctx, SPNG_IMG_COMPRESSION_STRATEGY, compression_strategy);
|
||||
|
||||
int ret;
|
||||
spng_encode_chunks(ctx);
|
||||
ret = spng_encode_image(ctx, nullptr, 0, SPNG_FMT_PNG, SPNG_ENCODE_PROGRESSIVE);
|
||||
if (channels > 1)
|
||||
{
|
||||
int error;
|
||||
if (ret == SPNG_OK)
|
||||
{
|
||||
if (depth == CV_16U)
|
||||
{
|
||||
AutoBuffer<ushort *> buff2;
|
||||
buff2.allocate(height);
|
||||
for (int y = 0; y < height; y++)
|
||||
buff2[y] = reinterpret_cast<unsigned short *>(img.data + y * img.step);
|
||||
|
||||
AutoBuffer<ushort> _buffer;
|
||||
_buffer.allocate(width * channels * 2);
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (channels == 3)
|
||||
{
|
||||
icvCvt_BGR2RGB_16u_C3R(buff2[y], 0,
|
||||
_buffer.data(), 0, Size(width, 1));
|
||||
}
|
||||
else if (channels == 4)
|
||||
{
|
||||
icvCvt_BGRA2RGBA_16u_C4R(buff2[y], 0,
|
||||
_buffer.data(), 0, Size(width, 1));
|
||||
}
|
||||
error = spng_encode_row(ctx, _buffer.data(), width * channels * 2);
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoBuffer<uchar *> buff;
|
||||
buff.allocate(height);
|
||||
for (int y = 0; y < height; y++)
|
||||
buff[y] = img.data + y * img.step;
|
||||
|
||||
AutoBuffer<uchar> _buffer;
|
||||
_buffer.allocate(width * channels);
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (channels == 3)
|
||||
{
|
||||
icvCvt_BGR2RGB_8u_C3R(buff[y], 0, _buffer.data(), 0, Size(width, 1));
|
||||
}
|
||||
else if (channels == 4)
|
||||
{
|
||||
icvCvt_BGRA2RGBA_8u_C4R(buff[y], 0, _buffer.data(), 0, Size(width, 1));
|
||||
}
|
||||
error = spng_encode_row(ctx, _buffer.data(), width * channels);
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (error == SPNG_EOI)
|
||||
{ // success
|
||||
spng_encode_chunks(ctx);
|
||||
ret = SPNG_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int error;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
error = spng_encode_row(ctx, img.data + y * img.step, width * channels * (depth == CV_16U ? 2 : 1));
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
if (error == SPNG_EOI)
|
||||
{ // success
|
||||
spng_encode_chunks(ctx);
|
||||
ret = SPNG_OK;
|
||||
}
|
||||
}
|
||||
if (ret == SPNG_OK)
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
spng_ctx_free(ctx);
|
||||
if (f)
|
||||
fclose((FILE *)f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void spngCvt_BGR2Gray_8u_C3C1R(const uchar *bgr, int bgr_step,
|
||||
uchar *gray, int gray_step,
|
||||
cv::Size size, int _swap_rb)
|
||||
{
|
||||
int i;
|
||||
for (; size.height--; gray += gray_step)
|
||||
{
|
||||
double cBGR0 = 0.1140441895;
|
||||
double cBGR2 = 0.2989807129;
|
||||
if (_swap_rb)
|
||||
std::swap(cBGR0, cBGR2);
|
||||
for (i = 0; i < size.width; i++, bgr += 3)
|
||||
{
|
||||
int t = static_cast<int>(cBGR0 * bgr[0] + 0.5869750977 * bgr[1] + cBGR2 * bgr[2]);
|
||||
gray[i] = (uchar)t;
|
||||
}
|
||||
|
||||
bgr += bgr_step - size.width * 3;
|
||||
}
|
||||
}
|
||||
|
||||
void spngCvt_BGRA2Gray_8u_C4C1R(const uchar *bgra, int rgba_step,
|
||||
uchar *gray, int gray_step,
|
||||
cv::Size size, int _swap_rb)
|
||||
{
|
||||
int i;
|
||||
for (; size.height--; gray += gray_step)
|
||||
{
|
||||
double cBGR0 = 0.1140441895;
|
||||
double cBGR2 = 0.2989807129;
|
||||
if (_swap_rb)
|
||||
std::swap(cBGR0, cBGR2);
|
||||
for (i = 0; i < size.width; i++, bgra += 4)
|
||||
{
|
||||
int t = cBGR0 * bgra[0] + 0.5869750977 * bgra[1] + cBGR2 * bgra[2];
|
||||
gray[i] = (uchar)t;
|
||||
}
|
||||
|
||||
bgra += rgba_step - size.width * 4;
|
||||
}
|
||||
}
|
||||
|
||||
void spngCvt_BGRA2Gray_16u_CnC1R(const ushort *bgr, int bgr_step,
|
||||
ushort *gray, int gray_step,
|
||||
cv::Size size, int ncn, int _swap_rb)
|
||||
{
|
||||
int i;
|
||||
for (; size.height--; gray += gray_step)
|
||||
{
|
||||
double cBGR0 = 0.1140441895;
|
||||
double cBGR2 = 0.2989807129;
|
||||
if (_swap_rb)
|
||||
std::swap(cBGR0, cBGR2);
|
||||
for (i = 0; i < size.width; i++, bgr += ncn)
|
||||
{
|
||||
int t = cBGR0 * bgr[0] + 0.5869750977 * bgr[1] + cBGR2 * bgr[2];
|
||||
gray[i] = (ushort)t;
|
||||
}
|
||||
|
||||
bgr += bgr_step - size.width * ncn;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* End of file. */
|
||||
@@ -0,0 +1,60 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef _GRFMT_SPNG_H_
|
||||
#define _GRFMT_SPNG_H_
|
||||
|
||||
#ifdef HAVE_SPNG
|
||||
|
||||
#include "grfmt_base.hpp"
|
||||
#include "bitstrm.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
class SPngDecoder CV_FINAL : public BaseImageDecoder
|
||||
{
|
||||
public:
|
||||
|
||||
SPngDecoder();
|
||||
virtual ~SPngDecoder();
|
||||
|
||||
bool readData( Mat& img ) CV_OVERRIDE;
|
||||
bool readHeader() CV_OVERRIDE;
|
||||
void close();
|
||||
|
||||
ImageDecoder newDecoder() const CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
|
||||
static int readDataFromBuf(void* sp_ctx, void *user, void* dst, size_t size);
|
||||
|
||||
int m_bit_depth;
|
||||
void* m_ctx;
|
||||
FILE* m_f;
|
||||
int m_color_type;
|
||||
size_t m_buf_pos;
|
||||
};
|
||||
|
||||
|
||||
class SPngEncoder CV_FINAL : public BaseImageEncoder
|
||||
{
|
||||
public:
|
||||
SPngEncoder();
|
||||
virtual ~SPngEncoder();
|
||||
|
||||
bool isFormatSupported( int depth ) const CV_OVERRIDE;
|
||||
bool write( const Mat& img, const std::vector<int>& params ) CV_OVERRIDE;
|
||||
|
||||
ImageEncoder newEncoder() const CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
static int writeDataToBuf(void *ctx, void *user, void *dst_src, size_t length);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif/*_GRFMT_PNG_H_*/
|
||||
@@ -238,7 +238,6 @@ public:
|
||||
bool TiffDecoder::readHeader()
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
TIFF* tif = static_cast<TIFF*>(m_tif.get());
|
||||
if (!tif)
|
||||
{
|
||||
@@ -312,6 +311,18 @@ bool TiffDecoder::readHeader()
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
//support 4-bit palette.
|
||||
if (photometric == PHOTOMETRIC_PALETTE)
|
||||
{
|
||||
CV_Check((int)sample_format, sample_format == SAMPLEFORMAT_UINT || sample_format == SAMPLEFORMAT_INT, "");
|
||||
int depth = sample_format == SAMPLEFORMAT_INT ? CV_8S : CV_8U;
|
||||
m_type = CV_MAKETYPE(depth, 3);
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
CV_Error(cv::Error::StsError, "bitsperpixel value is 4 should be palette.");
|
||||
break;
|
||||
case 8:
|
||||
{
|
||||
//Palette color, the value of the component is used as an index into the red,
|
||||
@@ -425,18 +436,15 @@ static void fixOrientationFull(Mat &img, int orientation)
|
||||
* For 8 bit some corrections are done by TIFFReadRGBAStrip/Tile already.
|
||||
* Not so for 16/32/64 bit.
|
||||
*/
|
||||
static void fixOrientation(Mat &img, uint16 orientation, int dst_bpp)
|
||||
static void fixOrientation(Mat &img, uint16 orientation, bool isOrientationFull)
|
||||
{
|
||||
switch(dst_bpp) {
|
||||
case 8:
|
||||
fixOrientationPartial(img, orientation);
|
||||
break;
|
||||
|
||||
case 16:
|
||||
case 32:
|
||||
case 64:
|
||||
fixOrientationFull(img, orientation);
|
||||
break;
|
||||
if( isOrientationFull )
|
||||
{
|
||||
fixOrientationFull(img, orientation);
|
||||
}
|
||||
else
|
||||
{
|
||||
fixOrientationPartial(img, orientation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -620,17 +628,7 @@ bool TiffDecoder::readData( Mat& img )
|
||||
(img_orientation == ORIENTATION_BOTRIGHT || img_orientation == ORIENTATION_RIGHTBOT ||
|
||||
img_orientation == ORIENTATION_BOTLEFT || img_orientation == ORIENTATION_LEFTBOT);
|
||||
int wanted_channels = normalizeChannelsNumber(img.channels());
|
||||
|
||||
if (dst_bpp == 8)
|
||||
{
|
||||
char errmsg[1024];
|
||||
if (!TIFFRGBAImageOK(tif, errmsg))
|
||||
{
|
||||
CV_LOG_WARNING(NULL, "OpenCV TIFF: TIFFRGBAImageOK: " << errmsg);
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool doReadScanline = false;
|
||||
|
||||
uint32 tile_width0 = m_width, tile_height0 = 0;
|
||||
|
||||
@@ -660,21 +658,123 @@ bool TiffDecoder::readData( Mat& img )
|
||||
const uint64_t MAX_TILE_SIZE = (CV_BIG_UINT(1) << 30);
|
||||
CV_CheckLE((int)ncn, 4, "");
|
||||
CV_CheckLE((int)bpp, 64, "");
|
||||
CV_Assert(((uint64_t)tile_width0 * tile_height0 * ncn * std::max(1, (int)(bpp / bitsPerByte)) < MAX_TILE_SIZE) && "TIFF tile size is too large: >= 1Gb");
|
||||
|
||||
if (dst_bpp == 8)
|
||||
{
|
||||
// we will use TIFFReadRGBA* functions, so allocate temporary buffer for 32bit RGBA
|
||||
bpp = 8;
|
||||
ncn = 4;
|
||||
const int _ncn = 4; // Read RGBA
|
||||
const int _bpp = 8; // Read 8bit
|
||||
|
||||
// if buffer_size(as 32bit RGBA) >= MAX_TILE_SIZE*95%,
|
||||
// we will use TIFFReadScanline function.
|
||||
|
||||
if (
|
||||
(uint64_t)tile_width0 * tile_height0 * _ncn * std::max(1, (int)(_bpp / bitsPerByte))
|
||||
>=
|
||||
( (uint64_t) MAX_TILE_SIZE * 95 / 100)
|
||||
)
|
||||
{
|
||||
uint16_t planerConfig = (uint16)-1;
|
||||
CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planerConfig));
|
||||
|
||||
doReadScanline = (!is_tiled) // no tile
|
||||
&&
|
||||
( ( ncn == 1 ) || ( ncn == 3 ) || ( ncn == 4 ) )
|
||||
&&
|
||||
( ( bpp == 8 ) || ( bpp == 16 ) )
|
||||
&&
|
||||
(tile_height0 == (uint32_t) m_height) // single strip
|
||||
&&
|
||||
(
|
||||
(photometric == PHOTOMETRIC_MINISWHITE)
|
||||
||
|
||||
(photometric == PHOTOMETRIC_MINISBLACK)
|
||||
||
|
||||
(photometric == PHOTOMETRIC_RGB)
|
||||
)
|
||||
&&
|
||||
(planerConfig != PLANARCONFIG_SEPARATE);
|
||||
|
||||
// Currently only EXTRASAMPLE_ASSOCALPHA is supported.
|
||||
if ( doReadScanline && ( ncn == 4 ) )
|
||||
{
|
||||
uint16_t extra_samples_num;
|
||||
uint16_t *extra_samples = NULL;
|
||||
CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &extra_samples_num, &extra_samples ));
|
||||
doReadScanline = ( extra_samples_num == 1 ) && ( extra_samples[0] == EXTRASAMPLE_ASSOCALPHA );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !doReadScanline )
|
||||
{
|
||||
// we will use TIFFReadRGBA* functions, so allocate temporary buffer for 32bit RGBA
|
||||
bpp = 8;
|
||||
ncn = 4;
|
||||
|
||||
char errmsg[1024];
|
||||
if (!TIFFRGBAImageOK(tif, errmsg))
|
||||
{
|
||||
CV_LOG_WARNING(NULL, "OpenCV TIFF: TIFFRGBAImageOK: " << errmsg);
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dst_bpp == 16)
|
||||
{
|
||||
// if buffer_size >= MAX_TILE_SIZE*95%,
|
||||
// we will use TIFFReadScanline function.
|
||||
if (
|
||||
(uint64_t)tile_width0 * tile_height0 * ncn * std::max(1, (int)(bpp / bitsPerByte))
|
||||
>=
|
||||
MAX_TILE_SIZE * 95 / 100
|
||||
)
|
||||
{
|
||||
uint16_t planerConfig = (uint16)-1;
|
||||
CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planerConfig));
|
||||
|
||||
doReadScanline = (!is_tiled) // no tile
|
||||
&&
|
||||
( ( ncn == 1 ) || ( ncn == 3 ) || ( ncn == 4 ) )
|
||||
&&
|
||||
( ( bpp == 8 ) || ( bpp == 16 ) )
|
||||
&&
|
||||
(tile_height0 == (uint32_t) m_height) // single strip
|
||||
&&
|
||||
(
|
||||
(photometric == PHOTOMETRIC_MINISWHITE)
|
||||
||
|
||||
(photometric == PHOTOMETRIC_MINISBLACK)
|
||||
||
|
||||
(photometric == PHOTOMETRIC_RGB)
|
||||
)
|
||||
&&
|
||||
(planerConfig != PLANARCONFIG_SEPARATE);
|
||||
|
||||
// Currently only EXTRASAMPLE_ASSOCALPHA is supported.
|
||||
if ( doReadScanline && ( ncn == 4 ) )
|
||||
{
|
||||
uint16_t extra_samples_num;
|
||||
uint16_t *extra_samples = NULL;
|
||||
CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &extra_samples_num, &extra_samples ));
|
||||
doReadScanline = ( extra_samples_num == 1 ) && ( extra_samples[0] == EXTRASAMPLE_ASSOCALPHA );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dst_bpp == 32 || dst_bpp == 64)
|
||||
{
|
||||
CV_Assert(ncn == img.channels());
|
||||
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP));
|
||||
}
|
||||
|
||||
if ( doReadScanline )
|
||||
{
|
||||
// Read each scanlines.
|
||||
tile_height0 = 1;
|
||||
}
|
||||
|
||||
const size_t src_buffer_bytes_per_row = divUp(static_cast<size_t>(ncn * tile_width0 * bpp), static_cast<size_t>(bitsPerByte));
|
||||
const size_t src_buffer_size = tile_height0 * src_buffer_bytes_per_row;
|
||||
CV_CheckLT(src_buffer_size, MAX_TILE_SIZE, "buffer_size is too large: >= 1Gb");
|
||||
const size_t src_buffer_unpacked_bytes_per_row = divUp(static_cast<size_t>(ncn * tile_width0 * dst_bpp), static_cast<size_t>(bitsPerByte));
|
||||
const size_t src_buffer_unpacked_size = tile_height0 * src_buffer_unpacked_bytes_per_row;
|
||||
const bool needsUnpacking = (bpp < dst_bpp);
|
||||
@@ -682,8 +782,20 @@ bool TiffDecoder::readData( Mat& img )
|
||||
uchar* src_buffer = _src_buffer.data();
|
||||
AutoBuffer<uchar> _src_buffer_unpacked(needsUnpacking ? src_buffer_unpacked_size : 0);
|
||||
uchar* src_buffer_unpacked = needsUnpacking ? _src_buffer_unpacked.data() : nullptr;
|
||||
|
||||
if ( doReadScanline )
|
||||
{
|
||||
CV_CheckGE(src_buffer_size,
|
||||
static_cast<size_t>(TIFFScanlineSize(tif)),
|
||||
"src_buffer_size is smaller than TIFFScanlineSize().");
|
||||
}
|
||||
|
||||
int tileidx = 0;
|
||||
|
||||
#define MAKE_FLAG(a,b) ( (a << 8) | b )
|
||||
const int convert_flag = MAKE_FLAG( ncn, wanted_channels );
|
||||
const bool isNeedConvert16to8 = ( doReadScanline ) && ( bpp == 16 ) && ( dst_bpp == 8);
|
||||
|
||||
for (int y = 0; y < m_height; y += (int)tile_height0)
|
||||
{
|
||||
int tile_height = std::min((int)tile_height0, m_height - y);
|
||||
@@ -699,7 +811,29 @@ bool TiffDecoder::readData( Mat& img )
|
||||
case 8:
|
||||
{
|
||||
uchar* bstart = src_buffer;
|
||||
if (!is_tiled)
|
||||
if (doReadScanline)
|
||||
{
|
||||
CV_TIFF_CHECK_CALL((int)TIFFReadScanline(tif, (uint32*)src_buffer, y) >= 0);
|
||||
|
||||
if ( isNeedConvert16to8 )
|
||||
{
|
||||
// Convert buffer image from 16bit to 8bit.
|
||||
int ix;
|
||||
for ( ix = 0 ; ix < tile_width * ncn - 4; ix += 4 )
|
||||
{
|
||||
src_buffer[ ix ] = src_buffer[ ix * 2 + 1 ];
|
||||
src_buffer[ ix + 1 ] = src_buffer[ ix * 2 + 3 ];
|
||||
src_buffer[ ix + 2 ] = src_buffer[ ix * 2 + 5 ];
|
||||
src_buffer[ ix + 3 ] = src_buffer[ ix * 2 + 7 ];
|
||||
}
|
||||
|
||||
for ( ; ix < tile_width * ncn ; ix ++ )
|
||||
{
|
||||
src_buffer[ ix ] = src_buffer[ ix * 2 + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!is_tiled)
|
||||
{
|
||||
CV_TIFF_CHECK_CALL(TIFFReadRGBAStrip(tif, y, (uint32*)src_buffer));
|
||||
}
|
||||
@@ -710,9 +844,65 @@ bool TiffDecoder::readData( Mat& img )
|
||||
bstart += (tile_height0 - tile_height) * tile_width0 * 4;
|
||||
}
|
||||
|
||||
uchar* img_line_buffer = (uchar*) img.ptr(y, 0);
|
||||
|
||||
for (int i = 0; i < tile_height; i++)
|
||||
{
|
||||
if (color)
|
||||
if (doReadScanline)
|
||||
{
|
||||
switch ( convert_flag )
|
||||
{
|
||||
case MAKE_FLAG( 1, 1 ): // GRAY to GRAY
|
||||
memcpy( (void*) img_line_buffer,
|
||||
(void*) bstart,
|
||||
tile_width * sizeof(uchar) );
|
||||
break;
|
||||
|
||||
case MAKE_FLAG( 1, 3 ): // GRAY to BGR
|
||||
icvCvt_Gray2BGR_8u_C1C3R( bstart, 0,
|
||||
img_line_buffer, 0,
|
||||
Size(tile_width, 1) );
|
||||
break;
|
||||
|
||||
case MAKE_FLAG( 3, 1): // RGB to GRAY
|
||||
icvCvt_BGR2Gray_8u_C3C1R( bstart, 0,
|
||||
img_line_buffer, 0,
|
||||
Size(tile_width, 1) );
|
||||
break;
|
||||
|
||||
case MAKE_FLAG( 3, 3 ): // RGB to BGR
|
||||
icvCvt_BGR2RGB_8u_C3R( bstart, 0,
|
||||
img_line_buffer, 0,
|
||||
Size(tile_width, 1) );
|
||||
break;
|
||||
|
||||
case MAKE_FLAG( 4, 1 ): // RGBA to GRAY
|
||||
icvCvt_BGRA2Gray_8u_C4C1R( bstart, 0,
|
||||
img_line_buffer, 0,
|
||||
Size(tile_width, 1) );
|
||||
break;
|
||||
|
||||
case MAKE_FLAG( 4, 3 ): // RGBA to BGR
|
||||
icvCvt_BGRA2BGR_8u_C4C3R( bstart, 0,
|
||||
img_line_buffer, 0,
|
||||
Size(tile_width, 1), 2 );
|
||||
break;
|
||||
|
||||
case MAKE_FLAG( 4, 4 ): // RGBA to BGRA
|
||||
icvCvt_BGRA2RGBA_8u_C4R(bstart, 0,
|
||||
img_line_buffer, 0,
|
||||
Size(tile_width, 1) );
|
||||
break;
|
||||
|
||||
default:
|
||||
CV_LOG_ONCE_ERROR(NULL, "OpenCV TIFF(line " << __LINE__ << "): Unsupported convertion :"
|
||||
<< " bpp = " << bpp << " ncn = " << (int)ncn
|
||||
<< " wanted_channels =" << wanted_channels );
|
||||
break;
|
||||
}
|
||||
#undef MAKE_FLAG
|
||||
}
|
||||
else if (color)
|
||||
{
|
||||
if (wanted_channels == 4)
|
||||
{
|
||||
@@ -741,7 +931,11 @@ bool TiffDecoder::readData( Mat& img )
|
||||
|
||||
case 16:
|
||||
{
|
||||
if (!is_tiled)
|
||||
if (doReadScanline)
|
||||
{
|
||||
CV_TIFF_CHECK_CALL((int)TIFFReadScanline(tif, (uint32*)src_buffer, y) >= 0);
|
||||
}
|
||||
else if (!is_tiled)
|
||||
{
|
||||
CV_TIFF_CHECK_CALL((int)TIFFReadEncodedStrip(tif, tileidx, (uint32*)src_buffer, src_buffer_size) >= 0);
|
||||
}
|
||||
@@ -862,7 +1056,11 @@ bool TiffDecoder::readData( Mat& img )
|
||||
}
|
||||
if (bpp < dst_bpp)
|
||||
img *= (1<<(dst_bpp-bpp));
|
||||
fixOrientation(img, img_orientation, dst_bpp);
|
||||
|
||||
// If TIFFReadRGBA* function is used -> fixOrientationPartial().
|
||||
// Otherwise -> fixOrientationFull().
|
||||
fixOrientation(img, img_orientation,
|
||||
( ( dst_bpp != 8 ) && ( !doReadScanline ) ) );
|
||||
}
|
||||
|
||||
if (m_hdr && depth >= CV_32F)
|
||||
@@ -887,6 +1085,7 @@ TiffEncoder::~TiffEncoder()
|
||||
|
||||
ImageEncoder TiffEncoder::newEncoder() const
|
||||
{
|
||||
cv_tiffSetErrorHandler();
|
||||
return makePtr<TiffEncoder>();
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,8 @@ bool WebPDecoder::readHeader()
|
||||
WebPBitstreamFeatures features;
|
||||
if (VP8_STATUS_OK == WebPGetFeatures(header, sizeof(header), &features))
|
||||
{
|
||||
CV_CheckEQ(features.has_animation, 0, "WebP backend does not support animated webp images");
|
||||
|
||||
m_width = features.width;
|
||||
m_height = features.height;
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "grfmt_pxm.hpp"
|
||||
#include "grfmt_pfm.hpp"
|
||||
#include "grfmt_tiff.hpp"
|
||||
#include "grfmt_spng.hpp"
|
||||
#include "grfmt_png.hpp"
|
||||
#include "grfmt_jpeg2000.hpp"
|
||||
#include "grfmt_jpeg2000_openjpeg.hpp"
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
#include <cerrno>
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
#include <opencv2/core/utils/configuration.private.hpp>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
|
||||
|
||||
|
||||
/****************************************************************************************\
|
||||
@@ -167,7 +169,10 @@ struct ImageCodecInitializer
|
||||
decoders.push_back( makePtr<TiffDecoder>() );
|
||||
encoders.push_back( makePtr<TiffEncoder>() );
|
||||
#endif
|
||||
#ifdef HAVE_PNG
|
||||
#ifdef HAVE_SPNG
|
||||
decoders.push_back( makePtr<SPngDecoder>() );
|
||||
encoders.push_back( makePtr<SPngEncoder>() );
|
||||
#elif defined(HAVE_PNG)
|
||||
decoders.push_back( makePtr<PngDecoder>() );
|
||||
encoders.push_back( makePtr<PngEncoder>() );
|
||||
#endif
|
||||
@@ -658,57 +663,14 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int start, int
|
||||
static
|
||||
size_t imcount_(const String& filename, int flags)
|
||||
{
|
||||
/// Search for the relevant decoder to handle the imagery
|
||||
ImageDecoder decoder;
|
||||
|
||||
#ifdef HAVE_GDAL
|
||||
if (flags != IMREAD_UNCHANGED && (flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL) {
|
||||
decoder = GdalDecoder().newDecoder();
|
||||
try{
|
||||
ImageCollection collection(filename, flags);
|
||||
return collection.size();
|
||||
} catch(cv::Exception const& e) {
|
||||
// Reading header or finding decoder for the filename is failed
|
||||
std::cerr << "imcount_('" << filename << "'): can't read header or can't find decoder: " << e.what() << std::endl << std::flush;
|
||||
}
|
||||
else {
|
||||
#else
|
||||
CV_UNUSED(flags);
|
||||
#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
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t imcount(const String& filename, int flags)
|
||||
@@ -754,7 +716,9 @@ static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
|
||||
}
|
||||
|
||||
encoder->setDestination( filename );
|
||||
CV_Assert(params.size() <= CV_IO_MAX_IMAGE_PARAMS*2);
|
||||
|
||||
CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs");
|
||||
CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), "");
|
||||
bool code = false;
|
||||
try
|
||||
{
|
||||
@@ -967,6 +931,157 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst )
|
||||
return *dst;
|
||||
}
|
||||
|
||||
static bool
|
||||
imdecodemulti_(const Mat& buf, int flags, std::vector<Mat>& mats, int start, int count)
|
||||
{
|
||||
CV_Assert(!buf.empty());
|
||||
CV_Assert(buf.isContinuous());
|
||||
CV_Assert(buf.checkVector(1, CV_8U) > 0);
|
||||
Mat buf_row = buf.reshape(1, 1); // decoders expects single row, avoid issues with vector columns
|
||||
|
||||
String filename;
|
||||
|
||||
ImageDecoder decoder = findDecoder(buf_row);
|
||||
if (!decoder)
|
||||
return 0;
|
||||
|
||||
if (count < 0) {
|
||||
count = std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
if (!decoder->setSource(buf_row))
|
||||
{
|
||||
filename = tempfile();
|
||||
FILE* f = fopen(filename.c_str(), "wb");
|
||||
if (!f)
|
||||
return 0;
|
||||
size_t bufSize = buf_row.total() * buf.elemSize();
|
||||
if (fwrite(buf_row.ptr(), 1, bufSize, f) != bufSize)
|
||||
{
|
||||
fclose(f);
|
||||
CV_Error(Error::StsError, "failed to write image data to temporary file");
|
||||
}
|
||||
if (fclose(f) != 0)
|
||||
{
|
||||
CV_Error(Error::StsError, "failed to write image data to temporary file");
|
||||
}
|
||||
decoder->setSource(filename);
|
||||
}
|
||||
|
||||
// read the header to make sure it succeeds
|
||||
bool success = false;
|
||||
try
|
||||
{
|
||||
// read the header to make sure it succeeds
|
||||
if (decoder->readHeader())
|
||||
success = true;
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
std::cerr << "imreadmulti_('" << filename << "'): can't read header: " << e.what() << std::endl << std::flush;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << "imreadmulti_('" << filename << "'): can't read header: unknown exception" << std::endl << std::flush;
|
||||
}
|
||||
|
||||
int current = start;
|
||||
while (success && current > 0)
|
||||
{
|
||||
if (!decoder->nextPage())
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
--current;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
decoder.release();
|
||||
if (!filename.empty())
|
||||
{
|
||||
if (0 != remove(filename.c_str()))
|
||||
{
|
||||
std::cerr << "unable to remove temporary file:" << filename << std::endl << std::flush;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// established the required input image size
|
||||
Size size = validateInputImageSize(Size(decoder->width(), decoder->height()));
|
||||
|
||||
// read the image data
|
||||
Mat mat(size.height, size.width, type);
|
||||
success = false;
|
||||
try
|
||||
{
|
||||
if (decoder->readData(mat))
|
||||
success = true;
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
std::cerr << "imreadmulti_('" << filename << "'): can't read data: " << e.what() << std::endl << std::flush;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << "imreadmulti_('" << filename << "'): can't read data: unknown exception" << std::endl << std::flush;
|
||||
}
|
||||
if (!success)
|
||||
break;
|
||||
|
||||
// optionally rotate the data if EXIF' orientation flag says so
|
||||
if ((flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED)
|
||||
{
|
||||
ApplyExifOrientation(decoder->getExifTag(ORIENTATION), mat);
|
||||
}
|
||||
|
||||
mats.push_back(mat);
|
||||
if (!decoder->nextPage())
|
||||
{
|
||||
break;
|
||||
}
|
||||
++current;
|
||||
}
|
||||
|
||||
if (!filename.empty())
|
||||
{
|
||||
if (0 != remove(filename.c_str()))
|
||||
{
|
||||
std::cerr << "unable to remove temporary file:" << filename << std::endl << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success)
|
||||
mats.clear();
|
||||
return !mats.empty();
|
||||
}
|
||||
|
||||
bool imdecodemulti(InputArray _buf, int flags, CV_OUT std::vector<Mat>& mats)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
Mat buf = _buf.getMat();
|
||||
return imdecodemulti_(buf, flags, mats, 0, -1);
|
||||
}
|
||||
|
||||
bool imencode( const String& ext, InputArray _image,
|
||||
std::vector<uchar>& buf, const std::vector<int>& params )
|
||||
{
|
||||
@@ -990,6 +1105,9 @@ bool imencode( const String& ext, InputArray _image,
|
||||
image = temp;
|
||||
}
|
||||
|
||||
CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs");
|
||||
CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), "");
|
||||
|
||||
bool code;
|
||||
if( encoder->setDestination(buf) )
|
||||
{
|
||||
@@ -1032,6 +1150,247 @@ bool haveImageWriter( const String& filename )
|
||||
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, static_cast<int>(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. */
|
||||
|
||||
@@ -211,7 +211,7 @@ TEST_P(Imgcodecs_ExtSize, write_imageseq)
|
||||
|
||||
const string all_exts[] =
|
||||
{
|
||||
#ifdef HAVE_PNG
|
||||
#if defined(HAVE_PNG) || defined(HAVE_SPNG)
|
||||
".png",
|
||||
#endif
|
||||
#ifdef HAVE_TIFF
|
||||
@@ -343,21 +343,45 @@ TEST(Imgcodecs_Hdr, regression)
|
||||
Mat img_no_rle = imread(name_no_rle, -1);
|
||||
ASSERT_FALSE(img_no_rle.empty()) << "Could not open " << name_no_rle;
|
||||
|
||||
double min = 0.0, max = 1.0;
|
||||
minMaxLoc(abs(img_rle - img_no_rle), &min, &max);
|
||||
ASSERT_FALSE(max > DBL_EPSILON);
|
||||
EXPECT_EQ(cvtest::norm(img_rle, img_no_rle, NORM_INF), 0.0);
|
||||
|
||||
string tmp_file_name = tempfile(".hdr");
|
||||
vector<int>param(1);
|
||||
vector<int> param(2);
|
||||
param[0] = IMWRITE_HDR_COMPRESSION;
|
||||
for(int i = 0; i < 2; i++) {
|
||||
param[0] = i;
|
||||
param[1] = i;
|
||||
imwrite(tmp_file_name, img_rle, param);
|
||||
Mat written_img = imread(tmp_file_name, -1);
|
||||
ASSERT_FALSE(written_img.empty()) << "Could not open " << tmp_file_name;
|
||||
minMaxLoc(abs(img_rle - written_img), &min, &max);
|
||||
ASSERT_FALSE(max > DBL_EPSILON);
|
||||
EXPECT_EQ(cvtest::norm(written_img, img_rle, NORM_INF), 0.0);
|
||||
}
|
||||
remove(tmp_file_name.c_str());
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_Hdr, regression_imencode)
|
||||
{
|
||||
string folder = string(cvtest::TS::ptr()->get_data_path()) + "/readwrite/";
|
||||
string name = folder + "rle.hdr";
|
||||
Mat img_ref = imread(name, -1);
|
||||
ASSERT_FALSE(img_ref.empty()) << "Could not open " << name;
|
||||
|
||||
vector<int> params(2);
|
||||
params[0] = IMWRITE_HDR_COMPRESSION;
|
||||
{
|
||||
vector<uchar> buf;
|
||||
params[1] = IMWRITE_HDR_COMPRESSION_NONE;
|
||||
imencode(".hdr", img_ref, buf, params);
|
||||
Mat img = imdecode(buf, -1);
|
||||
EXPECT_EQ(cvtest::norm(img_ref, img, NORM_INF), 0.0);
|
||||
}
|
||||
{
|
||||
vector<uchar> buf;
|
||||
params[1] = IMWRITE_HDR_COMPRESSION_RLE;
|
||||
imencode(".hdr", img_ref, buf, params);
|
||||
Mat img = imdecode(buf, -1);
|
||||
EXPECT_EQ(cvtest::norm(img_ref, img, NORM_INF), 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IMGCODEC_PXM
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
#ifdef HAVE_PNG
|
||||
#if defined(HAVE_PNG) || defined(HAVE_SPNG)
|
||||
|
||||
TEST(Imgcodecs_Png, write_big)
|
||||
{
|
||||
@@ -186,6 +186,225 @@ const string exif_files[] =
|
||||
INSTANTIATE_TEST_CASE_P(ExifFiles, Imgcodecs_PNG_Exif,
|
||||
testing::ValuesIn(exif_files));
|
||||
|
||||
|
||||
typedef testing::TestWithParam<string> Imgcodecs_Png_PngSuite;
|
||||
|
||||
TEST_P(Imgcodecs_Png_PngSuite, decode)
|
||||
{
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
const string filename = root + "pngsuite/" + GetParam() + ".png";
|
||||
const string xml_filename = root + "pngsuite/" + GetParam() + ".xml";
|
||||
FileStorage fs(xml_filename, FileStorage::READ);
|
||||
EXPECT_TRUE(fs.isOpened());
|
||||
|
||||
Mat src = imread(filename, IMREAD_UNCHANGED);
|
||||
Mat gt;
|
||||
fs.getFirstTopLevelNode() >> gt;
|
||||
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), src, gt);
|
||||
}
|
||||
|
||||
const string pngsuite_files[] =
|
||||
{
|
||||
"basi0g01",
|
||||
"basi0g02",
|
||||
"basi0g04",
|
||||
"basi0g08",
|
||||
"basi0g16",
|
||||
"basi2c08",
|
||||
"basi2c16",
|
||||
"basi3p01",
|
||||
"basi3p02",
|
||||
"basi3p04",
|
||||
"basi3p08",
|
||||
"basi4a08",
|
||||
"basi4a16",
|
||||
"basi6a08",
|
||||
"basi6a16",
|
||||
"basn0g01",
|
||||
"basn0g02",
|
||||
"basn0g04",
|
||||
"basn0g08",
|
||||
"basn0g16",
|
||||
"basn2c08",
|
||||
"basn2c16",
|
||||
"basn3p01",
|
||||
"basn3p02",
|
||||
"basn3p04",
|
||||
"basn3p08",
|
||||
"basn4a08",
|
||||
"basn4a16",
|
||||
"basn6a08",
|
||||
"basn6a16",
|
||||
"bgai4a08",
|
||||
"bgai4a16",
|
||||
"bgan6a08",
|
||||
"bgan6a16",
|
||||
"bgbn4a08",
|
||||
"bggn4a16",
|
||||
"bgwn6a08",
|
||||
"bgyn6a16",
|
||||
"ccwn2c08",
|
||||
"ccwn3p08",
|
||||
"cdfn2c08",
|
||||
"cdhn2c08",
|
||||
"cdsn2c08",
|
||||
"cdun2c08",
|
||||
"ch1n3p04",
|
||||
"ch2n3p08",
|
||||
"cm0n0g04",
|
||||
"cm7n0g04",
|
||||
"cm9n0g04",
|
||||
"cs3n2c16",
|
||||
"cs3n3p08",
|
||||
"cs5n2c08",
|
||||
"cs5n3p08",
|
||||
"cs8n2c08",
|
||||
"cs8n3p08",
|
||||
"ct0n0g04",
|
||||
"ct1n0g04",
|
||||
"cten0g04",
|
||||
"ctfn0g04",
|
||||
"ctgn0g04",
|
||||
"cthn0g04",
|
||||
"ctjn0g04",
|
||||
"ctzn0g04",
|
||||
"exif2c08",
|
||||
"f00n0g08",
|
||||
"f00n2c08",
|
||||
"f01n0g08",
|
||||
"f01n2c08",
|
||||
"f02n0g08",
|
||||
"f02n2c08",
|
||||
"f03n0g08",
|
||||
"f03n2c08",
|
||||
"f04n0g08",
|
||||
"f04n2c08",
|
||||
"f99n0g04",
|
||||
"g03n0g16",
|
||||
"g03n2c08",
|
||||
"g03n3p04",
|
||||
"g04n0g16",
|
||||
"g04n2c08",
|
||||
"g04n3p04",
|
||||
"g05n0g16",
|
||||
"g05n2c08",
|
||||
"g05n3p04",
|
||||
"g07n0g16",
|
||||
"g07n2c08",
|
||||
"g07n3p04",
|
||||
"g10n0g16",
|
||||
"g10n2c08",
|
||||
"g10n3p04",
|
||||
"g25n0g16",
|
||||
"g25n2c08",
|
||||
"g25n3p04",
|
||||
"oi1n0g16",
|
||||
"oi1n2c16",
|
||||
"oi2n0g16",
|
||||
"oi2n2c16",
|
||||
"oi4n0g16",
|
||||
"oi4n2c16",
|
||||
"oi9n0g16",
|
||||
"oi9n2c16",
|
||||
"pp0n2c16",
|
||||
"pp0n6a08",
|
||||
"ps1n0g08",
|
||||
"ps1n2c16",
|
||||
"ps2n0g08",
|
||||
"ps2n2c16",
|
||||
"s01i3p01",
|
||||
"s01n3p01",
|
||||
"s02i3p01",
|
||||
"s02n3p01",
|
||||
"s03i3p01",
|
||||
"s03n3p01",
|
||||
"s04i3p01",
|
||||
"s04n3p01",
|
||||
"s05i3p02",
|
||||
"s05n3p02",
|
||||
"s06i3p02",
|
||||
"s06n3p02",
|
||||
"s07i3p02",
|
||||
"s07n3p02",
|
||||
"s08i3p02",
|
||||
"s08n3p02",
|
||||
"s09i3p02",
|
||||
"s09n3p02",
|
||||
"s32i3p04",
|
||||
"s32n3p04",
|
||||
"s33i3p04",
|
||||
"s33n3p04",
|
||||
"s34i3p04",
|
||||
"s34n3p04",
|
||||
"s35i3p04",
|
||||
"s35n3p04",
|
||||
"s36i3p04",
|
||||
"s36n3p04",
|
||||
"s37i3p04",
|
||||
"s37n3p04",
|
||||
"s38i3p04",
|
||||
"s38n3p04",
|
||||
"s39i3p04",
|
||||
"s39n3p04",
|
||||
"s40i3p04",
|
||||
"s40n3p04",
|
||||
"tbbn0g04",
|
||||
"tbbn2c16",
|
||||
"tbbn3p08",
|
||||
"tbgn2c16",
|
||||
"tbgn3p08",
|
||||
"tbrn2c08",
|
||||
"tbwn0g16",
|
||||
"tbwn3p08",
|
||||
"tbyn3p08",
|
||||
"tm3n3p02",
|
||||
"tp0n0g08",
|
||||
"tp0n2c08",
|
||||
"tp0n3p08",
|
||||
"tp1n3p08",
|
||||
"z00n2c08",
|
||||
"z03n2c08",
|
||||
"z06n2c08",
|
||||
"z09n2c08",
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Png_PngSuite,
|
||||
testing::ValuesIn(pngsuite_files));
|
||||
|
||||
typedef testing::TestWithParam<string> Imgcodecs_Png_PngSuite_Corrupted;
|
||||
|
||||
TEST_P(Imgcodecs_Png_PngSuite_Corrupted, decode)
|
||||
{
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
const string filename = root + "pngsuite/" + GetParam() + ".png";
|
||||
|
||||
Mat src = imread(filename, IMREAD_UNCHANGED);
|
||||
|
||||
// Corrupted files should not be read
|
||||
EXPECT_TRUE(src.empty());
|
||||
}
|
||||
|
||||
const string pngsuite_files_corrupted[] = {
|
||||
"xc1n0g08",
|
||||
"xc9n2c08",
|
||||
"xcrn0g04",
|
||||
"xcsn0g01",
|
||||
"xd0n2c08",
|
||||
"xd3n2c08",
|
||||
"xd9n2c08",
|
||||
"xdtn0g01",
|
||||
"xhdn0g08",
|
||||
"xlfn0g04",
|
||||
"xs1n0g01",
|
||||
"xs2n0g01",
|
||||
"xs4n0g01",
|
||||
"xs7n0g01",
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Png_PngSuite_Corrupted,
|
||||
testing::ValuesIn(pngsuite_files_corrupted));
|
||||
|
||||
#endif // HAVE_PNG
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -28,7 +28,7 @@ const tuple<string, Size> images[] =
|
||||
#ifdef HAVE_JPEG
|
||||
make_tuple<string, Size>("../cv/imgproc/stuff.jpg", Size(640, 480)),
|
||||
#endif
|
||||
#ifdef HAVE_PNG
|
||||
#if defined(HAVE_PNG) || defined(HAVE_SPNG)
|
||||
make_tuple<string, Size>("../cv/shared/pic1.png", Size(400, 300)),
|
||||
#endif
|
||||
make_tuple<string, Size>("../highgui/readwrite/ordinary.bmp", Size(480, 272)),
|
||||
@@ -148,7 +148,7 @@ typedef string Ext;
|
||||
typedef testing::TestWithParam<Ext> Imgcodecs_Image;
|
||||
|
||||
const string exts[] = {
|
||||
#ifdef HAVE_PNG
|
||||
#if defined(HAVE_PNG) || defined(HAVE_SPNG)
|
||||
"png",
|
||||
#endif
|
||||
#ifdef HAVE_TIFF
|
||||
@@ -303,4 +303,201 @@ 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Imgcodecs_Params, imwrite_regression_22752)
|
||||
{
|
||||
const Mat img(16, 16, CV_8UC3, cv::Scalar::all(0));
|
||||
vector<int> params;
|
||||
params.push_back(IMWRITE_JPEG_QUALITY);
|
||||
// params.push_back(100)); // Forget it.
|
||||
EXPECT_ANY_THROW(cv::imwrite("test.jpg", img, params)); // parameters size or missing JPEG codec
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_Params, imencode_regression_22752)
|
||||
{
|
||||
const Mat img(16, 16, CV_8UC3, cv::Scalar::all(0));
|
||||
vector<int> params;
|
||||
params.push_back(IMWRITE_JPEG_QUALITY);
|
||||
// params.push_back(100)); // Forget it.
|
||||
vector<uchar> buf;
|
||||
EXPECT_ANY_THROW(cv::imencode("test.jpg", img, buf, params)); // parameters size or missing JPEG codec
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
#include "test_precomp.hpp"
|
||||
#include "opencv2/core/utils/logger.hpp"
|
||||
#include "opencv2/core/utils/configuration.private.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
@@ -46,6 +48,432 @@ TEST(Imgcodecs_Tiff, decode_tile16384x16384)
|
||||
EXPECT_EQ(0, remove(file4.c_str()));
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// See https://github.com/opencv/opencv/issues/22388
|
||||
|
||||
/**
|
||||
* Dummy enum to show combination of IMREAD_*.
|
||||
*/
|
||||
enum ImreadMixModes
|
||||
{
|
||||
IMREAD_MIX_UNCHANGED = IMREAD_UNCHANGED ,
|
||||
IMREAD_MIX_GRAYSCALE = IMREAD_GRAYSCALE ,
|
||||
IMREAD_MIX_COLOR = IMREAD_COLOR ,
|
||||
IMREAD_MIX_GRAYSCALE_ANYDEPTH = IMREAD_GRAYSCALE | IMREAD_ANYDEPTH ,
|
||||
IMREAD_MIX_GRAYSCALE_ANYCOLOR = IMREAD_GRAYSCALE | IMREAD_ANYCOLOR,
|
||||
IMREAD_MIX_GRAYSCALE_ANYDEPTH_ANYCOLOR = IMREAD_GRAYSCALE | IMREAD_ANYDEPTH | IMREAD_ANYCOLOR,
|
||||
IMREAD_MIX_COLOR_ANYDEPTH = IMREAD_COLOR | IMREAD_ANYDEPTH ,
|
||||
IMREAD_MIX_COLOR_ANYCOLOR = IMREAD_COLOR | IMREAD_ANYCOLOR,
|
||||
IMREAD_MIX_COLOR_ANYDEPTH_ANYCOLOR = IMREAD_COLOR | IMREAD_ANYDEPTH | IMREAD_ANYCOLOR
|
||||
};
|
||||
|
||||
typedef tuple< uint64_t, tuple<string, int>, ImreadMixModes > Bufsize_and_Type;
|
||||
typedef testing::TestWithParam<Bufsize_and_Type> Imgcodecs_Tiff_decode_Huge;
|
||||
|
||||
static inline
|
||||
void PrintTo(const ImreadMixModes& val, std::ostream* os)
|
||||
{
|
||||
PrintTo( static_cast<ImreadModes>(val), os );
|
||||
}
|
||||
|
||||
TEST_P(Imgcodecs_Tiff_decode_Huge, regression)
|
||||
{
|
||||
// Get test parameters
|
||||
const uint64_t buffer_size = get<0>(GetParam());
|
||||
const string mat_type_string = get<0>(get<1>(GetParam()));
|
||||
const int mat_type = get<1>(get<1>(GetParam()));
|
||||
const int imread_mode = get<2>(GetParam());
|
||||
|
||||
// Detect data file
|
||||
const string req_filename = cv::format("readwrite/huge-tiff/%s_%zu.tif", mat_type_string.c_str(), (size_t)buffer_size);
|
||||
const string filename = findDataFile( req_filename );
|
||||
|
||||
// Preparation process for test
|
||||
{
|
||||
// Convert from mat_type and buffer_size to tiff file information.
|
||||
const uint64_t width = 32768;
|
||||
int ncn = CV_MAT_CN(mat_type);
|
||||
int depth = ( CV_MAT_DEPTH(mat_type) == CV_16U) ? 2 : 1; // 16bit or 8 bit
|
||||
const uint64_t height = (uint64_t) buffer_size / width / ncn / depth;
|
||||
const uint64_t base_scanline_size = (uint64_t) width * ncn * depth;
|
||||
const uint64_t base_strip_size = (uint64_t) base_scanline_size * height;
|
||||
|
||||
// To avoid exception about pixel size, check it.
|
||||
static const size_t CV_IO_MAX_IMAGE_PIXELS = utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_PIXELS", 1 << 30);
|
||||
uint64_t pixels = (uint64_t) width * height;
|
||||
if ( pixels > CV_IO_MAX_IMAGE_PIXELS )
|
||||
{
|
||||
throw SkipTestException( cv::format("Test is skipped( pixels(%zu) > CV_IO_MAX_IMAGE_PIXELS(%zu) )",
|
||||
(size_t)pixels, CV_IO_MAX_IMAGE_PIXELS) );
|
||||
}
|
||||
|
||||
// If buffer_size >= 1GB * 95%, TIFFReadScanline() is used.
|
||||
const uint64_t BUFFER_SIZE_LIMIT_FOR_READS_CANLINE = (uint64_t) 1024*1024*1024*95/100;
|
||||
const bool doReadScanline = ( base_strip_size >= BUFFER_SIZE_LIMIT_FOR_READS_CANLINE );
|
||||
|
||||
// Update ncn and depth for destination Mat.
|
||||
switch ( imread_mode )
|
||||
{
|
||||
case IMREAD_UNCHANGED:
|
||||
break;
|
||||
case IMREAD_GRAYSCALE:
|
||||
ncn = 1;
|
||||
depth = 1;
|
||||
break;
|
||||
case IMREAD_GRAYSCALE | IMREAD_ANYDEPTH:
|
||||
ncn = 1;
|
||||
break;
|
||||
case IMREAD_GRAYSCALE | IMREAD_ANYCOLOR:
|
||||
ncn = (ncn == 1)?1:3;
|
||||
depth = 1;
|
||||
break;
|
||||
case IMREAD_GRAYSCALE | IMREAD_ANYCOLOR | IMREAD_ANYDEPTH:
|
||||
ncn = (ncn == 1)?1:3;
|
||||
break;
|
||||
case IMREAD_COLOR:
|
||||
ncn = 3;
|
||||
depth = 1;
|
||||
break;
|
||||
case IMREAD_COLOR | IMREAD_ANYDEPTH:
|
||||
ncn = 3;
|
||||
break;
|
||||
case IMREAD_COLOR | IMREAD_ANYCOLOR:
|
||||
ncn = 3;
|
||||
depth = 1;
|
||||
break;
|
||||
case IMREAD_COLOR | IMREAD_ANYDEPTH | IMREAD_ANYCOLOR:
|
||||
ncn = 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Memory usage for Destination Mat
|
||||
const uint64_t memory_usage_cvmat = (uint64_t) width * ncn * depth * height;
|
||||
|
||||
// Memory usage for Work memory in libtiff.
|
||||
uint64_t memory_usage_tiff = 0;
|
||||
if ( ( depth == 1 ) && ( !doReadScanline ) )
|
||||
{
|
||||
// TIFFReadRGBA*() request to allocate RGBA(32bit) buffer.
|
||||
memory_usage_tiff = (uint64_t)
|
||||
width *
|
||||
4 * // ncn = RGBA
|
||||
1 * // dst_bpp = 8 bpp
|
||||
height;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TIFFReadEncodedStrip() or TIFFReadScanline() request to allocate strip memory.
|
||||
memory_usage_tiff = base_strip_size;
|
||||
}
|
||||
|
||||
// Memory usage for Work memory in imgcodec/grfmt_tiff.cpp
|
||||
const uint64_t memory_usage_work =
|
||||
( doReadScanline ) ? base_scanline_size // for TIFFReadScanline()
|
||||
: base_strip_size; // for TIFFReadRGBA*() or TIFFReadEncodedStrip()
|
||||
|
||||
// Total memory usage.
|
||||
const uint64_t memory_usage_total =
|
||||
memory_usage_cvmat + // Destination Mat
|
||||
memory_usage_tiff + // Work memory in libtiff
|
||||
memory_usage_work; // Work memory in imgcodecs
|
||||
|
||||
// Output memory usage log.
|
||||
CV_LOG_DEBUG(NULL, cv::format("OpenCV TIFF-test: memory usage info : mat(%zu), libtiff(%zu), work(%zu) -> total(%zu)",
|
||||
(size_t)memory_usage_cvmat, (size_t)memory_usage_tiff, (size_t)memory_usage_work, (size_t)memory_usage_total) );
|
||||
|
||||
// Add test tags.
|
||||
if ( memory_usage_total >= (uint64_t) 6144 * 1024 * 1024 )
|
||||
{
|
||||
applyTestTag( CV_TEST_TAG_MEMORY_14GB, CV_TEST_TAG_VERYLONG );
|
||||
}
|
||||
else if ( memory_usage_total >= (uint64_t) 2048 * 1024 * 1024 )
|
||||
{
|
||||
applyTestTag( CV_TEST_TAG_MEMORY_6GB, CV_TEST_TAG_VERYLONG );
|
||||
}
|
||||
else if ( memory_usage_total >= (uint64_t) 1024 * 1024 * 1024 )
|
||||
{
|
||||
applyTestTag( CV_TEST_TAG_MEMORY_2GB, CV_TEST_TAG_LONG );
|
||||
}
|
||||
else if ( memory_usage_total >= (uint64_t) 512 * 1024 * 1024 )
|
||||
{
|
||||
applyTestTag( CV_TEST_TAG_MEMORY_1GB );
|
||||
}
|
||||
else if ( memory_usage_total >= (uint64_t) 200 * 1024 * 1024 )
|
||||
{
|
||||
applyTestTag( CV_TEST_TAG_MEMORY_512MB );
|
||||
}
|
||||
else
|
||||
{
|
||||
// do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
// TEST Main
|
||||
|
||||
cv::Mat img;
|
||||
ASSERT_NO_THROW( img = cv::imread(filename, imread_mode) );
|
||||
ASSERT_FALSE(img.empty());
|
||||
|
||||
/**
|
||||
* Test marker pixels at each corners.
|
||||
*
|
||||
* 0xAn,0x00 ... 0x00, 0xBn
|
||||
* 0x00,0x00 ... 0x00, 0x00
|
||||
* : : : :
|
||||
* 0x00,0x00 ... 0x00, 0x00
|
||||
* 0xCn,0x00 .., 0x00, 0xDn
|
||||
*
|
||||
*/
|
||||
|
||||
#define MAKE_FLAG(from_type, to_type) (((uint64_t)from_type << 32 ) | to_type )
|
||||
|
||||
switch ( MAKE_FLAG(mat_type, img.type() ) )
|
||||
{
|
||||
// GRAY TO GRAY
|
||||
case MAKE_FLAG(CV_8UC1, CV_8UC1):
|
||||
case MAKE_FLAG(CV_16UC1, CV_8UC1):
|
||||
EXPECT_EQ( 0xA0, img.at<uchar>(0, 0) );
|
||||
EXPECT_EQ( 0xB0, img.at<uchar>(0, img.cols-1) );
|
||||
EXPECT_EQ( 0xC0, img.at<uchar>(img.rows-1, 0) );
|
||||
EXPECT_EQ( 0xD0, img.at<uchar>(img.rows-1, img.cols-1) );
|
||||
break;
|
||||
|
||||
// RGB/RGBA TO BGR
|
||||
case MAKE_FLAG(CV_8UC3, CV_8UC3):
|
||||
case MAKE_FLAG(CV_8UC4, CV_8UC3):
|
||||
case MAKE_FLAG(CV_16UC3, CV_8UC3):
|
||||
case MAKE_FLAG(CV_16UC4, CV_8UC3):
|
||||
EXPECT_EQ( 0xA2, img.at<Vec3b>(0, 0) [0] );
|
||||
EXPECT_EQ( 0xA1, img.at<Vec3b>(0, 0) [1] );
|
||||
EXPECT_EQ( 0xA0, img.at<Vec3b>(0, 0) [2] );
|
||||
EXPECT_EQ( 0xB2, img.at<Vec3b>(0, img.cols-1)[0] );
|
||||
EXPECT_EQ( 0xB1, img.at<Vec3b>(0, img.cols-1)[1] );
|
||||
EXPECT_EQ( 0xB0, img.at<Vec3b>(0, img.cols-1)[2] );
|
||||
EXPECT_EQ( 0xC2, img.at<Vec3b>(img.rows-1, 0) [0] );
|
||||
EXPECT_EQ( 0xC1, img.at<Vec3b>(img.rows-1, 0) [1] );
|
||||
EXPECT_EQ( 0xC0, img.at<Vec3b>(img.rows-1, 0) [2] );
|
||||
EXPECT_EQ( 0xD2, img.at<Vec3b>(img.rows-1, img.cols-1)[0] );
|
||||
EXPECT_EQ( 0xD1, img.at<Vec3b>(img.rows-1, img.cols-1)[1] );
|
||||
EXPECT_EQ( 0xD0, img.at<Vec3b>(img.rows-1, img.cols-1)[2] );
|
||||
break;
|
||||
|
||||
// RGBA TO BGRA
|
||||
case MAKE_FLAG(CV_8UC4, CV_8UC4):
|
||||
case MAKE_FLAG(CV_16UC4, CV_8UC4):
|
||||
EXPECT_EQ( 0xA2, img.at<Vec4b>(0, 0) [0] );
|
||||
EXPECT_EQ( 0xA1, img.at<Vec4b>(0, 0) [1] );
|
||||
EXPECT_EQ( 0xA0, img.at<Vec4b>(0, 0) [2] );
|
||||
EXPECT_EQ( 0xA3, img.at<Vec4b>(0, 0) [3] );
|
||||
EXPECT_EQ( 0xB2, img.at<Vec4b>(0, img.cols-1)[0] );
|
||||
EXPECT_EQ( 0xB1, img.at<Vec4b>(0, img.cols-1)[1] );
|
||||
EXPECT_EQ( 0xB0, img.at<Vec4b>(0, img.cols-1)[2] );
|
||||
EXPECT_EQ( 0xB3, img.at<Vec4b>(0, img.cols-1)[3] );
|
||||
EXPECT_EQ( 0xC2, img.at<Vec4b>(img.rows-1, 0) [0] );
|
||||
EXPECT_EQ( 0xC1, img.at<Vec4b>(img.rows-1, 0) [1] );
|
||||
EXPECT_EQ( 0xC0, img.at<Vec4b>(img.rows-1, 0) [2] );
|
||||
EXPECT_EQ( 0xC3, img.at<Vec4b>(img.rows-1, 0) [3] );
|
||||
EXPECT_EQ( 0xD2, img.at<Vec4b>(img.rows-1, img.cols-1)[0] );
|
||||
EXPECT_EQ( 0xD1, img.at<Vec4b>(img.rows-1, img.cols-1)[1] );
|
||||
EXPECT_EQ( 0xD0, img.at<Vec4b>(img.rows-1, img.cols-1)[2] );
|
||||
EXPECT_EQ( 0xD3, img.at<Vec4b>(img.rows-1, img.cols-1)[3] );
|
||||
break;
|
||||
|
||||
// RGB/RGBA to GRAY
|
||||
case MAKE_FLAG(CV_8UC3, CV_8UC1):
|
||||
case MAKE_FLAG(CV_8UC4, CV_8UC1):
|
||||
case MAKE_FLAG(CV_16UC3, CV_8UC1):
|
||||
case MAKE_FLAG(CV_16UC4, CV_8UC1):
|
||||
EXPECT_LE( 0xA0, img.at<uchar>(0, 0) );
|
||||
EXPECT_GE( 0xA2, img.at<uchar>(0, 0) );
|
||||
EXPECT_LE( 0xB0, img.at<uchar>(0, img.cols-1) );
|
||||
EXPECT_GE( 0xB2, img.at<uchar>(0, img.cols-1) );
|
||||
EXPECT_LE( 0xC0, img.at<uchar>(img.rows-1, 0) );
|
||||
EXPECT_GE( 0xC2, img.at<uchar>(img.rows-1, 0) );
|
||||
EXPECT_LE( 0xD0, img.at<uchar>(img.rows-1, img.cols-1) );
|
||||
EXPECT_GE( 0xD2, img.at<uchar>(img.rows-1, img.cols-1) );
|
||||
break;
|
||||
|
||||
// GRAY to BGR
|
||||
case MAKE_FLAG(CV_8UC1, CV_8UC3):
|
||||
case MAKE_FLAG(CV_16UC1, CV_8UC3):
|
||||
EXPECT_EQ( 0xA0, img.at<Vec3b>(0, 0) [0] );
|
||||
EXPECT_EQ( 0xB0, img.at<Vec3b>(0, img.cols-1)[0] );
|
||||
EXPECT_EQ( 0xC0, img.at<Vec3b>(img.rows-1, 0) [0] );
|
||||
EXPECT_EQ( 0xD0, img.at<Vec3b>(img.rows-1, img.cols-1)[0] );
|
||||
// R==G==B
|
||||
EXPECT_EQ( img.at<Vec3b>(0, 0) [0], img.at<Vec3b>(0, 0) [1] );
|
||||
EXPECT_EQ( img.at<Vec3b>(0, 0) [0], img.at<Vec3b>(0, 0) [2] );
|
||||
EXPECT_EQ( img.at<Vec3b>(0, img.cols-1) [0], img.at<Vec3b>(0, img.cols-1)[1] );
|
||||
EXPECT_EQ( img.at<Vec3b>(0, img.cols-1) [0], img.at<Vec3b>(0, img.cols-1)[2] );
|
||||
EXPECT_EQ( img.at<Vec3b>(img.rows-1, 0) [0], img.at<Vec3b>(img.rows-1, 0) [1] );
|
||||
EXPECT_EQ( img.at<Vec3b>(img.rows-1, 0) [0], img.at<Vec3b>(img.rows-1, 0) [2] );
|
||||
EXPECT_EQ( img.at<Vec3b>(img.rows-1, img.cols-1) [0], img.at<Vec3b>(img.rows-1, img.cols-1)[1] );
|
||||
EXPECT_EQ( img.at<Vec3b>(img.rows-1, img.cols-1) [0], img.at<Vec3b>(img.rows-1, img.cols-1)[2] );
|
||||
break;
|
||||
|
||||
// GRAY TO GRAY
|
||||
case MAKE_FLAG(CV_16UC1, CV_16UC1):
|
||||
EXPECT_EQ( 0xA090, img.at<ushort>(0, 0) );
|
||||
EXPECT_EQ( 0xB080, img.at<ushort>(0, img.cols-1) );
|
||||
EXPECT_EQ( 0xC070, img.at<ushort>(img.rows-1, 0) );
|
||||
EXPECT_EQ( 0xD060, img.at<ushort>(img.rows-1, img.cols-1) );
|
||||
break;
|
||||
|
||||
// RGB/RGBA TO BGR
|
||||
case MAKE_FLAG(CV_16UC3, CV_16UC3):
|
||||
case MAKE_FLAG(CV_16UC4, CV_16UC3):
|
||||
EXPECT_EQ( 0xA292, img.at<Vec3w>(0, 0) [0] );
|
||||
EXPECT_EQ( 0xA191, img.at<Vec3w>(0, 0) [1] );
|
||||
EXPECT_EQ( 0xA090, img.at<Vec3w>(0, 0) [2] );
|
||||
EXPECT_EQ( 0xB282, img.at<Vec3w>(0, img.cols-1)[0] );
|
||||
EXPECT_EQ( 0xB181, img.at<Vec3w>(0, img.cols-1)[1] );
|
||||
EXPECT_EQ( 0xB080, img.at<Vec3w>(0, img.cols-1)[2] );
|
||||
EXPECT_EQ( 0xC272, img.at<Vec3w>(img.rows-1, 0) [0] );
|
||||
EXPECT_EQ( 0xC171, img.at<Vec3w>(img.rows-1, 0) [1] );
|
||||
EXPECT_EQ( 0xC070, img.at<Vec3w>(img.rows-1, 0) [2] );
|
||||
EXPECT_EQ( 0xD262, img.at<Vec3w>(img.rows-1, img.cols-1)[0] );
|
||||
EXPECT_EQ( 0xD161, img.at<Vec3w>(img.rows-1, img.cols-1)[1] );
|
||||
EXPECT_EQ( 0xD060, img.at<Vec3w>(img.rows-1, img.cols-1)[2] );
|
||||
break;
|
||||
|
||||
// RGBA TO RGBA
|
||||
case MAKE_FLAG(CV_16UC4, CV_16UC4):
|
||||
EXPECT_EQ( 0xA292, img.at<Vec4w>(0, 0) [0] );
|
||||
EXPECT_EQ( 0xA191, img.at<Vec4w>(0, 0) [1] );
|
||||
EXPECT_EQ( 0xA090, img.at<Vec4w>(0, 0) [2] );
|
||||
EXPECT_EQ( 0xA393, img.at<Vec4w>(0, 0) [3] );
|
||||
EXPECT_EQ( 0xB282, img.at<Vec4w>(0, img.cols-1)[0] );
|
||||
EXPECT_EQ( 0xB181, img.at<Vec4w>(0, img.cols-1)[1] );
|
||||
EXPECT_EQ( 0xB080, img.at<Vec4w>(0, img.cols-1)[2] );
|
||||
EXPECT_EQ( 0xB383, img.at<Vec4w>(0, img.cols-1)[3] );
|
||||
EXPECT_EQ( 0xC272, img.at<Vec4w>(img.rows-1, 0) [0] );
|
||||
EXPECT_EQ( 0xC171, img.at<Vec4w>(img.rows-1, 0) [1] );
|
||||
EXPECT_EQ( 0xC070, img.at<Vec4w>(img.rows-1, 0) [2] );
|
||||
EXPECT_EQ( 0xC373, img.at<Vec4w>(img.rows-1, 0) [3] );
|
||||
EXPECT_EQ( 0xD262, img.at<Vec4w>(img.rows-1,img.cols-1) [0] );
|
||||
EXPECT_EQ( 0xD161, img.at<Vec4w>(img.rows-1,img.cols-1) [1] );
|
||||
EXPECT_EQ( 0xD060, img.at<Vec4w>(img.rows-1,img.cols-1) [2] );
|
||||
EXPECT_EQ( 0xD363, img.at<Vec4w>(img.rows-1,img.cols-1) [3] );
|
||||
break;
|
||||
|
||||
// RGB/RGBA to GRAY
|
||||
case MAKE_FLAG(CV_16UC3, CV_16UC1):
|
||||
case MAKE_FLAG(CV_16UC4, CV_16UC1):
|
||||
EXPECT_LE( 0xA090, img.at<ushort>(0, 0) );
|
||||
EXPECT_GE( 0xA292, img.at<ushort>(0, 0) );
|
||||
EXPECT_LE( 0xB080, img.at<ushort>(0, img.cols-1) );
|
||||
EXPECT_GE( 0xB282, img.at<ushort>(0, img.cols-1) );
|
||||
EXPECT_LE( 0xC070, img.at<ushort>(img.rows-1, 0) );
|
||||
EXPECT_GE( 0xC272, img.at<ushort>(img.rows-1, 0) );
|
||||
EXPECT_LE( 0xD060, img.at<ushort>(img.rows-1, img.cols-1) );
|
||||
EXPECT_GE( 0xD262, img.at<ushort>(img.rows-1, img.cols-1) );
|
||||
break;
|
||||
|
||||
// GRAY to RGB
|
||||
case MAKE_FLAG(CV_16UC1, CV_16UC3):
|
||||
EXPECT_EQ( 0xA090, img.at<Vec3w>(0, 0) [0] );
|
||||
EXPECT_EQ( 0xB080, img.at<Vec3w>(0, img.cols-1)[0] );
|
||||
EXPECT_EQ( 0xC070, img.at<Vec3w>(img.rows-1, 0) [0] );
|
||||
EXPECT_EQ( 0xD060, img.at<Vec3w>(img.rows-1, img.cols-1)[0] );
|
||||
// R==G==B
|
||||
EXPECT_EQ( img.at<Vec3w>(0, 0) [0], img.at<Vec3w>(0, 0) [1] );
|
||||
EXPECT_EQ( img.at<Vec3w>(0, 0) [0], img.at<Vec3w>(0, 0) [2] );
|
||||
EXPECT_EQ( img.at<Vec3w>(0, img.cols-1) [0], img.at<Vec3w>(0, img.cols-1)[1] );
|
||||
EXPECT_EQ( img.at<Vec3w>(0, img.cols-1) [0], img.at<Vec3w>(0, img.cols-1)[2] );
|
||||
EXPECT_EQ( img.at<Vec3w>(img.rows-1, 0) [0], img.at<Vec3w>(img.rows-1, 0) [1] );
|
||||
EXPECT_EQ( img.at<Vec3w>(img.rows-1, 0) [0], img.at<Vec3w>(img.rows-1, 0) [2] );
|
||||
EXPECT_EQ( img.at<Vec3w>(img.rows-1, img.cols-1) [0], img.at<Vec3w>(img.rows-1, img.cols-1)[1] );
|
||||
EXPECT_EQ( img.at<Vec3w>(img.rows-1, img.cols-1) [0], img.at<Vec3w>(img.rows-1, img.cols-1)[2] );
|
||||
break;
|
||||
|
||||
// No supported.
|
||||
// (1) 8bit to 16bit
|
||||
case MAKE_FLAG(CV_8UC1, CV_16UC1):
|
||||
case MAKE_FLAG(CV_8UC1, CV_16UC3):
|
||||
case MAKE_FLAG(CV_8UC1, CV_16UC4):
|
||||
case MAKE_FLAG(CV_8UC3, CV_16UC1):
|
||||
case MAKE_FLAG(CV_8UC3, CV_16UC3):
|
||||
case MAKE_FLAG(CV_8UC3, CV_16UC4):
|
||||
case MAKE_FLAG(CV_8UC4, CV_16UC1):
|
||||
case MAKE_FLAG(CV_8UC4, CV_16UC3):
|
||||
case MAKE_FLAG(CV_8UC4, CV_16UC4):
|
||||
// (2) GRAY/RGB TO RGBA
|
||||
case MAKE_FLAG(CV_8UC1, CV_8UC4):
|
||||
case MAKE_FLAG(CV_8UC3, CV_8UC4):
|
||||
case MAKE_FLAG(CV_16UC1, CV_8UC4):
|
||||
case MAKE_FLAG(CV_16UC3, CV_8UC4):
|
||||
case MAKE_FLAG(CV_16UC1, CV_16UC4):
|
||||
case MAKE_FLAG(CV_16UC3, CV_16UC4):
|
||||
default:
|
||||
FAIL() << cv::format("Unknown test pattern: from = %d ( %d, %d) to = %d ( %d, %d )",
|
||||
mat_type, (int)CV_MAT_CN(mat_type ), ( CV_MAT_DEPTH(mat_type )==CV_16U)?16:8,
|
||||
img.type(), (int)CV_MAT_CN(img.type() ), ( CV_MAT_DEPTH(img.type() )==CV_16U)?16:8);
|
||||
break;
|
||||
}
|
||||
|
||||
#undef MAKE_FLAG
|
||||
}
|
||||
|
||||
// Basic Test
|
||||
const Bufsize_and_Type Imgcodecs_Tiff_decode_Huge_list_basic[] =
|
||||
{
|
||||
make_tuple<uint64_t, tuple<string,int>,ImreadMixModes>( 1073479680ull, make_tuple<string,int>("CV_8UC1", CV_8UC1), IMREAD_MIX_COLOR ),
|
||||
make_tuple<uint64_t, tuple<string,int>,ImreadMixModes>( 2147483648ull, make_tuple<string,int>("CV_16UC4", CV_16UC4), IMREAD_MIX_COLOR ),
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Imgcodecs_Tiff, Imgcodecs_Tiff_decode_Huge,
|
||||
testing::ValuesIn( Imgcodecs_Tiff_decode_Huge_list_basic )
|
||||
);
|
||||
|
||||
// Full Test
|
||||
|
||||
/**
|
||||
* Test lists for combination of IMREAD_*.
|
||||
*/
|
||||
const ImreadMixModes all_modes_Huge_Full[] =
|
||||
{
|
||||
IMREAD_MIX_UNCHANGED,
|
||||
IMREAD_MIX_GRAYSCALE,
|
||||
IMREAD_MIX_GRAYSCALE_ANYDEPTH,
|
||||
IMREAD_MIX_GRAYSCALE_ANYCOLOR,
|
||||
IMREAD_MIX_GRAYSCALE_ANYDEPTH_ANYCOLOR,
|
||||
IMREAD_MIX_COLOR,
|
||||
IMREAD_MIX_COLOR_ANYDEPTH,
|
||||
IMREAD_MIX_COLOR_ANYCOLOR,
|
||||
IMREAD_MIX_COLOR_ANYDEPTH_ANYCOLOR,
|
||||
};
|
||||
|
||||
const uint64_t huge_buffer_sizes_decode_Full[] =
|
||||
{
|
||||
1048576ull, // 1 * 1024 * 1024
|
||||
1073479680ull, // 1024 * 1024 * 1024 - 32768 * 4 * 2
|
||||
1073741824ull, // 1024 * 1024 * 1024
|
||||
2147483648ull, // 2048 * 1024 * 1024
|
||||
};
|
||||
|
||||
const tuple<string, int> mat_types_Full[] =
|
||||
{
|
||||
make_tuple<string, int>("CV_8UC1", CV_8UC1), // 8bit GRAY
|
||||
make_tuple<string, int>("CV_8UC3", CV_8UC3), // 24bit RGB
|
||||
make_tuple<string, int>("CV_8UC4", CV_8UC4), // 32bit RGBA
|
||||
make_tuple<string, int>("CV_16UC1", CV_16UC1), // 16bit GRAY
|
||||
make_tuple<string, int>("CV_16UC3", CV_16UC3), // 48bit RGB
|
||||
make_tuple<string, int>("CV_16UC4", CV_16UC4), // 64bit RGBA
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DISABLED_Imgcodecs_Tiff_Full, Imgcodecs_Tiff_decode_Huge,
|
||||
testing::Combine(
|
||||
testing::ValuesIn(huge_buffer_sizes_decode_Full),
|
||||
testing::ValuesIn(mat_types_Full),
|
||||
testing::ValuesIn(all_modes_Huge_Full)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
TEST(Imgcodecs_Tiff, write_read_16bit_big_little_endian)
|
||||
{
|
||||
// see issue #2601 "16-bit Grayscale TIFF Load Failures Due to Buffer Underflow and Endianness"
|
||||
@@ -359,6 +787,16 @@ TEST(Imgcodecs_Tiff, read_palette_color_image)
|
||||
ASSERT_EQ(CV_8UC3, img.type());
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_Tiff, read_4_bit_palette_color_image)
|
||||
{
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
const string filenameInput = root + "readwrite/4-bit_palette_color.tif";
|
||||
|
||||
const Mat img = cv::imread(filenameInput, IMREAD_UNCHANGED);
|
||||
ASSERT_FALSE(img.empty());
|
||||
ASSERT_EQ(CV_8UC3, img.type());
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_Tiff, readWrite_predictor)
|
||||
{
|
||||
/* see issue #21871
|
||||
@@ -432,6 +870,43 @@ TEST_P(Imgcodecs_Tiff_Modes, decode_multipage)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(Imgcodecs_Tiff_Modes, decode_multipage_use_memory_buffer)
|
||||
{
|
||||
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]);
|
||||
vector<Mat> pages;
|
||||
|
||||
FILE* fp = fopen(filename.c_str(), "rb");
|
||||
ASSERT_TRUE(fp != NULL);
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long pos = ftell(fp);
|
||||
|
||||
std::vector<uchar> buf;
|
||||
buf.resize((size_t)pos);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
buf.resize(fread(&buf[0], 1, buf.size(), fp));
|
||||
fclose(fp);
|
||||
|
||||
bool res = imdecodemulti(buf, mode, pages);
|
||||
ASSERT_TRUE(res == true);
|
||||
ASSERT_EQ(page_count, pages.size());
|
||||
for (size_t i = 0; i < page_count; i++)
|
||||
{
|
||||
const Mat page = imread(root + page_files[i], mode);
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), page, pages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const int all_modes[] =
|
||||
{
|
||||
IMREAD_UNCHANGED,
|
||||
|
||||
Reference in New Issue
Block a user