Mit ffmpeg lassen sich Bilderfolgen automatisiert auf der Kommandozeile und damit in einem Skript zu einem Video konvertieren. Es gibt noch viele andere Anwendungsfälle - Konvertieren, Schneiden, Skalieren, Überblenden etc.
Hier soll nur gezeigt werden, wie sich ein Video aus einer Menge von Bildern erzeugen lässt.
Generiere mit einem beliebigen Verfahren eine Abfolge von gleich großen Bildern als einfaches Textformat.
#!/usr/bin/env bash
header="P1
30 30"
for i in {00..29}; do
filename=image-$i.pbm
echo "$header" > $filename
for h in {00..29}; do
for w in {00..29}; do
pixel=0
if [ $h = $i ] ; then
pixel=1
fi
echo -n "$pixel "
done
echo
done >> $filename
done
Mit dem Skript werden per bash 30 pbm-Dateien erzeugt. Das könnte natürlich eine beliebige Programmiersprache sein, mit der wir in Dateien schreiben können oder zumindest Textzeilen auf stdout ausgeben können.
Die Endung pbm steht für Portable Bitmap Format und stammt aus dem Umfeld von netpbm. Das Format besteht aus einer Textzeile, in der die MagicNumber angegeben wird - in diesem Fall P1. In der zweiten Zeile wird die Auflösung des Bildes angegeben. Danach folgen die einzelnen Pixel zeilenweise von jeweils links nach rechts. Zeilenumbrüche können beliebig zur Erhöhung der Lesbarkeit eingefügt werden. Es hilft aber zum Experimentieren die jeweilige Zeilenbreite zu nutzen.
P1
10 10
1 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0
Sollten wir ein farbiges Bild
erstellen wollen, erzeugen wir den Header wie folgt:
#!/usr/bin/env bash
header="P3
30 30
255"
Die MagicNumber wird von P1 zu P3. In der dritten Zeile gibt man noch den Wertebereiche der einzelnen Farbkomponenten an - typischerweise 255. Die Daten sind jeweils mit drei Werten als Rot-Grün-Blau (RGB) zu kodieren, die zusammen ein Pixel darstellen. Die Endung ist dann ppm.
pixel="$red $green $blue "
echo -n "$pixel "
Irgendwann zeigt sich aber - bei großen farbigen Bildern - dass das pbm Format vielleicht nicht perfekt ist, weil es sehr große Dateien erzeugt. Für das Verstehen der Prinzipien ist es aber ganz hervorragend geeignet.
ffmpeg kann sowohl mit pbm-Dateien als auch anderen Formaten wie png umgehen. In jedem Fall aber sollte gleich beim Erzeugen der Bilddateien darauf geachtet werden, dass die Dateinamen mit dem impliziten Zähler zum einen bei 0 anfangen und zum anderen jeweils gleich viele Ziffern besitzen. Daher wird oben bei 00 begonnen und bis 29 hochgezählt.
Der typische minimale Aufruf besteht nun aus der Angabe, wieviele Bilder pro Sekunde eingelesen werden sollen (-r), welchem Format die eingelesenen Dateinamen (-i image-%02d.pbm) entsprechen, welcher Codec benutzt werden soll (-vcodec), das Ausgabepixelformat (-pix_fmt yuv420p) und dem Dateinamen der Zieldatei:
ffmpeg \
-y \
-r 10 \
-i image-%02d.pbm \
-vcodec libx264 \
-pix_fmt yuv420p \
pixel.mp4
Das Format für die Eingabedateien ist vielleicht etwas erklärungsbedürtig: Der Platzhalter %02d steht für eine zweistellige Dezimalzahl. So liest ffmpeg die Dateien mit den Namen image00.pbm, image01.pbm etc. nacheinander ein. Die beiden Werte für die Schalter -vcodec und -pix_fmt solltest Du vorerst, bis Du Dich tiefer mit Videokodierung beschäftigt haben, übernehmen.
Solltest Du mehr als 100 oder 1000 Bilder zu einem Film umwandeln wollen, müssen sie im Zähler bei 000 bzw. 0000 beginnen. Das funktioniert mit der for-Schleife und brace-Expansion recht gut. Solltest Du aus einem Datenstrom heraus Bilder erzeugen und in jeder Iteration einen Zähler hochzählen, sind führende Nullen sehr hinderlich. Dann ist ein üblicher Trick, den Zähler nicht bei 0 sondern beispielsweise bei 10000 beginnen zu lassen und die führende 1 als Teil des Dateinamens zu betrachten.
-i image-1%04d.pbm
ffmpeg besitzt so viele Schalter und Funktionen, dass Du Dich auf diversen Seiten darüber informieren solltest und auch in die Dokumentation schauen solltest ffmpeg.org. Einige Schalter seien hier aber noch angemerkt. Da das Video mit dem obigen Verfahren sehr klein ist (30x30 Pixel), kannst Du die einzelnen Bilder mit convert vorher hochskalieren und zugleich, um Speicherplatz zu sparen, in png-Dateien konvertieren.:
for i in {00..29}; do
convert -scale 300 image$i.pbm image$i.png
done
Alternativ kann das auch direkt ffmpeg:
ffmpeg \
-y \
-r 10 \
-i image-%02d.pbm \
-crf 23 \
-vcodec libx264 \
-vf scale=300x300:flags=neighbor \
-pix_fmt yuv420p \
pixelsharp.mp4
Bisweilen wollen wir explizit zwei Effekte vergleichen. Dann hilft es, sie nebeneinander aufzustapeln:
ffmpeg -y -i pixelsharp.mp4 -i pixel.mp4 -filter_complex hstack pixelboth.mp4
Wenn Du mit einer anderen Programmiersprache (z.B. Java) die Möglichkeit hast, einzelne Zustände herauszuschreiben, dort aber noch keinen Dateizugriff kennst, kannst Du mit einem einfachen Protokoll arbeiten. Wenn vor jedem Bild ein Marker gesetzt wird (das Wort next auf einer Zeilen oder einfach eine Leerzeile), kannst Du die Ausgabe in eine Pipe schicken und immer dann, wenn der Marker kommt, ein neues Bild erzeugen:
#!/usr/bin/env bash
# stream2pbm
num=10000
while read line; do
if test "$line" = "next"; then
((num++));
echo "P1" > image$num.pbm
echo "50 50" >> image$num.pbm
else
echo "$line" >> image$num.pbm
fi
done
java GameOfLife | ./stream2pbm.sh
Danach haben wir dann so viele Bilder, wie next geschrieben wurde in einzelnen, fortlaufend nummerierten Dateien und können sie wie oben mit ffmpeg zusammenbringen.
Auf besonderen Wunsch noch eine Vorlage zum Überblenden von Bildern mit Effekten. Das ist sicher starker Tobak und vermutlich nichts für Leute, die erstmalig mit ffmpeg oder gar der Bash in Kontakt kommen- aber denke an den Anwendungsfall, einfach ein paar Bilder in ein Verzeichnis zu kopieren und den passenden ffmpeg-Aufruf automatisch aus einem ls zu erzeugen (was natürlich alles in einem Skript steht) und eine wunderbare Präsentation für den Diaabend zu haben ...:
ffmpeg -y \
-loop 1 -t 5 -i img2.jpg \
-loop 1 -t 5 -i img1.jpg \
-loop 1 -t 5 -i img2.jpg \
-loop 1 -t 5 -i img1.jpg \
-loop 1 -t 5 -i img2.jpg \
-loop 1 -t 5 -i img1.jpg \
-filter_complex "
[0][1]xfade=transition=fadeblack:duration=0.5:offset=2[f1];
[f1][2]xfade=transition=diagtr:duration=0.5:offset=6[f2];
[f2][3]xfade=transition=radial:duration=0.5:offset=8[f3];
[f3][4]xfade=transition=circlecrop:duration=0.5:offset=10[f4];
[f4][5]xfade=transition=squeezeh:duration=0.5:offset=12,format=yuv420p[v]
" \
-map "[v]" \
-movflags \
+faststart \
-r 25 output.mp4
Text ein- und ausblenden auf einem Video mit Transparenz: Bauchbindengenerator.
ffmpeg -y -i ersti.mov -c:v libvpx-vp9 -filter_complex \
"[0]split[base][text];\
[text]drawtext=fontfile=Poppins-Medium.ttf:text='ffmpeg dilettant': \
fontcolor=#8FFFC5:fontsize=60:\
enable='between(t,0.5,6.5)':\
x=504:y=1080,\
format=yuva420p,\
fade=t=in:st=0.6:d=0.3:alpha=1,\
fade=t=out:st=5.9:d=0.3:alpha=1[subtitles];\
[base][subtitles]overlay,format=yuva420p" \
-pix_fmt yuva420p -lossless 1 -metadata:s:v:0 alpha_mode="1" -auto-alt-ref 0 converted.webm
Filter zum Schärfen und für Kontrasterhöhung:
ffmpeg -i input.mp4 -vf 'unsharp=lx=5:ly=5,eq=contrast:1.3' output.mp4
Vergleich zweier Videos nach Filtern:
ffmpeg -i input1.mp4 -i input2.mp4 -filter_complex "[0]crop=(iw/6)*3:ih:0:0[left];[1]crop=(iw/6)*3:ih:ow:0[right];[left][right]hstack=inputs=2" -pix_fmt yuv420p -movflags +faststart compare.mp4