Infrared Sky Camera

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.

#  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


  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

On my Raspberry Pi, as user Pi, I created a file in the home directory, “” 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 #copy script into this file
pi@Pi4:~ $ chmod 755 
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_errors.log &
[1] 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 ./
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 “” in the Pi home directory. I use “vi” as my text editor but you may prefer another 😉

pi@Pi4:~ $ cd
pi@Pi4:~ $ vi #copy script into this file
pi@Pi4:~ $ chmod 755 

# - 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)

# set video output file

# set number of seconds between loops (1200 is 20 minutes)

# set debug flag to see more info

# Handle passed flags
# Handles "-" once-char and simple "--" one word flags
OPTS=`getopt -o hvd -l help,verbose,debug -- "$@"`
if [ $? != 0 ]
    exit 1

eval set -- "$OPTS"

while true ; do
    case "$1" in
        -h) echo " usage: -[dhv]"; shift;;
        -v) DEBUG=true; shift;;
        -d) DEBUG=true; shift;;
        --help) echo " usage: -[dhv]"; shift;;
        --verbose) DEBUG=true; shift;;
        --debug) DEBUG=true; shift;;
        --) shift; break;;

#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
        # 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 "[012]" instead of "[012345]"
		rm $FRAMES_DIR/$DATE'0'[012345]*.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'[0123]*.jpg 2> /dev/null

        # clean-up for video creation - delete old video and index files
		rm /tmp/img*

        # ffmpeg needs frames indexed (ordered and numbered) 
        # create index files in "/tmp" dir
		for i in $FRAMES_DIR/*jpg
			   counter=$(printf %03d $x)
			   ln "$i" /tmp/img"$counter".jpg

        # 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 \
			ffmpeg \
			-loglevel quiet \
			-f image2 \
			-i /tmp/img%03d.jpg \
			-vf format=yuv420p \
			-framerate 24 \
			-c:v libx264 \
			-preset veryslow \
			-qp 18 \

        # 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

        # wait/sleep for LOOP_SECS seconds (set above) and loop
		sleep $LOOP_SECS

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.

pi@Pi4:~ $ nohup ./ > make_video.log &
[2] 29007
nohup: ignoring input and redirecting stderr to stdout

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 ./
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.