Share this post on:

最近参加AMD等厂家赞助的FPGA竞赛,从实验中心借到了PYNQ-Z2板子。废话不多说,直接开始。

____,启动!参考【精选】超详细pynq-z2入门_龙叙的博客-CSDN博客 进行板卡的启动和配置。

在烧录好系统镜像后,也许是USB供电的问题,系统迟迟不能启动(DONE的指示灯没亮),试了很多次才成功。(不敢关机了,一口气做完)

调整PC的IP地址使其和PYNQ处于同一网段,访问192.168.2.99:9090,即可进入Jupyter Notebook。

河童半导体的选题不可避免要使用HDMI进行视频流传输。一开始我认为这是个非常棘手的问题,直到我成功启动板卡,发现这里有不少文档(镜像版本V3.0.1,/base/),省去查找大量资料的时间。

接下来就是把这些生肉烤熟

一、开始

内置的pynq库为PYNQ板提供了与HDMI输入/输出相关的功能,import即可。

from pynq.overlays.base import BaseOverlay
from pynq.lib.video import *

base = BaseOverlay("base.bit")
hdmi_in = base.video.hdmi_in
hdmi_out = base.video.hdmi_out

首先,我们将使用默认像素格式,即每像素 24 位 BGR 格式的数据,以便OpenCV使用。

hdmi_in.configure()
hdmi_out.configure(hdmi_in.mode)

hdmi_in.start()
hdmi_out.start()

显示器应打开,并显示空白屏幕。 为了传递图像数据,我们可以将输出与输入联系起来,这将持续到我们发送其他要展示的东西为止。

否则会报错

于是你为此在宿舍内配置了一台显示器,又找来了一根HDMI线。一切就绪,重启整个Notebook:

屏幕上输出了:

但是这个基础的算法似乎被我的2K分辨率输出直接过载了,于是:

噔噔咚

好吧,让我们重启这个玩意。继续烤肉:

hdmi_in.tie(hdmi_out)

虽然这提供了通过管道传递视频数据的快速方法,但无法访问或修改帧。 为此,我们循环调用 readframewriteframe

import time

numframes = 600
start = time.time()

for _ in range(numframes):
    f = hdmi_in.readframe()
    hdmi_out.writeframe(f)
    
end = time.time()
print("Frames per second:  " + str(numframes / (end - start)))

输出:Frames per second: 59.18536696830421,即60帧。

接下来我们可以开始添加一些 OpenCV 处理。 对于以下所有示例,我们将使用拉普拉斯滤波器。 第一个循环,将在软件中执行灰度转换。

import cv2
import numpy as np

numframes = 10
grayscale = np.ndarray(shape=(hdmi_in.mode.height, hdmi_in.mode.width),
                       dtype=np.uint8)
result = np.ndarray(shape=(hdmi_in.mode.height, hdmi_in.mode.width),
                    dtype=np.uint8)

start = time.time()

for _ in range(numframes):
    inframe = hdmi_in.readframe()
    cv2.cvtColor(inframe,cv2.COLOR_BGR2GRAY,dst=grayscale)
    # inframe.freebuffer()
    cv2.Laplacian(grayscale, cv2.CV_8U, dst=result)

    outframe = hdmi_out.newframe()
    cv2.cvtColor(result, cv2.COLOR_GRAY2BGR,dst=outframe)
    hdmi_out.writeframe(outframe)
    
end = time.time()
print("Frames per second:  " + str(numframes / (end - start)))

输出:Frames per second: 7.339499886258246帧数一下就下来了

二、清理

使用完接口时,必须始终停止它们。否则,当比特流重新编程时可能会发生一些不好的事情。你也可以将HDMI接口用作上下文管理器,以确保始终执行清理工作。

hdmi_out.close()
hdmi_in.close()

三、可缓存帧和不可缓存帧

默认情况下,HDMI子系统使用的帧被标记为可缓存,这意味着CPU缓存可用于加速软件操作,但需要在将帧交给视频系统之前刷新帧。这个刷新操作由PYNQ处理,但仍然可能对32位ARM架构造成显著的性能损失。为了减轻这种情况,可以在hdmi_inhdmi_out子系统上将cacheable_frames属性设置为False。这将提高在加速器之间传递帧的性能,但以软件库的操作速度较慢甚至在某些情况下完全无法工作为代价。

base.download()

hdmi_in.configure()
hdmi_out.configure(hdmi_in.mode)
hdmi_out.cacheable_frames = False
hdmi_in.cacheable_frames = False
hdmi_out.start()
hdmi_in.start()

在下面这个普通的读写循环中,输出保持在60FPS:

numframes = 600
start = time.time()

for _ in range(numframes):
    f = hdmi_in.readframe()
    hdmi_out.writeframe(f)
    
end = time.time()
print("Frames per second:  " + str(numframes / (end - start)))

标记为不可缓存帧之后:

numframes = 10
start = time.time()

for _ in range(numframes):
    inframe = hdmi_in.readframe()
    cv2.cvtColor(inframe,cv2.COLOR_BGR2GRAY,dst=grayscale)
    # inframe.freebuffer()
    cv2.Laplacian(grayscale, cv2.CV_8U, dst=result)

    outframe = hdmi_out.newframe()
    cv2.cvtColor(result, cv2.COLOR_GRAY2BGR,dst=outframe)
    hdmi_out.writeframe(outframe)
    
end = time.time()
print("Frames per second:  " + str(numframes / (end - start)))

可以看出,这对帧率损失较大。最后别忘了清理干净:

hdmi_out.close()
hdmi_in.close()

四、灰度

通过在PYNQ应用新的基础架构,我们可以将颜色转换的任务委托给硬件,并仅在处理器之间传递单个灰度像素。

首先,重新配置管道以使用灰度模式,并将它们连接在一起:

base.download()

hdmi_in.configure(PIXEL_GRAY)
hdmi_out.configure(hdmi_in.mode)
hdmi_in.cacheable_frames = True
hdmi_out.cacheable_frames = True
hdmi_in.start()
hdmi_out.start()

hdmi_in.tie(hdmi_out)

现在我们可以重写循环,无需用软件进行颜色转换。

start = time.time()

numframes = 30

for _ in range(numframes):
    inframe = hdmi_in.readframe()
    outframe = hdmi_out.newframe()
    cv2.Laplacian(inframe, cv2.CV_8U, dst=outframe)
    # inframe.freebuffer()
    hdmi_out.writeframe(outframe)
    
end = time.time()
print("Frames per second:  " + str(numframes / (end - start)))
hdmi_out.close()
hdmi_in.close()

输出帧率:Frames per second: 12.99596536643446

五、其他模式

还有两种其他24位模式对于与PIL(Python Imaging Library)交互非常有用。第一种是常规的RGB模式,这对于使用Pillow轻松创建和显示帧非常有用。

base.download()

hdmi_in.configure(PIXEL_RGB)
hdmi_out.configure(hdmi_in.mode, PIXEL_RGB)

hdmi_in.start()
hdmi_out.start()

hdmi_in.tie(hdmi_out)

import PIL.Image

frame = hdmi_in.readframe()
image = PIL.Image.fromarray(frame)
image

输出就是这张图:

另一种模式是YCbCr。对于某些图像处理算法,或导出JPEG文件非常有用。因为我们没有改变每个像素的位数,所以可以动态更新输入的颜色空间。

hdmi_out.colorspace = COLOR_OUT_YCBCR

#现在可使用PIL来读取帧并为我们执行颜色空间的转换。
import PIL.Image

frame = hdmi_in.readframe()
image = PIL.Image.fromarray(frame, "YCbCr")
frame.freebuffer()
image.convert("RGB")
hdmi_out.close()
hdmi_in.close()
Share this post on:

Leave a Comment

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据