Sunday, February 13, 2011

Making a zooming out mosaic video

I have collected many photographs while I have been in Japan and I thought that I'll make a befitting compliment to the CCIL lab which was kind enough to allow to work with them.

Hence, I put together my photographs, used Ideamonk's PyMos to make a huge mosaic of the interior of CCIL photograph to show the interior of the CCIL lab. (the original photograph is from here, not taken by me).



Preparing the Mosaic
I use ShotWell for managing my photographs and I already had them tagged with Japan. Hence, all I had to do was to export them to a single folder (./NEC/source in my case) in a reduced size (240 pixel wide).

Using PyMos was surprisingly simple (thought not completely without any issues):

$ pymos -z 50 -ts 60 -f 2 -nc dest/NEC_CCIL.jpg dest/NEC_CCIL_mosaic.png source


However, the mosaic resulted in a photograph was a 68 Mb png, and would simply not work if I wanted to show it at the end of my presentation. Hence, I had to somehow reduce it in size while still being able to show that it constituted only of pictures I took myself of Japan.

Making the Video

So one of the solutions I thought of was recording the zooming of the images itself. So I needed snap shots of the mosaic at various zoom levels stitched together to form a video. I tried doing it with a few zooming snaps but I wanted a smoother transition. A quick calculation showed me that it would need more than 100 snaps to make the transition look smooth for even a small period of 5 seconds. However, I know what I wanted, so I chose to automate it using Python and PIL.

Creating Frames

The idea was starting from a zoomed in initial view, then increasing the picture size by 0.5% each time. This gave me 600+ snaps, good enough for a 20 second video. Here is the code I used:

import Image as I
src = I.open('./dest/NEC_CCIL_mosaic.png')

width, height = src.size
aspect_ratio = float(width) / height
c_x, c_y = (width / 2, height / 2)

# Initial height
img_ht = 600
img_wd = img_ht * aspect_ratio

# The speed (in percent) at which to zoom out.
rate = 1 + 0.5 / 100;
final_size = (1600, 1200)

# The file counter
counter = 0

while img_ht < height:
    box = (c_x - img_wd / 2, c_y - img_ht / 2, c_x + img_wd / 2, c_y + img_ht / 2)
    print 'Starting work on file ' + str(counter)
    new_img = src.crop(box)
    new_img = new_img.resize(final_size)
    new_img.save(file('./dest/mosaic_' + str(counter) + '.png', 'wb'), format='png')
    print 'File ' + str(counter) + ' written.'
    print
    counter += 1
    img_ht *= rate
    img_ht = img_ht if img_ht < width else width
    img_wd = img_ht * aspect_ratio

print 'Done!'

This took about 3 hours to run, though, and nearly drained all my swap space. However, the end result was worth it.

Creating the Video

I used only the command line tools ffmpeg and mencoder to make the final video. First part was animating the frames, which was done easily:

$ ffmpeg -i mosaic_%d.png -s vga -r 5 NEC_mosaic.avi


This made an AVI video with the frames (mosaic_<number>.png>) at the rate of 5 fps of size 640x480. However, the video seemed to start and end rather suddenly. Hence, I thought of including three seconds of still video in the start and the end:


# For the start 
$ ffmpeg -loop_input -vframes 15 -r 5 -i ./vid_percent/mosaic_0.png -s vga NEC_vid_start.avi 


# For the end 
$ ffmpeg -loop_input -vframes 15 -r 5 -i NEC_CCIL.png -s vga NEC_vid_end.avi


Now to put them all together, I used mencoder:

mencoder -ovc copy -forceidx NEC_start_vid.avi vid_percent/NEC_mosaic.avi NEC_start_vid.avi -o NEC_complete.avi



The final result is here:

~
musically_ut

No comments: