mirror of
https://github.com/opencv/opencv.git
synced 2025-08-06 06:26:29 +08:00
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
This commit is contained in:
parent
59c3b6c995
commit
e8e49ab7a8
@ -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
|
||||
}
|
||||
|
131
modules/imgcodecs/perf/perf_decode_encode.cpp
Normal file
131
modules/imgcodecs/perf/perf_decode_encode.cpp
Normal file
@ -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
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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).
|
||||
|
@ -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<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)) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user