使用PIL和OpenCV在PC上模拟动画OLED / LCD显示器

使ç¨PILåOpenCVå¨PCä¸æ¨¡æOLED LCDæ¾ç¤ºå¨

使用PIL和OpenCV,本指南将向您展示如何在PC上模拟连接到Raspberry Pi的小型全彩显示屏。然后,虚拟显示器将允许您最初在PC上创建动画或交互式内容。然后,通过一些代码修改,将Python代码移植到Raspberry Pi。我还在本指南中包含了示例测试代码。

Adafruit和Luma-OLED的Python库支持许多可以连接到Raspberry Pi的小型LCD或OLED显示器。这些库支持Python Image Library(PIL)为小型显示器创建内容。我们可以在各种平台上运行PIL包,包括Raspberry Pi和PC。但是,为了有效地调试显示内容,我们需要访问物理显示。

因此,本文将向您展示如何创建一个有效操作的虚拟显示器,就像连接到Raspberry Pi的真实显示器一样。然后,此虚拟显示器将允许在移植到Pi之前在PC上测试交互式或动画内容。您可以配置虚拟显示以匹配不同的显示大小和颜色深度。

使用虚拟LCD / OLED显示器,您可以开发和调试显示内容,而无需将PC或笔记本电脑连接到Raspberry Pi。此外,您可以即时访问桌面级应用程序,以帮助在同一环境中创建内容。此外,在使用PC或笔记本电脑的强大功能时,您可以使用自己喜欢的文本编辑器更有效地调试和运行Python代码来测试显示内容。此外,使用虚拟显示器使项目更加便携,使您可以更方便地在移动中尝试新想法。

您可以在此虚拟显示器上执行的操作:

  • 创建和调试动画和游戏场景。
  • 开发和测试菜单驱动系统。
  • 配置虚拟显示以匹配物理显示特性。

使用PIL和OpenCV进行虚拟显示

系统设置

如果您已按照此显示系列中的前两篇文章进行操作,则只需安装OpenCV和NumPy。但是,您只需要在要创建虚拟显示的平台上安装这些软件包。因此,在大多数情况下,执行以下命令将在您的平台上安装NumPy和OpenCV:

以下命令适用于Python版本3.因此,请调整命令以适合您正在使用的Python版本。

pip3 install numpy
pip3 install opencv-python

您可以在此处找到有关此OpenCV程序包的更多详细信息:

  • 用于Python的非官方OpenCV包 -  链接

但是,如果您是本系列的新手,则还需要按照前两篇文章来完成物理和虚拟显示设置。前两篇文章如下:

具有SSD1331的Raspberry Pi的OLED显示库设置

具有SSD1331的Raspberry Pi的OLED显示库设置  -  链接

以上指南介绍了如何在Raspberry Pi上设置OLED显示驱动程序。此驱动程序与PIL Python包兼容,将与本指南一起使用。但是,Adafruit显示库也兼容PIL,也可以使用本指南。 

使用枕头创建OLED或LCD显示内容 - 图形和字体

使用枕头创建OLED或LCD显示内容 - 图形和字体  -  链接

上面的指南介绍了如何在Raspberry Pi和PC上设置字体和库。此外,该指南还包括测试两个平台的示例代码。您还可以找到有关如何在两个不同平台之间移植显示内容代码的详细信息。

在OpenCV和显示驱动程序之间移植PIL

我们使用PIL Python包创建一个图像,然后将该图像传递给显示驱动程序。因此,要创建动画,我们需要将一组图像发送到显示驱动程序。然后,显示驱动程序将在每次图像更新时更新物理显示。

为了模拟物理显示,我们需要一种解决方案来在同一帧内显示图像流; 喜欢看视频。我发现的解决方案是使用OpenCV交互式GUI方法。这些GUI方法将允许我们将图像流式传输到类似于物理显示的静态帧或窗口。此外,GUI方法将接受有助于创建交互式显示的键盘输入。

要驱动物理显示,您可能正在使用类似于以下内容的代码:

我们首先导入驱动程序库,然后配置串口:

# Import Luma.OLED libraries
from luma.core.interface.serial import spi
from luma.oled.device import ssd1331
# Configure the serial port
serial = spi(device=0, port=0)
device = ssd1331(serial)

然后,要将PIL图像发送到物理显示器,我们使用以下代码: 

# Output to OLED display
device.display(image)

因此,驱动物理显示器的Python代码并不多。因此,您可以在交换到虚拟显示代码时轻松注释掉此代码。

使用OpenCV驱动虚拟显示:

我们为NumPy和OpenCV导入了两个库:

1

2

import cv2

import numpy as np

然后,我们将PIL图像发送到虚拟显示方法:

1

2

3

npImage = np.asarray(image)

frameBGR = cv2.cvtColor(npImage, cv2.COLOR_RGB2BGR)

cv2.imshow('Test', frameBGR)

为了OpenCV的利益,我们必须将PIL图像转换为NumPy数组的第一行。然后,我们将图像从RGB转换为BGR颜色模型。最后,我们在计算机桌面上显示生成的图像。

但是,添加了额外的代码行以提供通过按键关闭图像窗口的方法。但是,可以扩展此代码以捕获用于创建显示的交互式组件的进一步按键。在这种情况下,代码允许ESC键关闭程序。

k = cv2.waitKey(1) & 0xFF
if k == 27:
    break

在程序关闭之前,我们添加最后一行代码来关闭OpenCV窗口:

cv2.destroyAllWindows()

如果您查看下面的示例代码,您将看到上述代码如何适合PIL图像例程。在物理显示和虚拟显示之间移植时,PIL图像例程不受影响。在两种显示类型之间移植是注释和取消注释几行代码的情况。

动画PIL图像示例PYTHON代码

在这里,我们有两个代码示例执行相同的操作。但是,一个代码示例具有取消注释的虚拟显示代码行。另一个代码示例将物理显示驱动程序代码行取消注释。因此,一个示例代码在Raspberry Pi上运行,另一个在PC上运行。

示例代码是一种文本动画,它可以改变颜色,在物理显示和虚拟显示之间看起来相同。但是,虚拟显示的比例取决于PC显示器的分辨率。

RASPBERRY PI示例代码

使用PIL的SSD1331显示模块动画示例

使用PIL的SSD1331显示模块动画示例

以下示例代码将在Raspberry Pi上运行,并将代码的虚拟显示行注释掉。

# File: animated-hello-world-pi.py
import time
 
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
'''
# Virtual display
import cv2
import numpy as np
'''
# Import Luma.OLED libraries
from luma.core.interface.serial import spi
from luma.oled.device import ssd1331
# Configure the serial port
serial = spi(device=0, port=0)
device = ssd1331(serial)
 
 
def main():
    frameSize = (96, 64)
    xyCoord = [10,0]
    isUp = True
    timeCheck = time.time()
    colors = ["red", "orange", "yellow", "green", "blue"
                , "magenta", "white", "cyan"]
    color = 0
    time.sleep(0.1)
    while 1:
        image = Image.new('RGB', (frameSize), 'white')
        font = ImageFont.truetype("FreeMonoBold.ttf", 12)
        draw = ImageDraw.Draw(image)
        if isUp:
            xyCoord[1] += 1
        else:
            xyCoord[1] -= 1
            
        if xyCoord[1] == frameSize[1] - 13:
            isUp = False
            color += 1
        elif xyCoord[1] == 0:
            isUp = True
            color += 1
            
        if color == len(colors):
            color = 0
 
        draw.rectangle([(0,0), (95,63)], 'black', 'white')
        draw.text((xyCoord), 'Hello World', fill=colors[color], font=font)
        time.sleep(0.01)
        fps = "FPS: {0:0.3f}".format(1/(time.time() - timeCheck))
        timeCheck = time.time()
        draw.text((2, 0), fps, fill='white')
        
        # Output to OLED display
        device.display(image)
        
        '''
        # Virtual display
        npImage = np.asarray(image)
        frameBGR = cv2.cvtColor(npImage, cv2.COLOR_RGB2BGR)
        cv2.imshow('Test', frameBGR)
        k = cv2.waitKey(1) & 0xFF
        if k == 27:
            break
        '''
    
    # Virtual display
    #cv2.destroyAllWindows()
    
    
if __name__ == "__main__":
    main()

虚拟显示动画示例

使用PIL的虚拟显示动画示例

使用PIL的虚拟显示动画示例

此代码在已配置为运行虚拟显示的PC上运行。它包括物理显示的代码,但已被注释掉。

# File: animated-hello-world.py
import time
 
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
 
import cv2
import numpy as np
'''
# Import Luma.OLED libraries
from luma.core.interface.serial import spi
from luma.oled.device import ssd1331
# Configure the serial port
serial = spi(device=0, port=0)
device = ssd1331(serial)
'''
 
def main():
    frameSize = (96, 64)
    xyCoord = [10,0]
    isUp = True
    timeCheck = time.time()
    colors = ["red", "orange", "yellow", "green", "blue"
                , "magenta", "white", "cyan"]
    color = 0
    time.sleep(0.1)
    while 1:
        image = Image.new('RGB', (frameSize), 'white')
        font = ImageFont.truetype("FreeMonoBold.ttf", 12)
        draw = ImageDraw.Draw(image)
        if isUp:
            xyCoord[1] += 1
        else:
            xyCoord[1] -= 1
            
        if xyCoord[1] == frameSize[1] - 13:
            isUp = False
            color += 1
        elif xyCoord[1] == 0:
            isUp = True
            color += 1
            
        if color == len(colors):
            color = 0
 
        draw.rectangle([(0,0), (95,63)], 'black', 'white')
        draw.text((xyCoord), 'Hello World', fill=colors[color], font=font)
        time.sleep(0.01)
        fps = "FPS: {0:0.3f}".format(1/(time.time() - timeCheck))
        timeCheck = time.time()
        draw.text((2, 0), fps, fill='white')
        
        '''
        # Output to OLED display
        device.display(image)
        '''
 
        # Virtual display
        npImage = np.asarray(image)
        frameBGR = cv2.cvtColor(npImage, cv2.COLOR_RGB2BGR)
        cv2.imshow('Test', frameBGR)
        k = cv2.waitKey(1) & 0xFF
        if k == 27:
            break
    
    # Virtual display
    cv2.destroyAllWindows()
    
    
if __name__ == "__main__":
    main()

如果你想获得幻想

缩放的SSD1331彩色显示模块

上面的图片大小适合覆盖顶部的虚拟屏幕。但是,您可以使用要模拟的任何显示模块执行此操作。使用像Gimp这样的图形程序将图像缩放到适合虚拟显示屏的大小。

下面的Python代码片段显示了如何将图像叠加在另一个上面:

# Virtual display
# Open file for display background
oledDisplayFile = 'ssd1331-virtual-colour-display.jpg'
oledDisplay = cv2.imread(oledDisplayFile)
# Convert the PIL created image for OpenCV
npImage = np.asarray(image)
frameBGR = cv2.cvtColor(npImage, cv2.COLOR_RGB2BGR)
# Adjust the offsets to position 
# the screen on the background image
offsetY = 34
offsetX = 16
# Combine both images
oledDisplay[offsetY:offsetY + 64, offsetX:offsetX + 96] = frameBGR
# Show resulting image
cv2.imshow('oled', oledDisplay)

上面的代码修改了前面看到的虚拟显示示例代码中使用的小节代码。输出将类似于下图,顶部有新的屏幕叠加:

虚拟显示输出

虚拟显示输出

下一篇文章使用PIL包

在本系列的以下文章中,我将介绍如何使用PIL渲染菜单系统以输出到显示器上。这个菜单系统将使用我将在PyPi上发布的新菜单库。我将介绍如何使用虚拟显示器实现可在PC和Raspberry Pi之间使用的交互式功能。

相关文章

适用于Raspberry Pi指南的诺基亚5110 LCD显示器设置

诺基亚5110 LCD显示器设置为Raspberry Pi指南 -  链接

猜你喜欢

转载自blog.csdn.net/qq_42444944/article/details/89328724