With smartphones always at our hands, we gather lot of pictures that in the best case are stored somewhere on a local server or an external hard disk. While this guarantees that our collection will potentially last forever, this leads to a problem, at least for me: We will never again look at most of this pictures, so the storage becomes a digital grave. This becomes even worse if we have captured capture valuable moments in life. But what are the pictures taken for if we don’t look at them from time to time?
The Problem
In the recent past there was a product, which seemed to be the perfect solution: digital picture frames. With such a gadget, you could have a slideshow that rotates through a predefined set of pictures. However, these product category somehow was abandoned from the companies’ agenda. Products that are still available today stayed on a low technical standard: e.g. very low display resolution (HD, anyone?), no network connectivity, and so on. Lastly, the software is very restricted.
The Solution
When I heard of the Raspberry Pi Zero, I immediately thought of a possible purpose and quickly came to the scenario pictured above. This project has a lot of interesting areas:
- Program the software (Learn Python!)
- The physical frame
- Use a small screen
- Connect to the picture collection (on an external server)
In the end, when I had the time and the verve to start the project, the Raspberry Pi Zero W was already available, making it even easier to build such a project. In this blog post I want to share my software solution how to access and display the pictures from a local server.
Idea (Requirements)
The picture collection is located on a server that is accessed by the digital picture frame via SSH. We want to show a definable subset of our collection on the screen (some images are simply not intended for a picture frame). Pictures should be shown randomly, but in a limited successional way (if pictures from a birthday in 2016 are shown, show some more from that event before jumping to a totally unrelated date in 2004).
Implementation
Now we will realize what we have pictured as idea one by one in the following.
Create a Subset of Pictures
To get a definable subset of the collection of pictures on the server, we create symbolic links. Let us assume the collection can be found under /media/usbhdd/Pictures/
, we then mirror the whole directoy tree to /media/usbhdd/PictureFrame
using a bash script:
#!/bin/bash
if [[ -d $1 ]]; then
FOLDER=$1
elif [[ -f $1 ]]; then
FOLDER="$(dirname $1)"
else
echo "$1 is not valid"
exit 1
fi
find $FOLDER -type f -iname "*.jpg" -print0 | while IFS= read -r -d $'\0' line; do
symlink_filename="/media/usbhdd/PictureFrame/$(echo $line | cut -sd / -f 5-)"
symlink_folder="/media/usbhdd/PictureFrame/$(dirname $line | cut -sd / -f 5-)"
mkdir -vp $symlink_folder
ln -vs "$(readlink -e "$line")" "$symlink_filename"
#echo "$FOLDER $line $symlink_folder $symlink_filename"
done
Code language: Bash (bash)
The script is called with either a folder or file as an argument. In line 1-9 the folder path is extracted, depending on the type of the argument. Line 10 iterates over the files in this folder. In line 11 and 12 the folder and the filename of the sybolic link are created. Finally, in line 14f., the symbolic link is created. We can call this script with the path to picture collection as parameter:
softlink_to_pictureframe.sh /media/usbhdd/Pictures/
Code language: Bash (bash)
The result is a directory tree full of symbolic links to our picture collection. The idea is that the picture frame later accesses the images through these symbolic links; we can now create our subset of pictures by simply deleting the entries we don’t want to see on the digital picture frame.
Random Access with a Limited Successional Flavor
To achieve this a lot of ways are possible. My approach is that the digital picture frame iterates through a simple text file, in which line by line the pictures to show are listed. In each iteration the received picture path then can be used in a SSH command to retrieve the image and show it on the screen. The pictures are at the lowest level grouped by the week of the year in which they are taken. We want to shuffle on this weeks. The whole concept is simple and straight forward, but tricky, as you will see: With the following script (on server side) we create such a paths list:
#!/bin/bash
# create temp file, use file descriptor
tmpfile=$(mktemp /tmp/gen_shuffled_pics_list.XXXXXX)
shuffle_file="/media/usbhdd/PictureFrame/file_list.txt"
rm $shuffle_file
last_dirname=""
find /media/usbhdd/PictureFrame -mindepth 2 -maxdepth 3 -type d -not -path '*/\.*' | tac |
while read dirname
do
if ! [[ $last_dirname =~ $dirname ]]; then
echo $dirname >> $tmpfile
fi
last_dirname=$dirname
done
shuf $tmpfile |
while read dirname
do
find $dirname -iname "*.jpg" -print | shuf -n 6 | sort >> $shuffle_file
done
trap "rm -rf $tmpfile" EXIT
Code language: Bash (bash)
Line 9 needs a little clarification: my collection is sorted in the way PictureFrame/YEAR/MONTH/WEEK/
with some special folders like from photo shootings being at the YEAR level. With the find
command we get the WEEK folders with the pictures, the Special folders with pictures, but also the MONTH folders with the WEEK sub folders; in line 10-16 these are sorted out to prevent pictures in the sub folders being found again. In line 18ff. the directory entries are shuffled and then from each directory only 6(!) pictures are written to the picture text file. We do this to have a good mix of pictures from different dates even if some have huge amounts of pictures; this means also that the script ignores many pictures. In my setup, this text file is created every night using a cron job. With this approach the digital picture frame can start every time at the very first line of the text file and still show different pictures.
Small Access Script
The following script can be used by the digital picture frame software to get the amount of pictures (to later access the pictures using Euclidean division):
#!/bin/sh
ssh pi@192.168.0.201 "wc -l < /media/usbhdd/public/Pictures/PictureFrame/file_list.txt"
Code language: Bash (bash)
And to access the individual pictures the following script can be used:
#!/bin/sh
BACKGROUND_FILE="$(dirname $0)/background.jpg"
SSH_SCRIPT_FILE="$(dirname $0)/ssh_commands.sh"
HOST="pi@192.168.0.201"
# Load commands from script and run them on ssh server. Finally, save output to file
ssh ${HOST} 'bash -s' $1 < ${SSH_SCRIPT_FILE} > ${BACKGROUND_FILE}
Code language: Bash (bash)
The image is saved to background.jpg in the script’s folder. To retrieve the image the commands in ssh_commands.sh
are run. These commands can simply return the image or, like in my case, first manipulate the image (shrink it to the size of the digital picture frame, do some enhancements on it). The content of ssh_commands.sh
for my setup is as follows:
FILE=`sed "$1q;d" /media/usbhdd/public/Pictures/PictureFrame/file_list.txt`
BASE=`basename ${FILE}`
# if no file found in cache folder then create it
if [[ ! -f /media/usbhdd/public/Pictures/cache/${BASE} ]]; then
echo ${FILE} | /home/pi/scripts/resize_blurred.sh > /media/usbhdd/public/Pictures/cache/${BASE}
fi
# return cached version
cat /media/usbhdd/public/Pictures/cache/${BASE}
Code language: Bash (bash)
As you can see I also added a server-side cache mechanism that avoids processing the same image again and therefore reduces the load on the server as well as the response time for the whole access script. The image processing is done in resize_blurred.sh
; the content of it is out of the scope of this post and may be issued in a separate one.
Last Words
In this part I have shown how to build the subset of the picture collection that should be shown on the digital picture frame and how an access strategy that fulfills certain requirements could look like. Pictures can already be loaded from the server; the next part will then cover the python-based software on the digital picture frame, which will visualize them in a appealing way. It is not planned from my side to write about the construction of the hardware – I think this is left to everybody’s own discretion.