在 RPI 上直接从内存中使用 omxiv 显示图像

Show an image with omxiv direct from memory on RPI

我想在 PILLOW 中创建一个图像并在 Raspberry Pi 的屏幕上显示它,例如直接从内存中使用 omxiv,而不将其保存到存储卡中,如下所示:

Python 2.7:

from PIL import Image
import os

img = Image.new('RGB', size=(150, 50), color=(0, 0, 255))
....
im_file = ????
os.system('omxiv im_file')

有人能告诉我怎么做吗?

最简单的方法是确保您的 /tmp 文件系统安装在 tmpfs 上,它完全基于内存,因此不会写入您的 SD 卡。请注意,这意味着内容会在每次重新启动时丢失。

因此,您需要成为 root 并使用您最喜欢的编辑器来编辑 /etc/fstab,在我的情况下是:

sudo vi /etc/fstab

然后你需要添加这样一行:

tmpfs   /tmp    tmpfs   defaults,noatime,nosuid 0   0

然后保存文件并重启你的 RasPi。如果你然后 运行 df 你会看到 /tmptmpfs:

df
tmpfs             966620       0    966620   0% /tmp

那么,现在开始你的代码。如果这是一个新项目,如果您不知道如何开始,我想一定是这样,请考虑使用已经推出 10 年的 Python3 而不是 Python2 2个月。

现在您需要使用此代码:

#!/usr/bin/env python3

from 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)

请注意,您 "can" 在命令行上将图像传递给 omxiv

cat image.jpg | omxiv

或者,使用 Python 程序:

WriteImageWithPIL.py | omxiv

但是,这会产生两个问题。首先,它不再读取您在键盘上键入的任何键,因为它正在读取 stdin。其次,PIL 像这样写 omxiv 会很不高兴,因为它会在 PIL 刷新数据之前关闭管道,所以你可能不得不在那里开始使用 stdbuffer,这会造成很大的混乱。

我在 Raspberry Pi 4 上用帧缓冲区做了一些实验。这是我设法解决的问题...

您可以使用 fbset 命令获取屏幕分辨率,如下所示:

fbset -fb /dev/fb0 

示例输出

mode "1280x1024"
    geometry 1280 1024 1280 1024 32
    timings 0 0 0 0 0 0 0
    accel true
    rgba 8/16,8/8,8/0,0/0
endmode

这告诉我屏幕宽 1280 像素,高 1024 像素,我需要按 BGRA888 的顺序为每个像素写入 4 个字节。


因此,我可以使用 ImageMagick 进行快速测试,看看是否可以填满屏幕,如下所示:

# Write to screen buffer - BGRA8888, width=1280, height=1024
convert -size 1280x1024 -depth 8 gradient:lime-magenta  bgra:/dev/fb0

它用石灰-洋红色渐变填充屏幕。太棒了!


所以,在获得了这些知识和一点点信心之后,让我们尝试 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] 

然后我尝试显示图像 - 当然是 Lena。因此,为了简洁起见,我将 Lena 的大小设置得恰到好处,并使用 ImageMagick 添加了一个 alpha 通道:

convert lena.png -resize 1280x1024\! -alpha opaque png32:lena1280.png

然后在我上面开始的Python会话中进行如下:

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

请注意,使用 cv.imread(...,cv.IMREAD_UNCHANGED),使用 OpenCV 加载图像可能会做得更好,因为这将直接为您提供一个 Numpy 数组而无需转换,并且 BGR 顺序已经与帧缓冲区的顺序匹配.


其他有用的命令-供自己参考!

# 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

示例输出

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 is default 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=0
HDMI:EDID monitor range: vertical is 56-76 Hz, horizontal is 30-81 kHz, max pixel clock is 140 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 00
HDMI: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 8180 0101 0101 0101 0101 0101 0101 
HDMI:EDID found DMT format: code 21, 1152x864p @ 75 Hz (4:3) in standard timing 0
HDMI:EDID found DMT format: code 35, 1280x1024p @ 60 Hz (5:4) in standard timing 1
HDMI: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 of 11520
HDMI: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 of 18000
HDMI: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 of 29491
HDMI:EDID DMT mode (21) 1152x864p @ 75 Hz with pixel clock 108 MHz has a score of 62324
HDMI: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 of 49152
HDMI0: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

您可以像 this:

那样在控制台中转动 off/disbale 文本光标
sudo sh -c "TERM=linux setterm -foreground black -clear all >/dev/tty0"

并像这样重新启用它:

sudo sh -c "TERM=linux setterm -foreground white -clear all >/dev/tty0"

关键字: Raspberry Pi, RasPi, framebuffer, fb0, /dev/fb0, Python, Numpy, ImageMagick, 直接帧缓冲区访问, edid、HDMI、DVI、监视器功能、特性、tvservice、edidparser、分辨率、bgra8888、blit、bit-blit、光标