Skip to content

Performance tests for writing and reading animations #27605

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Aug 7, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 152 additions & 2 deletions modules/imgcodecs/perf/perf_decode_encode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,82 @@
namespace opencv_test
{

#ifdef HAVE_PNG

using namespace perf;

static Animation makeCirclesAnimation(Size size = Size(320, 240), int type = CV_8UC4, int nbits = 8, int frameCount = 40)
{
struct AnimatedCircle {
cv::Point2f pos;
cv::Point2f velocity;
float radius;
float radius_speed;
cv::Scalar color;
cv::Scalar border_color;
};

const int numCircles = 80;
const int maxval = (1 << nbits) - 1;

cv::RNG rng = theRNG();
std::vector<AnimatedCircle> circles;
Animation animation;

// Initialize animated circles
for (int i = 0; i < numCircles; ++i) {
AnimatedCircle c;
c.pos = cv::Point2f(rng.uniform(0.f, (float)size.width),
rng.uniform(0.f, (float)size.height));
c.velocity = cv::Point2f(rng.uniform(-2.f, 2.f),
rng.uniform(-2.f, 2.f));
c.radius = rng.uniform(10.f, 40.f);
c.radius_speed = rng.uniform(-0.5f, 0.5f);
c.color = cv::Scalar(rng.uniform(0, maxval),
rng.uniform(0, maxval),
rng.uniform(0, maxval),
rng.uniform(230, maxval));
c.border_color = c.color;
circles.push_back(c);
}

// Generate frames
for (int frame = 0; frame < frameCount; ++frame) {
cv::Mat img(size, type, cv::Scalar(20, 0, 10, 128));

for (size_t i = 0; i < circles.size(); ++i) {
AnimatedCircle& c = circles[i];

// Update position
c.pos += c.velocity;

// Bounce on edges
if (c.pos.x < 0 || c.pos.x > size.width) c.velocity.x *= -1;
if (c.pos.y < 0 || c.pos.y > size.height) c.velocity.y *= -1;

// Update radius
c.radius += c.radius_speed;
if (c.radius < 10.f || c.radius > 80.f) {
c.radius_speed *= -1;
c.radius = std::max(10.f, std::min(c.radius, 80.f));
}

c.color = c.color - Scalar(c.velocity.x, 0, c.velocity.y, rng.uniform(1, 4));

// Draw
cv::circle(img, c.pos, (int)c.radius, c.color, cv::FILLED, cv::LINE_AA);
cv::circle(img, c.pos, (int)c.radius, c.border_color, 1, cv::LINE_AA);
}

animation.frames.push_back(img);
animation.durations.push_back(20); // milliseconds
}

for (int i = (int)animation.frames.size() - 1; i >= 0; --i) {
animation.frames.push_back(animation.frames[i].clone());
animation.durations.push_back(15);
}
return animation;
}

typedef perf::TestBaseWithParam<std::string> Decode;
typedef perf::TestBaseWithParam<std::string> Encode;

Expand All @@ -32,7 +104,9 @@ const string exts[] = {
#ifdef HAVE_JPEGXL
".jxl",
#endif
#ifdef HAVE_PNG
".png",
#endif
#ifdef HAVE_IMGCODEC_PXM
".ppm",
#endif
Expand All @@ -54,7 +128,9 @@ const string exts_multi[] = {
#ifdef HAVE_IMGCODEC_GIF
".gif",
#endif
#ifdef HAVE_PNG
".png",
#endif
#ifdef HAVE_TIFF
".tiff",
#endif
Expand All @@ -63,6 +139,23 @@ const string exts_multi[] = {
#endif
};

const string exts_anim[] = {
#ifdef HAVE_AVIF
".avif",
#endif
#ifdef HAVE_IMGCODEC_GIF
".gif",
#endif
#ifdef HAVE_PNG
".png",
#endif
#ifdef HAVE_WEBP
".webp",
#endif
};

#ifdef HAVE_PNG

PERF_TEST_P(Decode, bgr, testing::ValuesIn(exts))
{
String filename = getDataPath("perf/1920x1080.png");
Expand Down Expand Up @@ -126,6 +219,63 @@ PERF_TEST_P(Encode, multi, testing::ValuesIn(exts_multi))

SANITY_CHECK_NOTHING();
}

#endif // HAVE_PNG

PERF_TEST_P(Encode, animation, testing::ValuesIn(exts_anim))
{
Animation animation = makeCirclesAnimation();

TEST_CYCLE()
{
vector<uchar> buf;
EXPECT_TRUE(imencodeanimation(GetParam().c_str(), animation, buf));
}

SANITY_CHECK_NOTHING();
}

PERF_TEST_P(Encode, multi_page, testing::ValuesIn(exts_multi))
{
Animation animation = makeCirclesAnimation();

TEST_CYCLE()
{
vector<uchar> buf;
EXPECT_TRUE(imencodemulti(GetParam().c_str(), animation.frames, buf));
}

SANITY_CHECK_NOTHING();
}

PERF_TEST_P(Decode, animation, testing::ValuesIn(exts_anim))
{
Animation animation = makeCirclesAnimation();
vector<uchar> buf;
ASSERT_TRUE(imencodeanimation(GetParam().c_str(), animation, buf));

TEST_CYCLE()
{
Animation tmp_animation;
EXPECT_TRUE(imdecodeanimation(buf, tmp_animation));
}

SANITY_CHECK_NOTHING();
}

PERF_TEST_P(Decode, multi_page, testing::ValuesIn(exts_multi))
{
Animation animation = makeCirclesAnimation();
vector<uchar> buf;
ASSERT_TRUE(imencodemulti(GetParam().c_str(), animation.frames, buf));

TEST_CYCLE()
{
vector<Mat> tmp_frames;
EXPECT_TRUE(imdecodemulti(buf, IMREAD_UNCHANGED, tmp_frames));
}

SANITY_CHECK_NOTHING();
}

} // namespace
Loading