Work-in-progress document. These notes are loosely arranged and named. Take what you may. Thanks to @garciadelcast for contributing with his discoveries!
Here is a guide with personal notes to use FFmpeg and Imagemagick to automate all kinds of tricks in gif creation, video conversion, frame extraction, image processing, and more. These notes express a love for automating the automatable.
FPS=1x30
FRAMES=10
# Export frames from video
fs:
@ffmpeg \
-i loop.mov \
-vf fps=$(FRAMES) \
frames/f_%02d.jpg
# Convert frames to GIF
gif:
@convert \
-delay $(FPS) \
-geometry 1400x800 \
frames/*.jpg \
bin/@15fps-$(FPS).gif
You need to have this Makefile inside a folder that contains an input.mp4
video. Then run:
make init
make gif
And here is the code for this commands.
gif:
ffmpeg -i input.mp4 -vf fps=10 frames/f_%02d.jpg
convert -delay 1x30 -geometry 800x400 frames/* -loop 0 bin/default.gif
convert -coalesce bin/default.gif -delete -1 -delete 0 -reverse -coalesce bin/default.gif -loop 0 bin/boomerang.gif
init:
mkdir frames
mkdir bin
Use -ss 00:00:04
for start time (this would be second four to start)
Use -t 00:00:02
for duration (this would be two seconds after second four)
This would basically extract frames from second 4 to second 6.
2600x1800
is the new canvas size we want the image to have, and 0x0
is the offset of the corner of the canvas.
convert \
-delay 1x1 \
frames/*.jpg \
-crop 2600x1800+0+0 +repage \
bin/ts-debug.gif
ffmpeg -framerate 1 -pattern_type glob -i '*.jpg' video.mp4
convert image.jpg -distort SRT -45 output_image.png
extract_original:
ffmpeg -i $(video_in) -vf fps=$(FPS) $(path)/$(name)_%02d_out.jpg
extract_resized:
ffmpeg -i $(video_in) -vf scale=-1:$(resize_width),fps=$(FPS) $(path)/$(name)_%02d_out.jpg
gif:
convert -delay 1x$(frames) $(path)/* bin/gif-$(resize_width)p-1x$(frames)-FPS$(FPS).gif
video:
ffmpeg -y -framerate $(frames) -pattern_type glob -i '$(path)/*.jpg' out.mp4
MILLING The pieces rotates over the roughing in around 20180622_142000
TESTS
Couldn’t really figure out a way to just input all images in the folder to ffmpeg
. Instead, did a massive renaming with Windows PowerShell:
$ Dir | %{Rename-Item $_ -NewName ("sanding_{0:d5}.jpg" -f $nr++)}
Then, used ffmpeg
to stitch them into a timelapse:
$ ffmpeg -framerate 30 -i sanding_%05d.jpg sanding_timelapse.mp4
MAC Do all files on a folder? This works on Mac
ffmpeg -framerate 30 -pattern_type glob -i '*.jpg' video.mp4
To rename files, followed this answer: https://stackoverflow.com/questions/3211595/renaming-files-in-a-folder-to-sequential-numbers#
a=1
for i in *.jpg; do
new=$(printf "%04d.jpg" "$a") #04 pad to length of 4
mv -i -- "$i" "$new"
let a=a+1
done
Worked great!
The milling video has a lot of corrupt jpegs, can skip them with this?
https://stackoverflow.com/a/45986689
ffmpeg -err_detect aggressive -fflags discardcorrupt -i rtsp://[ip address]:554/11 -r 1 -s 640x320 -an -f image2 c:\temp\snapshots\snapshot-%03d.jpg
This is actually going the other way around...
Tried this:
ffmpeg -framerate 30 -fflags discardcorrupt -pattern_type glob -i '*.jpg' video.mp4
Didn’t work. Cleaned them with this: https://stackoverflow.com/questions/4780424/detecting-corrupted-images-in-bash-script
for f in *.jpg ; do identify $f > /dev/null || echo $f >> /tmp/fail ; done ; cat /tmp/fail
FINAL MILLING So, ended up doing this to create a file with the list of bad files, and remove them:
for f in *.jpg ; do identify $f > /dev/null || (identify $f 2>> removed.txt ; rm $f) ; done ;
This means
for loop AND do identify and send non stderr to null output (no output) OR (if the previous operation yielded error) then do both identify and pipe stderr with append to removed.txt AND remove file
(that looks awful lol) It cleaned almost 2k JPEGs.
Then created the video with
ffmpeg -framerate 30 -fflags discardcorrupt -pattern_type glob -i '*.jpg' video.mp4
This worked great and didn’t crash after 10 mins of video. I guess the crashing was happening because of the 2k corrupted JPEGs. It still gave me a bunch of EOI missing, emulating
errors, but gave me none after the large corrupted batch, and went all way through with the 60k frames.
→ F**k, I need to crop the larger images! Also, there are still images with EOI missing creating flickering… Solve this!
To solve the remaining EOI files, used this:
for f in *.jpg ; do identify -verbose -regard-warnings $f > /dev/null || (identify -verbose $f 2>> removed_EOI.txt ; rm $f) ; done ;
-verbose
deep searches the file, yielding the right errors (although makes the thing slow AF)
-regard-warnings
makes identify
return false on warning, hence triggering the removal
Source: https://www.imagemagick.org/discourse-server/viewtopic.php?t=20045
There are larger images at 2304x1536 that need to be resized & cropped to 1080p:
mogrify -resize 1920x1080^ -gravity center -extent 1920x1080 *.jpg
mogrify
is like convert
, but overwrites images
the ^
operator fits the smaller dimension to the image.
-extent
is like -crop
but only keeps the focus part, doesn’t give you the crops too
Create a video with, for instance, 1x4 (4 frames per second).
ffmpeg -framerate 4 -fflags discardcorrupt -pattern_type glob -i '*.jpg' video.mp4
Create a list.txt
file with the following contents to loop the video:
file 'video.mp4'
file 'video.mp4'
file 'video.mp4'
file 'video.mp4'
Then do
ffmpeg -f concat -i list.txt -c copy output.mp4
mogrify -format jpg -path output_folder input_folder/*.jpg
This will extract the audio of any mp4 videos in your current folder with the same name of the original video.
find . -type f -name "*.mp4" -exec bash -c 'FILE="$1"; ffmpeg -i "${FILE}" -vn -c:a libmp3lame -y "${FILE%.mkv}.mp3";' _ '{}' \;
This will extract the audio of any mkv videos in your current folder with the same name of the original video.
find . -type f -name "*.mkv" -exec bash -c 'FILE="$1"; ffmpeg -i "${FILE}" -vn -c:a libmp3lame -y "${FILE%.mkv}.mp3";' _ '{}' \;
(Script from Daniel Shiffman.)
Created with videogrep by Sam Lavigne. https://github.com/antiboredom/videogrep
1. Install youtube-dl (https://ytdl-org.github.io/youtube-dl...)
2. youtube-dl https://www.youtube.com/channel/UCvjg... -f 22 --write-auto-sub
3. videogrep -i *.mp4 --use-vtt --search "your search terms"
Images are converted to the resize
size first, then padded (or extended
) to have the desired width and height, gravitating from the center
and filling the background as white
.
convert -background white -gravity center * -resize 1000x1000 -extent 1000x1000 result.png
Some new commands from Jose Luis.
ffmpeg -i input.mp4 -an -filter:v "setpts=0.1*PTS" output.mp4
0.1
accelerates x10 by dropping frames,0.01
would accelerate x100 and so on.-an
removes audio.
ffmpeg -i input.mp4 -ss 00:00:00 -to 01:30:15 -c:v copy -c:a copy output.mp4
Omit the -to
parameter to trim till the end. Use -t
parameter instead to specify duration, not end time.
Was doing a GIF/MP4 [and] exported all pages from PDF as PNGs in Acrobat, and then used imagemagick
to compose them as two-page spreads:
magick montage *.png -geometry 637x825 -tile 2x1 -background black spread.png
Using typical GIF generation with palette and ffmpeg
:
ffmpeg -y -i spread-%02d.png -vf palettegen palette.png
ffmpeg -r 6 -y -i spread-%02d.png -i palette.png -filter_complex "paletteuse" cad2raster.gif
Problem was, this was failing because the palette is generated from only one frame (the first), and since the cover was black and white, it was making the whole thing black and white. Same problem with MP4—even without using the palette.
Reading this article, I found a [one-liner] solution. It generates a GIF/MP4 creating one palette per frame:
ffmpeg -r 2 -i spread-%02d.png -filter_complex "[0:v] split [a][b];[a] palettegen=stats_mode=single [p];[b][p] paletteuse=new=1" catalog.gif
I am not really sure what most of the parameters do, but it worked!
ffmpeg -i input.mp4 -vf scale=1920:1080 -c:v libx264 smaller.mp4