Timelapse with time stamp overlay


Timelapse video of a house demolition with a time-stamp overlay

Sadly, our neighbors sold their house and it was recently demolished to make way for two new buildings. On the plus side, this gave me an opportunity to try out my Raspberry Pi camera and make a time lapse movie of the process. Specifically, I wanted to try a quick-and-easy way to add a time-stamp in the corner of the video to give an indication of the current time of the day.

Below is the resulting video, hosted on YouTube. Note the date and time stamp on the lower right. Python code for how the text overlay was created can be found further down.

How the video was created

Set up the Raspberry Pi

During the demolition process, I had the RasPi take one picture every minute. To do so, I set up a Cron job to run a customized call to the raspistill program. This script created a time-stamped file name for each image and saved it a folder I created in the home directory (/home/pi/timelapse). The file name follows the convention "YYYY-MM-DD_hhmm.jpg"

DATE=$(date +"%Y-%m-%d_%H%M")
raspistill -hf -vf -o /home/pi/timelapse/$DATE.jpg
To have it run every minute, I added this to my crontab:
*/1 6-19 * * * /home/pi/bin/make_raspistill.sh

Use Python Imaging Library to place a time stamp on each image

The Python Imaging Library (PIL) makes it very easy to overlay text on images. (I'm using the PILLOW fork of PIL.) I wrote a quick script that first resizes and crops each image, and then places the date and time stamp in the corner. The script needs all images to be placed into a folder, and then it needs to be run from within that folder. The script also needs a subdirectory called "resized" in which it will place the processed images with the timestamp overlay. The vertical crop borders (180 px), as well as text size and placement, were found by trial and error.

  1. # script to draw image date/time over the image itself
  3. from PIL import Image
  4. from PIL import ImageFont
  5. from PIL import ImageDraw
  6. import os
  8. font = ImageFont.truetype("/System/Library/Fonts/HelveticaNeue.dfont", 72)
  9. fontsmall = ImageFont.truetype("/System/Library/Fonts/HelveticaNeue.dfont", 32)
  10. fontcolor = (238,161,6)
  11. counter = 0
  12. # Go through each file in current directory
  13. for i in os.listdir(os.getcwd()):
  14. if i.endswith(".jpg"):
  15. counter += 1
  16. # For debugging: limit how many images are processed:
  17. # if counter>10:
  18. # break
  19. print("Image {0}: {1}".format(counter, i))
  20. # Files are named like YYYY-MM-DD_hhmm.jpg
  21. # * Date is the first chunk before the underscore
  22. # * Time is always the first 4 characters after the "_"
  23. splitup = i.split("_")
  24. date = splitup[0]
  25. t = splitup[1][0:4]
  26. # Add colon to time
  27. tformatted = t[0:2] + ":" + t[2:4]
  29. # Open the image and resize it to HD format
  30. # 720p (1280×720 px)
  31. # 1080p (1920x1080 px)
  32. widthtarget = 1920
  33. heighttarget = 1080
  34. img = Image.open(i)
  35. downSampleRatio = float(widthtarget) / float(img.width)
  36. imgDownSampled = img.resize( (widthtarget,round(img.height*downSampleRatio) ), resample=Image.LANCZOS)
  37. imgDownSampled = imgDownSampled.crop((0,180,widthtarget, heighttarget+180))
  39. # get a drawing context
  40. draw = ImageDraw.Draw(imgDownSampled)
  41. draw.text((imgDownSampled.width-220,imgDownSampled.height-150), date, fontcolor, font=fontsmall)
  42. draw.text((imgDownSampled.width-220,imgDownSampled.height-120), tformatted, fontcolor, font=font)
  43. filename = "resized/" + i[0:-4] + "-resized.jpg"
  44. imgDownSampled.save(filename)

Use FFMPEG to make the movie from the image sequence

My workflow for converting image sequences into a movie is as follows:

  1. Rename images so file names are numbered sequentially
  2. Run ffmpeg on the sequentially named images

ffmpeg won't accept the time-stamped image names as input. Luckily, there's an easy way to rename the files to end with a zero-padded numbered sequence. It uses a modified "rename" program that can be found here: http://www.volkerschatz.com/unix/uware/rename.html. (I called it "renum" on my system to keep it separate from the standard Linux/Mac "rename" shell command.) Here's an example:

Change "2016-08-09_0631-resized.jpg" into "image_0001.jpg", "2016-08-09_0632-resized.jpg" into "image_0002.jpg", etc. The "sprintf("image_%04d",${i})" part in the command below means that we'll get a file name that's zero-padded to four digits.

renum -n 's/(\d+)-(\d+)-(\d+)_(\d+)-resized/sprintf("image_%04d",${i})/e' *.jpg

The "-n" option above means you can test whether it works. The output will show whether the renaming works, i.e. whether your regular expression was correct. Change it to "-v" once you see it works.

Once we have our sequentially numbered images, we can call ffmpeg:

ffmpeg -framerate 30 -i image_%04d.jpg -c:v libx264 -r 30 -pix_fmt yuv420p output_filename.mp4
Photos / Video


This is an awesome script! thank you. I hope you don't mind if I have stolen it and modified it to fit my needs. I took out the resizing, and added an if statement to skip it if the file exists already. I am constantly rsyncing with my raspberry pi to test the timelapse, and this speeds up the process by not having to timestamp every file again after a few new ones are added. I've been in love with my raspi ever since I bought the camera module. Thanks for sharing!

Can you please share more detail on image renaming process, like which package to import in python file as I can see that rename command shared above by author is from Perl not from python.
Thank you in advance

I ran the rename script in bash after the Python script put timestamps on the individual images. So "rename" is not called from Python. It's from here: http://www.volkerschatz.com/unix/uware/rename.html
It should actually be fairly straightforward to incorporate the sequential naming (which was only needed because ffmpeg wanted it) into the python script itself, so you can skip the rename bash script step afterwards. You could just create a file name with a zero-padded number when saving each jpg file in the loop; e.g.
  1. fn = "image_{0:04d}.jpg".format( i )
  2. # results in 'image_0003.jpg' for i=3
Just make sure Python is looping through the files in chronological order for this to work. I would have done it this way, had I known about the limitation in ffmpeg concerning numbered files before running my python script on all the images already.

Neat timelapse! Glad you found the code useful.