Fotos zu einem Zeitraffer zusammenfügen mit ffmpeg

Manche Digitalkameras können Fotos in einem bestimmten Zeitintervall auslösen. Nach ein paar Stunden sammeln sich viele Fotos in einem Ordner an, welche man mit ffmpeg in ein Video umwandeln kann. Ich habe einige Zeit gebraucht, bis alles endlich funktioniert hat. Hier seht ihr, wie man das mit ffmpeg machen kann.

Schritt für Schritt

Ausgangslage

Als Ausgangslage habe ich viele Fotos in einem Verzeichnis:

$ ls
2011-07-08_13-59-58.jpg  2011-07-08_15-01-27.jpg  2011-07-08_16-02-57.jpg
2011-07-08_14-00-07.jpg  2011-07-08_15-01-38.jpg  2011-07-08_16-03-08.jpg
2011-07-08_14-00-17.jpg  2011-07-08_15-01-47.jpg  2011-07-08_16-03-17.jpg
[...]

In meinem Fall sind das 1104 Fotos:

$ ls *.jpg | wc -l
1104

Diese Fotos haben zusammen eine Grösse von 2.4 GB:

$ du -ch *.jpg
1.6M    2011-07-08_13-59-58.jpg
1.6M    2011-07-08_14-00-07.jpg
[...]
2.0M    2011-07-15_14-48-28.jpg
2.4G    insgesamt

Ich möchte von diesen Fotos ein HD-Video umwandeln. Die Fotos haben eine Auflösung von 2592 x 1944 Pixel:

$ jhead 2011-07-15_14-47-51.jpg | grep Resolution
Resolution   : 2592 x 1944

Fotos verkleinern

Da die Fotos zu gross sind, verkleinert man die Fotos auf zuerst auf 1080 Pixel in der Höhe:

for datei in *.jpg
do
  convert -resize x1080 -quality 50 "$datei" "small_$datei"
done

Fotos umbenennen

Damit ffmpeg die Fotos in ein Video umwandeln kann, müssen die Fotos im Format wie 0001.jpg vorliegen. Die führenden Nullen sind für ffmpeg wichtig. Die Fotos können mit convert (aus dem Paket imagemagick) konvertiert werden:

LENGTH=`ls | wc -l | wc -L`
NUMBER="1"
find *.jpg | while read foto
do
  mv $foto "`printf %0${LENGTH}d $NUMBER`".jpg
  NUMBER=$((NUMBER + 1))
done

Fotos in ein Video konvertieren

Jetzt kann man mit ffmpeg die Fotos in ein Video umwandeln:

ffmpeg -f image2 -i %0${LENGTH}d.jpg -r 25 -vcodec libx264 output.mpg

Fertig: Video abspielen

Das Video ist nach dem Konvertieren 28 MB gross:

$ du -h output.mpg
28M     output.mpg

Das Video kann jetz abgespielt werden

$ mplayer output.mpg

Automatisch mit einem Skript

Um diese Schritte zu vereinfachen, habe ich ein kleines Bash-Skript pic2vid geschrieben, welches all diese Schritte schön ausführt:

#!/bin/bash
########################################################################
#
# pic2vid
# Konvertiert mehrere Fotos zu einem Video (Zeitraffer)
#
# GNU General Public License
# 2012 by Emanuel Duss
#
########################################################################

########################################################################
# Variabeln
DIR="$1"
PICDIR="pic2vid"
SMALLDIR="$DIR/$PICDIR"
SIZE="x1080"
QUALITY="90"
OUTPUTFILE="output.mpg"
CODEC="libx264"
FRAMESPERSECOND="25"

########################################################################
# Funktionen
Printusage () {
  echo "Usage:
  $0 directory"

}

########################################################################
# Main

# Prüfe Fehler
if [ -z "$1" ]
then
  Printusage
  exit 1
fi

if [ ! -d "$DIR" ]
then
  echo "$DIR ist kein Verzeichnis"
  Printusage
  exit 1
fi

if [ -d "$SMALLDIR" ]
then
  echo "Verzeichnis für aufzubereitende Fotos bereits vorhanden."
else
  mkdir "$SMALLDIR"
fi
if [ ! -d "$SMALLDIR" ]
then
  echo "Kann $SMALLDIR nicht anlegen. Bitte Berechtigungen prüfen."
  exit 1
fi

cd $DIR


# Fotos verkleinern (nur, wenn noch nicht verkleinert)
TOTAL="`ls *.[Jj][Pp][Gg] | wc -l`"
COUNT="1"
for file in *.[Jj][Pp][Gg]
do
  if [ -f "${PICDIR}/$file" ]
  then
    echo "Überspringe $file, da bereits konvertiert (${PICDIR}/$file)"
  else
    echo "$file: Foto $COUNT von $TOTAL ($((100 * $COUNT / $TOTAL))%)"
    convert -resize $SIZE -quality $QUALITY "$file" "${PICDIR}/$file"
  fi
  ((COUNT++))
done

# Fotos umbenennen (in Form wie 0001.jpg bis 2342.jpg)
cd $PICDIR
LENGTH="`ls *.[Jj][Pp][Gg] | wc -l | wc -L`"
NUMBER="1"
for file in *.[Jj][Pp][Gg]
do
  NEWNAME="`printf %0${LENGTH}d $NUMBER`.jpg"
  if [ -f "$NEWNAME" ]
  then
    echo "Datei $file bereits umbenannt. OK."
  else
    # Symlink statt umbenennen, damit die Prüfung ob schon konvertiert auch funktioniert
    ln -s "$file" "`printf %0${LENGTH}d $NUMBER`".jpg
  fi
  ((NUMBER++))
done

# Bilder mit ffmpeg zu Video zusammenfügen
ffmpeg -f image2 -i %0${LENGTH}d.jpg -r $FRAMESPERSECOND -vcodec $CODEC ../$OUTPUTFILE

# EOF

Das Skript sagt einem, wie man es anwenden muss:

$ pic2vid
Usage:
  pic2vid directory

So erstelle ich z. B. im aktuellen Verzeichnis mit allen Bildern ein Video:

pic2vid .

Und schon hat man sein Zeitraffervideo.

Links und weitere Informationen

26 thoughts on “Fotos zu einem Zeitraffer zusammenfügen mit ffmpeg”

  1. LENGTH=`ls | wc -l | wc -L`
    NR=”1″
    find *.jpg | while read foto
    do
    mv $foto “`printf %0${LENGTH}d $NR`”.jpg
    NUMMER=$((NUMMER + 1)) <<<—– muss es nicht NR=$((NR+1)) heißen,
    sonst bleibt genau 1 Bild über
    done

    Reply
  2. Eine super Anleitung. Vielen Dank an dieser Stelle. Soweit ist alles kein Problem. Bis auf die Umwandlung per ffmpeg. Mit dem Codec libx264 bekomme ich zwar ein File das sich aber entweder nicht abspielen lässt, oder aber ohne Bild abgespielt wird (Windows VLC Player). Kodiere ich ohne libx264, also als mpeg1 bekomme ich ein Video, aber halt mit schlechter Qualität.
    Die Umwandlung läuft sauber und ohne Fehlermeldung, aber irgendwas stimmt mit dem mpeg File dann nicht. Hab jetzt schon Stunden mit div. Einstellungen rumprobiert und komme nicht weiter.
    Eine Idee?
    Gruß
    Alex

    Reply
    • Hallo Alex

      Bitte.

      Hast du die `x264` Library installiert? Unter ArchLinux ist sie im Paket `x264`. Mit `ffmpeg -codecs` siehst du, welche Codecs installiert sind. Vielleicht fehlt diese.

      Du kannst auch ein anderer Codec aus der Liste von `ffmpeg -codecs` ausprobieren.

      Wie sieht es mit anderen Mediaplayern aus? Was sagt `mplayer` oder `mpv`?

      Gruss
      Emanuel

      Reply
    • Hi Alex,
      hast Du es beim Kodieren mit libx264 einmal versucht das Pixelformat anzugeben. Was für ein Format haben Deine Ausgangsbilder?
      Die Option für das Pixelformat ist -pix_fmt yuv420p und wird bei PNGs empfohlen.

      Außerdem kann ffmpeg auch ohne das strenge Format 00001.jpg mit fortlaufender Nummer arbeiten, nämlich mit der Option -pattern_type glob

      ffmpeg -pattern_type glob -i ‘Bild*.jpg’ …

      Reply
  3. Hallo Emanuel,

    vielen Dank für deine Hilfe. Gerade die Umwandlung der Dateinamen hatte mir zuerst etwas Mühe bereitet. Allerdings ist in deinem “Mini-Skript” ganz oben noch ein Fehler:

    mv $foto “`printf %0${LENGTH}d $NR`”.jpg

    Hier muss natürlich $NR durch $NUMMER ersetzt werden, ansonsten hast du nach der Konvertierung nur eine Datei mit 000.jpg.

    Kannst du bei Gelegenheit ja mal ändern.

    Gruß, Olli

    Reply
  4. Besser bei “ls | wc -l | wc -L” schreiben: “ls -1 | ……”.

    Sonst kriegst du womöglich mehrere Files pro Zeile und zählst falsch.

    Reply
    • Hallo Thomas

      Wenn “ls” an die Standardausgabe schreibt, wird nur ein File pro Zeile ausgegeben, sprich das “-1” ist überflüssig.

      Gruss
      Emanuel

      Reply
  5. Ich habe das Problem, das ffmpeg einfach abbricht, wenn ein Bild mit 0 Byte in meinem Verzeichnis mit den Bildern ist.
    Ich hole mit wget per cronjob alle 2 Minuten ein Bild meiner Webcam und speichere es auf meinem Raspberry.
    Dabei kommt es scheinbar vor, das die Webcam das Bild noch gar nicht zu Ende geschrieben hat, aber per wget die unvollständige Datei trotzdem geholt wird…

    Reply
    • Hallo Andre

      Wenn es 0 Byte ist, ist es auch kein Bild mehr. So kannst du auf 0 Byte grosse Bilder testen:

      test ! -s image.jpg  && echo Datei ist nicht null

      Gruss
      Emanuel

      Reply
  6. Hallo Zusammen,

    habe das Scriptschnipsel zum umbenennen versucht, alles ist auch logisch.
    Allerdings schmeist mir meine QNAP nur Mist aus.
    Ich habe bspw. 12 Dateien und möchte diese umbenennen, das umbenennen funktioniert zwar, aber nicht wie gewünscht ;-(. Das kommt dabei raus’:
    _4V5GK~E.JPG
    _5552A~Z.JPG
    …..

    Kann mir einer erklären, wo der Fehler liegt?!?
    Danke schön, …

    Reply
    • Ich selber kenne QNAP nicht genau. Vielleicht läuft dort eine andere Shell, welche die Schreibweise NUMBER=$((NUMBER + 1)) nicht unterstützt. Diese könnte durch die Schreibweise NUMBER="`expr $NUMBER + 1`" ersetzt werden.

      Reply
  7. Habe es etwas anders hinbekommen.

    ‘LENGTH=`ls | wc -l | wc -L`
    LENGTH=”4″ …. da ich von nicht mehr als 9999 Dateien ausgehe.
    NUMBER=”1″
    find *.jpg | while read foto
    do
    mv $foto “`printf %0${LENGTH}d $NUMBER`”.jpg
    NUMBER=$((NUMBER + 1))
    done

    Dennoch danke für die Antwort.

    Reply
  8. Hallo Emanuel

    Vielen Dank für das Skript. Bei mir ist dasselbe Problem aufgetaucht wie bei Alex. VLC zeigt nichts an. Jedoch mit Kodi (XBMC) kann ich das Video anschauen.

    lg
    christoph

    Reply
  9. Hallo Emanuel,
    das Script ist wirklich sehr hilfreich. Es hat alles gut geklappt. Nun beginne ich auch zu verstehen, was da abläuft.
    Vielen Dank für diese Arbeit, die mich um Stunden nach vorn gebracht hat.

    Viele Grüße
    Jürgen

    Reply
  10. Hallo Emanuel,

    vielen Dank für die Anleitung. Ich bin ein Linux Anfänger und weiß nicht was die Fehlermeldung bedeutet…

    Could find no file with path ‘%0d.JPG’ and index in the range 0-4
    %0d.JPG: No such file or directory

    Diese tritt beim letzten Schritt auf. Bei: ffmpeg -f image2 -i %0${LENGTH}d.jpg -r 25 -vcodec libx264 output.mpg
    Kannst du mir helfen?

    Grüße Viktor

    Reply
    • Hoi Viktor

      Gern geschehen 🙂

      Sehe ich das richtig, dass du die Schritte manuell durchgeführt hast, ohne das Script zu verwenden?

      Das sieht aus, als ob die Variable $LENGTH leer ist. Du musst die Befehle vom Schritt “Fotos umbenennen” und “Fotos in ein Video konvertieren” in der gleichen Shell (= im gleichen Konsolen-Fenster) ausführen, damit die Variable $LENGTH auf die nötige Länge gesetzt werden kann. Diese wird dann im zweiten Schritt verwendet.

      Hast du schon probiert, alle Schritte in einem einzigen Schritt mittels dem Script durchzuführen? Das sollte eigentlich auf anhieb klappen.

      Gruss,
      Emanuel

      Reply
  11. Hi Emanuel,

    vielen Dank für die super Anleitung!
    Ich nutze das für eine IP Cam, die ich im Garten installiert habe. Die macht alle 5 Minuten ein Bild, so möchte ich den Wechsel vom Winter in den Sommer dokumentieren. Dein Skript funktioniert dafür super!
    FFMPEG gibt zwar einige Fehlermeldungen bzw. Warnungen aus, das Resultat ist aber einwandfrei!
    Viele Grüße
    Sebastian

    Reply
  12. Hallo Emanuel,

    vielen Dank für deine Anleitung. Ich versuche gerade eine Fotoserie aus einer GoPro in ein Zeitraffer zu verarbeiten, aber irgendwie kann ffmpeg die Dateien offenbar nicht verarbeiten:
    ~$ ffmpeg -f image2 -i %04d.jpg -r 25 -vcodec libx264 output.mpg
    [image2 @ 0x5639e80f9ee0] Could find no file with path ‘%04d.jpg’ and index in the range 0-4
    %04d.jpg: No such file or directory

    -> Dateien sind “0012.jpg” “0013.jpg” …
    Habe es auch mit 0%03d.jpg probiert, oder ähnliche Kombis, klappt alles nicht.

    Mit Fotos von einer anderen Kamera funktioniert es problemlos:
    ~$ ffmpeg -f image2 -i 20140714_%03d.jpg -r 25 -vcodec libx264 output.mpg

    -> “20140714_002.jpg” “20140714_003.jpg” …

    Habe schon vermutet, es liegt an den Dateien als solche, finde da aber auch keinen Unterschied (abgesehen von der Dateigröße). Bin also etwas ratlos.

    Reply

Leave a Comment