Created
March 28, 2026 23:58
-
-
Save jikamens/19fc31364709889ff947824fd7020f33 to your computer and use it in GitHub Desktop.
Quick-and-dirty script to blur a box in a video using ffmpeg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env perl | |
| # Quick-and-dirty script to blur a box in a video. | |
| # | |
| # Still to deal with later when I need it: | |
| # | |
| # * Blur multiple regions | |
| # * Only blur for part of the duration of the video (can add | |
| # "enable=between(t,#,3)" to filters for this) | |
| # * Handle videos with sound properly (might need "-map 0:a -c:a copy", | |
| # might need to use mplayer -identify to check if audio track exists) | |
| # | |
| # Lessons learned from: | |
| # https://shotstack.io/learn/ffmpeg-blur-video/ | |
| # https://www.youtube.com/watch?v=i_7WFG2C5cM | |
| # https://stackoverflow.com/questions/20847674/ffmpeg-libx264-height-not-divisible-by-2 | |
| # | |
| # By Jonathan Kamens <jik@kamens.us> | |
| # Released into the public domain. | |
| use strict; | |
| use warnings; | |
| use File::Basename; | |
| use File::Temp 'tempfile'; | |
| use Getopt::Long; | |
| my $whoami = basename $0; | |
| my $usage = "Usage: $whoami [--help] [--verbose] [--no-show] [--input-file path] | |
| [--output-file path] [--left #] [--top #] [--right #] [--bottom #] | |
| [--blur-radius #]\n"; | |
| my($verbose, $input_file, $output_file, $left, $top, $right, $bottom); | |
| my $radius = 2; | |
| my $show = 1; | |
| die $usage if (! GetOptions("help" => sub {print $usage; exit; }, | |
| "verbose" => \$verbose, | |
| "show!" => \$show, | |
| "input-file=s" => \$input_file, | |
| "output-file=s" => \$output_file, | |
| "left=i" => \$left, | |
| "top=i" => \$top, | |
| "right=i" => \$right, | |
| "bottom=i" => \$bottom, | |
| "blur-radius=i" => \$radius)); | |
| sub prompt { | |
| my($prompt) = @_; | |
| print(STDERR "$prompt: "); | |
| my $response = <STDIN>; | |
| chomp $response; | |
| exit if (! $response); | |
| return $response; | |
| } | |
| $input_file = &prompt("Input video file") if (! $input_file); | |
| die "$input_file does not exist\n" if (! -f $input_file); | |
| $output_file = &prompt("Output video file") if (! $output_file); | |
| my($fh, $still_file) = tempfile(undef, SUFFIX => ".jpg"); | |
| system('ffmpeg', '-y', '-loglevel', 'quiet', '-i', $input_file, '-frames', '1', | |
| $still_file) and die; | |
| if (! (defined $left && defined $top && defined $right && defined $bottom)) { | |
| if (! fork) { | |
| exec('gimp', $still_file); | |
| } | |
| } | |
| $left = &prompt("Left of area to be blurred") if (! defined $left); | |
| $top = &prompt("Top of area to be blurred") if (! defined $top); | |
| $right = &prompt("Right of area to be blurred") if (! defined $right); | |
| $bottom = &prompt("Bottom of area to be blurred") if (! defined $bottom); | |
| my $width = $right - $left; | |
| my $height = $bottom - $top; | |
| my(@cmd) = ("ffmpeg", "-y"); | |
| if (! $verbose) { | |
| push(@cmd, "-loglevel", "quiet"); | |
| } | |
| push(@cmd, | |
| "-i", $input_file, | |
| "-filter_complex", | |
| "[0:v]crop=$width:$height:$left:$top,boxblur=$radius\[mask];" . | |
| "[0:v][mask]overlay=$left:$top\[combined];" . | |
| # Just in case the video is an odd width or height and the target encoder | |
| # doesn't allow that. | |
| "[combined]pad=ceil(iw/2)*2:ceil(ih/2)*2[padded];", | |
| "-map", "[padded]", | |
| $output_file); | |
| print("@cmd\n"); | |
| system(@cmd) and die; | |
| system("open", $output_file) if ($show); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment