diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 841d5b32cf..2aeac8d60a 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -105,16 +105,16 @@ enum ImwriteFlags { 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_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set. See ImwriteTiffResolutionUnitFlags. Default is IMWRITE_TIFF_RESOLUTION_UNIT_INCH. IMWRITE_TIFF_XDPI = 257,//!< For TIFF, use to specify the X direction DPI IMWRITE_TIFF_YDPI = 258,//!< For TIFF, use to specify the Y direction DPI IMWRITE_TIFF_COMPRESSION = 259,//!< For TIFF, use to specify the image compression scheme. See cv::ImwriteTiffCompressionFlags. Note, for images whose depth is CV_32F, only libtiff's SGILOG compression scheme is used. For other supported depths, the compression scheme can be specified by this flag; LZW compression is the default. IMWRITE_TIFF_ROWSPERSTRIP = 278,//!< For TIFF, use to specify the number of rows per strip. - IMWRITE_TIFF_PREDICTOR = 317,//!< For TIFF, use to specify predictor. See cv::ImwriteTiffPredictorFlags. + IMWRITE_TIFF_PREDICTOR = 317,//!< For TIFF, use to specify predictor. See cv::ImwriteTiffPredictorFlags. Default is IMWRITE_TIFF_PREDICTOR_HORIZONTAL . IMWRITE_JPEG2000_COMPRESSION_X1000 = 272,//!< For JPEG2000, use to specify the target compression rate (multiplied by 1000). The value can be from 0 to 1000. Default is 1000. IMWRITE_AVIF_QUALITY = 512,//!< For AVIF, it can be a quality between 0 and 100 (the higher the better). Default is 95. IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10 or 12. If >8, it is stored/read as CV_32F. Default is 8. - IMWRITE_AVIF_SPEED = 514,//!< For AVIF, it is between 0 (slowest) and (fastest). Default is 9. + IMWRITE_AVIF_SPEED = 514,//!< For AVIF, it is between 0 (slowest) and 10(fastest). Default is 9. IMWRITE_JPEGXL_QUALITY = 640,//!< For JPEG XL, it can be a quality from 0 to 100 (the higher is the better). Default value is 95. If set, distance parameter is re-calicurated from quality level automatically. This parameter request libjxl v0.10 or later. IMWRITE_JPEGXL_EFFORT = 641,//!< For JPEG XL, encoder effort/speed level without affecting decoding speed; it is between 1 (fastest) and 10 (slowest). Default is 7. IMWRITE_JPEGXL_DISTANCE = 642,//!< For JPEG XL, distance level for lossy compression: target max butteraugli distance, lower = higher quality, 0 = lossless; range: 0 .. 25. Default is 1. @@ -175,7 +175,12 @@ enum ImwriteTiffPredictorFlags { IMWRITE_TIFF_PREDICTOR_NONE = 1, //!< no prediction scheme used IMWRITE_TIFF_PREDICTOR_HORIZONTAL = 2, //!< horizontal differencing IMWRITE_TIFF_PREDICTOR_FLOATINGPOINT = 3 //!< floating point predictor +}; +enum ImwriteTiffResolutionUnitFlags { + IMWRITE_TIFF_RESOLUTION_UNIT_NONE = 1, //!< no absolute unit of measurement. + IMWRITE_TIFF_RESOLUTION_UNIT_INCH = 2, //!< inch + IMWRITE_TIFF_RESOLUTION_UNIT_CENTIMETER = 3, //!< centimeter }; enum ImwriteEXRTypeFlags { diff --git a/modules/imgcodecs/src/grfmt_avif.cpp b/modules/imgcodecs/src/grfmt_avif.cpp index c1b86362e0..35b4411b3b 100644 --- a/modules/imgcodecs/src/grfmt_avif.cpp +++ b/modules/imgcodecs/src/grfmt_avif.cpp @@ -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)); + } } } diff --git a/modules/imgcodecs/src/grfmt_base.cpp b/modules/imgcodecs/src/grfmt_base.cpp index 9c9dead740..0cd372ec28 100644 --- a/modules/imgcodecs/src/grfmt_base.cpp +++ b/modules/imgcodecs/src/grfmt_base.cpp @@ -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; diff --git a/modules/imgcodecs/src/grfmt_base.hpp b/modules/imgcodecs/src/grfmt_base.hpp index 889fdd0a1d..3fec6055c2 100644 --- a/modules/imgcodecs/src/grfmt_base.hpp +++ b/modules/imgcodecs/src/grfmt_base.hpp @@ -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* 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 m_supported_encode_key; ///< Supported encode key list }; } diff --git a/modules/imgcodecs/src/grfmt_exr.cpp b/modules/imgcodecs/src/grfmt_exr.cpp index 44a0934517..f1d4126e16 100644 --- a/modules/imgcodecs/src/grfmt_exr.cpp +++ b/modules/imgcodecs/src/grfmt_exr.cpp @@ -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& 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& 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& 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& 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 } } diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index 1954e2d501..f5307ca39b 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -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& 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; diff --git a/modules/imgcodecs/src/grfmt_jpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg.cpp index 810ae1d63b..499036cedf 100644 --- a/modules/imgcodecs/src/grfmt_jpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg.cpp @@ -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& 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& 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& 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(params[i+1]); + sampling_factor = static_cast(value); switch ( sampling_factor ) { @@ -784,7 +806,7 @@ bool JpegEncoder::write( const Mat& img, const std::vector& 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; } diff --git a/modules/imgcodecs/src/grfmt_jpeg2000.cpp b/modules/imgcodecs/src/grfmt_jpeg2000.cpp index 9b8680ac1f..9611012c32 100644 --- a/modules/imgcodecs/src/grfmt_jpeg2000.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg2000.cpp @@ -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& 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(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; } } diff --git a/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp index 70832277ef..047a522eca 100644 --- a/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg2000_openjpeg.cpp @@ -330,14 +330,20 @@ opj_cparameters setupEncoderParameters(const std::vector& 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(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 diff --git a/modules/imgcodecs/src/grfmt_jpegxl.cpp b/modules/imgcodecs/src/grfmt_jpegxl.cpp index 72118184f6..0d8feb878a 100644 --- a/modules/imgcodecs/src/grfmt_jpegxl.cpp +++ b/modules/imgcodecs/src/grfmt_jpegxl.cpp @@ -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& 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(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& 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(distanceInt); } } diff --git a/modules/imgcodecs/src/grfmt_pam.cpp b/modules/imgcodecs/src/grfmt_pam.cpp index 2c15ab244c..12371e1f5d 100644 --- a/modules/imgcodecs/src/grfmt_pam.cpp +++ b/modules/imgcodecs/src/grfmt_pam.cpp @@ -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& 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 ) { diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index 4d3d8f9d8d..9a93f68b70 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -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& 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& 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; } } diff --git a/modules/imgcodecs/src/grfmt_pxm.cpp b/modules/imgcodecs/src/grfmt_pxm.cpp index 20c815e833..3ddab22b56 100644 --- a/modules/imgcodecs/src/grfmt_pxm.cpp +++ b/modules/imgcodecs/src/grfmt_pxm.cpp @@ -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& 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_; diff --git a/modules/imgcodecs/src/grfmt_spng.cpp b/modules/imgcodecs/src/grfmt_spng.cpp index e2f6f02bb9..1311757b44 100644 --- a/modules/imgcodecs/src/grfmt_spng.cpp +++ b/modules/imgcodecs/src/grfmt_spng.cpp @@ -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 ¶ms) 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 ) diff --git a/modules/imgcodecs/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp index ccc6579a01..142ad559a0 100644 --- a/modules/imgcodecs/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -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& img_vec, const std::vect cv::Ptr 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& 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; diff --git a/modules/imgcodecs/src/grfmt_webp.cpp b/modules/imgcodecs/src/grfmt_webp.cpp index c82750020a..ae11c7ce2c 100644 --- a/modules/imgcodecs/src/grfmt_webp.cpp +++ b/modules/imgcodecs/src/grfmt_webp.cpp @@ -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& 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(params[1]); + quality = static_cast(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) { diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp index 8e354c4dc9..31e7e4c709 100644 --- a/modules/imgcodecs/src/loadsave.cpp +++ b/modules/imgcodecs/src/loadsave.cpp @@ -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& 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) ) diff --git a/modules/imgcodecs/test/test_avif.cpp b/modules/imgcodecs/test/test_avif.cpp index 5b82452049..81cbfe9c66 100644 --- a/modules/imgcodecs/test/test_avif.cpp +++ b/modules/imgcodecs/test/test_avif.cpp @@ -128,12 +128,6 @@ TEST_P(Imgcodecs_Avif_Image_WriteReadSuite, imwrite_imread) { // Encode. const string output = cv::tempfile(".avif"); - if (!IsBitDepthValid()) { - EXPECT_NO_FATAL_FAILURE( - cv::imwrite(output, img_original, encoding_params_)); - EXPECT_NE(0, remove(output.c_str())); - return; - } EXPECT_NO_THROW(cv::imwrite(output, img_original, encoding_params_)); // Read from file. @@ -164,11 +158,6 @@ TEST_P(Imgcodecs_Avif_Image_EncodeDecodeSuite, imencode_imdecode) { bool result = true; EXPECT_NO_THROW( result = cv::imencode(".avif", img_original, buf, encoding_params_);); - - if (!IsBitDepthValid()) { - EXPECT_FALSE(result); - return; - } EXPECT_TRUE(result); // Read back. diff --git a/modules/imgcodecs/test/test_grfmt.cpp b/modules/imgcodecs/test/test_grfmt.cpp index f44e64ec7a..3ef0c9d6fb 100644 --- a/modules/imgcodecs/test/test_grfmt.cpp +++ b/modules/imgcodecs/test/test_grfmt.cpp @@ -166,6 +166,8 @@ TEST_P(Imgcodecs_ExtSize, write_imageseq) continue; if (cn == 1 && ext == ".gif") continue; + if (cn == 1 && ext == ".webp") + continue; string filename = cv::tempfile(format("%d%s", cn, ext.c_str()).c_str()); Mat img_gt(size, CV_MAKETYPE(CV_8U, cn), Scalar::all(0)); @@ -257,6 +259,9 @@ const string all_exts[] = #ifdef HAVE_IMGCODEC_GIF ".gif", #endif +#ifdef HAVE_WEBP + ".webp", +#endif }; vector all_sizes() @@ -307,6 +312,35 @@ TEST_P(Imgcodecs_pbm, write_read) INSTANTIATE_TEST_CASE_P(All, Imgcodecs_pbm, testing::Bool()); #endif +// See https://github.com/opencv/opencv/issues/27557 +typedef testing::TestWithParam Imgcodecs_invalid_key; + +TEST_P(Imgcodecs_invalid_key, encode_regression27557) +{ + const string ext = GetParam(); + const int matType = ((ext == ".pbm") || (ext == ".pgm"))? CV_8UC1 : CV_8UC3; + Mat src(100, 100, matType, Scalar(0, 255, 0)); + std::vector buf; + bool status = false; + EXPECT_NO_THROW(status = imencode(ext, src, buf, { -1, -1 })); + EXPECT_TRUE(status); +} + +TEST_P(Imgcodecs_invalid_key, write_regression27557) +{ + const string ext = GetParam(); + string fname = tempfile(ext.c_str()); + + const int matType = ((ext == ".pbm") || (ext == ".pgm"))? CV_8UC1 : CV_8UC3; + Mat src(100, 100, matType, Scalar(0, 255, 0)); + std::vector buf; + bool status = false; + EXPECT_NO_THROW(status = imwrite(fname, src, { -1, -1 })); + EXPECT_TRUE(status); + remove(fname.c_str()); +} + +INSTANTIATE_TEST_CASE_P(All, Imgcodecs_invalid_key, testing::ValuesIn(all_exts)); //================================================================================================== diff --git a/modules/imgcodecs/test/test_webp.cpp b/modules/imgcodecs/test/test_webp.cpp index 6414e60469..b3fe9b2caf 100644 --- a/modules/imgcodecs/test/test_webp.cpp +++ b/modules/imgcodecs/test/test_webp.cpp @@ -72,7 +72,7 @@ TEST(Imgcodecs_WebP, encode_decode_lossy_webp) { std::vector params; params.push_back(IMWRITE_WEBP_QUALITY); - params.push_back(q); + params.push_back(MAX(q,1)); string output = cv::tempfile(".webp"); EXPECT_NO_THROW(cv::imwrite(output, img, params)); diff --git a/modules/videoio/src/cap_images.cpp b/modules/videoio/src/cap_images.cpp index f472ff711b..1d7190003e 100644 --- a/modules/videoio/src/cap_images.cpp +++ b/modules/videoio/src/cap_images.cpp @@ -389,12 +389,8 @@ void CvVideoWriter_Images::write(InputArray image) cv::String filename = cv::format(filename_pattern.c_str(), (int)currentframe); CV_Assert(!filename.empty()); - std::vector image_params = params; - image_params.push_back(0); // append parameters 'stop' mark - image_params.push_back(0); - cv::Mat img = image.getMat(); - cv::imwrite(filename, img, image_params); + cv::imwrite(filename, img); currentframe++; }