# Annotating Video Clips With MoviePy

> Introduce yourself to MoviePy through a simple example to add text to video.

- Canonical: https://dennisokeeffe.com/blog/2021-07-31-annotating-video-clips-with-moviepy
- Published: 2021-07-31
- Updated: 2021-07-31
- Tags: beginner, python, 100daysofpython

> This is Day 12 of the #100DaysOfPython challenge.

This post will use the [MoviePy](https://zulko.github.io/moviepy) library to annotate some captions onto a short video I recorded about my site [`workingoutloud.dev`](https://www.workingoutloud.dev/).

## Prerequisites

1. Familiarity with [Pipenv](https://pipenv.pypa.io/en/latest/). See [here](https://blog.dennisokeeffe.com/blog/2020-07-04-abcs-of-pipenv) for my post on Pipenv.

## Getting started

Let's create the `hello-moviepy` directory and install Pillow.

```s
# Make the `hello-moviepy` directory
$ mkdir hello-moviepy
$ cd hello-moviepy
# We will write everything in main.py
$ touch main.py
# Add this stage, you will need to add you video clip - I added mine to the `hello-moviepy` directory
$ cp path/to/wol-dev.mp4 .

# Init the virtual environment
$ pipenv --three
$ pipenv install moviepy
```

At this stage, we can write our script to annotate the video.

## Annotating the video

As an example, here is a Gif of my video that I am editing:

![Original video](https://cdn.dennisokeeffe.com/assets/2021-07-31-annotating-video-clips-with-moviepy/original.gif)

The video is a short clip of my [`workingoutloud.dev`](https://www.workingoutloud.dev/) site. It is around 9 seconds long and I want to add some text to it.

From watching the video, there are three things that are essentially in the demo:

1. Viewing tasks and subtasks.
2. Sorting subtasks.
3. Opening the current goals.

These occur at the 0:00, 0:03 and 0:06 second mark respectively.

We can add in some captions for each of these easily thanks to MoviePy.

In `main.py`, add the following:

```py
from moviepy.editor import VideoFileClip, TextClip, CompositeVideoClip

video = VideoFileClip("wol-dev.mp4")
```

In the above code, we simply import the required modules and then we create a `VideoFileClip` object from the video file (relative to the project root directory).

We now want to create a `TextClip` object for each of the three points in the video.

According to the [MoviePy TextClip docs](https://zulko.github.io/moviepy/ref/VideoClip/VideoClip.html#textclip), we can see the `TextClip` constructor can take some relevant arguments around the text, font, color and font size.

The `TextClip` instance itself then has access to methods to set the position, duration and start time. We will abstract our sensible defaults into a function and implement this:

```py
def text_clip(text: str, duration: int, start_time: int = 0):
    """Return a description string on the bottom-left of the video

    Args:
				text (str): Text to show
				duration (int): Duration of clip
				start_time (int, optional): Time in video to start at (in seconds). Defaults to 0.

    Returns:
				moviepy.editor.TextClip: A instance of a TextClip
    """
    return (TextClip(text, font="Arial", fontsize=24, color='black')
            .set_position((20, video.h - 44))
            .set_duration(duration)
            .set_start(start_time))


# Make the text. Many more options are available.
text_clip_one = text_clip("Create tasks and subtasks", 3)
text_clip_two = text_clip("Sort subtasks by table header", 3, 3)
text_clip_three = text_clip("View goals", 3, 6)
```

After writing out the helper method, we are creating three `TextClip` instances for each of our different annotations that we wish to add to the video.

Finally, we can compose them all together into a `CompositeVideoClip` object and write out the video file.

> I am also writing out a `.gif` file to use as a preview of the video on this post.

```py
# Overlay text on video
result = CompositeVideoClip(
    [video, text_clip_one, text_clip_two, text_clip_three])
result.write_videofile("wol_dev_edited.mp4", fps=25)
result.write_gif("wol_dev_edited.gif", fps=8)
```

We can now run our script with `python main.py`. There will be some progress bars to indicate the progress.

After the script has completed, we can now see `wol_dev_edited.mp4` and `wol_dev_edited.gif` in the `hello-moviepy` directory.

The outcome looks like so:

![Annotated video](https://cdn.dennisokeeffe.com/assets/2021-07-31-annotating-video-clips-with-moviepy/wol_dev_edited.gif)

## Summary

Today's post demonstrated how to use the `MoviePy` package to add some text captions to a video programmatically.

I will be hoping to make use of this to help annotate some of my videos that I am working through to save some time in future.

The final code looks like so:

```py
from moviepy.editor import VideoFileClip, TextClip, CompositeVideoClip

video = VideoFileClip("wol-dev.mp4")


def text_clip(text: str, duration: int, start_time: int = 0):
    """Return a description string on the bottom-left of the video

    Args:
				text (str): Text to show
				duration (int): Duration of clip
				start_time (int, optional): Time in video to start at (in seconds). Defaults to 0.

    Returns:
				moviepy.editor.TextClip: A instance of a TextClip
    """
    return (TextClip(text, font="Arial", fontsize=24, color='black')
            .set_position((20, video.h - 44))
            .set_duration(duration)
            .set_start(start_time))


# Make the text. Many more options are available.
text_clip_one = text_clip("Create tasks and subtasks", 3)
text_clip_two = text_clip("Sort subtasks by table header", 3, 3)
text_clip_three = text_clip("View goals", 3, 6)

# Overlay text on video
result = CompositeVideoClip(
    [video, text_clip_one, text_clip_two, text_clip_three])
result.write_videofile("wol_dev_edited.mp4", fps=25)
result.write_gif("wol_dev_edited.gif", fps=8)
```

## Resources and further reading

1. [The ABCs of Pipenv](https://blog.dennisokeeffe.com/blog/2020-07-04-abcs-of-pipenv)
2. [MoviePy TextClip docs](https://zulko.github.io/moviepy/ref/VideoClip/VideoClip.html#textclip)
3. [Pipenv](https://pipenv.pypa.io/en/latest/)
4. [MoviePy](https://zulko.github.io/moviepy)
5. [GitHub - project code]()

_Photo credit: [`element5digital`](https://unsplash.com/@element5digital)_
