Creating GIFs with OpenCV

In this tutorial, you will learn how to create animated GIFs using OpenCV, Python, and ImageMagick.

You’ll then combine all of these techniques to build a meme generator with OpenCV!

We all need a good laugh every now and then. And perhaps the best way to find LOLs is from memes.

Some of my favorite all-time memes include:

  • Kermit the Frog’s “But That’s None of My Business”
  • Grumpy Cat
  • Epic Fail
  • Good Guy Greg

But for me personally, none of these memes compare to the “Deal With It” meme (an example of which is at the top of this post), normally:

  1. Used as a response or retort to someone being disapproving of something you did/said
  2. Involving putting on sunglasses as you walk away, leaving them to “deal with it”

A few years ago I saw a lighthearted blog post from an author I now cannot remember on how to generate these memes with computer vision. Last week I couldn’t find the tutorial anywhere, so, as a blogger, computer vision expert, and meme connoisseur, I decided to create my own! (BTW, if you do happen to know the original source of the idea please let me know so I can credit the author UPDATE: I just found out the original article I’m thinking of is from Kirk Kaiser’s blog, MakeArtWithPython).

Building a Deal With It meme generator using OpenCV can teach us a number of valuable techniques used in practice, including:

  1. How to perform deep learning-based face detection
  2. How to use the dlib library to apply facial landmark detection and extract the eye regions
  3. How to take these two regions and compute the rotation angle between the eyes
  4. And finally, how to generate animated GIFs with OpenCV (with a little help from ImageMagick)

Today’s tutorial is meant to be fun, jovial, and entertaining — all while teaching you valuable
computer vision skills that are used in the real world.

To learn how to generate GIFs and memes with OpenCV, just keep reading!

Looking for the source code to this post?
Jump right to the downloads section.

Creating GIFs with OpenCV

In today’s blog post, we are going to create animated GIFs with OpenCV, dlib, and the ImageMagick toolbox.

In the first part of the tutorial, we’ll discuss the prerequisites and dependencies for this project, including how to properly configure your development environment.

From there we’ll review the project/directory structure for our OpenCV GIF creator.

Once we understand the project structure we’ll review (1) our configuration file, and (2) our Python script responsible for creating GIFs with OpenCV.

Finally, we’ll take a look at the results of our OpenCV GIF creator by building a meme generator for the popular “Deal With It” meme.

Prerequisites and dependencies

Figure 1: To create GIFs with OpenCV we’ll be taking advantage of OpenCV, dlib, and ImageMagick.

OpenCV and dlib

OpenCV will be used for face detection and basic image processing. Be sure to follow one of my OpenCV installation guides if you do not have OpenCV installed on your system.

Dlib will be utilized for detecting facial landmarks, enabling us to find the two eyes of a face and lower the sunglasses on top of them. You can install dlib with this guide.


If you aren’t familiar with ImageMagick, you should be. ImageMagick is a cross-platform command line based tool providing quite a bit of image processing functionality.

Want to turn a PNG/JPG into a PDF with a single command? No problem.

Have multiple images you’d like to turn into PDF slides? That’s easy.

Do you have a need to draw polygons, lines, and other shapes? Go for it.

What about batch color adjustments or adjusting the spatial dimensions of an entire image dataset in a single command? There’s no point in writing multiple lines of Python to use OpenCV for that.

Using ImageMagick we can generate GIF images via a set of input images as well.

To install ImageMagick on Ubuntu (or Raspbian) simply use apt:

Or if you’re on macOS, you may utilize HomeBrew:


Much of my blog posts and book/course content take advantage of my handy package of image processing convenience functions called imutils. You can install imutils on your system or virtual environment by using pip:

Project structure

Figure 2: Our OpenCV GIF generator project structure consists of two directories, a config file, and a single Python script.

Our project has two directories:

  • images/ : Our example input images that we wish to create the animated “Deal With it” GIF for. I’ve provided a selection of images of me but feel free to add your own.
  • assets/ : This folder contains our face detector, facial landmark detector and all images + associated masks. We’ll be overlaying the “sunglasses” and “Deal With It” text on input image with these assets.

Due to the number of configurable parameters, I decided to create a JSON configuration file (1) making it easier to edit parameters, and (2) requiring fewer command line arguments. All configuration parameters we’ll need for this project are contained inside config.json .

Today we’ll be reviewing both config.json  and .

The entire project is available for you to hack with in the “Downloads” section of today’s tutorial!

Generating GIFs with OpenCV

Let’s go ahead and get started implementing our OpenCV GIF generator!

Understanding our JSON configuration file

We’ll start with the JSON configuration file first and from there move into the Python script.

Go ahead and open a new file called config.json  and then insert the following key/value pairs:

Lines 2 and 3 are OpenCV’s deep learning face detector model files.

Line 4 is the path to dlib’s facial landmark predictor.

And now we have some image file paths:

Lines 5-8 are the paths to our Deal With It sunglasses, text, and associated masks for both, respectively — each of which is pictured below.

First our fancy sunglasses and associated mask:

Figure 3: Do you dislike pixelated sunglasses? Deal with it.

Figure 4: Are you wondering WTH this sunglasses mask is for? Deal with it — or just read the rest of the blog post for the answer.

And now our “DEAL WITH IT” text and mask:

Figure 5: Do you loathe Helvetica Neue Condensed? Deal with it.

Figure 6: This mask will allow for a border. Oh, I’m guessing you don’t want a border around your text. Deal with it.

We need the masks in order to overlay the corresponding image on the photo, a process I’ll show you how to perform later in today’s tutorial.

Now let’s set some parameters for our meme generator:

Below you can find definitions for each of the parameters:

  • min_confidence : Minimum required probability of a positive face detection.
  • steps : # of frames we’ll be generating for the output GIF. Each “step” will move the sunglasses from the top of the frame farther down towards the target (i.e., the eyes).
  • delay : # of hundredths of a second delay between each frame.
  • final_delay : # of hundredths of a second delay for the final frame (useful in this context since we want the “Deal With It” text to be visible for longer than the rest of the frames).
  • loop : Whether or not the GIF will loop (a value of zero indicates the GIF will loop forever, otherwise supply a positive integer for the number of times the GIF is allowed to loop).
  • temp_dir : Temporary output directory where each of the frames will be stored prior to constructing the final GIF image.

Memes, GIFs, and OpenCV

Now that we’ve created our JSON configuration file, let’s move into the actual code.

Open up a new file, name it , and insert the following code:

On Lines 2-12 we import our necessary packages. Notably, we’ll be using imutils, dlib, and OpenCV. To install these dependencies see the “Prerequisites and dependencies” section above.

Now that our script has the required packages at our disposal, let’s define the overlay_image  function:

The overlay_image  function overlays a foreground image ( fg ) on top of a background image ( bg ) at location coords  (which are (x, y)-coordinates), allowing for alpha transparency via the foreground mask fgMask .

To review OpenCV basics such as working with masks, be sure to give this tutorial a read.

To finish the overlay process we need to apply alpha blending:

This implementation of alpha blending is also covered on the LearnOpenCV blog.

In essence, we’ll convert the foreground, background, and alpha layers to floats in the range of [0, 1] (Lines 46-48). Then we’ll perform the actual alpha blending (Lines 51 and 52). And finally, we’ll add the foreground and background to obtain our output which we then return to the calling function (Lines 56-59).

Let’s also create a helper function that will enable us to generate a GIF from a set of image paths using ImageMagick and the convert  command:

The create_gif  function takes a set of images and assembles them in a GIF animation with the specified delay between frames and loops if necessary. All of this is handled by ImageMagick — we’re simply wrapping the command line convert  command with a function that dynamically handles different parameters.

To review the available command line arguments for the convert  command, refer to the documentation. As you can tell from the docs, there is a lot of functionality built into convert !

Specifically, in this function we:

  • Grab imagePaths  (Line 63).
  • Grab the last image path since it will have a separate delay (Line 66).
  • Reassign the imagePaths  to exclude the last image path (Line 67).
  • Assemble the command string with command line arguments and then instruct the operating system to execute convert  to create the GIF (Lines 72-75).

Let’s construct our own script’s command line arguments:

We have three command line arguments which are processed at runtime:

  • --config : The path to our JSON configuration file. We reviewed the config file in the previous section.
  • --image : Path to our input image. We’ll be creating our animation with this image (i.e. finding the face + adding sunglasses and then adding the “Deal With It” text).
  • --output : The target path to our output GIF.

Each of these arguments is required when you execute the script in your command line/terminal.

Let’s load the config file as well as our sunglasses + associated mask:

Here we load the configuration file (which can be accessed like a Python dictionary from here forward) on Line 89. Then we load the sunglasses image and sunglasses mask (Lines 90 and 91).

In case there are any remnants from a previous run of the script, we remove the temporary directory from disk and then recreate an empty temporary directory (Lines 96 and 97). The temporary folder will hold each individual frame in the GIF.

Now let’s load OpenCV’s deep learning face detector into memory:

To load OpenCV’s deep learning face detector, we call cv2.dnn.readNetFromCaffe  (Lines 101 and 102). The dnn  module is only accessible in OpenCV 3.3 or later. The face detector will enable us to detect the presence of faces in images:

Figure 7: The OpenCV DNN face detector finds faces in images.

Then, on Line 103, we load dlib’s facial landmark predictor. The facial landmark predictor, on the other hand, will enable us to localize structures of the face, such as eyes, eyebrows, nose, mouth, and jawline:

Figure 8: Shown on my face are overlays of the facial landmarks detected by dlib.

Later in this script, we’ll be extracting just the eye regions.

Moving on, let’s detect the face:

In this block we:

  • Load input image  (Line 106).
  • Construct a blob  to send through the face detector neural network (Lines 108 and 109). You can learn how OpenCV’s blobFromImage  works in this blog post.
  • Perform face detection (Lines 113 and 114).
  • Determine the face detection with the largest probability and check versus the confidence threshold (Lines 119-124). If the criteria isn’t met, we simply exit the script (Line 125). Otherwise, we’ll continue on.

Let’s extract the face and calculate facial landmarks:

To extract the face and find facial landmarks, we:

  • Extract the bounding box coordinates of the face (Lines 128 and 129).
  • Construct a dlib rectangle  object (Line 133) and apply facial landmark localization (Lines 134 and 135).
  • Extract the (x, y)-coordinates for the leftEyePts  and rightEyePts  respectively (Lines 139-142).

Given the coordinates of the eyes we can calculate where and how the sunglasses need to be placed:

First, we compute the center of each eye then calculate the angle between the eye centroids (Lines 145-151), just as if we were performing face alignment.

From there we can rotate (Line 155) and resize (Lines 161 and 162) the sunglasses. Notice that we’re using the rotate_bound function. We’re using rotate_bound  here instead of just rotate  to ensure OpenCV does not clip off parts of the image that are out of view after the affine transformation.

The same operations we applied to the sunglasses themselves need to be applied to the mask. But first, we need to convert the mask to grayscale and binarize it (Lines 170 and 171) since masks are always binary. Then we proceed to rotate and resize the mask exactly as we did to the sunglasses on Lines 172 and 173.

Note: Notice that we are using nearest neighbor interpolation when resizing the mask. This is because our mask should only have two values (0 and 255). Other interpolation methods may be more aesthetically pleasing to the eye but will actually be harmful to our mask. You can read more about nearest neighbor interpolation here.

The remaining three code blocks will create the frames of our GIF:

Our sunglasses will drop down from the top of the image. Each frame, in turn, will show the sunglasses progressively getting closer to the face until they cover the eyes. Using our JSON configuration variable "steps"  (the number of steps ), let’s generate evenly spaced y-values to place the sunglasses on each respective frame. This is handled on Lines 178 and 179 where we take advantage of NumPy’s linspace  function effortlessly.

Given the steps , we’ll iterate over them (they are simply y-values) to compute the translation for the sunglasses.

Glancing at Lines 189 and 190, you might be thinking, “WTH?”

Referring to my code documentation comment on the preceding lines, I simply explain that we’re ensuring that the sunglasses cover each entire eye rather than just reaching the point at which the eye center is. I determined the percentage values to calculate both the x-shift and y-shift on Lines 189 and 190 empirically. Line 191 is applied to ensure we do not have negative values.

Taking advantage of our overlay_image  function, we generate our output  frame on Lines 194 and 195.

Our final output frame is a special case as it as the “DEAL WITH IT” text which we’ll draw on by the frame by means of another masking operation:

If we’re on the last step (Line 199), we need to overlay our “DEAL WITH IT” image (Figure 5) — this is again a special case.

The “text” is actually another “image” here.

I opted to use an image since OpenCV’s font rendering capacities are quite limited, and furthermore, I wanted to add a drop shadow and border to the text which again, is something OpenCV cannot do.

The rest of the above code block loads both the image and mask and then proceeds to perform alpha blending in order to generate the final frame output.

And now we just need to output each frame to disk followed by creating our GIF:

On Lines 222-224 we write the loop’s output  frame to disk.

Once all frames have been generated, we call our create_gif  function to generate the GIF animation file (Lines 229 and 230). Remember, the create_gif  function is a wrapper that passes parameters to ImageMagick’s convert  command line tool.

Finally, we clean up by deleting the temporary output directory + individual image files.

OpenCV GIF Results

Now for the fun part — let’s take a look at what our meme generator created!

Make sure you use the “Downloads” section of this blog post to download the source code, example images, and deep learning models. From there, open up your terminal and execute the following command:

Figure 9: Generating GIFs with OpenCV and ImageMagick is easy with today’s Python script.

Here you can see a GIF created with OpenCV and ImageMagick showing that:

  1. My face has been correctly detected.
  2. My eyes have been localized and their centers computed.
  3. The sunglasses are correctly lowered down across my face.

Many of you know that I’m a huge Jurassic Park nerd and often include Jurassic Park references inside my books, courses, and tutorials.

Don’t like Jurassic Park?

Well, here’s my response:

Figure 10: A GIF made with OpenCV of me at the recent Jurassic Park: Fallen Kingdom movie showing.

Here I am at the opening of Jurassic Park: Fallen Kingdom sporting my special Jurassic Park shirt, pint glass, and collectors edition book.

Fun story:

Five or six years ago my now wife and I visited EPCOT Center in Walt Disney World in Orlando, FL.

We decided to take the trip to get away from the harsh Connecticut winters — we were in desperate need of sunlight.

Unfortunately, it rained the entire time we in FL and the weather barely exceeded 50F degrees.

Trisha took the following photo of me outside of the “Canadian gardens” at Epcot — she says I look like I vampire with my pale skin, dark clothes, and hood up, in contrast to the lush gardens behind me:

Figure 11: You can create a “Deal with it” GIF or another type of GIF using OpenCV and Python.

Trisha decided to post the photo on social media later that evening — I was left to deal with it.

For those of you that attended PyImageConf 2018 (read the recap here), you know that I’m always one for a joke. Here’s a good joke:

Q: Why did the rooster cross the road?

Figure 12: Even in low contrast, my face is detected and my sunglasses are put on by OpenCV, making for an excellent “Deal With It” meme/GIF.

A: I’m not telling you the answer — deal with it.

Finally, let’s wrap up today’s tutorial on creating GIFs with OpenCV with a good-hearted meme.

Approximately six years ago my dad and I adopted the family beagle, Jemma.

Here you can see tiny Jemma puppy sitting on my shoulder:

Figure 13: Jemma is adorable. Don’t think so? Then “Deal With It!” This GIF was made with OpenCV and Python.

Don’t think she’s a cute puppy? Deal with it.

Did you encounter an AttributeError?

Not to worry!

If you saw the following error:

Then you just need to upgrade the imutils package:

Why, you ask?

By default imutils.face_utils  will use the 68-point landmark detector built into dlib (as does this blog post). There is also a faster dlib 5-point landmark detector that now works with imutils as well. I recently updated imutils to support both (which is the reason why you may see the error).


In today’s tutorial, you learned how to create GIFs using OpenCV.

To keep the post lighthearted and fun, we learned how to use OpenCV to generate GIFs of the “Deal With It” meme, a popular meme (and also my personal favorite) that can be found in some capacity on nearly every social media website.

In order to build our meme generator, we leveraged computer vision and deep learning in a number of practical ways, including:

  • Face detection
  • Facial landmark prediction
  • Extracting regions of the face (in this case, the eyes)
  • Computing the angle between the eyes, a requirement for face alignment
  • Generating transparent overlays via alpha blending

Finally, we took our set of generated images and then created an animated GIF using OpenCV and ImageMagick.

I hope you enjoyed today’s tutorial on GIF creation with OpenCV!

If you enjoyed it, please do leave a comment and let me know.

And if you didn’t enjoy the tutorial, whatever, deal with it 😉

To download the source code to today’s post, and be notified when future tutorials are published here on PyImageSearch, just enter your email address in the form below!


If you would like to download the code and images used in this post, please enter your email address in the form below. Not only will you get a .zip of the code, I’ll also send you a FREE 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning. Inside you'll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL! Sound good? If so, enter your email address and I’ll send you the code immediately!

, , , ,

24 Responses to Creating GIFs with OpenCV

  1. Roberto November 5, 2018 at 12:44 pm #

    Ahahah you are the best Adrian!!!??? Deal with it

    • Adrian Rosebrock November 5, 2018 at 1:08 pm #

      Thanks Roberto, I’m glad you liked it!

  2. Nick Hulea November 5, 2018 at 12:56 pm #

    Nice work this is great haha!

    • Adrian Rosebrock November 5, 2018 at 1:08 pm #

      Thanks so much Nick! 🙂

  3. Dennis November 5, 2018 at 2:53 pm #

    Nice one!!! Good tutorial! Laughing can be the best medice to deal with it 8D

    • Adrian Rosebrock November 5, 2018 at 3:02 pm #

      Thanks Dennis!

  4. sinhue November 5, 2018 at 9:24 pm #

    What I liked the most about this post was the PD line of the mail from the newsletter:

    P.S. Didn’t like today’s tutorial?

    • Adrian Rosebrock November 6, 2018 at 1:08 pm #

      Hah! I’m glad you liked it 🙂

  5. Jane November 5, 2018 at 9:57 pm #

    Nice work

    • Adrian Rosebrock November 6, 2018 at 1:07 pm #

      Thanks Jane!

  6. Vishal Sanserwal November 5, 2018 at 10:11 pm #


    Have you tried Darknet YOLO for object classification and recognition??

    • Adrian Rosebrock November 6, 2018 at 1:07 pm #

      Yes, I have. I’ll actually be publishing a tutorial on YOLO next week. Stay tuned!

  7. Dark Knight November 5, 2018 at 10:59 pm #

    Hi Adrian!
    You are really doing great work for beginners. These things are not taught in universities.
    I am really thankful to you because before, i didn’t find any proper platform for learning basics and more than that of computer vision.
    Lot of respect for you <3
    Sorry if my English was bad.. Respect from a Pakistani 🙂

    • Adrian Rosebrock November 6, 2018 at 1:06 pm #

      Thank you so much for the kind words! I’m really happy you enjoyed the tutorial and found it helpful 🙂

  8. Johann November 6, 2018 at 11:18 pm #

    Fantastic post, as usual, Adrian. You really motivate me to keep working!

    • Adrian Rosebrock November 9, 2018 at 6:30 am #

      Thanks Johann! 🙂

  9. James November 7, 2018 at 4:46 pm #

    In Windows, you have to modify line 75 in create_gif() to: cmd = magick convert ... or you will get an error invalid parameter. Learned a lot as always, thanks Adrian!

    • Adrian Rosebrock November 9, 2018 at 6:30 am #

      Thank you for sharing, James! I’m sure Windows readers will be very appreciative 🙂

    • Clink December 2, 2018 at 8:26 am #

      Thanks a lot, James. I was stuck and this info helped me 🙂

  10. Andy Woods September 10, 2019 at 5:59 am #

    If you have a png with transparency, you can use the below (removes need for separate transparency file).

    sg = cv2.imread(config[“sunglasses”], -1)

    b, g, r, a = cv2.split(sg)
    sg = cv2.merge((b, g, r))

    sgMask = a

    • Adrian Rosebrock September 12, 2019 at 11:33 am #

      Thanks for sharing, Andy!

  11. sagartrivedi September 12, 2019 at 5:37 am #

    Creating GIFs is difficult. I tried but I can’t create the perfect one GIF. But after reading your blog I can solve my problem. Thank you so much for sharing this helpful content. I appreciate your blog. Thanks and keep sharing.

    • Adrian Rosebrock September 12, 2019 at 11:27 am #

      You’re welcome!

  12. Susan November 21, 2019 at 10:43 am #

    Hi, Adrian. Your post is helpful. Thanks a lot!
    But I want to smooth out the edges that are blurry in the instance segmentation code.
    I found the alpha blend function in your post, and I’d like to combine these two codes. I don’t know how to put it together.
    Can you give me a hint?

Before you leave a comment...

Hey, Adrian here, author of the PyImageSearch blog. I'd love to hear from you, but before you submit a comment, please follow these guidelines:

  1. If you have a question, read the comments first. You should also search this page (i.e., ctrl + f) for keywords related to your question. It's likely that I have already addressed your question in the comments.
  2. If you are copying and pasting code/terminal output, please don't. Reviewing another programmers’ code is a very time consuming and tedious task, and due to the volume of emails and contact requests I receive, I simply cannot do it.
  3. Be respectful of the space. I put a lot of my own personal time into creating these free weekly tutorials. On average, each tutorial takes me 15-20 hours to put together. I love offering these guides to you and I take pride in the content I create. Therefore, I will not approve comments that include large code blocks/terminal output as it destroys the formatting of the page. Kindly be respectful of this space.
  4. Be patient. I receive 200+ comments and emails per day. Due to spam, and my desire to personally answer as many questions as I can, I hand moderate all new comments (typically once per week). I try to answer as many questions as I can, but I'm only one person. Please don't be offended if I cannot get to your question
  5. Do you need priority support? Consider purchasing one of my books and courses. I place customer questions and emails in a separate, special priority queue and answer them first. If you are a customer of mine you will receive a guaranteed response from me. If there's any time left over, I focus on the community at large and attempt to answer as many of those questions as I possibly can.

Thank you for keeping these guidelines in mind before submitting your comment.

Leave a Reply