2013-03-07

Sleep, Toss and Turn

Ever wonder if your toddler is a sound sleeper? I did, so I made a little timelapse video:
Stop reading if you don't want the technical details of how I made this.

Raw Capture

I used tinyCam Monitor PRO from my Android phone to record a still photo at 0.1 FPS, i.e. one frame every 10 seconds. The app saved all of the JPEGs to the SD card using a directory and file structure like this:
── 2013-03-04
│   ├── 21.54.38.189.jpg
│   ├── 21.54.54.224.jpg
│   ├── 21.55.04.364.jpg
...
│   └── 23.59.56.995.jpg
└── 2013-03-05
    ├── 00.00.07.247.jpg
    ├── 00.00.17.224.jpg
...
Note how they are named using the time the photo was recorded.

Turning the frames into a movie

After using adb to pull the images off my phone and onto my laptop, I turned to creating the movie. From a previous timelapse video project, I knew that ffmpeg required frames to be numbered sequentially. I used the following shell commands to get these individual files into a set of sequentially numbered ones:
$ cd 2013-03-04
$ for f in `ls *.jpg`; do cp $f `printf %05d ${n}`.jpg; n=`expr $n + 1`; done
$ n=747
$ for f in `ls ../2013-03-05/*.jpg`; do cp $f `printf %05d ${n}`.jpg; n=`expr $n + 1`; done
This exploits the fact that ls lists files alphabetically, and the file numbering scheme used by tinyCam maintains chronological order when sorted lexicographically. I used expr to create a counter to keep track of how many frames I copied, and printf to ensure I had leading zeroes in the filenames. Note that at midnight, the files are created in another directory, so I just continue the numbering where we left off for the second directory. The easy part was actually creating the movie:
$ ffmpeg  -i %05d.jpg -sameq -r 24 -vcodec libx264 output.mp4

Adding the time indicator

My first thought was to use ImageMagick to programmatically embed the time of each frame into the image file itself. But with 3500+ frames, that would be very time-consuming, even if I could automate it. ffmpeg has the ability to burn subtitles into a movie… hey, wait! Subtitles in SubRip format!

After considering Perl for a few minutes, I came to my senses and looked around for a Ruby gem to help me. Fortunately, I found srt, a small gem to parse and create SRT files. After fixing a small bug in the library, and getting the math right, I ended up with this script:

#!/usr/bin/ruby

total_frames = 3684
capture_rate = 6.0 # in frames per minute
display_rate = 24.0 # in frames per second
wallclock_start = Time.new(2013, 03, 04, 21, 54, 54) 
out = SRT::File.new
subtitle_start = Time.new(2013, 03, 04, 00, 00, 00) 
subtitle_interval = capture_rate/display_rate

# Generate a line in the file every 6 frames
0.upto((total_frames-1)/capture_rate.to_i) {|frame|
    line = SRT::Line.new
    line.sequence = frame
    line.start_time = (subtitle_start + subtitle_interval * frame)
    line.end_time = line.start_time + subtitle_interval
    line.text = (wallclock_start + 60*frame).strftime("%R")

    out.lines << line
}

print out.to_s
which produced a subtitle file like this:
0
00:00:00,000 --> 00:00:00,250
21:54

1
00:00:00,250 --> 00:00:00,500
21:55

2
00:00:00,500 --> 00:00:00,750
21:56

Putting it all together

The last step was to upload the video to YouTube, upload the subtitle file as a caption file, and then pick some music that was the same length as the video. I set the options to make the captions display by default.

No comments:

Post a Comment