Panoramavideo

automatisch aus einem Panoramabild ein Panoramavideo in Stapelverarbeitung erstellen

Panoramabilder, gerade wenn sie sehr viel breiter als hoch sind lassen sich auf Bildschirmen nur unbefriedigend darstellen. Dieser Vorschlag erstellt aus dem Bild ein Video, dass ein einfaches Drehbuchhat: Panoramabild in Totale, kurze Pause, Zoom in Nahe am linken Bildrand, Schwenk nach rechts bis zum Bildrand, kurze Pause, Ende.

Dies lässt sich sehr schön Automatisieren, es genügt dann ein Befehl, oder in eine GUI aeingebunden auch ein Mausklick um dieses Video zu erstellen. Das geht unter Linux mit Hilfe von ImageMagick, x264 und ein wenig Bash. Das Skript verwendet Multitasking mit wählbarer Anzahl der Tasks. Es benötigt einiges an Plattenplatz während der Erstellung. Das Ergebnis ist eine sehr effizient kodierte Videodatei, da die Differenz von Bild zu Bild jeweils nur wenig neue Bildinformation enthält und der meiste Teil des Bildes jeweils präzise aus einem horizontalen Verschiebevektor gebildet wird. Genau das kann die H.264 perfekt.

Hier ein Panoramabild Beispiel Panoramabild

Download<--Klicke hier für Video

download

#!/bin/bash
# © 2016 Joachim Schwender
# Wandelt ein Panorama-Bild (viel breiter als hoch) in ein Video-Schwenk von links nach rechts.
# Mit vielen Kameras kann man Panoramafolgen aufnehmen, die aus einzelnen Bildern bestehen und
# mit Hugin (oder notfalls auch Gimp) aneinander gesetzt werden können. Daraus entsteht dann ein Panoramabild.
# Dieses wird auf jedem Monitor nur als schmaler Streifen dargestellt.
# Der Film hiervon ist einfach besser zu betrachten.
#
# Benötigt: ImageMagick, jpeginfo, mjpeg tools
# WARNUNG: benötigt viel Plattenplatz im Temporären Verzeichnis, ca 1,3Mb pro Bild )!!!!!!
#
# ungefähre Anzahl der Bilder (Schritte nach links), der endgültige Wert wird so berechnet, 
# dass die Rundungsfehler minimal sind
STEPS=700            # Anzahl der Schritte für den Schwenk
ZoomSchrittweite=56  # Schrittweite beim zoomen
VidBreite=1280       # Grösse des Video X und Y (sollte nicht verändert werden)
VidHoehe=720
MAXTHREADS=4
#
myexit()  {  echo "${1}"; exit; }
fnullen() {  printf "%04i" $1; }

log() {
    case "$2" in
    ("1")	echo -e -n "$1"       #  log auf konsole
	;;
    ("2")	echo -e "$1"  >>$LOG  # log in Datei
	;;
    ("3")	echo -e -n "$1"       #  log auf konsole
		echo -e "$1"  >>$LOG  # log in Datei
	;;
 esac
}

TMPDIR=/tmp/video$$
LOG=${TMPDIR}/log
mkdir ${TMPDIR}
QDIR=$(pwd)
[ -z ${1} ] && myexit "Bitte Quelldatei angeben und optional die Anzahl der Frames!" # die Quelldatei muss angegeben werden
[ -z ${2} ] || STEPS=${2}  # Als optionaler Parameter kann die Anzahl der Schritte angegeben werden
SRC=${1}
FNAME=$(basename ${SRC} ".jpg")
TMP=${TMPDIR}/tempimage.ppm
# Pan-bild analysieren.
PanBildBreite=$(jpeginfo ${SRC}|cut -d "x" -f1|cut -d " " -f2)
PanBildHoehe=$(jpeginfo ${SRC}|cut -d "x" -f2|cut -d " " -f2)
# Framebreite aus der Höhe berechnen, angenommen ist ein Seitenverhältnis von 16/9
FrameBreite=$(echo "scale=0; ( ${PanBildHoehe} * 16 ) / 9" | bc)         #"""
# 
LEN=$(expr ${PanBildBreite} - ${FrameBreite})
INCREMENT=$(expr ${LEN} / ${STEPS})  # Der Wert ist abgerundet, also entsteht ein Rundungsfehler
STEPS=$(expr ${LEN} / ${INCREMENT})  # dies korrigiert den Rudungsfehler
log "Quellbild: PanBildHoehe: ${PanBildHoehe} PanBildBreite: ${PanBildBreite}, \
-- Ziel: Breite: ${FrameBreite} Schritte: ${STEPS}, Schrittweite: ${INCREMENT}\n" 3
log "Video:  $VidBreite  x $VidHoehe     mit $MAXTHREADS Threads\n" 3
#---------------------------------------
# Zoombeginn
i=0
X1=${VidBreite}
BB=1000
Y1=10
while [ 1 ]
do 
    Y1=$(echo "scale=0;( ${PanBildHoehe} * ${X1} ) / ${PanBildBreite}" | bc)  #"
    [ ${Y1} -lt ${VidHoehe} ] ||break
    BB=$(echo "scale=0;( ${VidHoehe} - ${Y1} ) / 2 + 1" |bc)     #"
    XX=$(fnullen ${i})
    ZA=$(expr $Y1 + $BB + $BB)
    log "\rZoomstatus: ${X1}x${Y1} Rand: ${VidBreite}x${VidHoehe}+${BB} Index: ${i}   $ZA  " 3
    convert -resize ${X1}x${Y1} -border ${BB} -bordercolor black \
    -crop ${VidBreite}x${VidHoehe}+${BB} ${SRC} ${TMPDIR}/a_$XX.ppm
    echo "${i}">${TMPDIR}/index      # für While-schleifen-Beendung den letzten Index merken!
    i=$(expr ${i} + 1)
    X1=$(expr ${X1} + ${ZoomSchrittweite})
done
log "\nZoom fertig." 3
# ich weiss nicht wie das besser geht den Wert aus der while schleife zu retten?
i=$(cat ${TMPDIR}/index)
log "nächster Index ${i}" 3
#---------------------------------------
convppm() {
    convert -crop ${FrameBreite}x${PanBildHoehe}+${1}+0  ${2} -comment "Frame $3" \
    -resize ${VidBreite}x${VidHoehe}^  ${TMPDIR}/a_$3.ppm
}
# kleine Pause nach Zoom mit still stehendem Bild
X1=$i
XX=$(fnullen ${i})
  convppm 0 ${SRC} ${XX}
i=$(expr ${i} + 1)
X2=$(expr ${i} +  25)
for (( n=$i;n<$X2; n=$(expr $n + 1) ))
do 
    TT=$(fnullen ${n})
    SS=$(fnullen $i)          # 25 Kopien der gleichen Datei ergeben 1 s Standbild
    cp ${TMPDIR}/a_$XX.ppm ${TMPDIR}/a_$TT.ppm
done
#
i=$X2          # index nachführen
log "\nPause fertig, index: $X2" 3
#---------------------------------------
XPOS=0
while [ ${XPOS} -lt ${LEN} ] ; do
    for (( Thread=1;Thread<${MAXTHREADS}; Thread=$(expr $Thread + 1) ))
    do                             # Parallelverarbeitung von Bildern

	if [ ${XPOS} -lt ${LEN} ]
	then
	    XX=$(fnullen ${i})
	    convppm ${XPOS}  ${SRC} ${XX} &
	    echo "${XX}">${TMPDIR}/index        # für While-schleifen-Beendung den letzten Index merken!
	    XPOS=$(expr ${XPOS} + ${INCREMENT})
	    i=$(expr ${i} + 1)
	fi
    done
    wait
    log "\rPanstatus: Bild ${XX} / ${STEPS},   X-Pos.: ${XPOS} / ${LEN}     " 3
done
log "\nPan fertig.\n" 3
cd ${TMPDIR}
# letzten index lesen
XX=$(cat index)
log "${XX} Bilder generiert.\n" 3
# jetzt wandeln wir die einzelbilder in einen Videostrom 
# Ein kleiner Vor- und Abspann wird mit dieser Aufzählung erzeugt, das Bild läuft nicht sofort los,
#  und bleibt am Ende etwas stehen.
FIFO=$TMPDIR/fifo$$
mkfifo $FIFO        # x264 kann nicht von StdIn lesen also muss eine Fifo Datei her
cat a_0000.ppm  a_0000.ppm  a_0000.ppm  a_0000.ppm  a_0000.ppm \
 a_0000.ppm  a_0000.ppm  a_0000.ppm  a_0000.ppm a_0000.ppm \
 a_0000.ppm  a_0000.ppm  a_0000.ppm  a_0000.ppm a_0000.ppm \
 a_0000.ppm  a_0000.ppm  a_0000.ppm  a_0000.ppm a_0000.ppm \
 a_0000.ppm  a_0000.ppm  a_0000.ppm  a_0000.ppm a_0000.ppm \
 a_0000.ppm  a_0000.ppm  a_0000.ppm  a_0000.ppm a_0000.ppm \
 a_0000.ppm  a_0000.ppm  a_0000.ppm  a_0000.ppm a_0000.ppm \
 a_0000.ppm  a_0000.ppm  a_0000.ppm  a_0000.ppm  a_0000.ppm a_0000.ppm \
 a_*.ppm\
 a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm \
 a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm \
 a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm \
 a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm \
 a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm \
 a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm \
 a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm \
 a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm a_${XX}.ppm \
 |ppmtoy4m -S 420mpeg2 -v 0 -F 16:9 > $FIFO &
x264 --sar 1:1 --fps 25 --trellis 2 --merange 24 -o ${QDIR}/${FNAME}.mkv $FIFO
rm $FIFO
cd ${QDIR}
touch -r "${SRC}" "${FNAME}.mkv"
log "fertig. Temporäres Verzeichnis ${TMPDIR} ggf. manuell löschen!\n" 3