Sorry for delay to put it in a vcs, some hours ago this
[cascardo/movie.git] / generate-zooming-video.sh
1 #!/bin/bash
2
3 #   generate-zooming-video - make a video from a big picture zooming datails
4 #   Copyright (C) 2008  Aurélio A. Heckert
5 #
6 #   This program is free software: you can redistribute it and/or modify
7 #   it under the terms of the GNU General Public License as published by
8 #   the Free Software Foundation, either version 3 of the License, or
9 #   (at your option) any later version.
10 #
11 #   This program is distributed in the hope that it will be useful,
12 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
13 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 #   GNU General Public License for more details.
15
16 #   Version 0.1
17
18 # Collect all args and create local variables:
19 for arg in "$@"; do
20   if ( echo -n $arg | grep -q '^--.*=' ); then
21     key="$( echo -n "$arg" |
22             sed 's/^--\([^=]*\)=.*$/\1/; s/[^a-zA-Z0-9]/_/' )"
23     val="$( echo -n "$arg" |
24             sed 's/^--\([^=]*\)=\(.*\)$/\2/' )"
25     eval "$key=\"$val\""
26   fi
27   if ( echo -n $arg | grep -q '^--[^=]\+$' ); then
28     key="$( echo -n "$arg" |
29             sed 's/^--\(.*\)$/\1/; s/[^a-zA-Z0-9]/_/' )"
30     eval "$key=true"
31   fi
32 done
33
34 if ! test -e "$pic"; then
35   echo "Picture missed" >&2
36   exit 1
37 fi
38
39 eval "$( identify -format 'pic_w=%w\npic_h=%h' "$pic" )"
40
41 if ! test -e "$data_file"; then
42   echo "Data file missed" >&2
43   exit 1
44 fi
45
46 [ "$video_size" = "" ] && video_size=400x300
47 video_w=$( echo $video_size | sed 's/^\([0-9]*\)x.*$/\1/' )
48 video_h=$( echo $video_size | sed 's/^.*x\([0-9]*\)$/\1/' )
49 if [ "$video_w" = "" -o "$video_h" = "" ]; then
50   echo "Video size missed" >&2
51   exit 1
52 fi
53
54 output="$output_video"
55 replace_output="$replace_output_video"
56 if [ "$output" = "" ]; then
57   echo "Output video file missed" >&2
58   exit 1
59 fi
60
61 if test -e "$output" -a "$replace_output" != "true"; then
62   echo -n "Replace \"$output\"? [y,n] "
63   read -sn 1 resp
64   resp=$( echo "$resp" | tr y Y )
65   test "$resp" != "Y" && resp=N
66   echo $resp
67   if [ $resp = N ]; then
68     echo "So, please, restart with a new name."
69     exit 0
70   fi
71 fi
72
73 function calc() {
74   echo "$@" | bc -l | sed 's/^\(-\?\)\./\10./; s/^$/0/'
75 }
76
77 function round() {
78   calc "$@" | sed 's/\..*$//'
79 }
80
81 function test_calc() {
82   r=$( calc "$1" )
83   if [ "$r" = "$2" ]; then
84     err=Ok
85   else
86     err=Error
87   fi
88   echo "$1 = $2 ($err) => $( round "$@" )"
89 }
90 #test_calc '0 + 0'
91 #test_calc '0 + 1'
92 #test_calc '3 / 98700000'
93 #test_calc '0.102030'
94 #test_calc '80 * 2'
95 #test_calc '80.8 * 2'
96 #test_calc '-123'
97 #test_calc '-.123'
98 #test_calc '-.0123' '-0.0123'
99 #test_calc '010 + 1' 11
100 #test_calc '0010 + 1' 11
101
102 function beault_num() {
103   sed 's/\.\(.*[^0]\)\?0*$/.\1/; s/\.$//'
104 }
105
106 # Video Size Delta:
107 vsd=$( calc "$video_h / $video_w" | beault_num )
108
109 [ "$zoom_out_pct" = "" ] && zoom_out_pct=50
110 zoom_out_pct=$( calc "$zoom_out_pct / 100" )
111
112 # Zoom Out Size:
113 if [ "$zow" = "" -o "$zoh" = "" ]; then
114   zoh=$( round "$pic_h * $zoom_out_pct" )
115   zow=$( round "$zoh / $vsd" )
116   if [ $zow -gt $( round "$pic_w * $zoom_out_pct" ) ]; then
117     zow=$( round "$pic_w * $zoom_out_pct" )
118     zoh=$( round "$zow * $vsd" )
119   fi
120 fi
121
122 [ "$frames_stoped"  = "" ] && frames_stoped=20
123 [ "$frames_moveing" = "" ] && frames_moveing=10
124
125 echo "Video configuration:
126   Picture: $pic
127   Pic Size: w:$pic_w h:$pic_h
128   Zoom Out Size: w:$zow h:$zoh
129   Frames in Detail: $frames_stoped
130   Frames Moving: $frames_moveing
131   Data file: $data_file
132   Output video: $output
133   Output Size: w:${video_w} h:${video_h} delta:$vsd
134 "
135
136 work_dir=$( mktemp -d )
137
138 cat "$data_file" | ( # start frame generator loop
139
140 f=0
141 old_x=$( round "$pic_w / 2" )
142 old_y=$( round "$pic_h / 2" )
143
144 oldz_h=$pic_h
145 oldz_w=$( round "$oldz_h / $vsd" )
146 if [ $oldz_w -gt $pic_w ]; then
147   oldz_w=$pic_w
148   oldz_h=$( round "$oldz_w * $vsd" )
149 fi
150
151 # Variables:
152 #   Generated with Linear function:
153 #     step  : Frame Step (Natural Numbers)
154 #     sm_#  : Step Moving in Frame Step (0..1 scale)
155 #   Generated with Sigmoid function:
156 #     szo_# : Step Old Zoom in Frame Step (0..1 scale)
157 #     szn_# : Step New Zoom in Frame Step (0..1 scale)
158
159 eval "$( perl -e "
160   \$e=2.7182818284;
161   for ( \$step=0; \$step<=$frames_moveing; \$step++ ) {
162     \$sm = \$step / $frames_moveing;
163     \$szo = \$e**(-(\$sm-0.25)*20) / ( 1 + ( \$e**(-(\$sm-0.25)*20) ) );
164     \$szo = 1 if \$sm == 0;
165     \$szo = 0 if \$sm >= 0.5;
166     \$szn = \$e**((\$sm-0.75)*20) / ( 1 + ( \$e**((\$sm-0.75)*20) ) );
167     \$szn = 1 if \$sm == 1;
168     \$szn = 0 if \$sm <= 0.5;
169     print \"sm_\$step=\$sm \t; szo_\$step=\$szo \t; szn_\$step=\$szn\n\"
170   }
171 " )"
172
173 frame=0
174
175 function move_camera() {
176   mid_step=$( round "$frames_moveing / 2" )
177   for step in $( seq 0 $frames_moveing ); do
178     let frame++
179     eval "sm=\$sm_$step ; szo=\$szo_$step ; szn=\$szn_$step"
180     if [ $step -le $mid_step ]; then
181       zw=$( calc "( $oldz_w * $szo ) + ( $zow * (1-$szo) )" )
182       zh=$( calc "( $oldz_h * $szo ) + ( $zoh * (1-$szo) )" )
183     else
184       zw=$( calc "( $zow * (1-$szn) ) + ( $zoom_w * $szn )" )
185       zh=$( calc "( $zoh * (1-$szn) ) + ( $zoom_h * $szn )" )
186     fi
187     x=$( calc "( $old_x * (1-$sm) ) + ( $goto_x * $sm )" | beault_num )
188     y=$( calc "( $old_y * (1-$sm) ) + ( $goto_y * $sm )" | beault_num )
189     x1=$( round "$x - ( $zw / 2 )" )
190     x2=$( round "$x + ( $zw / 2 )" )
191     y1=$( round "$y - ( $zh / 2 )" )
192     y2=$( round "$y + ( $zh / 2 )" )
193     echo -e " frame:$frame \tstep:$step \tsm:$sm \tx:$x \ty:$y"
194     #echo -e " szo:$szo \tszn:$szn \tzw=$zw \tzh=$zh"
195     echo -e " x1=$x1 \t x2=$x2 \t y1=$y1 \t y2=$y2"
196     if [ $x1 -lt 0 ]; then
197       x2=$(( $x2 - $x1 ))
198       x1=0
199     fi
200     if [ $x2 -gt $pic_w ]; then
201       x1=$(( $x1 - ( $x2 - $pic_w ) ))
202       x2=$pic_w
203     fi
204     if [ $y1 -lt 0 ]; then
205       y2=$(( $y2 - $y1 ))
206       y1=0
207     fi
208     if [ $y2 -gt $pic_h ]; then
209       y1=$(( $y1 - ( $y2 - $pic_h ) ))
210       y2=$pic_h
211     fi
212     echo -e " x1=$x1 \t x2=$x2 \t y1=$y1 \t y2=$y2"
213     convert "$pic" \
214             -crop $( round $zw )x$( round $zh )+$( round $x1 )+$( round $y1 ) \
215             +repage -resize "${video_w}x${video_h}!" \
216             -quality 100 $work_dir/f_$frame.jpg
217   done
218 }
219
220 while read line; do
221   eval "$( echo "$line" | sed "s/'/\´/g; s/^\([^\s]*\),\([^\s]*\) \([^\s]*\) \(.*\)$/goto_x='\1'\ngoto_y='\2'\nzoom_w='\3'\nname='\4'/" )" #'
222   zoom_h=$( round "$zoom_w * $vsd" )
223   echo "Zooming $name (${old_x},${old_y}:${oldz_w}x${oldz_h} -> ${goto_x},${goto_y}:${zoom_w}x${zoom_h})"
224   move_camera
225   last_frame=$frame
226   for step in $( seq 2 $frames_stoped ); do
227     let frame++
228     # copy the frame
229     cp $work_dir/f_$last_frame.jpg $work_dir/f_$frame.jpg
230   done
231   echo ""
232   old_x=$goto_x
233   old_y=$goto_y
234   oldz_w=$zoom_w
235   oldz_h=$zoom_h
236 done
237
238 goto_x=$( round "$pic_w / 2" )
239 goto_y=$( round "$pic_h / 2" )
240 zoom_h=$pic_h
241 zoom_w=$( round "$zoom_h / $vsd" )
242 if [ $zoom_w -gt $zoom_w ]; then
243   zoom_w=$pic_w
244   zoom_h=$( round "$zoom_w * $vsd" )
245 fi
246 echo "Zooming The End (${old_x},${old_y}:${oldz_w}x${oldz_h} -> ${goto_x},${goto_y}:${zoom_w}x${zoom_h})"
247 move_camera
248
249 ) # end frame generator loop
250
251 echo "Generating the movie..."
252
253 rm "$output" >&2
254 if ffmpeg -i "$work_dir/f_%d.jpg" "$output"; then
255   echo " Done: $output"
256   vlc "$output"
257 else
258   echo Ups...
259 fi
260
261 rm -r $work_dir
262