Display NumPy array as an image using fbi on Ubuntu

154 Views Asked by At

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!

1

There are 1 best solutions below

0
On BEST ANSWER

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.