This is yet another take on how-to create a time-lapse video. In my case, a website updates a photo of the sky on a regular basis. I wanted to turn that into a time-lapse video and deploy to another, or possibly the same, website. I decided to use a Raspberry Pi 4 that was “sitting around.”
My project evolved as I tried different techniques and apps. I ended up creating a couple of bash scripts to do the job. I used the “ffmpeg” audio/video framework as it was bash command line friendly. I have tested my scripts on my Raspberry Pi 4 with Raspbian OS as well as on different Linux boxes running Ubuntu and Debian.
For how-to scripts with instructions, click on “more“
The first script I developed was to grab a remote image and store it locally in order to process images into video frames. I copy the remote image file using curl. If you have file access to the remote server, you might prefer to use ftp, sftp or scp. I rename the downloaded file (mv) to avoid processing a file that is not finished downloading. I occasionally saw an unfinished frame (top part of image only) when viewing video. Waiting for a completed download – and then renaming – fixed this issue. The stored files are named with the date and time and image type suffix – in my case “.jpg” Some other possibilities are .png, .jpeg, .tiff, etc. You should ensure correct conversion to video by using “ffmpeg” options for input file type, if needed.
#!/bin/bash # # grab_frames.sh # copy remote image file to local directory # - rename file to current date + time # - sleep for set amount of seconds and loop # - 120 seconds is a good choice for time-lapse video creation # replace with your image URL IMAGE_URL="https://server.com/subdir/webcam.jpg" FRAMES_DIR="/home/pi/lapse_frames" WAIT_SECS=120 while(true) do curl -s -o $FRAMES_DIR/temp_image.bin $IMAGE_URL DATE=$(date +"%Y-%m-%d_%H%M") mv $FRAMES_DIR/temp_image.bin $FRAMES_DIR/$DATE.jpg sleep $WAIT_SECS done
On my Raspberry Pi, as user Pi, I created a file in the home directory, “grab_files.sh” with the above shell script. After editing, change the permissions to make executable. My script saves the video frames to a directory, “/home/pi/lapse_frames” You need to create this dir before running the script 😉
pi@Pi4:~ $ vi grab_frames.sh #copy script into this file pi@Pi4:~ $ chmod 755 grab_frames.sh pi@Pi4:~ $ mkdir lapse_frames
To start the script in the background and keep it running even after logout, you use the “nohup” command in conjunction with (send to background) “&” You can also save any error messages to a log file:
pi@Pi4:~ $ nohup ./grab_frames.sh > grab_errors.log &  18053 nohup: ignoring input and redirecting stderr to stdout
The nohup command will return a process ID that you can use to kill the process – when you want it to stop. You’ll also (probably) see a message indicating that output (standard and errors) will be sent to “stdout” – which was set tp the file, “grab_errors.log” You can also find the process ID later or from a different terminal session using the “ps” command. See below for an example on how to find the PID and stop (kill) the process:
pi@Pi4:~ $ ps aux | grep -i grab_frames pi 18053 0.0 0.0 7676 2476 pts/0 S 13:30 0:00 /bin/bash ./grab_frames.sh pi@Pi4:~ $ kill 18053
OK, we now have a process running that will grab a remote image every 2 minutes and download to a local directory with date and time-stamp formatted name. We need to process the images (frames) into a video and clean-up the directory, as needed, so we don’t end up with a bunch of files that use up our disk space. I chose to make a cumulative video every 20 minutes for a single day. I also decided to create the video(s) only during daylight hours – as my need was for tracking clouds.
Create and edit “make_video.sh” in the Pi home directory. I use “vi” as my text editor but you may prefer another 😉
pi@Pi4:~ $ cd pi@Pi4:~ $ vi make_video.sh #copy script into this file pi@Pi4:~ $ chmod 755 make_video.sh
#!/bin/sh # make_video.sh - Andy Felong - 15 Mar 2020 # grabs images from local dir: /home/pi/lapse_frames/ # filters images to today and selected (daylight) hours # creates H.264, MP4 video file - set path below in $VIDEO_OUT # set directory for input images (frames) FRAMES_DIR="/home/pi/lapse_frames" # set video output file VIDEO_OUT="/home/pi/my_video_H264.mp4" # set number of seconds between loops (1200 is 20 minutes) LOOP_SECS=1200 # set debug flag to see more info DEBUG=false # Handle passed flags # Handles "-" once-char and simple "--" one word flags OPTS=`getopt -o hvd -l help,verbose,debug -- "$@"` if [ $? != 0 ] then exit 1 fi eval set -- "$OPTS" while true ; do case "$1" in -h) echo "make_video.sh: usage: make_video.sh -[dhv]"; shift;; -v) DEBUG=true; shift;; -d) DEBUG=true; shift;; --help) echo "make_video.sh: usage: make_video.sh -[dhv]"; shift;; --verbose) DEBUG=true; shift;; --debug) DEBUG=true; shift;; --) shift; break;; esac done #start of main loop while(true) ; do DATE=$(date +"%Y-%m-%d_") YESTERDAY=$(date -d "yesterday 13:00" +"%Y-%m-%d") if $DEBUG; then echo 'date: '$DATE echo 'yesterday: '$YESTERDAY printf 'number of jpg files: ' ls $FRAMES_DIR/*.jpg | wc -w fi # creating video for today - delete old images from yesterday (if any) rm $FRAMES_DIR/$YESTERDAY'_'*.jpg 2> /dev/null # file names are in the format: YYYY-MM-DD_HHMM # An example: 2020-03-17_1249.jpg # # I'm looking for daylight images - from 6am - only # delete time entries with hours "00" to "05" # # NOTE: you may need to allow for time zone difference of your source! # 6am Eastern is 3am Pacific - you may need to use "" instead of "" rm $FRAMES_DIR/$DATE'0'*.jpg 2> /dev/null # assumes nighttime around 20:00 (8pm) - delete later images # NOTE: you may need to allow for time zone difference of your source! rm $FRAMES_DIR/$DATE'2'*.jpg 2> /dev/null # clean-up for video creation - delete old video and index files rm $VIDEO_OUT rm /tmp/img* # ffmpeg needs frames indexed (ordered and numbered) # create index files in "/tmp" dir x=1 for i in $FRAMES_DIR/*jpg do counter=$(printf %03d $x) ln "$i" /tmp/img"$counter".jpg x=$(($x+1)) done # use ffmpeg to create video # - ffmpeg has many options - check its manual page # if DEBUG flag is set - show lots of info # - if not set, keep silent if $DEBUG; then ffmpeg \ -f image2 \ -i /tmp/img%03d.jpg \ -vf format=yuv420p \ -framerate 24 \ -c:v libx264 \ -preset veryslow \ -qp 18 \ $VIDEO_OUT else ffmpeg \ -loglevel quiet \ -f image2 \ -i /tmp/img%03d.jpg \ -vf format=yuv420p \ -framerate 24 \ -c:v libx264 \ -preset veryslow \ -qp 18 \ $VIDEO_OUT fi # COPY video to a destination if desired using cp, scp, ftp, sftp, etc. # ensure you have keys in place for no-login or add user + password to destination URL # you do NOT want to enter authorization info every 20 minutes! scp $VIDEO_OUT userame@remote_server.com:~ # wait/sleep for LOOP_SECS seconds (set above) and loop sleep $LOOP_SECS done
You can change the script to have complete 24 hour coverage if/as needed. You could also filter for specific hours as I have done. Remove or edit the “rm” statements in lines 65 & 69.
You can start/stop video creation using the same method as the frame-grabber script.
#start pi@Pi4:~ $ nohup ./make_video.sh > make_video.log &  29007 nohup: ignoring input and redirecting stderr to stdout #stop pi@Pi4:~ $ ps aux | grep -i make_video pi 29007 0.2 0.0 1940 1276 pts/0 S 17:42 0:00 /bin/sh ./make_video.sh pi@Pi4:~ $ kill 29007
You can modify the scripts for your own needs. They could be turned into services that auto-start at system boot. I’ve run different pieces on different servers. My images already have the date and time in the lower right corner but, if desired, you could use “ImageMagick” tools to manipulate the images or add text with the “convert” command – such as a title or date and time.
LMK if you have questions or issues in the comments.