#!/bin/bash -x # # Copyright Rob Homsi 28/3/2009 # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. This program is distributed in # the hope that it will be useful, but WITHOUT ANY WARRANTY; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. See the GNU General Public License for more # details. You should have received a copy of the GNU General Public # License along with this program. If not, see # . # # Based on Fred Weinhaus's fxtransition http://www.fmwconcepts.com/imagemagick/fxtransitions/index.php # If you redistribute or incorporate this script into other free # applications, you may use this scripts by simply referencing Fred's # name and his web page: Fred Weinhaus and # http://www.fmwconcepts.com/imagemagick/index.html. # # USAGE: fxwipe -f frames [-e effect] [-d delay] [-p pause] [-b bgcolor] [-r] infolder1 infolder2 [outfile] [outfolder] # USAGE: fxwipe [-h or -help] # # OPTIONS: # # -e effect transition effect type: blur, explode, implode, pixelize, # recursion, spin, spread, swirl, zoomin, zoomout; # default=blur # -f frames number of frames for each image sequence in the transition; frames>1; no default # -d delay delay between frames; has no effect on outfolder; delay>0; default=20 # -p pause pause delay for two undistorted input images; has no effect on preview or outfile; # pause>0; default=50 # -b bgcolor background color for use only with effect=zoomout # -r reverse the animation sequence and append it to the end. Has no effect on outfolder. # # The two input folders must have images with the same frame size. # # Leave out outfile and outfolder if you only want to see a preview. # # Output file, if given, must be a gif. # ### # # NAME: FXWIPE # # PURPOSE: To create an animated transition between two image sequences using a special effect distortion. # # DESCRIPTION: FXWIPE creates an animated transition between two images sequences using a # special effect distortion. The special effects distortions include: blur, explode, # implode, pixelize, recursion, spin, spread, swirl, zoomin and zoomout. # # OPTIONS: # # -e effect ... EFFECT is the type of distortion to use as the means for creating # the animated transition between the two input image sequences. The choices are: blur, explode, # implode, pixelize, recursion, spin, spread, swirl, zoomin and zoomout. The default=blur. # # -f frames ... FRAMES is the number of frames in the animation for # each of the two input image sequences. Values are integers > 1. The # number of frames must be given. The total number of frames will be # 2*frames. If the animation is reversed, then the total number of # frames will be 4*frames-2. # # -d delay ... DELAY between frames. This can only be used when making # a gif file or preview. Values are integers>0. The default=20 # # -p pause ... PAUSE is the number of images in the infolder1 allowed # to play before the transition begins, as well as the number of # images in infolder2 allowed to play after the transition is # over. The default=0. Can only be used for outfolders. # # -b bgcolor ... BGCOLOR is the background color to use for fill when the # effect is zoomout. Any valid IM color specification. The default=black. # # -r ... If supplied, then reverse the animation sequence, remove the first and # last frames of the reversed sequence and append these reversed frames to # the end of the animation. Can only be used for outfile or preview. # # # CAVEAT: No guarantee that this script will work on all platforms, # nor that trapping of inconsistent parameters is complete and # foolproof. Use At Your Own Risk. # ###### # # set default values effect="blur" delay=20 pause=0 reverse="no" bgcolor="black" # set directory for temporary files dir="." # suggestions are dir="." or dir="/tmp" # set up functions to report Usage and Usage with Description PROGNAME=`type $0 | awk '{print $3}'` # search for executable on path PROGDIR=`dirname $PROGNAME` # extract directory of program PROGNAME=`basename $PROGNAME` # base name of program usage1() { echo >&2 "" echo >&2 "$PROGNAME:" "$@" sed >&2 -n '/^###/q; /^#/!q; s/^#//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME" } usage2() { echo >&2 "" echo >&2 "$PROGNAME:" "$@" sed >&2 -n '/^######/q; /^#/!q; s/^#*//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME" } # function to report error messages errMsg() { echo "" echo $1 echo "" usage1 exit 1 } # function to test for minus at start of value of second part of option 1 or 2 checkMinus() { test=`echo "$1" | grep -c '^-.*$'` # returns 1 if match; 0 otherwise [ $test -eq 1 ] && errMsg "$errorMsg" } # test for correct number of arguments and get values if [ $# -eq 0 ] then # help information echo "" usage2 exit 0 elif [ $# -gt 14 ] then errMsg "--- TOO MANY ARGUMENTS WERE PROVIDED ---" else while [ $# -gt 0 ] do # get parameter values case "$1" in -h|-help) # help information echo "" usage2 exit 0 ;; -e) # get effect shift # to get the next parameter - effect # test if parameter starts with minus sign errorMsg="--- INVALID EFFECT SPECIFICATION ---" checkMinus "$1" effect="$1" case "$effect" in blur) ;; explode) ;; implode) ;; pixelize) ;; recursion) ;; spin) ;; spread) ;; swirl) ;; zoomin) ;; zoomout) ;; *) errMsg "--- INVALID EFFECT ---" ;; esac ;; -f) # get frames shift # to get the next parameter - frames # test if parameter starts with minus sign errorMsg="--- INVALID FRAMES SPECIFICATION ---" checkMinus "$1" frames=`expr "$1" : '\([0-9]*\)'` [ "$frames" = "" ] && errMsg "FRAMES=$frames MUST BE AN INTEGER" framestest=`echo "$frames <= 1" | bc` [ $framestest -eq 1 ] && errMsg "--- FRAMES=$frames MUST BE AN INTEGER GREATER THAN 1 ---" ;; -d) # get delay shift # to get the next parameter - delay # test if parameter starts with minus sign errorMsg="--- INVALID DELAY SPECIFICATION ---" checkMinus "$1" delay=`expr "$1" : '\([0-9]*\)'` [ "$delay" = "" ] && errMsg "DELAY=$delay MUST BE A NON-NEGATIVE INTEGER" ;; -p) # get pause shift # to get the next parameter - pause # test if parameter starts with minus sign errorMsg="--- INVALID PAUSE SPECIFICATION ---" checkMinus "$1" pause=`expr "$1" : '\([0-9]*\)'` [ "$pause" = "" ] && errMsg "PAUSE=$pause MUST BE A NON-NEGATIVE INTEGER" ;; -b) # get bgcolor shift # to get the next parameter - bgcolor # test if parameter starts with minus sign errorMsg="--- INVALID BGCOLOR SPECIFICATION ---" checkMinus "$1" bgcolor="$1" ;; -r) # set frame reversal append reverse="yes" ;; -) # STDIN and end of arguments break ;; -*) # any other - argument errMsg "--- UNKNOWN OPTION ---" ;; *) # end of arguments break ;; esac shift # next option done # # get infile and outfile infolder1=$1 infolder2=$2 out=$3 fi [ "$frames" = "" ] && errMsg "NUMBER OF FRAMES MUST BE GIVEN" # test that infolder1 is provided [ "$infolder1" = "" ] && errMsg "NO INPUT FOLDER 1 SPECIFIED" # test that infolder1 is provided [ "$infolder2" = "" ] && errMsg "NO INPUT FOLDER 2 SPECIFIED" # set temporary files tmpA="$dir/fxtransitions_1_$$.mpc" tmpB="$dir/fxtransitions_2_$$.mpc" tmpC="$dir/fxtransitions_1_$$.cache" tmpD="$dir/fxtransitions_2_$$.cache" tmp0="$dir/fxtransitions_0_$$.gif" tmp1="$dir/fxtransitions_1_$$.gif" trap "rm -f $tmpA $tmpB $tmpC $tmpD $tmp0 $tmp1; exit 0" 0 trap "rm -f $tmpA $tmpB $tmpC $tmpD $tmp0 $tmp1; exit 1" 1 2 3 15 N1=$(ls $infolder1 | wc -l) N2=$(ls $infolder2 | wc -l) let FnP=$frames+$pause [ $FnP -gt $N1 ] && errMsg "NOT ENOUGH FILES IN $infolder1 FOR THIS TRANSITION" [ $FnP -gt $N2 ] && errMsg "NOT ENOUGH FILES IN $infolder2 FOR THIS TRANSITION" COUNT1=1 COUNT2=1 COUNT3=1 renaming1 () { VAR=$out/`printf "%03d" $COUNT1`.png let COUNT1=COUNT1+1 } renaming2 () { VAR1=$(printf "%03d" $COUNT2).png if convert -quiet -regard-warnings "$infolder1/$VAR1" +repage "$tmpA"; then : ' Do Nothing ' else errMsg "--- FILE in $infolder1/$VAR1 DOES NOT EXIST, IS NOT AN ORDINARY FILE, NOT READABLE, HAS ZERO SIZE OR THE SEQUENCE IS NOT PROPERLY NAMED FROM 001.png to 999.png ---" fi let COUNT2=COUNT2+1 } renaming3 () { VAR2=$(printf "%03d" $COUNT3).png if convert -quiet -regard-warnings "$infolder2/$VAR2" +repage "$tmpB"; then : ' Do Nothing ' else errMsg "--- FILE in $infolder2/$VAR2 DOES NOT EXIST, IS NOT AN ORDINARY FILE, NOT READABLE, HAS ZERO SIZE OR THE SEQUENCE IS NOT PROPERLY NAMED FROM 001.png to 999.png ---" fi let COUNT3=COUNT3+1 } renaming2 renaming3 w1=`convert $tmpA -format "%w" info:` h1=`convert $tmpA -format "%h" info:` w2=`convert $tmpB -format "%w" info:` h2=`convert $tmpB -format "%h" info:` [ $w1 -ne $w2 -a $h1 -ne $h2 ] && errMsg "--- IMAGE SIZES DO NOT MATCH ---" case "$effect" in blur) dim=`convert xc: -format "%[fx:min($w1,$h1)]" info:` # solve for x^(frames)=1/dim factor=`convert xc: -format "%[fx:10^(log(1/$dim)/$frames)]" info:` expression="100*$factor^\$k" func1="-resize" op="%" parm="" func2="-resize ${w1}x${h1}!" ;; explode) factor=`convert xc: -format "%[fx:100/$frames]" info:` expression="-100*\(\(1000^\(\$k*$factor/100\)-1\)/999\)" func1="-implode" op="" parm="" func2="" ;; implode) factor=`convert xc: -format "%[fx:2/$frames]" info:` expression="2*\(\(1000^\(\$k*$factor/2\)-1\)/999\)" func1="-implode" op="" parm="" func2="" ;; pixelize) dim=`convert xc: -format "%[fx:min($w1,$h1)]" info:` # solve for x^(frames)=1/dim factor=`convert xc: -format "%[fx:10^(log(1/$dim)/$frames)]" info:` expression="100*$factor^\$k" func1="-filter box -resize" op="%" parm="" func2="-resize ${w1}x${h1}!" ;; recursion) dim=`convert xc: -format "%[fx:min($w1,$h1)]" info:` # solve for x^(frames)=1/dim factor=`convert xc: -format "%[fx:10^(log(1/$dim)/$frames)]" info:` expression="100*$factor^\$k" func1="-resize" op="%" parm="" ;; spin) factor=`convert xc: -format "%[fx:359.9/$frames]" info:` expression="359.9*\(\(1000^\(\$k*$factor/359.9\)-1\)/999\)\)" func1="-radial-blur" op="" parm="" func2="" ;; spread) dim=`convert xc: -format "%[fx:min($w1,$h1)]" info:` # solve for x^(frames)=1/dim factor=`convert xc: -format "%[fx:1/$frames)]" info:` expression="100*\(10^\($factor*\$k\)-1\)/9" func1="-spread" op="%" parm="" func2="" ;; swirl) factor=`convert xc: -format "%[fx:360/$frames]" info:` expression="3*360*\(\(1000^\(\$k*$factor/360\)-1\)/999\)" func1="-swirl" op="" parm="" func2="" ;; zoomin) factor=`convert xc: -format "%[fx:1/$frames]" info:` f1=`convert xc: -format "%[fx:20*(20^($factor)-1)/19]" info:` min=`convert xc: -format "%[fx:$f1<1?1-0.9*$f1:0]" info:` expression="$min+20*\(20^\(\$k*$factor\)-1\)/19\)" func1="-distort SRT" op="" parm="0" func2="" ;; zoomout) dim=`convert xc: -format "%[fx:min($w1,$h1)]" info:` # solve for x^(frames)=1/dim factor=`convert xc: -format "%[fx:10^(log(1/$dim)/$frames)]" info:` expression="100*$factor^\$k" func1="-resize" op="%" parm="" func2="-background $bgcolor -gravity center -extent ${w1}x${h1}" ;; esac #function to make frame transitions transitionFrames() { echo "" echo "" i=0 if [[ $out = *.* ]] || [[ $out = "" ]]; then convert $tmpA $tmp0 renaming1 while [ $i -lt $frames ]; do k=`expr $i + 1` eval ee=$expression ff=`convert xc: -format "%[fx:$ee]" info:` convert $tmpA $func1 "$ff${op} $parm" $func2 miff:- |\ convert -delay $delay $tmp0 -page +0+0 - -page +0+0 $tmp0 i=`expr $i + 1` renaming1 renaming2 done echo "" echo "" i=1 k=$frames while [ $k -gt 0 ] do eval ee=$expression ff=`convert xc: -format "%[fx:$ee]" info:` convert $tmpB $func1 "$ff${op} $parm" $func2 miff:- |\ convert -delay $delay $tmp0 -page +0+0 - -page +0+0 $tmp0 i=`expr $i + 1` k=`expr $k - 1` renaming1 renaming3 done convert $tmpB miff:- |\ convert -delay $delay $tmp0 -page +0+0 - -page +0+0 $tmp0 else if [[ -d $out ]]; then echo "destination directory already exists" exit 1 else mkdir $out fi renaming1 i=1 if [ $pause -gt 0 ]; then while [ $i -le $pause ]; do convert $tmpA $VAR i=`expr $i + 1` renaming1 renaming2 done fi i=1 while [ $i -le $frames ]; do k=`expr $i + 1` eval ee=$expression ff=`convert xc: -format "%[fx:$ee]" info:` convert $tmpA $func1 "$ff${op} $parm" $func2 $VAR i=`expr $i + 1` renaming1 [ $i -le $frames ] && renaming2 done k=$frames while [ $k -gt 0 ]; do eval ee=$expression ff=`convert xc: -format "%[fx:$ee]" info:` convert $tmpB $func1 "$ff${op} $parm" $func2 $VAR k=`expr $k - 1` [ $pause = 0 ] && [ $k -gt 0 ] && renaming3 && renaming1 [ $pause != 0 ] && renaming3 && renaming1 done i=1 if [ $pause -gt 0 ]; then while [ $i -le $pause ]; do convert $tmpB $VAR i=`expr $i + 1` [ $i -le $pause ] && renaming3 && renaming1 done fi fi } # make transitions for image 1 and image 2 transitionFrames # change delay first and last frames if [[ $out = *.* ]] || [[ $out = "" ]]; then if [ "$reverse" = "yes" -a "$effect" = "recursion" ] then echo "" echo "Reversing Animation By Swapping Images And Regenerating Sequence" convert $tmp0 $tmp1 convert $infile1 $tmpB convert $infile2 $tmpA transitionFrames echo "" echo "Merging Both Sequences" convert \( $tmp1 -coalesce \) \( $tmp0 -coalesce -delete 0,-1 \) \ -quiet -layers Optimize $tmp0 elif [ "$reverse" = "yes" -a "$effect" != "recursion" ] then echo "" echo "Reversing Animation - Please Wait" convert $tmp0 -coalesce \( -clone -2-1 \) \ -quiet -layers Optimize $tmp0 fi if [ "$out" != "" ] then convert $tmp0 -loop 0 $out else animate $tmp0 fi fi exit 0