2. 图像数据流特点
做图像处理的硬件加速,第一件事就是搞清楚数据长什么样。我见过不少新手,上来就写RTL,结果发现数据格式不对,整个架构推倒重来。嗯,咱们今天就把这个基础打牢。
像素数据格式
图像说白了就是一堆像素点。每个像素点怎么表示?这取决于色彩空间和位宽。
常见的像素格式:
- 灰度图(Gray8):每个像素8位,0-255表示亮度。我在做医疗影像项目时,CT图像基本都是这个格式,简单直接。
- RGB888:红绿蓝各8位,共24位。这是显示器的标准格式,也是我们做图像加速最常碰到的。
- RGB565:红5位、绿6位、蓝5位,共16位。嵌入式设备里常见,省带宽。
- YUV420:亮度Y单独,色度UV共用。视频编码标准格式,H.264/H.265都用它。
重点:在FPGA里,像素数据通常以AXI4-Stream的形式传输。每个像素可能占1个或多个beat。比如RGB888,我习惯用32位总线,一个beat传一个像素,高8位闲置。
行/帧同步信号
图像数据不是一股脑儿倒进来的。它有节奏——行同步和帧同步。
典型的同步信号包括:
- VSYNC(帧同步):一帧开始时的脉冲。高电平有效或低电平有效,看具体协议。
- HSYNC(行同步):一行开始时的脉冲。
- DE(数据使能):有效数据指示。这个信号我最喜欢用,因为它直接告诉硬件「这拍数据是有效的」。
举个例子,1080p@60fps的时序:
| 参数 | 值 |
|---|---|
| 有效像素/行 | 1920 |
| 行消隐 | 280(含HSYNC) |
| 有效行/帧 | 1080 |
| 帧消隐 | 45(含VSYNC) |
| 总行/帧 | 1125 |
我的经验:在AXI4-Stream里,我们用TLAST信号表示行结束,TVALID和TREADY做握手。TUSER可以携带帧起始信息。我曾经在一个项目中,因为TLAST位置算错了一拍,导致整帧图像错位,排查了两天才找到问题。所以,同步信号的时序仿真一定要做。
带宽计算
带宽是硬件设计的命门。算不对,后面全白干。
带宽公式很简单:
带宽 = 分辨率 × 帧率 × 每像素位数
举个例子:
- 1080p@60fps,RGB888:1920×1080×60×24 = 2.99 Gbps
- 4K@30fps,YUV420:3840×2160×30×12 = 2.99 Gbps
你看,4K YUV420的带宽和1080p RGB888差不多。这就是为什么视频编码喜欢用YUV420——省带宽。
实际工程中要考虑的:
- AXI总线效率不是100%。我一般按70%-80%估算。
- DDR带宽要留余量,至少1.5倍。
- 多路视频处理时,带宽要累加。
缓存需求
图像处理离不开缓存。为什么?因为算法需要「看」到相邻像素。
常见的缓存场景:
- 行缓存(Line Buffer):做3×3滤波,至少需要缓存2行。我习惯用BRAM实现,深度等于图像宽度。
- 帧缓存(Frame Buffer):做帧间处理,比如运动估计,需要存1-2帧。这个量就大了,1080p RGB888一帧要1920×1080×3 = 6.2 MB。BRAM肯定不够,得上DDR。
- 窗口缓存(Window Buffer):做局部处理,比如去噪,缓存一个N×N的窗口。
注意:行缓存的深度必须等于图像的实际宽度,不是有效像素数。因为你要考虑消隐区。我曾经有个项目,图像宽度设成了1920,但实际行总长2200,结果缓存溢出,图像底部出现花屏。嗯,这个坑我替你们踩过了。
知识体系总览
下面这张图,是我总结的图像数据流核心知识点。你看一眼,心里就有谱了。
说白了,图像数据流就是三件事:数据长什么样、怎么告诉硬件数据来了、硬件需要多少资源来处理。搞清楚了这三点,后面的设计就顺了。
个人建议:开始写RTL之前,先花一天时间把数据流图画出来。像素格式、同步信号、带宽、缓存大小,全部标清楚。我每次都是这么干的,省得后面返工。