Handling Animated Image Files#

Original author

Suleyman Turkmen (with help of ChatGPT)

Compatibility

OpenCV >= 4.11

Goal#

In this tutorial, you will learn how to:

  • Use cv::imreadanimation to load frames from animated image files.

  • Understand the structure and parameters of the cv::Animation structure.

  • Display individual frames from an animation.

  • Use cv::imwriteanimation to write cv::Animation to a file.

Source Code#

  • Downloadable code: Click here

  • Code at a glance:

    #include <opencv2/highgui.hpp>
    #include <opencv2/imgcodecs.hpp>
    #include <opencv2/imgproc.hpp>
    #include <iostream>
    
    using namespace cv;
    
    int main( int argc, const char** argv )
    {
        std::string filename = argc > 1 ? argv[1] : "animated_image.webp";
    
        if (argc == 1)
        {
            Animation animation_to_save;
            Mat image(128, 256, CV_8UC4, Scalar(150, 150, 150, 255));
            int duration = 200;
    
            for (int i = 0; i < 10; ++i) {
                animation_to_save.frames.push_back(image.clone());
                putText(animation_to_save.frames[i], format("Frame %d", i), Point(30, 80), FONT_HERSHEY_SIMPLEX, 1.5, Scalar(255, 100, 0, 255), 2);
                animation_to_save.durations.push_back(duration);
            }
            imwriteanimation("animated_image.webp", animation_to_save, { IMWRITE_WEBP_QUALITY, 100 });
        }
    
        Animation animation;
    
        bool success = imreadanimation(filename, animation);
        if (!success) {
            std::cerr << "Failed to load animation frames\n";
            return -1;
        }
    
        while (true)
        for (size_t i = 0; i < animation.frames.size(); ++i) {
            imshow("Animation", animation.frames[i]);
            int key_code = waitKey(animation.durations[i]); // Delay between frames
            if (key_code == 27)
                exit(0);
        }
    
        return 0;
    }
    
  • Downloadable code: Click here

  • Code at a glance:

    import cv2 as cv
    import numpy as np
    
    def main(filename):
        if filename == "animated_image.webp":
            # Create an Animation instance to save
            animation_to_save = cv.Animation()
    
            # Generate a base image with a specific color
            image = np.full((128, 256, 4), (150, 150, 150, 255), dtype=np.uint8)
            duration = 200
            frames = []
            durations = []
    
            # Populate frames and durations in the Animation object
            for i in range(10):
                frame = image.copy()
                cv.putText(frame, f"Frame {i}", (30, 80), cv.FONT_HERSHEY_SIMPLEX, 1.5, (255, 100, 0, 255), 2)
                frames.append(frame)
                durations.append(duration)
    
            animation_to_save.frames = frames
            animation_to_save.durations = durations
    
            # Write the animation to file
            cv.imwriteanimation(filename, animation_to_save, [cv.IMWRITE_WEBP_QUALITY, 100])
    
        animation = cv.Animation()
    
        success, animation = cv.imreadanimation(filename)
        if not success:
            print("Failed to load animation frames")
            return
    
        while True:
            for i, frame in enumerate(animation.frames):
                cv.imshow("Animation", frame)
                key_code = cv.waitKey(animation.durations[i])
                if key_code == 27:  # Exit if 'Esc' key is pressed
                    return
    
    if __name__ == "__main__":
        import sys
        main(sys.argv[1] if len(sys.argv) > 1 else "animated_image.webp")
    

Explanation#

Initializing the Animation Structure#

Initialize a cv::Animation structure to hold the frames from the animated image file.

Animation animation;
animation = cv.Animation()

Loading Frames#

Use cv::imreadanimation to load frames from the specified file. Here, we load all frames from an animated WebP image.

bool success = imreadanimation(filename, animation);
if (!success) {
    std::cerr << "Failed to load animation frames\n";
    return -1;
}
success, animation = cv.imreadanimation(filename)
if not success:
    print("Failed to load animation frames")
    return

Displaying Frames#

Each frame in the animation.frames vector can be displayed as a standalone image. This loop iterates through each frame, displaying it in a window with a short delay to simulate the animation.

Note: Frame durations in cv::Animation are expressed in milliseconds. When displaying frames manually using cv::waitKey, make sure to use the corresponding duration value to preserve the original animation timing.

while (true)
for (size_t i = 0; i < animation.frames.size(); ++i) {
    imshow("Animation", animation.frames[i]);
    int key_code = waitKey(animation.durations[i]); // Delay between frames
    if (key_code == 27)
        exit(0);
}
while True:
    for i, frame in enumerate(animation.frames):
        cv.imshow("Animation", frame)
        key_code = cv.waitKey(animation.durations[i])
        if key_code == 27:  # Exit if 'Esc' key is pressed
            return

Saving Animation#

if (argc == 1)
{
    Animation animation_to_save;
    Mat image(128, 256, CV_8UC4, Scalar(150, 150, 150, 255));
    int duration = 200;

    for (int i = 0; i < 10; ++i) {
        animation_to_save.frames.push_back(image.clone());
        putText(animation_to_save.frames[i], format("Frame %d", i), Point(30, 80), FONT_HERSHEY_SIMPLEX, 1.5, Scalar(255, 100, 0, 255), 2);
        animation_to_save.durations.push_back(duration);
    }
    imwriteanimation("animated_image.webp", animation_to_save, { IMWRITE_WEBP_QUALITY, 100 });
}
if filename == "animated_image.webp":
    # Create an Animation instance to save
    animation_to_save = cv.Animation()

    # Generate a base image with a specific color
    image = np.full((128, 256, 4), (150, 150, 150, 255), dtype=np.uint8)
    duration = 200
    frames = []
    durations = []

    # Populate frames and durations in the Animation object
    for i in range(10):
        frame = image.copy()
        cv.putText(frame, f"Frame {i}", (30, 80), cv.FONT_HERSHEY_SIMPLEX, 1.5, (255, 100, 0, 255), 2)
        frames.append(frame)
        durations.append(duration)

    animation_to_save.frames = frames
    animation_to_save.durations = durations

    # Write the animation to file
    cv.imwriteanimation(filename, animation_to_save, [cv.IMWRITE_WEBP_QUALITY, 100])

Summary#

The cv::imreadanimation and cv::imwriteanimation functions make it easy to work with animated image files by loading frames into a cv::Animation structure, allowing frame-by-frame processing. With these functions, you can load, process, and save frames from animated image files like GIF, AVIF, APNG, and WebP.