I would like to display a NumPy array as an image on the screen from a Python script. I need to do this without starting X, so I can't use OpenCV, PIL, etc.
Is it possible to do this using fbi in a subprocess started in the Python script?
Below is what I have tried to do. I expected to see the images in the 'images' folder displayed with a 1-second delay between them, but instead I get the error BrokenPipeError: [Errno 32] Broken Pipe
import os
import time
import numpy as np
from PIL import Image
from subprocess import Popen, PIPE
# Define the folder containing the images
folder_path = 'images'
# Loop through each file in the folder
for filename in os.listdir(folder_path):
# Check if the file is an image
if filename.endswith('.jpg') or filename.endswith('.jpeg') or filename.endswith('.png'):
# Load the image into a NumPy array using PIL
image = np.array(Image.open(os.path.join(folder_path, filename)))
# Edit the image as desired
edited_image = image
# Build the fbi command to display the edited image in full-screen mode
command = 'sudo fbi -T 1 -noverbose -a -t 1 -u -blend 500 - '
# Create a new process for fbi, and pass the edited image to its standard input
with Popen(command.split(), stdin=PIPE) as process:
process.stdin.write(Image.fromarray(edited_image).tobytes())
process.communicate() # Wait for fbi to complete
# Wait for 1 second before displaying the next image
time.sleep(1)
Thanks in advance!
I am still confused by your question, but now I think you are generating Numpy arrays rather than images and want to send them to the frame buffer from Python - maybe on a Raspberry Pi?
Here is an example of that:
#!/usr/bin/env python3
import numpy as np
def RGB888toRGB565(rgb888):
"""Convert RGB888 input array to RGB565 output array"""
# Create 16-bit output image, same height and width as input
rgb565 = np.zeros(rgb888.shape[:2], np.uint16)
rgb565[:] = (rgb888[:,:,0] & 0x1f).astype(np.uint16) << 11
rgb565[:] |= (rgb888[:,:,1] & 0x3f).astype(np.uint16) << 5
rgb565[:] |= (rgb888[:,:,2] & 0x1f).astype(np.uint16)
return rgb565
# Get the framebuffer height, width and pixel format (e.g. RGB565) by running "fbset" command
# $ fbset
# mode "1024x600"
# geometry 1024 600 1024 600 16
# timings 0 0 0 0 0 0 0
# accel true
# rgba 5/11,6/5,5/0,0/0
# endmode
#
w, h = 1024, 600
# Create a pure black RGB888 image same size as framebuffer
# then a red, green and blue one
blk = RGB888toRGB565( np.zeros((h,w,3), np.uint8) )
red = RGB888toRGB565( np.full((h,w,3), [255,0,0], np.uint8) )
grn = RGB888toRGB565( np.full((h,w,3), [0,255,0], np.uint8) )
blu = RGB888toRGB565( np.full((h,w,3), [0,0,255], np.uint8) )
# Open the framebuffer
with open('/dev/fb0', 'wb') as fb:
fb.seek(0)
fb.write(blk.tobytes()) # send black frame
time.sleep(0.5)
fb.seek(0)
fb.write(red.tobytes()) # send red frame
time.sleep(0.5)
fb.seek(0)
fb.write(grn.tobytes()) # send green frame
time.sleep(0.5)
fb.seek(0)
fb.write(blu.tobytes()) # send blue frame
time.sleep(0.5)
Obviously the code can be refactored to be tidier, but I wanted to just demonstrate the techniques rather than confuse anyone with super-compact, efficient, non-repetitive tricky code.