How I added crossfading image transitions to my digital picture frame using Pi3D (May 2020 Version) 2

How I added crossfading image transitions to my digital picture frame using Pi3D (May 2020 Version)

Building a great digital picture frame is not just about putting together a minicomputer, a display, and a frame. The crucial part is the software, especially the image viewer.

The image viewer software controls the type of transitions between your pictures. And the kind of transition makes the difference between a good and a bad digital picture frame.

I have tried many types of transitions until I finally found the one that I wholeheartedly recommend. It is based on software called Pi3D, and in this article, I will describe in detail how you can install Pi3D to build a wonderful digital picture frame.

Tested with: Raspberry Pi OS Buster, Raspberry Pi 2, 3 and 4, Pi3D 2.37, PictureFrame2020 Script June 2020, 1080p and 4K displays.
Although Pi3D also works with a Pi Zero, we found that it becomes very unstable as soon as you have other applications running at the same time.

A world of difference

The standard image viewer on Linux is a package called feh. It is a lightweight image viewer that many tutorials on self-made digital picture frames suggest to use. I do not recommend this approach.

When I built my first digital picture frame, I initially used feh for the lack of something better. But the problem with feh is that it offers no transitions between images. It can only do a hard cut between images.

This may be fine for outdoor advertising, but it is unsuited for any social setting.

The hard cuts between photos cause quite a disturbance, especially with a larger screen. There may be a sudden change in brightness, which is uncomfortable, something which distracts and doesn’t feel right. And it gets boring quickly.

I have tested many types of transition effects for over 12 years now. Crossfading transition effects with the right duration are the key to digital picture frame heaven.

You may say, a standard image hard cut image viewer is good enough. But then you would be missing out on something magical. A lack of transitions means that there is no emotion, nothing that touches you.

Here is an example of a 10 seconds crossfade transition effect (comes with soft piano music):

I will explain this in more detail in my article “5 essential tips I learned from building digital picture frames“.

This is where Pi3D comes to rescue.

Taking the Raspberry Pi’s graphical capabilities up a few notches

Back in 2014, I had posted in the English Raspberry Pi forum that I was looking for an image transition software.

One guy suggested that I take a look at 2D/3D rendering software that came with many demo applications. There was one demo that got my attention in particular: SlideTransition. It built a slideshow of all files in a directory with four transition options.

How I added crossfading image transitions to my digital picture frame using Pi3D (May 2020 Version) 3

Among them was crossfading. The one I was looking for.

The “guy” from the forum turned out to be Paddy Gaunt, who, together with Tim Skillman and Tom Ritchford, had written Pi3D, a Python Open GL ES 2.0 library for the Raspberry Pi.

The software dramatically simplifies writing 3D in Python while giving access to the power of the Raspberry Pi GPU. What that means in layman’s terms is that this software fully exploits the graphical capabilities of the small Raspberry Pi.

Paddy showed interest in my digital picture frame project and offered to write a Python script that would produce beautiful and customizable image transitions for my exact purpose: PictureFrame.py was born.

Let’s install Pi3D first and then go into the details of the script.

Installing Pi3D on your Raspberry Pi 2, 3, and 4

The following instructions assume that you have prepared your Raspberry Pi along with the instructions in the article “How to set up your Raspberry Pi for your digital picture frame“.
If you haven’t done yet, please read that article first.

Make especially sure that you have downloaded the “Raspberry Pi OS with Desktop” Image. “Raspberry Pi OS Lite” does not include some software packages that you will need.

There are some special settings for the Raspberry Pi 2 and 3 based on a difference in the graphics driver. The special settings are highlighted in orange.

Open Terminal and connect to your Raspberry Pi via ssh

ssh pi@IP-of-your-Raspberry-Pi (e.g. 192.178.134.79)

Then enter:

sudo apt update && sudo apt upgrade -y

The following settings refer to the graphics engine and change the raspi-config settings via command line.

If you have a Pi 4 copy and paste this in the Terminal:

sudo raspi-config nonint do_boot_behaviour B2 && sudo raspi-config nonint do_memory_split 256 && sudo raspi-config

In the raspi-config module, go to 7 Advanced Options > A8 GL Driver > Choose G2 GL Fake KMS.

For the Raspberry Pi 2 and 3:

sudo raspi-config nonint do_boot_behaviour B4 && sudo raspi-config nonint do_memory_split 128 && sudo raspi-config 

In the raspi-config module, go to 7 Advanced Options > A8 GL Driver > Choose G1 (Legacy)

Reboot and enter in Terminal:

sudo pip3 install pi3d && wget https://github.com/pi3d/pi3d_demos/archive/master.zip && unzip master.zip && rm master.zip && mv pi3d_demos-master pi3d_demos

You’re done. Pi3D, the most important program for your digital picture frame project, is now installed on your Raspberry Pi.

You can test the program by entering (make sure you have a monitor connected to your Raspberry Pi):

For the Raspberry Pi 4:

cd /home/pi/pi3d_demos && sudo xinit /usr/bin/python3 /home/pi/pi3d_demos/Earth.py :0 -- -s off -dpms -s noblank

For the Raspberry Pi 2 and 3:

cd /home/pi/pi3d_demos && python3 Earth.py

If you see a rotating globe, your installation has been successful. If not, check your settings.

Hit “CTRL+C” to stop the globe.

Now we need to look at the Python script for our digital picture frame use case.

PictureFrame2020.py

The script that controls the display of your images is PictureFrame2020.py. It is a Python script which you will find in the pi3d_demos folder. PictureFrame2020.py has a separate config file called PictureFrame2020config.py.

PictureFrame2020.py is controlled either through PictureFrame2020config.py where all the configuration settings are made permanently, or through command line arguments. While command line arguments maybe helpful for testing purposes, I will focus on the PictureFrame2020config.py file.

Open PictureFrame2020config.py in an editor (e.g., Sublime Text).

You will see this section in the file:

parse.add_argument("-a", "--blur_amount",   default=12, type=float, help="larger values than 12 will increase processing load quite a bit")
parse.add_argument("-b", "--blur_edges",    default=False, type=str_to_bool, help="use blurred version of image to fill edges - will override FIT = False")
parse.add_argument("-c", "--check_dir_tm",  default=60.0, type=float, help="time in seconds between checking if the image directory has changed")
parse.add_argument("-d", "--verbose",       default=False, type=str_to_bool, help="show try/exception messages")
parse.add_argument("-e", "--edge_alpha",    default=0.5, type=float, help="background colour at edge. 1.0 would show reflection of image")
parse.add_argument("-f", "--fps",           default=20.0, type=float)
parse.add_argument("-g", "--background",    default=(0.2, 0.2, 0.3, 1.0), type=str_to_tuple, help="RGBA to fill edges when fitting")
parse.add_argument("-i", "--no_files_img",  default="PictureFrame2020img.jpg", help="image to show if none selected")
parse.add_argument("-j", "--blend_type",    default="blend", choices=["blend", "burn", "bump"], help="type of blend the shader can do")
parse.add_argument("-k", "--keyboard",      default=False, type=str_to_bool, help="set to False when running headless to avoid curses error. True for debugging")
parse.add_argument("-m", "--use_mqtt",      default=False)
parse.add_argument(      "--mqtt_server",   default="mqtt.eclipse.org")
parse.add_argument(      "--mqtt_port",     default=1883, type=int)
parse.add_argument(      "--mqtt_login",    default="")
parse.add_argument(      "--mqtt_password", default="")
parse.add_argument("-n", "--recent_n",      default=0, type=int, help="when shuffling the keep n most recent ones to play before the rest")
parse.add_argument("-o", "--font_file",     default="/home/pi/pi3d_demos/fonts/NotoSans-Regular.ttf")
parse.add_argument("-p", "--pic_dir",       default="/home/pi/Pictures")
parse.add_argument("-q", "--shader",        default="/home/pi/pi3d_demos/shaders/blend_new")
parse.add_argument("-r", "--reshuffle_num", default=1, type=int, help="times through before reshuffling")
parse.add_argument("-s", "--show_names_tm", default=0.0, type=float, help="time to show text over image with file name")
parse.add_argument("-t", "--fit",           default=False, type=str_to_bool, help="shrink to fit screen i.e. don't crop")
parse.add_argument("-u", "--kenburns",      default=False, type=str_to_bool, help="will set FIT->False and BLUR_EDGES->False")
parse.add_argument("-v", "--time_delay",    default=30.0, type=float, help="time between consecutive slide starts - can be changed by MQTT")
parse.add_argument("-w", "--fade_time",     default=4.0, type=float, help="change time during which slides overlap - can be changed by MQTT")
parse.add_argument("-x", "--shuffle",       default=True, type=str_to_bool, help="shuffle on reloading image files - can be changed by MQTT")
parse.add_argument("-y", "--subdirectory",  default="", help="subdir of pic_dir - can be changed by MQTT")
parse.add_argument("-z", "--blur_zoom",     default=1.0, type=float, help="must be >= 1.0 which expands the backgorund to just fill the space around the image")
parse.add_argument(      "--auto_resize",   default=True, type=str_to_bool, help="set this to false if you want to use 4K resolution on Raspberry Pi 4. You should ensure your images are the correct size for the display")

This may seem like a lot of options first but you can leave most default values as they are. Let me walk you through the various arguments.

For a full directory of all Pi3D parameters, look here.

If you want to get going with the most often used settings, just look at these four:

Pictures directory

This line defines the directory where you put your images. Note that it is recursive, so all subdirectories are included. In your case, it will probably be “/home/pi/Pictures”. If you change it, don’t forget to include “/home/pi/”.

parse.add_argument("-p", "--pic_dir", default="/home/pi/Pictures")

Shuffle mode

This line activates the shuffle mode for your image playlist. Set it to “True”.

parse.add_argument("-x", "--shuffle", default=True, type=str_to_bool, help="shuffle on reloading image files - can be changed by MQTT")

Delay between images

This line specifies the time between two images in seconds. For a living room setting, I have found a value of 200 ideal. Don’t make it too short as you need to include the time for the transitions.

parse.add_argument("-v", "--time_delay", default=200.0, type=float, help="time between consecutive slide starts - can be changed by MQTT")

Transition time

This line defines the length of the transitions in seconds. I have found a value of 10 ideal. If you have a 4K display connected, 5 is ideal.

parse.add_argument("-w", "--fade_time", default=10.0, type=float, help="change time during which slides overlap - can be changed by MQTT")

These four settings are all you need to get going. If you are in a hurry, you can skip the other parameters and jump to “Testing Pi3D”.

Let’s talk about the other parameters.

Times before reshuffling

When Pi3D starts, it creates a shuffled playlist of all the images. This is to ensure that every picture is shown exactly once before the playlist restarts. If you set “default=1”, it will reshuffle the playlist every time it has finished. “default=5” will show the same shuffled list five times. I recommend setting it to “1”.

parse.add_argument("-r", "--reshuffle_num", default=1, type=int, help="times through before reshuffling")

Fit to screen

Often, the aspect ratio of an image will not correspond to the aspect ratio of the screen. “default=True” means that the entire image is shown which can lead to black bars left/right or top/bottom (pillar or letterboxing). “default=False” means that the image is stretched until it fills the entire screen. I only upload landscape formatted images on my frame, so I choose “False”.

parse.add_argument("-t", "--fit", default=True, type=str_to_bool, help="shrink to fit screen i.e. don't crop")

Show file names

You can show the filename with the image. Just set “default=True”.

parse.add_argument("-s", "--show_names", default=False, type=str_to_bool, help="text over image with file name")

Show latest images first after adding new ones

Whenever you add new images to your pictures folder, Pi3D will reshuffle the playlist. “recent_n” will play the newest ones first (based on the Exif date), which is nice. You can set this number to “default=10” if you want to show the ten newest images or “0” if you want to turn this functionality off.

parse.add_argument("-n", "--recent_n", default=10, type=int, help="when shuffling the keep n most recent ones to play before the rest")

Checking for new photos

Pi3D regularly check if new images have been added to your pictures folder. “default=60.0” means that the check happens every minute. I would not change this value.

parse.add_argument("-c", "--check_dir_tm", default=60.0, type=float, help="time in seconds between checking if the image directory has changed")

Frames per second

This value specifies the number of frames during the transition period from one image to another. The more powerful your Raspberry Pi, the higher this value can be, but even with a Pi 4 I found no real difference above “20”, so I would not change this one.

parse.add_argument("-f", "--fps", default=20.0, type=float)

Activate and configure MQTT

This is a very important setting if you want to remote control your picture frame either through voice with Alexa, Home Assistant, iPhone, or Internet of Things devices. Set it to “default=True” if you want to use it.

And if you do, don’t forget to add your MQTT broker server address. If you are using a locally installed broker, just add the IP address or the Bonjour counterpart (“pictureframe.local”). If you don’t have a user and password, just leave the value empty.

Unless you haven’t already done so, don’t forget to install the paho package on your Raspberry Pi which allows for the receiving of MQTT messages. It is just one line:

sudo pip3 install paho-mqtt

For information on installing a local Mosquitto MQTT broker, see here.

parse.add_argument("-m", "--use_mqtt",      default=True)
parse.add_argument(      "--mqtt_server",   default="pictureframe.local")
parse.add_argument(      "--mqtt_port",     default=1883, type=int)
parse.add_argument(      "--mqtt_login",    default="")
parse.add_argument(      "--mqtt_password", default="")

I will describe the Ken Burns, blur and the reflections effects, as well as all remaining settings, in a separate article.

Testing Pi3D

Before you can test Pi3D, you have to upload a few images to your Raspberry Pi Pictures folder (or whatever folder you specified) via file sharing and check if everything works.

In the terminal window enter:

For the Raspberry Pi 4:

cd /home/pi/pi3d_demos && sudo xinit /usr/bin/python3 /home/pi/pi3d_demos/PictureFrame2020.py -- -s off -dpms -s noblank

For the Raspberry Pi 2 and 3:

cd /home/pi/pi3d_demos && python3 PictureFrame2020.py

The slideshow should start after a few seconds. If you have A LOT of images, the first parsing of the Exif data can take up to a minute.

You can stop the slideshow by hitting “CTRL+C”.

Automatically start the Pi3D slideshow at boot

We will use the systemd services to manage the Pi3D script at boot. There is again a slight variation for the Raspberry models.

For the Raspberry Pi 4

Create a new service file

sudo nano /etc/systemd/system/pi3donpi4.service

and paste the following text into the file:

[Unit]
Description=Pi3D on Pi4
After=multi-user.target

[Service]
Type=idle

User=root
ExecStart=xinit /usr/bin/python3 /home/pi/pi3d_demos/PictureFrame2020.py -- -s off -dpms -s noblank

Restart=always

[Install]
WantedBy=multi-user.target

Save with CTRL+O and close with CTRL+X.

Now, we need to change the file permissions to make it readable by all by typing

sudo chmod 644 /etc/systemd/system/pi3donpi4.service

As the last step, you need to tell the system that you have added this file and want to enable this service so that it starts at boot.

sudo systemctl daemon-reload
sudo systemctl enable pi3donpi4.service

Reboot your Pi, and you are all set!

For the Raspberry Pi 2 and 3

Create a new service file

sudo nano /etc/systemd/system/pi3donpi3.service

and paste the following text into the file

[Unit]
Description=Pi3D on Pi3
After=multi-user.target

[Service]
Type=idle

User=pi
ExecStart=/usr/bin/python3 /home/pi/pi3d_demos/PictureFrame2020.py

Restart=always

[Install]
WantedBy=multi-user.target

Save with CTRL+O and close with CTRL+X.

Now, we need to change the file permissions to make it readable by all by typing

sudo chmod 644 /etc/systemd/system/pi3donpi3.service

As the last step, you need to tell the system that you have added this file and want to enable this service so that it starts at boot.

sudo systemctl daemon-reload
sudo systemctl enable pi3donpi3.service

Reboot your Pi, and you are all set!

Useful systemd commands

If you are not yet familiar with systemd, you can find a full introduction here.

But, to quickly recap, if you are making changes to the service files, you may find these commands useful to stop, start, or restart Pi3d (replace with the correct service name for Pi3 or Pi4):

sudo systemctl stop pi3donpi4.service
sudo systemctl start pi3donpi4.service
sudo systemctl restart pi3donpi4.service

Conclusion

Provided everything works as expected, you can now upload your full image collection to your photo folder. Congratulations!

Thanks again to Paddy and his colleagues for such an outstanding contribution and the 2020 update. I have yet to find a similar program that makes such beautiful transitions on the Raspberry Pi.

You can read more about Pi3D here.

Was this article helpful?

Scroll to Top