diff --git a/modules/highgui/src/window_w32.cpp b/modules/highgui/src/window_w32.cpp index 2543c81c6a..8e041c9609 100644 --- a/modules/highgui/src/window_w32.cpp +++ b/modules/highgui/src/window_w32.cpp @@ -2170,9 +2170,15 @@ static void showSaveDialog(CvWindow& window) #ifdef HAVE_WEBP "WebP files (*.webp)\0*.webp\0" #endif - "Portable image format (*.pbm;*.pgm;*.ppm;*.pxm;*.pnm)\0*.pbm;*.pgm;*.ppm;*.pxm;*.pnm\0" + "Portable image format (*.pbm;*.pgm;*.ppm;*.pnm;*.pam)\0*.pbm;*.pgm;*.ppm;*.pnm;*.pam\0" #ifdef HAVE_OPENEXR "OpenEXR Image files (*.exr)\0*.exr\0" +#endif +#ifdef HAVE_AVIF + "AVIF files (*.avif)\0*.avif\0" +#endif +#ifdef HAVE_IMGCODEC_GIF + "Graphics Interchange Format 89a(*.gif)\0*.gif\0" #endif "Radiance HDR (*.hdr;*.pic)\0*.hdr;*.pic\0" "Sun raster files (*.sr;*.ras)\0*.sr;*.ras\0" @@ -2194,7 +2200,7 @@ static void showSaveDialog(CvWindow& window) } #else CV_UNUSED(window); - CV_LOG_WARNING("Save dialog requires enabled 'imgcodecs' module."); + CV_LOG_WARNING(NULL, "Save dialog requires enabled 'imgcodecs' module."); return; #endif } diff --git a/modules/imgcodecs/perf/perf_decode_encode.cpp b/modules/imgcodecs/perf/perf_decode_encode.cpp new file mode 100644 index 0000000000..ce693cb878 --- /dev/null +++ b/modules/imgcodecs/perf/perf_decode_encode.cpp @@ -0,0 +1,131 @@ +// This file is part of OpenCV project. +// It is subject to the license terms in the LICENSE file found in the top-level directory +// of this distribution and at http://opencv.org/license.html + +#include "perf_precomp.hpp" + +namespace opencv_test +{ + +#ifdef HAVE_PNG + +using namespace perf; + +typedef perf::TestBaseWithParam Decode; +typedef perf::TestBaseWithParam Encode; + +const string exts[] = { +#ifdef HAVE_AVIF + ".avif", +#endif + ".bmp", +#ifdef HAVE_IMGCODEC_GIF + ".gif", +#endif +#if (defined(HAVE_JASPER) && defined(OPENCV_IMGCODECS_ENABLE_JASPER_TESTS)) \ + || defined(HAVE_OPENJPEG) + ".jp2", +#endif +#ifdef HAVE_JPEG + ".jpg", +#endif +#ifdef HAVE_JPEGXL + ".jxl", +#endif + ".png", +#ifdef HAVE_IMGCODEC_PXM + ".ppm", +#endif +#ifdef HAVE_IMGCODEC_SUNRASTER + ".ras", +#endif +#ifdef HAVE_TIFF + ".tiff", +#endif +#ifdef HAVE_WEBP + ".webp", +#endif +}; + +const string exts_multi[] = { +#ifdef HAVE_AVIF + ".avif", +#endif +#ifdef HAVE_IMGCODEC_GIF + ".gif", +#endif + ".png", +#ifdef HAVE_TIFF + ".tiff", +#endif +#ifdef HAVE_WEBP + ".webp", +#endif +}; + +PERF_TEST_P(Decode, bgr, testing::ValuesIn(exts)) +{ + String filename = getDataPath("perf/1920x1080.png"); + + Mat src = imread(filename); + EXPECT_FALSE(src.empty()) << "Cannot open test image perf/1920x1080.png"; + vector buf; + EXPECT_TRUE(imencode(GetParam(), src, buf)); + + TEST_CYCLE() imdecode(buf, IMREAD_UNCHANGED); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(Decode, rgb, testing::ValuesIn(exts)) +{ + String filename = getDataPath("perf/1920x1080.png"); + + Mat src = imread(filename); + EXPECT_FALSE(src.empty()) << "Cannot open test image perf/1920x1080.png"; + vector buf; + EXPECT_TRUE(imencode(GetParam(), src, buf)); + + TEST_CYCLE() imdecode(buf, IMREAD_COLOR_RGB); + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(Encode, bgr, testing::ValuesIn(exts)) +{ + String filename = getDataPath("perf/1920x1080.png"); + + Mat src = imread(filename); + EXPECT_FALSE(src.empty()) << "Cannot open test image perf/1920x1080.png"; + vector buf; + + TEST_CYCLE() imencode(GetParam(), src, buf); + + std::cout << "Encoded buffer size: " << buf.size() + << " bytes, Compression ratio: " << std::fixed << std::setprecision(2) + << (static_cast(buf.size()) / (src.total() * src.channels())) * 100.0 << "%" << std::endl; + + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(Encode, multi, testing::ValuesIn(exts_multi)) +{ + String filename = getDataPath("perf/1920x1080.png"); + vector vec; + EXPECT_TRUE(imreadmulti(filename, vec)); + vec.push_back(vec.back().clone()); + circle(vec.back(), Point(100, 100), 45, Scalar(0, 0, 255, 0), 2, LINE_AA); + vector buf; + EXPECT_TRUE(imwrite("test" + GetParam(), vec)); + + TEST_CYCLE() imencode(GetParam(), vec, buf); + + std::cout << "Encoded buffer size: " << buf.size() + << " bytes, Compression ratio: " << std::fixed << std::setprecision(2) + << (static_cast(buf.size()) / (vec[0].total() * vec[0].channels())) * 100.0 << "%" << std::endl; + + SANITY_CHECK_NOTHING(); +} +#endif // HAVE_PNG + +} // namespace diff --git a/modules/imgcodecs/src/grfmt_avif.cpp b/modules/imgcodecs/src/grfmt_avif.cpp index d3fb500604..c35eb50306 100644 --- a/modules/imgcodecs/src/grfmt_avif.cpp +++ b/modules/imgcodecs/src/grfmt_avif.cpp @@ -298,11 +298,6 @@ bool AvifEncoder::isFormatSupported(int depth) const { return (depth == CV_8U || depth == CV_16U); } -bool AvifEncoder::write(const Mat &img, const std::vector ¶ms) { - std::vector img_vec(1, img); - return writemulti(img_vec, params); -} - bool AvifEncoder::writeanimation(const Animation& animation, const std::vector ¶ms) { int bit_depth = 8; diff --git a/modules/imgcodecs/src/grfmt_avif.hpp b/modules/imgcodecs/src/grfmt_avif.hpp index 87b765619e..9f097aaf55 100644 --- a/modules/imgcodecs/src/grfmt_avif.hpp +++ b/modules/imgcodecs/src/grfmt_avif.hpp @@ -41,7 +41,6 @@ class AvifEncoder CV_FINAL : public BaseImageEncoder { ~AvifEncoder() CV_OVERRIDE; bool isFormatSupported(int depth) const CV_OVERRIDE; - bool write(const Mat& img, const std::vector& params) CV_OVERRIDE; bool writeanimation(const Animation& animation, const std::vector& params) CV_OVERRIDE; ImageEncoder newEncoder() const CV_OVERRIDE; diff --git a/modules/imgcodecs/src/grfmt_base.cpp b/modules/imgcodecs/src/grfmt_base.cpp index 1e09882780..dc3d07ab78 100644 --- a/modules/imgcodecs/src/grfmt_base.cpp +++ b/modules/imgcodecs/src/grfmt_base.cpp @@ -140,6 +140,11 @@ bool BaseImageEncoder::setDestination( std::vector& buf ) return true; } +bool BaseImageEncoder::write(const Mat &img, const std::vector ¶ms) { + std::vector img_vec(1, img); + return writemulti(img_vec, params); +} + bool BaseImageEncoder::writemulti(const std::vector& img_vec, const std::vector& params) { if(img_vec.size() > 1) @@ -157,6 +162,7 @@ bool BaseImageEncoder::writemulti(const std::vector& img_vec, const std::ve bool BaseImageEncoder::writeanimation(const Animation&, const std::vector& ) { + CV_LOG_WARNING(NULL, "No Animation encoder for specified file extension"); return false; } @@ -165,7 +171,7 @@ ImageEncoder BaseImageEncoder::newEncoder() const return ImageEncoder(); } -void BaseImageEncoder::throwOnEror() const +void BaseImageEncoder::throwOnError() const { if(!m_last_error.empty()) { diff --git a/modules/imgcodecs/src/grfmt_base.hpp b/modules/imgcodecs/src/grfmt_base.hpp index a90bd8a3de..ae5622528c 100644 --- a/modules/imgcodecs/src/grfmt_base.hpp +++ b/modules/imgcodecs/src/grfmt_base.hpp @@ -202,12 +202,11 @@ public: /** * @brief Encode and write the image data. - * This is a pure virtual function that must be implemented by derived classes. * @param img The Mat object containing the image data to be encoded. * @param params A vector of parameters controlling the encoding process (e.g., compression level). * @return true if the image was successfully written, false otherwise. */ - virtual bool write(const Mat& img, const std::vector& params) = 0; + virtual bool write(const Mat& img, const std::vector& params); /** * @brief Encode and write multiple images (e.g., for animated formats). @@ -236,7 +235,7 @@ public: * @brief Throw an exception based on the last error encountered during encoding. * This method can be used to propagate error conditions back to the caller. */ - virtual void throwOnEror() const; + virtual void throwOnError() const; protected: String m_description; ///< Description of the encoder (e.g., format name, capabilities). diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index 05b27e4ddd..5758cc38f7 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -514,19 +514,11 @@ GifEncoder::~GifEncoder() { close(); } -bool GifEncoder::isFormatSupported(int depth) const { - return depth == CV_8U; -} - -bool GifEncoder::write(const Mat &img, const std::vector ¶ms) { - std::vector img_vec(1, img); - return writemulti(img_vec, params); -} - bool GifEncoder::writeanimation(const Animation& animation, const std::vector& params) { if (animation.frames.empty()) { return false; } + CV_CheckDepthEQ(animation.frames[0].depth(), CV_8U, "GIF encoder supports only 8-bit unsigned images"); if (m_buf) { if (!strm.open(*m_buf)) { diff --git a/modules/imgcodecs/src/grfmt_gif.hpp b/modules/imgcodecs/src/grfmt_gif.hpp index 8f520745ba..8552718d00 100644 --- a/modules/imgcodecs/src/grfmt_gif.hpp +++ b/modules/imgcodecs/src/grfmt_gif.hpp @@ -83,9 +83,6 @@ public: GifEncoder(); ~GifEncoder() CV_OVERRIDE; - bool isFormatSupported(int depth) const CV_OVERRIDE; - - bool write(const Mat& img, const std::vector& params) CV_OVERRIDE; bool writeanimation(const Animation& animation, const std::vector& params) CV_OVERRIDE; ImageEncoder newEncoder() const CV_OVERRIDE; diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index f7a19c2bf5..825122304a 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -1412,6 +1412,9 @@ void PngEncoder::deflateRectFin(unsigned char* zbuf, uint32_t* zsize, int bpp, i bool PngEncoder::writeanimation(const Animation& animation, const std::vector& params) { + int frame_type = animation.frames[0].type(); + 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"); int compression_level = 6; int compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy bool isBilevel = false; @@ -1435,7 +1438,8 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vectorwritemulti(write_vec, params); - encoder->throwOnEror(); + encoder->throwOnError(); CV_Assert( code ); } catch (const cv::Exception& e)