mirror of
https://github.com/opencv/opencv.git
synced 2025-07-20 19:17:36 +08:00
Merge pull request #26849 from sturkmen72:apng-writeanimation
APNG encoding optimization #26849 related #26840 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
parent
97abffbdac
commit
dbd4e4549d
@ -96,6 +96,7 @@ enum ImwriteFlags {
|
|||||||
IMWRITE_PNG_COMPRESSION = 16, //!< For PNG, it can be the compression level from 0 to 9. A higher value means a smaller size and longer compression time. If specified, strategy is changed to IMWRITE_PNG_STRATEGY_DEFAULT (Z_DEFAULT_STRATEGY). Default value is 1 (best speed setting).
|
IMWRITE_PNG_COMPRESSION = 16, //!< For PNG, it can be the compression level from 0 to 9. A higher value means a smaller size and longer compression time. If specified, strategy is changed to IMWRITE_PNG_STRATEGY_DEFAULT (Z_DEFAULT_STRATEGY). Default value is 1 (best speed setting).
|
||||||
IMWRITE_PNG_STRATEGY = 17, //!< One of cv::ImwritePNGFlags, default is IMWRITE_PNG_STRATEGY_RLE.
|
IMWRITE_PNG_STRATEGY = 17, //!< One of cv::ImwritePNGFlags, default is IMWRITE_PNG_STRATEGY_RLE.
|
||||||
IMWRITE_PNG_BILEVEL = 18, //!< Binary level PNG, 0 or 1, default is 0.
|
IMWRITE_PNG_BILEVEL = 18, //!< Binary level PNG, 0 or 1, default is 0.
|
||||||
|
IMWRITE_PNG_FILTER = 19, //!< One of cv::ImwritePNGFilterFlags, default is IMWRITE_PNG_FILTER_SUB.
|
||||||
IMWRITE_PXM_BINARY = 32, //!< For PPM, PGM, or PBM, it can be a binary format flag, 0 or 1. Default value is 1.
|
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_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_COMPRESSION = (3 << 4) + 1 /* 49 */, //!< override EXR compression type (ZIP_COMPRESSION = 3 is default)
|
||||||
@ -211,6 +212,17 @@ enum ImwritePNGFlags {
|
|||||||
IMWRITE_PNG_STRATEGY_FIXED = 4 //!< Using this value prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications.
|
IMWRITE_PNG_STRATEGY_FIXED = 4 //!< Using this value prevents the use of dynamic Huffman codes, allowing for a simpler decoder for special applications.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! Imwrite PNG specific values for IMWRITE_PNG_FILTER parameter key
|
||||||
|
enum ImwritePNGFilterFlags {
|
||||||
|
IMWRITE_PNG_FILTER_NONE = 8, //!< Applies no filter to the PNG image (useful when you want to save the raw pixel data without any compression filter).
|
||||||
|
IMWRITE_PNG_FILTER_SUB = 16, //!< Applies the "sub" filter, which calculates the difference between the current byte and the previous byte in the row.
|
||||||
|
IMWRITE_PNG_FILTER_UP = 32, //!< applies the "up" filter, which calculates the difference between the current byte and the corresponding byte directly above it.
|
||||||
|
IMWRITE_PNG_FILTER_AVG = 64, //!< applies the "average" filter, which calculates the average of the byte to the left and the byte above.
|
||||||
|
IMWRITE_PNG_FILTER_PAETH = 128, //!< applies the "Paeth" filter, a more complex filter that predicts the next pixel value based on neighboring pixels.
|
||||||
|
IMWRITE_PNG_FAST_FILTERS = (IMWRITE_PNG_FILTER_NONE | IMWRITE_PNG_FILTER_SUB | IMWRITE_PNG_FILTER_UP), //!< This is a combination of IMWRITE_PNG_FILTER_NONE, IMWRITE_PNG_FILTER_SUB, and IMWRITE_PNG_FILTER_UP, typically used for faster compression.
|
||||||
|
IMWRITE_PNG_ALL_FILTERS = (IMWRITE_PNG_FAST_FILTERS | IMWRITE_PNG_FILTER_AVG | IMWRITE_PNG_FILTER_PAETH) //!< This combines all available filters (NONE, SUB, UP, AVG, and PAETH), which will attempt to apply all of them for the best possible compression.
|
||||||
|
};
|
||||||
|
|
||||||
//! Imwrite PAM specific tupletype flags used to define the 'TUPLETYPE' field of a PAM file.
|
//! Imwrite PAM specific tupletype flags used to define the 'TUPLETYPE' field of a PAM file.
|
||||||
enum ImwritePAMFlags {
|
enum ImwritePAMFlags {
|
||||||
IMWRITE_PAM_FORMAT_NULL = 0,
|
IMWRITE_PAM_FORMAT_NULL = 0,
|
||||||
|
@ -101,7 +101,7 @@ PERF_TEST_P(Encode, bgr, testing::ValuesIn(exts))
|
|||||||
|
|
||||||
TEST_CYCLE() imencode(GetParam(), src, buf);
|
TEST_CYCLE() imencode(GetParam(), src, buf);
|
||||||
|
|
||||||
std::cout << "Encoded buffer size: " << buf.size()
|
std::cout << " Encoded buffer size: " << buf.size()
|
||||||
<< " bytes, Compression ratio: " << std::fixed << std::setprecision(2)
|
<< " bytes, Compression ratio: " << std::fixed << std::setprecision(2)
|
||||||
<< (static_cast<double>(buf.size()) / (src.total() * src.channels())) * 100.0 << "%" << std::endl;
|
<< (static_cast<double>(buf.size()) / (src.total() * src.channels())) * 100.0 << "%" << std::endl;
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ PERF_TEST_P(Encode, multi, testing::ValuesIn(exts_multi))
|
|||||||
|
|
||||||
TEST_CYCLE() imencode(GetParam(), vec, buf);
|
TEST_CYCLE() imencode(GetParam(), vec, buf);
|
||||||
|
|
||||||
std::cout << "Encoded buffer size: " << buf.size()
|
std::cout << " Encoded buffer size: " << buf.size()
|
||||||
<< " bytes, Compression ratio: " << std::fixed << std::setprecision(2)
|
<< " bytes, Compression ratio: " << std::fixed << std::setprecision(2)
|
||||||
<< (static_cast<double>(buf.size()) / (vec[0].total() * vec[0].channels())) * 100.0 << "%" << std::endl;
|
<< (static_cast<double>(buf.size()) / (vec[0].total() * vec[0].channels())) * 100.0 << "%" << std::endl;
|
||||||
|
|
||||||
|
@ -11,7 +11,10 @@ namespace opencv_test
|
|||||||
|
|
||||||
using namespace perf;
|
using namespace perf;
|
||||||
|
|
||||||
typedef perf::TestBaseWithParam<std::string> PNG;
|
CV_ENUM(PNGStrategy, IMWRITE_PNG_STRATEGY_DEFAULT, IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY, IMWRITE_PNG_STRATEGY_RLE, IMWRITE_PNG_STRATEGY_FIXED);
|
||||||
|
CV_ENUM(PNGFilters, IMWRITE_PNG_FILTER_NONE, IMWRITE_PNG_FILTER_SUB, IMWRITE_PNG_FILTER_UP, IMWRITE_PNG_FILTER_AVG, IMWRITE_PNG_FILTER_PAETH, IMWRITE_PNG_FAST_FILTERS, IMWRITE_PNG_ALL_FILTERS);
|
||||||
|
|
||||||
|
typedef perf::TestBaseWithParam<testing::tuple<PNGStrategy, PNGFilters, int>> PNG;
|
||||||
|
|
||||||
PERF_TEST(PNG, decode)
|
PERF_TEST(PNG, decode)
|
||||||
{
|
{
|
||||||
@ -58,6 +61,30 @@ PERF_TEST(PNG, encode)
|
|||||||
SANITY_CHECK_NOTHING();
|
SANITY_CHECK_NOTHING();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PERF_TEST_P(PNG, params,
|
||||||
|
testing::Combine(
|
||||||
|
testing::Values(IMWRITE_PNG_STRATEGY_DEFAULT, IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY, IMWRITE_PNG_STRATEGY_RLE, IMWRITE_PNG_STRATEGY_FIXED),
|
||||||
|
testing::Values(IMWRITE_PNG_FILTER_NONE, IMWRITE_PNG_FILTER_SUB, IMWRITE_PNG_FILTER_UP, IMWRITE_PNG_FILTER_AVG, IMWRITE_PNG_FILTER_PAETH, IMWRITE_PNG_FAST_FILTERS, IMWRITE_PNG_ALL_FILTERS),
|
||||||
|
testing::Values(1, 6)))
|
||||||
|
{
|
||||||
|
String filename = getDataPath("perf/1920x1080.png");
|
||||||
|
const int strategy = get<0>(GetParam());
|
||||||
|
const int filter = get<1>(GetParam());
|
||||||
|
const int level = get<2>(GetParam());
|
||||||
|
|
||||||
|
Mat src = imread(filename);
|
||||||
|
EXPECT_FALSE(src.empty()) << "Cannot open test image perf/1920x1080.png";
|
||||||
|
vector<uchar> buf;
|
||||||
|
|
||||||
|
TEST_CYCLE() imencode(".png", src, buf, { IMWRITE_PNG_COMPRESSION, level, IMWRITE_PNG_STRATEGY, strategy, IMWRITE_PNG_FILTER, filter });
|
||||||
|
|
||||||
|
std::cout << " Encoded buffer size: " << buf.size()
|
||||||
|
<< " bytes, Compression ratio: " << std::fixed << std::setprecision(2)
|
||||||
|
<< (static_cast<double>(buf.size()) / (src.total() * src.channels())) * 100.0 << "%" << std::endl;
|
||||||
|
|
||||||
|
SANITY_CHECK_NOTHING();
|
||||||
|
}
|
||||||
|
|
||||||
#endif // HAVE_PNG
|
#endif // HAVE_PNG
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -814,6 +814,10 @@ PngEncoder::PngEncoder()
|
|||||||
next_seq_num = 0;
|
next_seq_num = 0;
|
||||||
trnssize = 0;
|
trnssize = 0;
|
||||||
palsize = 0;
|
palsize = 0;
|
||||||
|
m_compression_level = Z_BEST_SPEED;
|
||||||
|
m_compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy
|
||||||
|
m_filter = IMWRITE_PNG_FILTER_SUB; // Default filter
|
||||||
|
m_isBilevel = false;
|
||||||
memset(palette, 0, sizeof(palette));
|
memset(palette, 0, sizeof(palette));
|
||||||
memset(trns, 0, sizeof(trns));
|
memset(trns, 0, sizeof(trns));
|
||||||
memset(op, 0, sizeof(op));
|
memset(op, 0, sizeof(op));
|
||||||
@ -870,6 +874,9 @@ bool PngEncoder::write( const Mat& img, const std::vector<int>& params )
|
|||||||
{
|
{
|
||||||
if( setjmp( png_jmpbuf ( png_ptr ) ) == 0 )
|
if( setjmp( png_jmpbuf ( png_ptr ) ) == 0 )
|
||||||
{
|
{
|
||||||
|
bool set_compression_level = false;
|
||||||
|
bool set_filter = false;
|
||||||
|
|
||||||
if( m_buf )
|
if( m_buf )
|
||||||
{
|
{
|
||||||
png_set_write_fn(png_ptr, this,
|
png_set_write_fn(png_ptr, this,
|
||||||
@ -882,45 +889,39 @@ bool PngEncoder::write( const Mat& img, const std::vector<int>& params )
|
|||||||
png_init_io( png_ptr, (png_FILE_p)f );
|
png_init_io( png_ptr, (png_FILE_p)f );
|
||||||
}
|
}
|
||||||
|
|
||||||
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 )
|
for( size_t i = 0; i < params.size(); i += 2 )
|
||||||
{
|
{
|
||||||
if( params[i] == IMWRITE_PNG_COMPRESSION )
|
if( params[i] == IMWRITE_PNG_COMPRESSION )
|
||||||
{
|
{
|
||||||
compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy
|
m_compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy
|
||||||
compression_level = params[i+1];
|
m_compression_level = params[i+1];
|
||||||
compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION);
|
m_compression_level = MIN(MAX(m_compression_level, 0), Z_BEST_COMPRESSION);
|
||||||
|
set_compression_level = true;
|
||||||
}
|
}
|
||||||
if( params[i] == IMWRITE_PNG_STRATEGY )
|
if( params[i] == IMWRITE_PNG_STRATEGY )
|
||||||
{
|
{
|
||||||
compression_strategy = params[i+1];
|
m_compression_strategy = params[i+1];
|
||||||
compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED);
|
m_compression_strategy = MIN(MAX(m_compression_strategy, 0), Z_FIXED);
|
||||||
}
|
}
|
||||||
if( params[i] == IMWRITE_PNG_BILEVEL )
|
if( params[i] == IMWRITE_PNG_BILEVEL )
|
||||||
{
|
{
|
||||||
isBilevel = params[i+1] != 0;
|
m_isBilevel = params[i+1] != 0;
|
||||||
|
}
|
||||||
|
if( params[i] == IMWRITE_PNG_FILTER )
|
||||||
|
{
|
||||||
|
m_filter = params[i+1];
|
||||||
|
set_filter = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( m_buf || f )
|
if( m_buf || f )
|
||||||
{
|
{
|
||||||
if( compression_level >= 0 )
|
if (!set_compression_level || set_filter)
|
||||||
{
|
png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, m_filter);
|
||||||
png_set_compression_level( png_ptr, compression_level );
|
png_set_compression_level(png_ptr, m_compression_level);
|
||||||
}
|
png_set_compression_strategy(png_ptr, m_compression_strategy);
|
||||||
else
|
|
||||||
{
|
|
||||||
// tune parameters for speed
|
|
||||||
// (see http://wiki.linuxquestions.org/wiki/Libpng)
|
|
||||||
png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB);
|
|
||||||
png_set_compression_level(png_ptr, Z_BEST_SPEED);
|
|
||||||
}
|
|
||||||
png_set_compression_strategy(png_ptr, compression_strategy);
|
|
||||||
|
|
||||||
png_set_IHDR( png_ptr, info_ptr, width, height, depth == CV_8U ? isBilevel?1:8 : 16,
|
png_set_IHDR( png_ptr, info_ptr, width, height, depth == CV_8U ? m_isBilevel ? 1 : 8 : 16,
|
||||||
channels == 1 ? PNG_COLOR_TYPE_GRAY :
|
channels == 1 ? PNG_COLOR_TYPE_GRAY :
|
||||||
channels == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA,
|
channels == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA,
|
||||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
||||||
@ -928,7 +929,7 @@ bool PngEncoder::write( const Mat& img, const std::vector<int>& params )
|
|||||||
|
|
||||||
png_write_info( png_ptr, info_ptr );
|
png_write_info( png_ptr, info_ptr );
|
||||||
|
|
||||||
if (isBilevel)
|
if (m_isBilevel)
|
||||||
png_set_packing(png_ptr);
|
png_set_packing(png_ptr);
|
||||||
|
|
||||||
png_set_bgr( png_ptr );
|
png_set_bgr( png_ptr );
|
||||||
@ -1399,7 +1400,7 @@ void PngEncoder::deflateRectFin(unsigned char* zbuf, uint32_t* zsize, int bpp, i
|
|||||||
fin_zstream.zalloc = Z_NULL;
|
fin_zstream.zalloc = Z_NULL;
|
||||||
fin_zstream.zfree = Z_NULL;
|
fin_zstream.zfree = Z_NULL;
|
||||||
fin_zstream.opaque = Z_NULL;
|
fin_zstream.opaque = Z_NULL;
|
||||||
deflateInit2(&fin_zstream, Z_BEST_COMPRESSION, 8, 15, 8, op[n].filters ? Z_FILTERED : Z_DEFAULT_STRATEGY);
|
deflateInit2(&fin_zstream, m_compression_level, 8, 15, 8, op[n].filters ? Z_FILTERED : m_compression_strategy);
|
||||||
|
|
||||||
fin_zstream.next_out = zbuf;
|
fin_zstream.next_out = zbuf;
|
||||||
fin_zstream.avail_out = zbuf_size;
|
fin_zstream.avail_out = zbuf_size;
|
||||||
@ -1415,30 +1416,27 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
|
|||||||
int frame_type = animation.frames[0].type();
|
int frame_type = animation.frames[0].type();
|
||||||
int frame_depth = animation.frames[0].depth();
|
int frame_depth = animation.frames[0].depth();
|
||||||
CV_CheckType(frame_type, frame_depth == CV_8U || frame_depth == CV_16U, "APNG decoder supports only 8 or 16 bit unsigned images");
|
CV_CheckType(frame_type, frame_depth == CV_8U || frame_depth == CV_16U, "APNG decoder supports only 8 or 16 bit unsigned images");
|
||||||
int compression_level = 6;
|
|
||||||
int compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy
|
|
||||||
bool isBilevel = false;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < params.size(); i += 2)
|
for (size_t i = 0; i < params.size(); i += 2)
|
||||||
{
|
{
|
||||||
if (params[i] == IMWRITE_PNG_COMPRESSION)
|
if (params[i] == IMWRITE_PNG_COMPRESSION)
|
||||||
{
|
{
|
||||||
compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy
|
m_compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy
|
||||||
compression_level = params[i + 1];
|
m_compression_level = params[i + 1];
|
||||||
compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION);
|
m_compression_level = MIN(MAX(m_compression_level, 0), Z_BEST_COMPRESSION);
|
||||||
}
|
}
|
||||||
if (params[i] == IMWRITE_PNG_STRATEGY)
|
if (params[i] == IMWRITE_PNG_STRATEGY)
|
||||||
{
|
{
|
||||||
compression_strategy = params[i + 1];
|
m_compression_strategy = params[i + 1];
|
||||||
compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED);
|
m_compression_strategy = MIN(MAX(m_compression_strategy, 0), Z_FIXED);
|
||||||
}
|
}
|
||||||
if (params[i] == IMWRITE_PNG_BILEVEL)
|
if (params[i] == IMWRITE_PNG_BILEVEL)
|
||||||
{
|
{
|
||||||
isBilevel = params[i + 1] != 0;
|
m_isBilevel = params[i + 1] != 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBilevel)
|
if (m_isBilevel)
|
||||||
CV_LOG_WARNING(NULL, "IMWRITE_PNG_BILEVEL parameter is not supported yet.");
|
CV_LOG_WARNING(NULL, "IMWRITE_PNG_BILEVEL parameter is not supported yet.");
|
||||||
uint32_t first =0;
|
uint32_t first =0;
|
||||||
uint32_t loops= animation.loop_count;
|
uint32_t loops= animation.loop_count;
|
||||||
@ -1556,13 +1554,13 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
|
|||||||
op_zstream1.zalloc = Z_NULL;
|
op_zstream1.zalloc = Z_NULL;
|
||||||
op_zstream1.zfree = Z_NULL;
|
op_zstream1.zfree = Z_NULL;
|
||||||
op_zstream1.opaque = Z_NULL;
|
op_zstream1.opaque = Z_NULL;
|
||||||
deflateInit2(&op_zstream1, compression_level, 8, 15, 8, compression_strategy);
|
deflateInit2(&op_zstream1, m_compression_level, 8, 15, 8, m_compression_strategy);
|
||||||
|
|
||||||
op_zstream2.data_type = Z_BINARY;
|
op_zstream2.data_type = Z_BINARY;
|
||||||
op_zstream2.zalloc = Z_NULL;
|
op_zstream2.zalloc = Z_NULL;
|
||||||
op_zstream2.zfree = Z_NULL;
|
op_zstream2.zfree = Z_NULL;
|
||||||
op_zstream2.opaque = Z_NULL;
|
op_zstream2.opaque = Z_NULL;
|
||||||
deflateInit2(&op_zstream2, compression_level, 8, 15, 8, Z_FILTERED);
|
deflateInit2(&op_zstream2, m_compression_level, 8, 15, 8, Z_FILTERED);
|
||||||
|
|
||||||
idat_size = (rowbytes + 1) * height;
|
idat_size = (rowbytes + 1) * height;
|
||||||
zbuf_size = idat_size + ((idat_size + 7) >> 3) + ((idat_size + 63) >> 6) + 11;
|
zbuf_size = idat_size + ((idat_size + 7) >> 3) + ((idat_size + 63) >> 6) + 11;
|
||||||
|
@ -232,6 +232,10 @@ private:
|
|||||||
unsigned char trns[256];
|
unsigned char trns[256];
|
||||||
uint32_t palsize, trnssize;
|
uint32_t palsize, trnssize;
|
||||||
uint32_t next_seq_num;
|
uint32_t next_seq_num;
|
||||||
|
int m_compression_level;
|
||||||
|
int m_compression_strategy;
|
||||||
|
int m_filter;
|
||||||
|
bool m_isBilevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -535,9 +535,12 @@ bool SPngEncoder::write(const Mat &img, const std::vector<int> ¶ms)
|
|||||||
struct spng_ihdr ihdr = {};
|
struct spng_ihdr ihdr = {};
|
||||||
ihdr.height = height;
|
ihdr.height = height;
|
||||||
ihdr.width = width;
|
ihdr.width = width;
|
||||||
int compression_level = -1; // Invalid value to allow setting 0-9 as valid
|
int compression_level = Z_BEST_SPEED;
|
||||||
int compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy
|
int compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy
|
||||||
|
int filter = IMWRITE_PNG_FILTER_SUB; // Default filter
|
||||||
bool isBilevel = false;
|
bool isBilevel = false;
|
||||||
|
bool set_compression_level = false;
|
||||||
|
bool set_filter = false;
|
||||||
|
|
||||||
for (size_t i = 0; i < params.size(); i += 2)
|
for (size_t i = 0; i < params.size(); i += 2)
|
||||||
{
|
{
|
||||||
@ -546,6 +549,7 @@ bool SPngEncoder::write(const Mat &img, const std::vector<int> ¶ms)
|
|||||||
compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy
|
compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy
|
||||||
compression_level = params[i + 1];
|
compression_level = params[i + 1];
|
||||||
compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION);
|
compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION);
|
||||||
|
set_compression_level = true;
|
||||||
}
|
}
|
||||||
if (params[i] == IMWRITE_PNG_STRATEGY)
|
if (params[i] == IMWRITE_PNG_STRATEGY)
|
||||||
{
|
{
|
||||||
@ -556,6 +560,11 @@ bool SPngEncoder::write(const Mat &img, const std::vector<int> ¶ms)
|
|||||||
{
|
{
|
||||||
isBilevel = params[i + 1] != 0;
|
isBilevel = params[i + 1] != 0;
|
||||||
}
|
}
|
||||||
|
if( params[i] == IMWRITE_PNG_FILTER )
|
||||||
|
{
|
||||||
|
filter = params[i+1];
|
||||||
|
set_filter = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ihdr.bit_depth = depth == CV_8U ? isBilevel ? 1 : 8 : 16;
|
ihdr.bit_depth = depth == CV_8U ? isBilevel ? 1 : 8 : 16;
|
||||||
@ -579,15 +588,9 @@ bool SPngEncoder::write(const Mat &img, const std::vector<int> ¶ms)
|
|||||||
|
|
||||||
if (m_buf || f)
|
if (m_buf || f)
|
||||||
{
|
{
|
||||||
if (compression_level >= 0)
|
if (!set_compression_level || set_filter)
|
||||||
{
|
spng_set_option(ctx, SPNG_FILTER_CHOICE, filter);
|
||||||
spng_set_option(ctx, SPNG_IMG_COMPRESSION_LEVEL, compression_level);
|
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);
|
spng_set_option(ctx, SPNG_IMG_COMPRESSION_STRATEGY, compression_strategy);
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -3,31 +3,10 @@
|
|||||||
// of this distribution and at http://opencv.org/license.html.
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
#include "test_precomp.hpp"
|
#include "test_precomp.hpp"
|
||||||
|
#include "test_common.hpp"
|
||||||
|
|
||||||
namespace opencv_test { namespace {
|
namespace opencv_test { namespace {
|
||||||
|
|
||||||
static void readFileBytes(const std::string& fname, std::vector<unsigned char>& buf)
|
|
||||||
{
|
|
||||||
FILE * wfile = fopen(fname.c_str(), "rb");
|
|
||||||
if (wfile != NULL)
|
|
||||||
{
|
|
||||||
fseek(wfile, 0, SEEK_END);
|
|
||||||
size_t wfile_size = ftell(wfile);
|
|
||||||
fseek(wfile, 0, SEEK_SET);
|
|
||||||
|
|
||||||
buf.resize(wfile_size);
|
|
||||||
|
|
||||||
size_t data_size = fread(&buf[0], 1, wfile_size, wfile);
|
|
||||||
|
|
||||||
if(wfile)
|
|
||||||
{
|
|
||||||
fclose(wfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_EQ(data_size, wfile_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool fillFrames(Animation& animation, bool hasAlpha, int n = 14)
|
static bool fillFrames(Animation& animation, bool hasAlpha, int n = 14)
|
||||||
{
|
{
|
||||||
// Set the path to the test image directory and filename for loading.
|
// Set the path to the test image directory and filename for loading.
|
||||||
|
@ -48,4 +48,21 @@ Mat generateTestImageGrayscale()
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void readFileBytes(const std::string& fname, std::vector<unsigned char>& buf)
|
||||||
|
{
|
||||||
|
FILE * wfile = fopen(fname.c_str(), "rb");
|
||||||
|
if (wfile != NULL)
|
||||||
|
{
|
||||||
|
fseek(wfile, 0, SEEK_END);
|
||||||
|
size_t wfile_size = ftell(wfile);
|
||||||
|
fseek(wfile, 0, SEEK_SET);
|
||||||
|
|
||||||
|
buf.resize(wfile_size);
|
||||||
|
size_t data_size = fread(&buf[0], 1, wfile_size, wfile);
|
||||||
|
fclose(wfile);
|
||||||
|
|
||||||
|
EXPECT_EQ(data_size, wfile_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -9,6 +9,7 @@ namespace opencv_test {
|
|||||||
|
|
||||||
Mat generateTestImageBGR();
|
Mat generateTestImageBGR();
|
||||||
Mat generateTestImageGrayscale();
|
Mat generateTestImageGrayscale();
|
||||||
|
void readFileBytes(const std::string& fname, std::vector<unsigned char>& buf);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
// 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
|
// of this distribution and at http://opencv.org/license.html
|
||||||
#include "test_precomp.hpp"
|
#include "test_precomp.hpp"
|
||||||
|
#include "test_common.hpp"
|
||||||
|
|
||||||
namespace opencv_test { namespace {
|
namespace opencv_test { namespace {
|
||||||
|
|
||||||
@ -327,6 +328,47 @@ const string pngsuite_files_corrupted[] = {
|
|||||||
INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Png_PngSuite_Corrupted,
|
INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Png_PngSuite_Corrupted,
|
||||||
testing::ValuesIn(pngsuite_files_corrupted));
|
testing::ValuesIn(pngsuite_files_corrupted));
|
||||||
|
|
||||||
|
CV_ENUM(PNGStrategy, IMWRITE_PNG_STRATEGY_DEFAULT, IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY, IMWRITE_PNG_STRATEGY_RLE, IMWRITE_PNG_STRATEGY_FIXED);
|
||||||
|
CV_ENUM(PNGFilters, IMWRITE_PNG_FILTER_NONE, IMWRITE_PNG_FILTER_SUB, IMWRITE_PNG_FILTER_UP, IMWRITE_PNG_FILTER_AVG, IMWRITE_PNG_FILTER_PAETH, IMWRITE_PNG_FAST_FILTERS, IMWRITE_PNG_ALL_FILTERS);
|
||||||
|
|
||||||
|
typedef testing::TestWithParam<testing::tuple<string, PNGStrategy, PNGFilters, int>> Imgcodecs_Png_Encode;
|
||||||
|
|
||||||
|
TEST_P(Imgcodecs_Png_Encode, params)
|
||||||
|
{
|
||||||
|
const string root = cvtest::TS::ptr()->get_data_path();
|
||||||
|
const string filename = root + "pngsuite/" + get<0>(GetParam());
|
||||||
|
|
||||||
|
const int strategy = get<1>(GetParam());
|
||||||
|
const int filter = get<2>(GetParam());
|
||||||
|
const int compression_level = get<3>(GetParam());
|
||||||
|
|
||||||
|
std::vector<uchar> file_buf;
|
||||||
|
readFileBytes(filename, file_buf);
|
||||||
|
Mat src = imdecode(file_buf, IMREAD_UNCHANGED);
|
||||||
|
EXPECT_FALSE(src.empty()) << "Cannot decode test image " << filename;
|
||||||
|
|
||||||
|
vector<uchar> buf;
|
||||||
|
imencode(".png", src, buf, { IMWRITE_PNG_COMPRESSION, compression_level, IMWRITE_PNG_STRATEGY, strategy, IMWRITE_PNG_FILTER, filter });
|
||||||
|
EXPECT_EQ(buf.size(), file_buf.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(/**/,
|
||||||
|
Imgcodecs_Png_Encode,
|
||||||
|
testing::Values(
|
||||||
|
make_tuple("f00n0g08.png", IMWRITE_PNG_STRATEGY_DEFAULT, IMWRITE_PNG_FILTER_NONE, 6),
|
||||||
|
make_tuple("f00n2c08.png", IMWRITE_PNG_STRATEGY_DEFAULT, IMWRITE_PNG_FILTER_NONE, 6),
|
||||||
|
make_tuple("f01n0g08.png", IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_FILTER_SUB, 6),
|
||||||
|
make_tuple("f01n2c08.png", IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_FILTER_SUB, 6),
|
||||||
|
make_tuple("f02n0g08.png", IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_FILTER_UP, 6),
|
||||||
|
make_tuple("f02n2c08.png", IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_FILTER_UP, 6),
|
||||||
|
make_tuple("f03n0g08.png", IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_FILTER_AVG, 6),
|
||||||
|
make_tuple("f03n2c08.png", IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_FILTER_AVG, 6),
|
||||||
|
make_tuple("f04n0g08.png", IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_FILTER_PAETH, 6),
|
||||||
|
make_tuple("f04n2c08.png", IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_FILTER_PAETH, 6),
|
||||||
|
make_tuple("z03n2c08.png", IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_ALL_FILTERS, 3),
|
||||||
|
make_tuple("z06n2c08.png", IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_ALL_FILTERS, 6),
|
||||||
|
make_tuple("z09n2c08.png", IMWRITE_PNG_STRATEGY_FILTERED, IMWRITE_PNG_ALL_FILTERS, 9)));
|
||||||
|
|
||||||
typedef testing::TestWithParam<testing::tuple<string, int, size_t>> Imgcodecs_Png_ImwriteFlags;
|
typedef testing::TestWithParam<testing::tuple<string, int, size_t>> Imgcodecs_Png_ImwriteFlags;
|
||||||
|
|
||||||
TEST_P(Imgcodecs_Png_ImwriteFlags, compression_level)
|
TEST_P(Imgcodecs_Png_ImwriteFlags, compression_level)
|
||||||
|
Loading…
Reference in New Issue
Block a user