Crop black bars from a video without affecting resolution using ffmpeg
How to use the ffmpeg command line tool to zoom in to a video, while maintaining aspect ratio and resolution.
When I first posted my No Bull guide to Figma course on Skillshare, my class was temporarily closed as I had black bars around the Figma window on a few lessons. I wanted to fix these as swiftly as possible, without re-recording them. Here's how I went through and found a way to batch process my videos.
What the hell is ffmpeg?
The first major professional software engineering project I worked on was for a European-wide advert delivery network, serving more than 50% of all ads you'd ever see.
Videos needed converting to the standards required of each country or locale, so we had to make adjustments for:
- aspect ratio
- resolution
- frames per second
- file format
Among others.
ffmpeg
is an open-source command-line tool that works on unix systems, that let us automate the conversion process in the cloud. It takes an input file, conversion options, and an output file.
For example, to change the resolution of a video:
ffmpeg \ # start the command
-i input.mp4 \ # input a file
-filter:v scale=1280:720 -c:a copy \ # conversion options
output.mp4 # choose where to save it
Finding the right search terms
It took a while to find the right search terms, as most solutions I found would crop the video frame, but then it wouldn't be the original 1920x1080 pixel resolution.
Most solutions suggested VLC or other tools, then I remembered about ffmpeg.
Some search terms I used include:
- how to crop a video on mac without affecting resolution
- how to zoom without affecting aspect ratio on a mac
- how to change aspect ratio in iMovie
- how to reframe a video on mac
- ffmpeg crop video
- ffmpeg examples
- ffmpeg convert negative space
- ffmpeg remove black space in video
- ffmpeg zoom video but maintain aspect ratio
A working proof-of-concept
After some more searching, I found the exact answer I was looking for on superuser.com:
ffmpeg -i input.mp4 -vf "scale=2*iw:-1, crop=iw/2:ih/2" output.mp4
User slhck broke it down so clearly and succinctly:
- Zoom your video centrally, by a chosen factor
- Crop the video again, by the same factor inversed
This way we keep the portion of the video we want, but maintain the aspect ratio (16:9), and the resolution (1080p).
Here's a link to the original question and answer:
Finding the crop factor I needed
To be honest, I used good old trial-and-error to figure out the crop factor I needed. Looking through the commend, someone used a factor of 1.5
, so I figured I'd need to use smaller increments and test until I found what I needed.
If you swapped out the factor for variables in the example, it'd look something like this:
ffmpeg -i input.mp4 \
-vf "scale=${FACTOR}*iw:-1, crop=iw/${FACTOR}:ih/${FACTOR}" \
output.mp4
I landed on 1.01
as my base factor, and some videos needed more cropping at 1.04
, and others a bit less at 1.005
.
Maintaining quality
I noticed the file size was much smaller after conversion, so a quick Google led me to find the ability to change how "lossy" videos are after conversion.
Simply use the -crf
flag e.g. -crf 18
.
It goes from 0-51 with 23 being the default, 1 being lossless, and 17-28 being the the perceptible range.
I chose 18.
Scripting the conversion
With my constructed command, a simple script would help simplify the work, and I could convert other things in future. Here's the script:
#!/bin/bash
FILE=$1
FACTOR=$2
CMD=("ffmpeg -i $1 -vf \"scale=$FACTOR*iw:-1,crop=iw/$FACTOR:ih/$FACTOR\" -crf 18 resized/$1")
echo -e "\n\nResizing by factor [$FACTOR] using:\n$CMD\n\n"
ffmpeg -i $FILE -vf "scale=$FACTOR*iw:-1,crop=iw/$FACTOR:ih/$FACTOR" -crf 18 resized/$FILE
To use it, navigate to the script and run:
./convert.sh input.mp4 1.01
This would put the processed video into a new folder called resized
.
Yes, I could tidy it up more, but this is good enough for me and my abilities.
Batch converting
With 31 videos across 8 modules to process, I needed to set a different factor for each module since I typically recorded each module in a single session.
Using command line foo, I can do something like this:
for i in $(ls | grep no-bull-04); do ./convert $i 1.01; done
What this does is:
- list out all files in my current directory
- filter for files containing
no-bull-04
in the name - for each result, run the convert script on it with a factor of
1.01
Sometimes I would test a single file and check the result, before doing the entire module.
Other approaches
I could have found a way to frame the videos, but since I recorded each of them as one take, it was more straightforward to convert each lesson in whole.
And that's all there is! Hope that helped ☺️.
.
.
Oh, and here's 1 month FREE on Skill share 😉: https://chk.fyi/skillshare
Liked this? Forward to a friend, subscribe to my newsletter, Medium, YouTube, or become a Medium member to support my writing (and other writers, too!).