This backports all changes to the PNG reading code since 4.11.0, mainly to get the fix for #2345306, PNG read fails on big-endian. It's difficult to backport just that fix to 4.11.0 as it's built on all the earlier changes, and the earlier changes look like useful fixes too in any case. See upstream https://github.com/opencv/opencv/issues/26913 and https://github.com/opencv/opencv/pull/26915 . Resolves: rhbz#2345306 Signed-off-by: Adam Williamson <awilliam@redhat.com>
367 lines
13 KiB
Diff
367 lines
13 KiB
Diff
From 8aa1086ab475ef0040d3865e49edd6feba4eb7d3 Mon Sep 17 00:00:00 2001
|
|
From: Suleyman TURKMEN <sturkmen@hotmail.com>
|
|
Date: Tue, 4 Feb 2025 12:21:55 +0300
|
|
Subject: [PATCH 09/10] Merge pull request #26872 from
|
|
sturkmen72:ImageEncoders_revisions
|
|
|
|
Performance tests for image encoders and decoders and code cleanup #26872
|
|
|
|
### 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
|
|
- [ ] 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
|
|
---
|
|
modules/highgui/src/window_w32.cpp | 10 +-
|
|
modules/imgcodecs/perf/perf_decode_encode.cpp | 131 ++++++++++++++++++
|
|
modules/imgcodecs/src/grfmt_avif.cpp | 5 -
|
|
modules/imgcodecs/src/grfmt_avif.hpp | 1 -
|
|
modules/imgcodecs/src/grfmt_base.cpp | 8 +-
|
|
modules/imgcodecs/src/grfmt_base.hpp | 5 +-
|
|
modules/imgcodecs/src/grfmt_gif.cpp | 10 +-
|
|
modules/imgcodecs/src/grfmt_gif.hpp | 3 -
|
|
modules/imgcodecs/src/grfmt_png.cpp | 6 +-
|
|
modules/imgcodecs/src/loadsave.cpp | 2 +-
|
|
10 files changed, 155 insertions(+), 26 deletions(-)
|
|
create mode 100644 modules/imgcodecs/perf/perf_decode_encode.cpp
|
|
|
|
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<std::string> Decode;
|
|
+typedef perf::TestBaseWithParam<std::string> 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<uchar> 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<uchar> 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<uchar> buf;
|
|
+
|
|
+ TEST_CYCLE() imencode(GetParam(), src, buf);
|
|
+
|
|
+ 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();
|
|
+}
|
|
+
|
|
+PERF_TEST_P(Encode, multi, testing::ValuesIn(exts_multi))
|
|
+{
|
|
+ String filename = getDataPath("perf/1920x1080.png");
|
|
+ vector<Mat> 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<uchar> 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<double>(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<int> ¶ms) {
|
|
- std::vector<Mat> img_vec(1, img);
|
|
- return writemulti(img_vec, params);
|
|
-}
|
|
-
|
|
bool AvifEncoder::writeanimation(const Animation& animation,
|
|
const std::vector<int> ¶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<int>& params) CV_OVERRIDE;
|
|
bool writeanimation(const Animation& animation, const std::vector<int>& 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<uchar>& buf )
|
|
return true;
|
|
}
|
|
|
|
+bool BaseImageEncoder::write(const Mat &img, const std::vector<int> ¶ms) {
|
|
+ std::vector<Mat> img_vec(1, img);
|
|
+ return writemulti(img_vec, params);
|
|
+}
|
|
+
|
|
bool BaseImageEncoder::writemulti(const std::vector<Mat>& img_vec, const std::vector<int>& params)
|
|
{
|
|
if(img_vec.size() > 1)
|
|
@@ -157,6 +162,7 @@ bool BaseImageEncoder::writemulti(const std::vector<Mat>& img_vec, const std::ve
|
|
|
|
bool BaseImageEncoder::writeanimation(const Animation&, const std::vector<int>& )
|
|
{
|
|
+ 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<int>& params) = 0;
|
|
+ virtual bool write(const Mat& img, const std::vector<int>& 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 5a65ae04b1..b0533b644f 100644
|
|
--- a/modules/imgcodecs/src/grfmt_gif.cpp
|
|
+++ b/modules/imgcodecs/src/grfmt_gif.cpp
|
|
@@ -488,19 +488,11 @@ GifEncoder::~GifEncoder() {
|
|
close();
|
|
}
|
|
|
|
-bool GifEncoder::isFormatSupported(int depth) const {
|
|
- return depth == CV_8U;
|
|
-}
|
|
-
|
|
-bool GifEncoder::write(const Mat &img, const std::vector<int> ¶ms) {
|
|
- std::vector<Mat> img_vec(1, img);
|
|
- return writemulti(img_vec, params);
|
|
-}
|
|
-
|
|
bool GifEncoder::writeanimation(const Animation& animation, const std::vector<int>& 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<int>& params) CV_OVERRIDE;
|
|
bool writeanimation(const Animation& animation, const std::vector<int>& 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<int>& 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::vector<in
|
|
}
|
|
}
|
|
|
|
- CV_UNUSED(isBilevel);
|
|
+ if (isBilevel)
|
|
+ CV_LOG_WARNING(NULL, "IMWRITE_PNG_BILEVEL parameter is not supported yet.");
|
|
uint32_t first =0;
|
|
uint32_t loops= animation.loop_count;
|
|
uint32_t coltype= animation.frames[0].channels() == 1 ? PNG_COLOR_TYPE_GRAY : animation.frames[0].channels() == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
|
|
diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp
|
|
index ec25f8c610..37b0701c8a 100644
|
|
--- a/modules/imgcodecs/src/loadsave.cpp
|
|
+++ b/modules/imgcodecs/src/loadsave.cpp
|
|
@@ -1372,7 +1372,7 @@ bool imencode( const String& ext, InputArray _img,
|
|
else
|
|
code = encoder->writemulti(write_vec, params);
|
|
|
|
- encoder->throwOnEror();
|
|
+ encoder->throwOnError();
|
|
CV_Assert( code );
|
|
}
|
|
catch (const cv::Exception& e)
|
|
--
|
|
2.48.1
|
|
|