Removing obnoxious logos from videos

When archiving videos, often times there is a logo of some sort that really distracts. Or even worse, is put in by someone who had nothing to do with actually making the video like they are taking credit for the hard work of the production company.

I ran into a case of the latter, and wanted to remove the obnoxious lettering they added and make it look as clean as possible.

ffmpeg has two filters for doing just that. There is the delogo filter and the removelogo filter. The delogo filter is a rectangular bound space that is very simple and straight forward to use, but doesn’t look great if the logo isn’t rectangular (as in the case of burned in words).

Simple Rectangular Logos

If your logo is a simple square/rectangle, then you would surround it using a command similar to:

ffmpeg -i movie.mp4 -vf delogo=x=90:y=945:w=85:h=85

This works like you might expect. It applies the delogo filter starting 90 pixels across, and 945 pixels down, with a width and a height of 85 pixels. A helpful trick to finding the proper boundaries if you aren’t sure is to call ffplay with the same parameters, and add :show=1 to the definition to give you a bound box you can see. e.g.

ffplay -i movie.mp4 -vf delogo=x=90:y=945:w=85:h=85:show=1

This will play back the video, removing the logo on the fly, and show you exactly where it is removing so you can adjust until you find the best values.

More Complex Logos (like text)

For larger, less uniform logos, the delogo filter may produce more distortion than is necessary. There is a removelogo filter that uses an image file that contains a mask to use for the area to apply the removal to. Basically, create a PNG file the same size as a frame of the video, and have black where there is proper video, and white where there is logo (or any other undesirable thing in the video for that matter). You can get smoother results feathering the edges using grays.

Creating the mask was an interesting challenge. So I decided to invoke some of the magic in ImageMagick to do just that. This technique requires that the object/logo you are removing is in every frame, and remains constant. You will likely get bad results otherwise.

The first thing we need to do is extract a bunch of frames from the video to analyze. The more you use, the better, though the more you have also increases the processing time. Ideally you will have a wide variance in all the areas that aren’t the logo, and the logo itself will remain constant.

Fortunately, ffmpeg can dump the video as image files quite easily:

ffmpeg -ss 10 -t 60 -v 0 -i movie.mp4 -f image2 movie-%06d.jpg

In this example, it will start 10 seconds into the video (to allow for a few frames at the beginning that may not contain the logo), and will generate 60 seconds worth of frames. The number of frames you actually generate will depend on the frame rate of the video. i.e. if you have NTSC video, you’ll get 29.97 frames per second, or approximately 1798 frames to process. Adjust the start time and duration to give you frames that all contain the logo, and have enough to give you usable results. You will be able to look at the mask that is created to make sure.

Now that we have a big block of frames to analyze, we need to calculate the minimum and maximum (the time to run these will depend on how many frames you generated, and how fast your machine is):

convert movie-*.jpg -evaluate-sequence min min.png
convert movie-*.jpg -evaluate-sequence max max.png

This will generate two PNG files that we can then use to generate the mask file:

convert max.png min.png -compose difference -composite -separate -evaluate-sequence max -auto-level -negate mask.png

This should give you a file where the logo stands out brightly, and the rest of the area will be black or at least near black. From here, we can then use a threshold algorithm to create the final mask.

convert -channel RGBA -colorspace gray -threshold 50% -blur 0x2 mask.png finalmask.png

You may want to play with the threshold percentage to get the best results, but 50% would be a good starting point. Also, blurring the mask adds feathering to the edges so that the logo removal is smoother. When I did not use the blur, it removed the logo, but the distortion was distinct enough you could still infer the shape of the words that were removed, and it was more distracting than having the feathered edges.

If you find that you end up with a lot of extra noise in the mask file, you can clean it up in an image editing program and “erase” the unwanted noise. Since that method is more hands on, I needed a way to achieve the same results in a more automated fashion. To achieve this, you can create a solid black video frame:

convert -size 640x480 canvas:black  canvas_black.png

Just specify the same size as your video source. Now, to use the frame effectively, you can crop out the area of the logo as a smaller image file:

convert mask.png -crop 0x0+296+425 maskarea.png

In my case, the logo was in the lower right, and was rather large. You will have to adjust the numbers to crop out the area of your logo. I cropped all the way to the edges to make it easier to overlay. Now that I have the small section of just the logo, I can overlay it on top of the solid black canvas I created:

convert canvas_black.png maskarea.png -gravity southeast -composite blankedmask.png

This creates a composite of the two images, with the mask placed in the “southeast” i.e. lower right corner of the canvas. Now I have a blacked out image, with only the logo area. Apply the same threshold command as above on the blankedmask.png file, and you should get a proper mask.

Now we do the final mask removal with ffmpeg. The audio is copied as-is. I use the prores codec as it is (effectively) lossless. This creates a movie file that I can then feed into HandBrake to do my final video encode. If you are more comfortable encoding your final videos with ffmpeg, then just use your usual settings here.

ffmpeg -y -v 0 -i "$1" -vf "removelogo=finalmask.png" -c:a copy -c:v prores

As before, you can preview and make sure you’re happy with the mask using ffplay instead, just to see the results.

I am working on a script to automate most of the work described here. I will put it here once I am finished an happy with the results.

Leave a Comment