1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 15:53:03 +04:00

Merge pull request #27621 from Kumataro:trial27557

Add strict validation for encoding parameters #27621

Close https://github.com/opencv/opencv/issues/27557

### 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
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Kumataro
2025-08-12 22:04:45 +09:00
committed by GitHub
parent d33999cb6e
commit c5bb6a014a
22 changed files with 448 additions and 84 deletions
+16 -3
View File
@@ -319,6 +319,7 @@ AvifEncoder::AvifEncoder() {
m_support_metadata[(size_t)IMAGE_METADATA_XMP] = true;
m_support_metadata[(size_t)IMAGE_METADATA_ICCP] = true;
encoder_ = avifEncoderCreate();
m_supported_encode_key = { IMWRITE_AVIF_QUALITY, IMWRITE_AVIF_DEPTH, IMWRITE_AVIF_SPEED };
}
AvifEncoder::~AvifEncoder() {
@@ -334,9 +335,13 @@ bool AvifEncoder::writeanimation(const Animation& animation,
int bit_depth = 8;
int speed = AVIF_SPEED_FASTEST;
for (size_t i = 0; i < params.size(); i += 2) {
const int value = params[i + 1];
if (params[i] == IMWRITE_AVIF_QUALITY) {
const int quality = std::min(std::max(params[i + 1], AVIF_QUALITY_WORST),
const int quality = std::min(std::max(value, AVIF_QUALITY_WORST),
AVIF_QUALITY_BEST);
if (value != quality) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_AVIF_QUALITY must be between 0 to 100. It is fallbacked to %d", value, quality));
}
#if CV_AVIF_USE_QUALITY
encoder_->quality = quality;
#else
@@ -346,9 +351,17 @@ bool AvifEncoder::writeanimation(const Animation& animation,
AVIF_QUANTIZER_WORST_QUALITY;
#endif
} else if (params[i] == IMWRITE_AVIF_DEPTH) {
bit_depth = params[i + 1];
bit_depth = value;
if ((bit_depth != 8) && (bit_depth !=10) && (bit_depth !=12))
{
bit_depth = 8;
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_AVIF_DEPTH must be 8, 10 or 12. It is fallbacked to %d", value, bit_depth));
}
} else if (params[i] == IMWRITE_AVIF_SPEED) {
speed = params[i + 1];
speed = std::min(std::max(value,0),10);
if (value != speed) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_AVIF_SPEED must be between 0 to 10. It is fallbacked to %d", value, speed));
}
}
}
+8
View File
@@ -155,6 +155,14 @@ bool BaseImageEncoder::isFormatSupported( int depth ) const
return depth == CV_8U;
}
bool BaseImageEncoder::isValidEncodeKey(const int key) const
{
auto first = m_supported_encode_key.begin();
auto last = m_supported_encode_key.end();
return (std::find(first, last, key) != last);
}
String BaseImageEncoder::getDescription() const
{
return m_description;
+8
View File
@@ -224,6 +224,13 @@ public:
*/
virtual bool isFormatSupported(int depth) const;
/**
* @brief Validates if the encoder supports a specific key.
* @param key The key of the encode parameter.
* @return true if key is supported for this encoder, false otherwise.
*/
virtual bool isValidEncodeKey(const int key) const;
/**
* @brief Set the destination for encoding as a file.
* @param filename The name of the file to which the image will be written.
@@ -290,6 +297,7 @@ protected:
std::vector<uchar>* m_buf; ///< Pointer to the buffer for encoded data if using memory-based destination.
bool m_buf_supported; ///< Flag indicating whether buffer-based encoding is supported.
String m_last_error; ///< Stores the last error message encountered during encoding.
std::vector<int> m_supported_encode_key; ///< Supported encode key list
};
}
+15 -5
View File
@@ -741,6 +741,7 @@ ImageDecoder ExrDecoder::newDecoder() const
ExrEncoder::ExrEncoder()
{
m_description = "OpenEXR Image files (*.exr)";
m_supported_encode_key = {IMWRITE_EXR_TYPE, IMWRITE_EXR_COMPRESSION, IMWRITE_EXR_DWA_COMPRESSION_LEVEL};
}
@@ -767,9 +768,10 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& params )
for( size_t i = 0; i < params.size(); i += 2 )
{
const int value = params[i+1];
if( params[i] == IMWRITE_EXR_TYPE )
{
switch( params[i+1] )
switch( value )
{
case IMWRITE_EXR_TYPE_HALF:
type = HALF;
@@ -778,12 +780,14 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& params )
type = FLOAT;
break;
default:
CV_Error(Error::StsBadArg, "IMWRITE_EXR_TYPE is invalid or not supported");
type = FLOAT;
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_EXR_TYPE must be one of ImwriteEXRTypeFlags. It is fallbacked to IMWRITE_EXR_TYPE_FLOAT", value));
break;
}
}
if ( params[i] == IMWRITE_EXR_COMPRESSION )
{
switch ( params[i + 1] )
switch ( value )
{
case IMWRITE_EXR_COMPRESSION_NO:
header.compression() = NO_COMPRESSION;
@@ -821,7 +825,9 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& params )
break;
#endif
default:
CV_Error(Error::StsBadArg, "IMWRITE_EXR_COMPRESSION is invalid or not supported");
header.compression() = ZIP_COMPRESSION;
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_EXR_COMPRESSION must be one of ImwriteEXRCompressionFlags. It is fallbacked to IMWRITE_EXR_COMPRESSION_ZIP", value));
break;
}
}
if (params[i] == IMWRITE_EXR_DWA_COMPRESSION_LEVEL)
@@ -831,7 +837,11 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& params )
#elif OPENEXR_VERSION_MAJOR < 3
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)");
#else
header.dwaCompressionLevel() = params[i + 1];
// See https://github.com/AcademySoftwareFoundation/openexr/blob/v3.3.5/src/lib/OpenEXR/ImfDwaCompressor.cpp#L85
header.dwaCompressionLevel() = std::max(params[i + 1], 0);
if(value < 0) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_EXR_DWA_COMPRESSION_LEVEL must be 0 or more. It is fallbacked to 0", value));
}
#endif
}
}
+20 -5
View File
@@ -554,6 +554,8 @@ GifEncoder::GifEncoder() {
colorNum = 256; // the number of colors in the color table, default 256
dithering = 0; // the level dithering, default 0
globalColorTableSize = 256, localColorTableSize = 0;
m_supported_encode_key = {IMWRITE_GIF_QUALITY, IMWRITE_GIF_DITHER, IMWRITE_GIF_TRANSPARENCY, IMWRITE_GIF_COLORTABLE};
}
GifEncoder::~GifEncoder() {
@@ -576,6 +578,7 @@ bool GifEncoder::writeanimation(const Animation& animation, const std::vector<in
// confirm the params
for (size_t i = 0; i < params.size(); i += 2) {
const int value = params[i+1];
switch (params[i]) {
case IMWRITE_GIF_LOOP:
CV_LOG_WARNING(NULL, "IMWRITE_GIF_LOOP is not functional since 4.12.0. Replaced by cv::Animation::loop_count.");
@@ -584,17 +587,26 @@ bool GifEncoder::writeanimation(const Animation& animation, const std::vector<in
CV_LOG_WARNING(NULL, "IMWRITE_GIF_SPEED is not functional since 4.12.0. Replaced by cv::Animation::durations.");
break;
case IMWRITE_GIF_DITHER:
dithering = std::min(std::max(params[i + 1], -1), 3);
dithering = std::min(std::max(value, -1), 3);
if(value != dithering) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_GIF_DITHER must be between -1 to 3. It is fallbacked to %d", value, dithering));
}
fast = false;
break;
case IMWRITE_GIF_TRANSPARENCY:
criticalTransparency = (uchar)std::min(std::max(params[i + 1], 0), 255);
criticalTransparency = (uchar)std::min(std::max(value, 0), 255);
if(value != criticalTransparency) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_GIF_TRANSPARENCY must be between 0 to 255. It is fallbacked to %d", value, criticalTransparency));
}
break;
case IMWRITE_GIF_COLORTABLE:
localColorTableSize = std::min(std::max(params[i + 1], 0), 1);
localColorTableSize = std::min(std::max(value, 0), 1);
if(value != localColorTableSize) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_GIF_COLORTABLE must be 0 or 1. It is fallbacked to %d", value, localColorTableSize));
}
break;
case IMWRITE_GIF_QUALITY:
switch (params[i + 1]) {
switch (value) {
case IMWRITE_GIF_FAST_FLOYD_DITHER:
fast = true;
dithering = GRFMT_GIF_FloydSteinberg;
@@ -604,10 +616,13 @@ bool GifEncoder::writeanimation(const Animation& animation, const std::vector<in
dithering = GRFMT_GIF_None;
break;
default:
lzwMinCodeSize = std::min(std::max(params[i + 1], 3), 8);
lzwMinCodeSize = std::min(std::max(value, 3), 8);
colorNum = 1 << lzwMinCodeSize;
globalColorTableSize = colorNum;
fast = false;
if(value != lzwMinCodeSize) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_GIF_QUALITY must be one of ImwriteGIFCompressionFlags. It is fallbacked to %d", value, lzwMinCodeSize));
}
break;
}
break; // case IMWRITE_GIF_QUALITY
+14 -1
View File
@@ -43,6 +43,7 @@
#include "precomp.hpp"
#include "grfmt_hdr.hpp"
#include "rgbe.hpp"
#include "opencv2/core/utils/logger.hpp"
#ifdef HAVE_IMGCODEC_HDR
@@ -139,6 +140,7 @@ ImageDecoder HdrDecoder::newDecoder() const
HdrEncoder::HdrEncoder()
{
m_description = "Radiance HDR (*.hdr;*.pic)";
m_supported_encode_key = {IMWRITE_HDR_COMPRESSION};
}
HdrEncoder::~HdrEncoder()
@@ -162,10 +164,21 @@ bool HdrEncoder::write( const Mat& input_img, const std::vector<int>& params )
int compression = IMWRITE_HDR_COMPRESSION_RLE;
for (size_t i = 0; i + 1 < params.size(); i += 2)
{
const int value = params[i+1];
switch (params[i])
{
case IMWRITE_HDR_COMPRESSION:
compression = params[i + 1];
switch(value)
{
case IMWRITE_HDR_COMPRESSION_NONE:
case IMWRITE_HDR_COMPRESSION_RLE:
compression = value;
break;
default:
compression = IMWRITE_HDR_COMPRESSION_RLE;
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_HDR_COMPRESSION must be one of ImwriteHDRCompressionFlags. It is fallbacked to IMWRITE_HDR_COMPRESSION_RLE", value));
break;
}
break;
default:
break;
+33 -11
View File
@@ -616,6 +616,7 @@ JpegEncoder::JpegEncoder()
m_support_metadata[(size_t)IMAGE_METADATA_EXIF] = true;
m_support_metadata[(size_t)IMAGE_METADATA_XMP] = true;
m_support_metadata[(size_t)IMAGE_METADATA_ICCP] = true;
m_supported_encode_key = {IMWRITE_JPEG_QUALITY, IMWRITE_JPEG_PROGRESSIVE, IMWRITE_JPEG_OPTIMIZE, IMWRITE_JPEG_RST_INTERVAL, IMWRITE_JPEG_LUMA_QUALITY, IMWRITE_JPEG_CHROMA_QUALITY, IMWRITE_JPEG_SAMPLING_FACTOR};
}
@@ -724,27 +725,39 @@ bool JpegEncoder::write( const Mat& img, const std::vector<int>& params )
for( size_t i = 0; i < params.size(); i += 2 )
{
const int value = params[i+1];
if( params[i] == IMWRITE_JPEG_QUALITY )
{
quality = params[i+1];
quality = MIN(MAX(quality, 0), 100);
quality = MIN(MAX(value, 0), 100);
if(value != quality) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_QUALITY must be between 0 to 100. It is fallbacked to %d", value, quality));
}
}
if( params[i] == IMWRITE_JPEG_PROGRESSIVE )
{
progressive = params[i+1];
progressive = MIN(MAX(value,0), 1);
if(value != progressive) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_PROGRESSIVE must be 0 or 1. It is fallbacked to %d", value, progressive));
}
}
if( params[i] == IMWRITE_JPEG_OPTIMIZE )
{
optimize = params[i+1];
optimize = MIN(MAX(value,0), 1);
if(value != optimize) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_OPTIMIZE must be 0 or 1. It is fallbacked to %d", value, optimize));
}
}
if( params[i] == IMWRITE_JPEG_LUMA_QUALITY )
{
if (params[i+1] >= 0)
if (value >= 0)
{
luma_quality = MIN(MAX(params[i+1], 0), 100);
luma_quality = MIN(MAX(value, 0), 100);
if(value != luma_quality) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_LUMA_QUALITY must be between 0 to 100. It is fallbacked to %d.", value, luma_quality));
}
quality = luma_quality;
@@ -752,6 +765,8 @@ bool JpegEncoder::write( const Mat& img, const std::vector<int>& params )
{
chroma_quality = luma_quality;
}
} else {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_LUMA_QUALITY must be between 0 to 100. It is ignored.", value));
}
}
@@ -759,19 +774,26 @@ bool JpegEncoder::write( const Mat& img, const std::vector<int>& params )
{
if (params[i+1] >= 0)
{
chroma_quality = MIN(MAX(params[i+1], 0), 100);
chroma_quality = MIN(MAX(value, 0), 100);
if(value != chroma_quality) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_CHROMA_QUALITY must be between 0 to 100. It is fallbacked to %d.", value, chroma_quality));
}
} else {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_CHROMA_QUALITY must be between 0 to 100. It is ignored.", value));
}
}
if( params[i] == IMWRITE_JPEG_RST_INTERVAL )
{
rst_interval = params[i+1];
rst_interval = MIN(MAX(rst_interval, 0), 65535L);
rst_interval = MIN(MAX(value, 0), 65535L);
if(value != rst_interval) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_RST_INTERVAL must be between 0 to 65535. It is fallbacked to %d.", value, rst_interval));
}
}
if( params[i] == IMWRITE_JPEG_SAMPLING_FACTOR )
{
sampling_factor = static_cast<uint32_t>(params[i+1]);
sampling_factor = static_cast<uint32_t>(value);
switch ( sampling_factor )
{
@@ -784,7 +806,7 @@ bool JpegEncoder::write( const Mat& img, const std::vector<int>& params )
break;
default:
CV_LOG_WARNING(NULL, cv::format("Unknown value for IMWRITE_JPEG_SAMPLING_FACTOR: 0x%06x", sampling_factor ) );
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG_SAMPLING_FACTOR must be one of ImwriteJPEGSamplingFactorParams. It is fallbacked to IMWRITE_JPEG_SAMPLING_FACTOR_420.", value));
sampling_factor = 0;
break;
}
+9 -1
View File
@@ -493,6 +493,7 @@ bool Jpeg2KDecoder::readComponent16u( unsigned short *data, void *_buffer,
Jpeg2KEncoder::Jpeg2KEncoder()
{
m_description = "JPEG-2000 files (*.jp2)";
m_supported_encode_key = {IMWRITE_JPEG2000_COMPRESSION_X1000};
}
@@ -526,10 +527,17 @@ bool Jpeg2KEncoder::write( const Mat& _img, const std::vector<int>& params )
double target_compression_rate = 1.0;
for( size_t i = 0; i < params.size(); i += 2 )
{
const int value = params[i+1];
switch(params[i])
{
case cv::IMWRITE_JPEG2000_COMPRESSION_X1000:
target_compression_rate = std::min(std::max(params[i+1], 0), 1000) / 1000.0;
{
const int compression = std::min(std::max(value, 0), 1000);
target_compression_rate = static_cast<double>(compression) / 1000.0;
if(value != compression){
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG2000_COMPRESSION_X1000 must be between 0 to 1000. It is fallbacked to %d", value, compression));
}
}
break;
}
}
@@ -330,14 +330,20 @@ opj_cparameters setupEncoderParameters(const std::vector<int>& params)
bool rate_is_specified = false;
for (size_t i = 0; i < params.size(); i += 2)
{
const int value = params[i + 1];
switch (params[i])
{
case cv::IMWRITE_JPEG2000_COMPRESSION_X1000:
parameters.tcp_rates[0] = 1000.f / std::min(std::max(params[i + 1], 1), 1000);
rate_is_specified = true;
{
const int compression = std::min(std::max(value, 1), 1000);
parameters.tcp_rates[0] = 1000.f / static_cast<float>(compression);
if(value != compression) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEG2000_COMPRESSION_X1000 must be between 1 to 1000. It is fallbacked to 1", value));
}
rate_is_specified = true;
}
break;
default:
CV_LOG_WARNING(NULL, "OpenJPEG2000(encoder): skip unsupported parameter: " << params[i]);
break;
}
}
@@ -685,6 +691,7 @@ ImageDecoder Jpeg2KJ2KOpjDecoder::newDecoder() const
Jpeg2KOpjEncoder::Jpeg2KOpjEncoder()
{
m_description = "JPEG-2000 files (*.jp2)";
m_supported_encode_key = {IMWRITE_JPEG2000_COMPRESSION_X1000};
}
ImageEncoder Jpeg2KOpjEncoder::newEncoder() const
+10 -4
View File
@@ -473,6 +473,7 @@ JpegXLEncoder::JpegXLEncoder()
{
m_description = "JPEG XL files (*.jxl)";
m_buf_supported = true;
m_supported_encode_key = {IMWRITE_JPEGXL_QUALITY, IMWRITE_JPEGXL_EFFORT, IMWRITE_JPEGXL_DISTANCE, IMWRITE_JPEGXL_DECODING_SPEED};
}
JpegXLEncoder::~JpegXLEncoder()
@@ -522,11 +523,14 @@ bool JpegXLEncoder::write(const Mat& img, const std::vector<int>& params)
float distance = -1.0; // Negative means not set
for( size_t i = 0; i < params.size(); i += 2 )
{
const int value = params[i+1];
if( params[i] == IMWRITE_JPEGXL_QUALITY )
{
#if JPEGXL_MAJOR_VERSION > 0 || JPEGXL_MINOR_VERSION >= 10
int quality = params[i+1];
quality = MIN(MAX(quality, 0), 100);
const int quality = MIN(MAX(value, 0), 100);
if(value != quality) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEGXL_QUALITY must be between 0 to 100, It is fallbacked to %d", value, quality));
}
distance = JxlEncoderDistanceFromQuality(static_cast<float>(quality));
#else
CV_LOG_ONCE_WARNING(NULL, "Quality parameter is supported with libjxl v0.10.0 or later");
@@ -534,8 +538,10 @@ bool JpegXLEncoder::write(const Mat& img, const std::vector<int>& params)
}
if( params[i] == IMWRITE_JPEGXL_DISTANCE )
{
int distanceInt = params[i+1];
distanceInt = MIN(MAX(distanceInt, 0), 25);
const int distanceInt = MIN(MAX(value, 0), 25);
if(value != distanceInt) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_JPEGXL_DISTANCE must be between 0 to 25, It is fallbacked to %d", value, distanceInt));
}
distance = static_cast<float>(distanceInt);
}
}
+19 -4
View File
@@ -55,6 +55,7 @@
#include "utils.hpp"
#include "grfmt_pam.hpp"
#include "opencv2/core/utils/logger.hpp"
namespace cv {
@@ -657,6 +658,7 @@ PAMEncoder::PAMEncoder()
{
m_description = "Portable arbitrary format (*.pam)";
m_buf_supported = true;
m_supported_encode_key = {IMWRITE_PAM_TUPLETYPE};
}
@@ -689,12 +691,25 @@ bool PAMEncoder::write( const Mat& img, const std::vector<int>& params )
int x, y, tmp, bufsize = 256;
/* parse save file type */
for( size_t i = 0; i < params.size(); i += 2 )
for( size_t i = 0; i < params.size(); i += 2 ) {
const int value = params[i+1];
if( params[i] == IMWRITE_PAM_TUPLETYPE ) {
if ( params[i+1] > IMWRITE_PAM_FORMAT_NULL &&
params[i+1] < (int) PAM_FORMATS_NO)
fmt = &formats[params[i+1]];
switch(value) {
case IMWRITE_PAM_FORMAT_NULL:
case IMWRITE_PAM_FORMAT_BLACKANDWHITE:
case IMWRITE_PAM_FORMAT_GRAYSCALE:
case IMWRITE_PAM_FORMAT_GRAYSCALE_ALPHA:
case IMWRITE_PAM_FORMAT_RGB:
case IMWRITE_PAM_FORMAT_RGB_ALPHA:
fmt = &formats[value];
break;
default:
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PAM_TUPLETYPE must be one of ImwritePAMFlags. It is ignored", value));
fmt = nullptr;
break;
}
}
}
if( m_buf )
{
+49 -8
View File
@@ -913,6 +913,7 @@ PngEncoder::PngEncoder()
memset(palette, 0, sizeof(palette));
memset(trns, 0, sizeof(trns));
memset(op, 0, sizeof(op));
m_supported_encode_key = {IMWRITE_PNG_COMPRESSION, IMWRITE_PNG_STRATEGY, IMWRITE_PNG_BILEVEL, IMWRITE_PNG_FILTER, IMWRITE_PNG_ZLIBBUFFER_SIZE};
}
PngEncoder::~PngEncoder()
@@ -983,26 +984,61 @@ bool PngEncoder::write( const Mat& img, const std::vector<int>& params )
for( size_t i = 0; i < params.size(); i += 2 )
{
const int value = params[i+1];
switch (params[i])
{
case IMWRITE_PNG_COMPRESSION:
m_compression_strategy = IMWRITE_PNG_STRATEGY_DEFAULT; // Default strategy
m_compression_level = params[i+1];
m_compression_level = MIN(MAX(m_compression_level, 0), Z_BEST_COMPRESSION);
m_compression_level = MIN(MAX(value, 0), Z_BEST_COMPRESSION);
if(value != m_compression_level) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_COMPRESSION must be between 0 to 9. It is fallbacked to %d", value, m_compression_level));
}
set_compression_level = true;
break;
case IMWRITE_PNG_STRATEGY:
m_compression_strategy = params[i+1];
m_compression_strategy = MIN(MAX(m_compression_strategy, 0), Z_FIXED);
{
switch(value) {
case IMWRITE_PNG_STRATEGY_DEFAULT:
case IMWRITE_PNG_STRATEGY_FILTERED:
case IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY:
case IMWRITE_PNG_STRATEGY_RLE:
case IMWRITE_PNG_STRATEGY_FIXED:
m_compression_strategy = value;
break;
default:
m_compression_strategy = IMWRITE_PNG_STRATEGY_RLE;
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_STRATEGY must be one of ImwritePNGFlags. It is fallbacked to IMWRITE_PNG_STRATEGY_RLE", value));
break;
}
}
break;
case IMWRITE_PNG_BILEVEL:
m_isBilevel = params[i+1] != 0;
m_isBilevel = value != 0;
if((value != 0) && (value != 1)) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_BILEVEL must be 0 or 1. It is fallbacked to 1", value ));
}
break;
case IMWRITE_PNG_FILTER:
m_filter = params[i+1];
{
switch(value) {
case IMWRITE_PNG_FILTER_NONE:
case IMWRITE_PNG_FILTER_SUB:
case IMWRITE_PNG_FILTER_UP:
case IMWRITE_PNG_FILTER_AVG:
case IMWRITE_PNG_FILTER_PAETH:
case IMWRITE_PNG_FAST_FILTERS:
case IMWRITE_PNG_ALL_FILTERS:
m_filter = value;
break;
default:
m_filter = IMWRITE_PNG_FILTER_SUB;
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_FILTER must be one of ImwritePNGFilterFlags. It is fallbacked to IMWRITE_PNG_FILTER_SUB", value ));
break;
}
}
set_filter = true;
break;
@@ -1011,11 +1047,16 @@ bool PngEncoder::write( const Mat& img, const std::vector<int>& params )
// The minimum limit is 6, which is from from https://github.com/opencv/opencv/blob/4.12.0/3rdparty/libpng/pngset.c#L1600 .
// The maximum limit is 1 MiB, which has been provisionally set. libpng limitation is 2 GiB(INT32_MAX), but it is too large.
// For normal use, 128 or 256 KiB may be sufficient. See https://zlib.net/zlib_how.html .
png_set_compression_buffer_size(png_ptr, MIN(MAX(params[i+1],6), 1024*1024));
{
const int zlen = MIN(MAX(value, 6), 1024*1024);
if(value != zlen) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_ZLIBBUFFER_SIZE must be between 6 to 1024*1024. It is fallbacked to %d", value , zlen));
}
png_set_compression_buffer_size(png_ptr, zlen);
}
break;
default:
CV_LOG_WARNING(NULL, "An unknown or unsupported ImwriteFlags value was specified and has been ignored.");
break;
}
}
+8 -1
View File
@@ -389,6 +389,7 @@ PxMEncoder::PxMEncoder(PxMMode mode) :
CV_Error(Error::StsInternal, "");
}
m_buf_supported = true;
m_supported_encode_key = {IMWRITE_PXM_BINARY};
}
PxMEncoder::~PxMEncoder()
@@ -414,8 +415,14 @@ bool PxMEncoder::write(const Mat& img, const std::vector<int>& params)
for( size_t i = 0; i < params.size(); i += 2 )
{
const int value = params[i+1];
if( params[i] == IMWRITE_PXM_BINARY )
isBinary = params[i+1] != 0;
{
isBinary = value != 0;
if((value != 0) && (value != 1)) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PXM_BINARY must be 0 or 1. It is fallbacked to 1", value));
}
}
}
int mode = mode_;
+38 -6
View File
@@ -482,6 +482,7 @@ SPngEncoder::SPngEncoder()
m_support_metadata[IMAGE_METADATA_EXIF] = true;
m_support_metadata[IMAGE_METADATA_XMP] = true;
m_support_metadata[IMAGE_METADATA_ICCP] = true;
m_supported_encode_key = {IMWRITE_PNG_COMPRESSION, IMWRITE_PNG_STRATEGY, IMWRITE_PNG_BILEVEL, IMWRITE_PNG_FILTER, IMWRITE_PNG_ZLIBBUFFER_SIZE};
}
SPngEncoder::~SPngEncoder()
@@ -534,25 +535,56 @@ bool SPngEncoder::write(const Mat &img, const std::vector<int> &params)
for (size_t i = 0; i < params.size(); i += 2)
{
const int value = params[i+1];
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);
compression_level = MIN(MAX(value, 0), Z_BEST_COMPRESSION);
if(value != m_compression_level) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_COMPRESSION must be between 0 to 9. It is fallbacked to %d", value, m_compression_level));
}
set_compression_level = true;
}
if (params[i] == IMWRITE_PNG_STRATEGY)
{
compression_strategy = params[i + 1];
compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED);
switch(value) {
case IMWRITE_PNG_STRATEGY_DEFAULT:
case IMWRITE_PNG_STRATEGY_FILTERED:
case IMWRITE_PNG_STRATEGY_HUFFMAN_ONLY:
case IMWRITE_PNG_STRATEGY_RLE:
case IMWRITE_PNG_STRATEGY_FIXED:
compression_strategy = value;
break;
default:
compression_strategy = IMWRITE_PNG_STRATEGY_RLE:
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_STRATEGY must be one of ImwritePNGFlags. It is fallbacked to IMWRITE_PNG_STRATEGY_RLE", value));
break;
}
}
if (params[i] == IMWRITE_PNG_BILEVEL)
{
isBilevel = params[i + 1] != 0;
isBilevel = value != 0;
if((value != 0) && (value != 1)) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_BILEVEL must be 0 or 1. It is fallbacked to 1", value ));
}
}
if( params[i] == IMWRITE_PNG_FILTER )
{
filter = params[i+1];
switch(value) {
case IMWRITE_PNG_FILTER_NONE:
case IMWRITE_PNG_FILTER_SUB:
case IMWRITE_PNG_FILTER_UP:
case IMWRITE_PNG_FILTER_AVG:
case IMWRITE_PNG_FILTER_PAETH:
case IMWRITE_PNG_FAST_FILTERS:
case IMWRITE_PNG_ALL_FILTERS:
filter = value;
break;
default:
filter = IMWRITE_PNG_FILTER_SUB;
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_PNG_FILTER must be one of ImwritePNGFilterFlags. It is fallbacked to IMWRITE_PNG_FILTER_SUB", value ));
break;
}
set_filter = true;
}
if( params[i] == IMWRITE_PNG_ZLIBBUFFER_SIZE )
+86 -9
View File
@@ -1083,6 +1083,7 @@ TiffEncoder::TiffEncoder()
{
m_description = "TIFF Files (*.tiff;*.tif)";
m_buf_supported = true;
m_supported_encode_key = {IMWRITE_TIFF_RESUNIT, IMWRITE_TIFF_XDPI, IMWRITE_TIFF_YDPI, IMWRITE_TIFF_COMPRESSION, IMWRITE_TIFF_ROWSPERSTRIP, IMWRITE_TIFF_PREDICTOR};
}
TiffEncoder::~TiffEncoder()
@@ -1218,15 +1219,92 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
cv::Ptr<void> tif_cleanup(tif, cv_tiffCloseHandle);
//Settings that matter to all images
int compression = COMPRESSION_LZW;
int predictor = PREDICTOR_HORIZONTAL;
int compression = IMWRITE_TIFF_COMPRESSION_LZW;
int predictor = IMWRITE_TIFF_PREDICTOR_HORIZONTAL;
int resUnit = -1, dpiX = -1, dpiY = -1;
readParam(params, IMWRITE_TIFF_COMPRESSION, compression);
readParam(params, IMWRITE_TIFF_PREDICTOR, predictor);
readParam(params, IMWRITE_TIFF_RESUNIT, resUnit);
readParam(params, IMWRITE_TIFF_XDPI, dpiX);
readParam(params, IMWRITE_TIFF_YDPI, dpiY);
if(readParam(params, IMWRITE_TIFF_COMPRESSION, compression))
{
switch(compression) {
case IMWRITE_TIFF_COMPRESSION_NONE:
case IMWRITE_TIFF_COMPRESSION_CCITTRLE:
case IMWRITE_TIFF_COMPRESSION_CCITTFAX3: // IMWRITE_TIFF_COMPRESSION_CCITT_T4:
case IMWRITE_TIFF_COMPRESSION_CCITTFAX4: // IMWRITE_TIFF_COMPRESSION_CCITT_T6:
case IMWRITE_TIFF_COMPRESSION_LZW:
case IMWRITE_TIFF_COMPRESSION_OJPEG:
case IMWRITE_TIFF_COMPRESSION_JPEG:
case IMWRITE_TIFF_COMPRESSION_T85:
case IMWRITE_TIFF_COMPRESSION_T43:
case IMWRITE_TIFF_COMPRESSION_NEXT:
case IMWRITE_TIFF_COMPRESSION_CCITTRLEW:
case IMWRITE_TIFF_COMPRESSION_PACKBITS:
case IMWRITE_TIFF_COMPRESSION_THUNDERSCAN:
case IMWRITE_TIFF_COMPRESSION_IT8CTPAD:
case IMWRITE_TIFF_COMPRESSION_IT8LW:
case IMWRITE_TIFF_COMPRESSION_IT8MP:
case IMWRITE_TIFF_COMPRESSION_IT8BL:
case IMWRITE_TIFF_COMPRESSION_PIXARFILM:
case IMWRITE_TIFF_COMPRESSION_PIXARLOG:
case IMWRITE_TIFF_COMPRESSION_DEFLATE:
case IMWRITE_TIFF_COMPRESSION_ADOBE_DEFLATE:
case IMWRITE_TIFF_COMPRESSION_DCS:
case IMWRITE_TIFF_COMPRESSION_JBIG:
case IMWRITE_TIFF_COMPRESSION_SGILOG:
case IMWRITE_TIFF_COMPRESSION_SGILOG24:
case IMWRITE_TIFF_COMPRESSION_JP2000:
case IMWRITE_TIFF_COMPRESSION_LERC:
case IMWRITE_TIFF_COMPRESSION_LZMA:
case IMWRITE_TIFF_COMPRESSION_ZSTD:
case IMWRITE_TIFF_COMPRESSION_WEBP:
case IMWRITE_TIFF_COMPRESSION_JXL:
break;
default:
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_COMPRESSION must be one of ImwriteTiffCompressionFlags. It is fallbacked to IMWRITE_TIFF_COMPRESSION_LZW", compression));
compression = IMWRITE_TIFF_COMPRESSION_LZW;
break;
}
}
if(readParam(params, IMWRITE_TIFF_PREDICTOR, predictor))
{
switch(predictor) {
case IMWRITE_TIFF_PREDICTOR_NONE:
case IMWRITE_TIFF_PREDICTOR_HORIZONTAL:
case IMWRITE_TIFF_PREDICTOR_FLOATINGPOINT:
break;
default:
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_PREDICTOR must be one of ImwriteTiffPredictorFlags. It is fallbacked to IMWRITE_TIFF_PREDICTOR_HORIZONTAL", predictor));
predictor = IMWRITE_TIFF_PREDICTOR_HORIZONTAL;
break;
}
}
if(readParam(params, IMWRITE_TIFF_RESUNIT, resUnit))
{
switch(resUnit) {
case IMWRITE_TIFF_RESOLUTION_UNIT_NONE:
case IMWRITE_TIFF_RESOLUTION_UNIT_INCH:
case IMWRITE_TIFF_RESOLUTION_UNIT_CENTIMETER:
break;
default:
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_RESUNIT must be one of ImwriteTiffResolutionUnitFlags. It is fallbacked to IMWRITE_TIFF_RESOLUTION_UNIT_INCH", resUnit));
resUnit = IMWRITE_TIFF_RESOLUTION_UNIT_INCH;
break;
}
}
if(readParam(params, IMWRITE_TIFF_XDPI, dpiX))
{
if(dpiX <= 0) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_XDPI must be positive. It is ignored.", dpiX));
dpiX = -1;
}
}
if(readParam(params, IMWRITE_TIFF_YDPI, dpiY))
{
if(dpiY <= 0) {
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_YDPI must be positive. It is ignored.", dpiX));
dpiY = -1;
}
}
//Iterate through each image in the vector and write them out as Tiff directories
for (size_t page = 0; page < img_vec.size(); page++)
@@ -1249,8 +1327,7 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PAGENUMBER, page, img_vec.size()));
}
int compression_param = -1; // OPENCV_FUTURE
if (type == CV_32FC3 && (!readParam(params, IMWRITE_TIFF_COMPRESSION, compression_param) || compression_param == COMPRESSION_SGILOG))
if (type == CV_32FC3 && compression == COMPRESSION_SGILOG)
{
if (!write_32FC3_SGILOG(img, tif))
return false;
+6 -3
View File
@@ -338,6 +338,7 @@ WebPEncoder::WebPEncoder()
m_support_metadata[IMAGE_METADATA_EXIF] = true;
m_support_metadata[IMAGE_METADATA_XMP] = true;
m_support_metadata[IMAGE_METADATA_ICCP] = true;
m_supported_encode_key = {IMWRITE_WEBP_QUALITY};
}
WebPEncoder::~WebPEncoder() { }
@@ -356,15 +357,17 @@ bool WebPEncoder::write(const Mat& img, const std::vector<int>& params)
bool comp_lossless = true;
float quality = 100.0f;
if (params.size() > 1)
for(size_t i = 0; i < params.size(); i += 2)
{
if (params[0] == IMWRITE_WEBP_QUALITY)
const int value = params[i+1];
if (params[i] == IMWRITE_WEBP_QUALITY)
{
comp_lossless = false;
quality = static_cast<float>(params[1]);
quality = static_cast<float>(value);
if (quality < 1.0f)
{
quality = 1.0f;
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_WEBP_QUALITY must be between 1 to 100(lossy) or more(lossless). It is fallbacked to 1", value));
}
if (quality > 100.0f)
{
+55
View File
@@ -364,6 +364,20 @@ static ImageEncoder findEncoder( const String& _ext )
return ImageEncoder();
}
static bool isValidEncodeKeyAtAll(const int key)
{
bool ret = false;
ImageCodecInitializer& codecs = getCodecs();
for( size_t i = 0; i < codecs.encoders.size(); i++ )
{
if( codecs.encoders[i]->isValidEncodeKey(key) == true )
{
ret = true;
break;
}
}
return ret;
}
static void ExifTransform(int orientation, OutputArray img)
{
@@ -1110,6 +1124,27 @@ static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
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), "");
for(size_t v = 0; v < params.size(); v+= 2)
{
const int key = params[v];
if(encoder->isValidEncodeKey(key))
{
// Current encoder supports specified key.
// Do nothing.
}
else if(isValidEncodeKeyAtAll(key))
{
// Current encoder does not support specified key, but some encoder supports it.
CV_LOG_WARNING(nullptr, cv::format("An unsupported key(%d) was specified and has been ignored.", key));
}
else
{
// No encoder supports specified key.
CV_LOG_WARNING(nullptr, cv::format("An unknown key(%d) was specified and has been ignored.", key));
}
}
bool code = false;
try
{
@@ -1663,6 +1698,26 @@ bool imencodeWithMetadata( const String& ext, InputArray _img,
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), "");
for(size_t v = 0; v < params.size(); v+= 2)
{
const int key = params[v];
if(encoder->isValidEncodeKey(key))
{
// Current encoder supports specified key.
// Do nothing.
}
else if(isValidEncodeKeyAtAll(key))
{
// Current encoder does not support specified key, but some encoder supports it.
CV_LOG_WARNING(nullptr, cv::format("An unsupported key(%d) was specified and has been ignored.", key));
}
else
{
// No encoder supports specified key.
CV_LOG_WARNING(nullptr, cv::format("An unknown key(%d) was specified and has been ignored.", key));
}
}
bool code = false;
String filename;
if( !encoder->setDestination(buf) )