The goal of this github repo is to offer Python π scripts for converting animated gif, still pictures (gif, jpeg, png...) and videos (mp4, webm...) in the ssd1306-like OLED panel format, with an optional zlib compression. You will also find MicroPython π examples for demonstrating on a "real case" how to use the converted images...
The ssd1306 OLED panel is pretty famous on Arduino/Nucleo-based electronic devices, like on the AlphaBot2-Ar from Waveshare. You can also find it on the Mars rover - WB55 version from vittascience.
The zlib feature combined with the ssd1306 conversion saves both CPU and disk usage:
- 300bytes for a full screen still picture!
- 3kb for an animated gif with 36 full screen frames!
- 47kb for a complex video with 200 full screen frames!
Notes
- The above AlphaBot2 video has been recorded in mp4 then converted to an animated gif thanks to ffmpeg, more details here
- The video example contains the famous Big Buck Bunny video from Blender Studio under the license CC-BY, converted to fit the size of the animated gif, more details below.
- The video example contains a cat picture from Gretchen Auer on UnSplash and is under the Unsplash License, more details below.
Table of contents
- How to use the scripts
- Result examples
- How to use the generated images in MicroPython
- Other possible solutions with different dependencies
- Any questions or comments are welcome π¦
The main Python script is "convert_animated_gif_to_ssd1306_images.py".
The only dependency is Pillow (the friendly fork of PIL Python Imaging Library). You can install it with pip install pillow
or get more details from the Pillow installation documentation.
Hereafter few script examples:
# Get the help
./convert_animated_gif_to_ssd1306_images.py --help
# Convert an animated GIF without compression (result: examples/animated_python.128x64.36img.raw)
./convert_animated_gif_to_ssd1306_images.py examples/animated_python.gif
# Convert an animated GIF with compression (result: examples/animated_python.128x64.36img.z)
./convert_animated_gif_to_ssd1306_images.py examples/animated_python.gif --compress
The Python script "convert_ssd1306_images_to_animated_gif.py" is useful to check the content of the .raw and .z images by converting them into the GIF format.
# Get the help
./convert_ssd1306_images_to_animated_gif.py --help
# Check the conversion (result: animated_python-generated.gif)
./convert_ssd1306_images_to_animated_gif.py examples/animated_python.128x64.36img.z
# Check the conversion with overwrite & 20ms animation delay (result: animated_python-generated.gif)
./convert_ssd1306_images_to_animated_gif.py examples/animated_python.128x64.36img.raw -f -d 20
# Then, open the generated gif with your favorite viewer (web browser, gimp, eog...)
GIF Input | Generated GIF (1-bit) | Description, raw & zlib sizes |
---|---|---|
256 colors, 74kB, read details |
2 colors |
128x64, 36 frames animated_python.128x64.36img.raw (37kB) animated_python.128x64.36img.z (3kB) |
256 colors, 13kB, read details |
2 colors |
128x64, 1 frame still_mycat.128x64.1img.raw (1kB) still_mycat.128x64.1img.z (0.3kB) |
256 colors, 1063kB, read details |
2 colors |
128x64, 200 frames video_Big_Buck_Bunny_monow.128x64.200img.raw (205kB) video_Big_Buck_Bunny_monow.128x64.200img.z (47kB) |
This animation has been generated thanks to the GFTO online animation tool with the following parameters:
TEXT/FONT: "Python", FreakomixbyAdvo, 28, normal, 0
COLORS,BACKGROUND: white, black, black
ANIMATION: 0.8s, 0.1s, 42, -42, 0, 0, 0, 0
SCREEN: 128x64
This image has been created with the great Inkscape, check "still_mycat.svg" for details. The cat photo has been downloaded from https://unsplash.com/photos/EMnLrtASNKE, the author is Gretchen Auer and the License is the Unsplash License.
The video is the famous Big Buck Bunny from Blender Studio under the license CC-BY.
# convert video into animated gif in 8-bit per pixel (256colors)
wget https://upload.wikimedia.org/wikipedia/commons/transcoded/c/c0/Big_Buck_Bunny_4K.webm/Big_Buck_Bunny_4K.webm.480p.webm
export input_filename="Big_Buck_Bunny_4K.webm.480p.webm"
export output_filename="Big_Buck_Bunny_256colors.gif"
ffmpeg -ss 318 -t 20 -i ${input_filename} -vf "fps=10,scale=128x64:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 ${output_filename}
# convert video into animated gif in 1-bit per pixel (monow)
export input_filename="Big_Buck_Bunny_4K.webm.480p.webm"
export output_filename="Big_Buck_Bunny_monow.gif"
ffmpeg -ss 318 -t 20 -i ${input_filename} -vf "fps=10,scale=128x64:flags=lanczos,format=monow,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 ${output_filename}
The Python/MicroPython source code for the generated image reader is located in ssd1306_image_reader.py. You mainly need to call the constructor ssd1306_image_reader.SSD1306_ImageReader(filename)
and the next_frame()
method. Here below a short example for reference:
... # Initialize your ssd1306 oled panel + whatever is required for your board
import ssd1306_image_reader # See ssd1306_image_reader.py
img_filename = "myanim.128x64.36img.z" # Your animation file
img_reader = ssd1306_image_reader.SSD1306_ImageReader(img_filename) # Call the constructor
for frame in range(img_reader.frames): # Display the animation frames by frames
img_buf = img_reader.next_frame() # Get the current frame
oled.send_buffer(img_buf) # Display it
In the directory "examples/vittascience_alphabot2, you can find a full MicroPython example based on the Mars rover - WB55 version from vittascience. To use it, copy the full content of this directory to your board. Do not forget to copy the related images (.raw or .z) on your board too and update "main.py" according to your need.
The oled.send_buffer()
function is maybe not available in your MicroPython firmware, you can find its source code in "examples/vittascience_alphabot2/stm32_ssd1306.py".
Note The AlphaBot2 video has been recorded in mp4 then converted to an animated gif thanks to the following ffmpeg commands:
export input_filename="alphabot2_example_original.mp4"
export output_filename="alphabot2_example.gif"
ffmpeg -i ${input_filename} -vf "fps=10,scale=512x288:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 ${output_filename}
There are of course more solutions than this PIL single-dependency Python script :-)
For instance, you can have a look to Arduino ssd1306-related sw tools...
You can also use simple command lines for a solution close to this Python script... but without the zlib/gzip compression and the ssd1306-format conversion, that are probably the two most important features to save CPU and disk usage ;-)...
Here below an example based on gifsicle and ImageMagick:
# Installation
sudo apt-get install imagemagick # for convert tool
sudo apt-get install gifsicle # for gifsicle tool
export input_filename="animated_python.gif"
# Convert to 1-bit with the median-cut filter and extract all frames as gif output.gif.xyz
rm -rf frames
mkdir frames
gifsicle --colors 2 --color-method median-cut --explode ${input_filename} -o frames/output.gif
# Get data size thanks to resolution (we /8 as 1-bit / pixel)
bytes=$(identify -format "%[fx:h*w/8]" frames/output.gif.000)
# Check the above computation (simple trace)
echo filesize $bytes bytes
# convert to bmp then remove bmp header (keep only data at the end of the bmp)
for f in frames/*.*; do convert $f bmp:- | tail -c $bytes > $f.raw ; done
cat frames/*.raw > output.raw
# The result is in output.raw
ls -al output.raw
# Note: we may use pbm instead of bmp but pbm are inverted so it is easier to vertically flip bmp :-)
Note Do not forget to use ssd1306_image_converter.py function
to_ssd1306()
to convert your raw file to the ssd1603 format before sending it to the panel. The MicroPython source code could be then:
... # Initialize your ssd1306 oled panel + whatever is required for your board
import ssd1306_image_converter # See ssd1306_image_reader.py
size = (128 * 64) // 8 # Full screen image size in bytes
### STILL PICTURE
img_filename = "still_image.raw" # Your still picture file
f = open(img_filename, 'rb') # Open, read and close the file
img = f.read(size)
f.close()
buf = bytearray(size) # Zeroified the output buffer
ssd1306_image_converter.to_ssd1306(128, 64, img, buf) # Convert to ssd1306 format (ie cpu cost)
oled.send_buffer(buf) # Display the image
text = input("please press enter to continue")
### ANIMATION
### no compression, we read image by image to save memory.
### we may read the entire file and use a memoryview.
img_filename = "animation.raw" # Your animation file
f = open(img_filename, "rb")
while 1: # Infinite animation loop
img = f.read(size)
if len(img) == 0: # looping to the file beginning
f.seek(0)
img = f.read(size)
buf = bytearray(size) # Zeroified the output buffer
ssd1306_image_converter.to_ssd1306(128, 64, img, buf) # Convert to ssd1306 format (ie cpu cost)
oled.send_buffer(img) # Display the image
If you have any comments or questions, feel free to send me an email at coolcornucopia@outlook.com π§.
--
Peace
coolcornucopia π