Show An Image With Omxiv Direct From Memory On Rpi
Solution 1:
I did some experiments with the framebuffer on my Raspberry Pi 4. Here is what I managed to work out...
You can get the screen resolution using the fbset
command like this:
fbset -fb /dev/fb0
Sample Output
mode"1280x1024"geometry1280 1024 1280 1024 32timings0000000acceltruergba8/16,8/8,8/0,0/0endmode
That tells me the screen is 1280 px wide and 1024 px high and I need to write 4 bytes per pixel in the order BGRA888.
So, I can do a quick test with ImageMagick to see if I can fill the screen, like this:
# Write to screen buffer - BGRA8888, width=1280, height=1024
convert -size 1280x1024 -depth 8 gradient:lime-magenta bgra:/dev/fb0
and it fills the screen with a lime-magenta gradient. Excellent!
So, having acquired that knowledge and a smattering of confidence, let's try Python...
#!/usr/bin/env python3
import numpy as np
# Map the screen as Numpy array# N.B. Numpy stores in format HEIGHT then WIDTH, not WIDTH then HEIGHT!# c is the number of channels, 4 because BGRA
h, w, c = 1024, 1280, 4
fb = np.memmap('/dev/fb0', dtype='uint8',mode='w+', shape=(h,w,c))
# Fill entire screen with blue - takes 29 ms on Raspi 4
fb[:] = [255,0,0,255]
# Fill top half with red - takes 15 ms on Raspi 4
fb[:h//2] = [0,0,255,255]# Fill bottom right quarter with green - takes 7 ms on Raspi 4
fb[h//2:, w//2:] = [0,255,0,255]
I then tried displaying an image - Lena, of course. So, just for brevity and simplicity, I made Lena exactly the right size and added an alpha channel with ImageMagick:
convert lena.png -resize1280x1024\! -alpha opaque png32:lena1280.png
Then carried on as follows in the Python session I started above:
from PIL import Image
# Load Lena image
im = Image.open('/home/pi/lena1280.png')
# Convert from PIL Image to Numpy array
n = np.array(im)
# Blit to screen - takes 30ms
fp[:] = n
Note that you would probably do better using OpenCV to load the image, with cv.imread(...,cv.IMREAD_UNCHANGED)
, because that will deliver you a Numpy array directly without needing conversion and the BGR ordering will already match that of the frame buffer.
Other useful commands - for my own reference!
# Retrieve EDID settings from monitor and write into a file called "edid"
tvservice -d edid
# Parse the file we just created to see what the attached monitor is capable of
edidparser edid
Sample Output
Enabling fuzzy format match...
Parsing edid...
HDMI:EDID version 1.3, 0 extensions, screen size 38x30 cm
HDMI:EDID features - videodef 0x80 standby suspend active off; colour encoding:RGB444|YCbCr444|YCbCr422; sRGB isdefault colourspace; preferred format is native; does not support GTF
HDMI:EDID found monitor S/N descriptor tag 0xff
HDMI:EDID found monitor name descriptor tag 0xfc
HDMI:EDID monitor name is DELL_1907FP
HDMI:EDID found monitor range descriptor tag 0xfd
HDMI:EDID monitor range offsets: V min=0, V max=0, H min=0, H max=0HDMI:EDID monitor range: vertical is56-76 Hz, horizontal is30-81 kHz, max pixel clock is140 MHz
HDMI:EDID monitor range does not support GTF
HDMI:EDID found preferred DMT detail timing format: 1280x1024p @ 60 Hz (35)
HDMI:EDID established timing I/II bytes are A5 4B 00HDMI:EDID found DMT format: code 4, 640x480p @ 60 Hz in established timing I/II
HDMI:EDID found DMT format: code 6, 640x480p @ 75 Hz in established timing I/II
HDMI:EDID found DMT format: code 9, 800x600p @ 60 Hz in established timing I/II
HDMI:EDID found DMT format: code 11, 800x600p @ 75 Hz in established timing I/II
HDMI:EDID found DMT format: code 16, 1024x768p @ 60 Hz in established timing I/II
HDMI:EDID found DMT format: code 18, 1024x768p @ 75 Hz in established timing I/II
HDMI:EDID found DMT format: code 36, 1280x1024p @ 75 Hz in established timing I/II
HDMI:EDID standard timings block x 8: 0x714F 8180010101010101010101010101HDMI:EDID found DMT format: code 21, 1152x864p @ 75 Hz (4:3) in standard timing 0HDMI:EDID found DMT format: code 35, 1280x1024p @ 60 Hz (5:4) in standard timing 1HDMI:EDID filtering formats with pixel clock unlimited MHz or h. blanking unlimited
HDMI:EDID best score mode initialised to DMT (4) 640x480p @ 60 Hz with pixel clock 25 MHz (score 0)
HDMI:EDID best score mode is now DMT (4) 640x480p @ 60 Hz with pixel clock 25 MHz (score 36864)
HDMI:EDID DMT mode (6) 640x480p @ 75 Hz with pixel clock 31 MHz has a score of11520HDMI:EDID best score mode is now DMT (9) 800x600p @ 60 Hz with pixel clock 40 MHz (score 57600)
HDMI:EDID DMT mode (11) 800x600p @ 75 Hz with pixel clock 49 MHz has a score of18000HDMI:EDID best score mode is now DMT (16) 1024x768p @ 60 Hz with pixel clock 65 MHz (score 94370)
HDMI:EDID DMT mode (18) 1024x768p @ 75 Hz with pixel clock 78 MHz has a score of29491HDMI:EDID DMT mode (21) 1152x864p @ 75 Hz with pixel clock 108 MHz has a score of62324HDMI:EDID best score mode is now DMT (35) 1280x1024p @ 60 Hz with pixel clock 108 MHz (score 5260929)
HDMI:EDID DMT mode (36) 1280x1024p @ 75 Hz with pixel clock 135 MHz has a score of49152HDMI0:EDID preferred mode remained as DMT (35) 1280x1024p @ 60 Hz with pixel clock 108 MHz
HDMI:EDID has only DVI support and no audio support
edidparser exited with code 0
You can turn off/disbale the text cursor in the console like this:
sudo sh -c "TERM=linux setterm -foreground black -clear all >/dev/tty0"
and re-enable it like this:
sudo sh -c "TERM=linux setterm -foreground white -clear all >/dev/tty0"
Keywords: Raspberry Pi, RasPi, framebuffer, fb0, /dev/fb0, Python, Numpy, ImageMagick, direct frame buffer access, edid, HDMI, DVI, monitor capabilities, features, tvservice, edidparser, resolution, bgra8888, blit, bit-blit, cursor
Solution 2:
The easiest way to do this is to make sure your /tmp
filesystem is mounted on tmpfs
which is purely memory-based and therefore is not written to your SD card. Note that means that the contents are lost on each reboot.
So, you need to become root
and use your favourite editor to edit /etc/fstab
, in my case that would be:
sudo vi /etc/fstab
And then you need to add a line like this:
tmpfs /tmp tmpfs defaults,noatime,nosuid 0 0
Then save the file and reboot your RasPi. If you then run df
you will see that /tmp
is on tmpfs
:
df
tmpfs 966620 0 966620 0% /tmp
So, now to your code. If this is a new project, which I guess it must be if you don't know how to get started, please consider using Python3 which has been out 10 years rather than Python2 which is being discontinued in 2 months.
Now you need to use this code:
#!/usr/bin/env python3from PIL import Image
import sys, os
# Create a new 640x480 magenta image
img = Image.new('RGB', size=(640, 480), color=(255, 0, 255))
filename = '/tmp/image.jpg'
img.save(filename)
os.system('omxiv ' + filename)
Note that you "can" pass an image to omxiv
on the command-line:
cat image.jpg | omxiv
or, with a Python program:
WriteImageWithPIL.py | omxiv
But, that produces two problems. Firstly, it no longer reads any keys you type on the keyboard because it is reading from stdin
. Secondly, PIL gets upset writing to omxiv
like that because it closes the pipe before PIL can flush the data, so you'd probably have to start using stdbuffer
in there and that will make big mess.
Post a Comment for "Show An Image With Omxiv Direct From Memory On Rpi"