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:
Suleyman TURKMEN 2025-03-05 10:42:43 +03:00 committed by GitHub
parent 97abffbdac
commit dbd4e4549d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 155 additions and 72 deletions

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;
}; };
} }

View File

@ -535,9 +535,12 @@ bool SPngEncoder::write(const Mat &img, const std::vector<int> &params)
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> &params)
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> &params)
{ {
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> &params)
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;

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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)