图像处理基础:像素与色彩空间、分辨率与帧率

各位同学,欢迎来到第二章。这一章我们不讲代码,也不谈时序。咱们先把图像处理里最基础的概念理清楚。你想想看,FPGA 处理图像,说白了就是处理一堆数字。那这堆数字从哪来?长什么样?怎么在芯片里跑?这就是今天要聊的事。

我个人习惯,在开始任何加速设计之前,先把“数据长什么样”画在纸上。这一步省了,后面大概率要返工。

2.1 像素与色彩空间

图像的最小单位是像素。每个像素就是一个采样点,记录着该位置的颜色信息。那颜色怎么用数字表示?这就引出了色彩空间。

2.1.1 RGB 色彩空间

最常见的是 RGB。红、绿、蓝三个分量,每个分量通常用 8 bit 表示,范围 0~255。一个像素就是 24 bit。显示器、摄像头传感器,基本都是这个路子。

色彩空间 分量 位宽(常见) 典型用途
RGB R, G, B 8+8+8 = 24 bit 显示、采集
YUV/YCbCr Y, U, V 8+8+8 = 24 bit 视频压缩、传输
灰度 Y 8 bit 边缘检测、二值化
我的经验:做实时处理时,我建议优先用 YUV 420 格式。亮度 Y 分量保留完整分辨率,色度 U/V 减半。数据量直接少一半,对 FPGA 的带宽压力小很多。人眼对色度不敏感,画质损失几乎看不出来。

2.1.2 色彩空间转换

摄像头出来通常是 RGB,但很多算法(比如边缘检测)只关心亮度。那就得转成灰度或 YUV。公式其实不复杂:

Y = 0.299 * R + 0.587 * G + 0.114 * B
U = -0.147 * R - 0.289 * G + 0.436 * B
V = 0.615 * R - 0.515 * G - 0.100 * B

在 FPGA 里做这个,浮点乘法太奢侈。我一般用定点数加移位来实现。比如乘以 0.299,可以近似成 (R * 77) >> 8。精度够用,资源省一大截。

注意:我曾经在一个项目里直接用浮点 IP 核做色彩空间转换,结果 LUT 用掉 2000 多个,时序还跑不到 150 MHz。后来换成定点移位,LUT 降到 400,频率直接拉到 200 MHz。所以,能不用浮点就别用。

2.2 图像分辨率与帧率

这两个参数直接决定了你的 FPGA 设计要处理多少数据。咱们算一笔账。

2.2.1 分辨率

分辨率就是图像宽 x 高。比如 1920x1080,总共 2,073,600 个像素。每个像素 24 bit,一帧就是 49.76 Mbit,约 6.22 MB。

2.2.2 帧率

帧率是每秒显示多少帧。常见的 60 fps,意味着每秒要处理 60 帧数据。

那总数据带宽是多少?

带宽 = 分辨率 × 每像素位数 × 帧率
     = 1920 × 1080 × 24 × 60
     = 2,985,984,000 bit/s
     ≈ 2.98 Gbit/s

3 Gbps 的原始数据流。这还没算消隐区。你想想看,FPGA 要在这么高的速率下完成滤波、边缘检测、目标跟踪,对架构设计的要求有多高。

分辨率 像素/帧 @30 fps 带宽 @60 fps 带宽
640x480 (VGA) 307,200 221 Mbps 442 Mbps
1280x720 (HD) 921,600 663 Mbps 1.33 Gbps
1920x1080 (FHD) 2,073,600 1.49 Gbps 2.98 Gbps
3840x2160 (4K) 8,294,400 5.97 Gbps 11.94 Gbps
核心结论:分辨率每翻一倍,数据量翻四倍。FPGA 的时钟频率、DDR 带宽、内部 FIFO 深度,全都要跟着涨。做设计前,先算带宽,再定架构。

2.3 数字图像在 FPGA 中的表示

图像在 FPGA 里就是一堆寄存器、BRAM 或者 DDR 里的数据。但怎么组织这些数据,决定了你的处理效率。

2.3.1 像素流

最常见的表示方式是像素流。每个时钟周期进来一个像素,按行扫描顺序排列。比如 1920x1080 的图像,就是先传第 0 行 1920 个像素,再传第 1 行,以此类推。

在 Verilog 里,我习惯这样定义:

// 像素数据接口
input  wire        clk;          // 像素时钟
input  wire        vsync;        // 帧同步
input  wire        hsync;        // 行同步
input  wire        de;           // 数据有效
input  wire [23:0] pixel_data;   // RGB 888

de 信号是关键。只有 de 为高时,pixel_data 才是有效像素。vsync 和 hsync 用来对齐帧和行的边界。

2.3.2 数据位宽的选择

每个像素用多少 bit,直接影响 BRAM 和 DSP 的资源消耗。我做过一个项目,客户要求 10 bit 色深,结果 BRAM 用量比 8 bit 多了 25%。后来跟客户沟通,确认最终输出设备只支持 8 bit,果断砍回 8 bit。

建议:除非后端算法真的需要高精度(比如 HDR 融合),否则用 8 bit 就够了。省下来的 BRAM 可以多放几行缓存,对做窗口滤波很有帮助。

2.4 行缓存与帧缓存概念

这是 FPGA 图像处理里最核心的存储结构。很多算法(比如 3x3 滤波、中值滤波)都需要同时访问相邻像素。那怎么在串行的像素流里拿到上下行的数据?答案就是行缓存和帧缓存。

2.4.1 行缓存(Line Buffer)

行缓存就是存一行像素的 FIFO 或 BRAM。比如你要做 3x3 的 Sobel 滤波,需要同时知道当前像素、上一行同列像素、下一行同列像素。那你就得缓存两行数据。

结构很简单:

  • Line Buffer 0:存第 N-1 行
  • Line Buffer 1:存第 N 行
  • 当前输入:第 N+1 行

每来一个新像素,三个行缓存同时输出对应列的数据,组成 3x3 窗口。

避坑:我曾经在行缓存深度上吃过亏。图像宽度是 1920,但我只开了 1920 深度的 FIFO。结果忘了考虑消隐区的像素,导致行对齐错位,图像出现斜条纹。后来改成 2200 深度(含消隐),问题解决。记住,行缓存深度要按行总长度算,不是有效像素宽度。

2.4.2 帧缓存(Frame Buffer)

帧缓存就是存一帧完整图像。通常用外部 DDR 实现。什么时候需要帧缓存?

  • 需要跨帧处理(比如运动检测、背景建模)
  • 需要随机访问任意像素(比如缩放、旋转)
  • 需要帧率转换(比如输入 60 fps,输出 30 fps)

但帧缓存的代价很高。DDR 的读写带宽有限,而且有延迟。我一般的原则是:能用行缓存解决的,绝不用帧缓存。

2.4.3 行缓存 vs 帧缓存对比

特性 行缓存 帧缓存
存储介质 BRAM / FIFO DDR / SRAM
容量 几 KB ~ 几十 KB 几 MB ~ 几十 MB
延迟 几个时钟周期 几十 ~ 几百个时钟周期
适用场景 窗口滤波、边缘检测 运动估计、帧率转换
资源消耗 低 高(需要 DDR 控制器)

2.5 本章知识体系

下面这张图把本章的核心概念串起来了。你可以看到,从像素到色彩空间,再到分辨率和帧率,最终落到 FPGA 里的行缓存和帧缓存。每一步都环环相扣。

图像处理基础 - 知识体系图 图像处理基础 - 知识体系 像素 (Pixel) 色彩空间 (RGB / YUV / 灰度) 分辨率 × 帧率 = 数据带宽 FPGA 表示 (像素流 / 位宽) 行缓存 (Line Buffer) vs 帧缓存 (Frame Buffer) 核心思路 从像素出发 理解数据流 选择合适存储