

FPGA 广泛应用于各种图像处理应用,包括医疗和科学成像、空间成像、汽车和国防领域。
不管用哪种解决方案,高级算法可能不一样,但最基础的部分都是一样的:它们都得先跟图像传感器或者摄像头连接,把拍到的画面处理一下,然后整理成视频流,好让显示器显示或者通过网络传出去。
在本项目中,我们将详细了解使用图像传感器时涉及的不同阶段和元素。
我们将从头开始:图像传感器实际上是如何工作的。

图像传感器是一种神奇的设备,因为它们不仅使我们能够看到可见光谱内的东西,而且还使我们能够看到人类可见光范围之外的东西,例如X射线和红外线。

图像传感器主要基于两种技术:
两者的工作原理都是将撞击半导体的光子转换成电压。
电荷耦合器件- 利用势阱形成像素阵列,势阱在入射光子撞击像素时形成。在积分时间(即捕获图像的时间)内,撞击像素的光子产生的电荷会累积,就像水灌满桶一样,将势阱填满。积分时间结束时,逐个像素输出,将电荷转换为电压。
其工作原理类似于移位寄存器:定时信号将存储信号移位通过像素阵列。为了加快读取速度,可以实现多个输出通道。CCD 是模拟的,信号的定时和电压电平会影响数据传输和图像的整体质量。通常使用外部 ADC 将像素电压转换为数字表示,以便进一步处理。
CCD 如今已不太常见,但由于其卓越的性能,仍用于天文学和空间成像等高端成像应用。
CMOS 图像传感器- CIS 使用光电二极管形成像素阵列,在每个像素上将光子转换为电压。该模拟电压直接在芯片上转换为数字输出。
这种转换方式比CCD读取速度更快,但是CIS的噪声性能通常较差。如今大多数相机都使用CIS,因为它们更易于操作和数字化集成。
如果我们想要在可见光谱之外成像,就必须选择合适的设备。CMOS 和 CCD 传感器都可以捕获 X 射线到近红外 (NIR) 波长。
随着波长向红外光谱方向递减,电子能量也随之降低,需要比硅更先进的半导体。根据观察到的光谱,典型的器件包括:
当我们观看静止图像或视频帧时,它是二维的。然而,如何创建二维图像则取决于具体应用。
例如,如果目标物体正在移动(例如在生产线上),可以使用单行像素,并通过移动生成二维图像。这种方法在生产线检测和轨道卫星成像中很常见,因为轨道的运动提供了生成图像所需的运动。
更常见的替代方法是使用 2D 传感器,无需移动即可捕获 2D 图像。

图像传感器的一个关键性能指标是量子效率 (QE)。QE 测量的是入射到器件的光子数与像素中检测到的光子数之比。
当制造图像传感器时,设备上的结构可以降低前照式设计中的 QE(即光子撞击传感器的前部)。
为了实现更好的量子效率 (QE),人们采用了背照式设计,以减少结构对光子探测的影响。然而,背照式设计需要额外的工艺,这会降低良率并增加成本。

使用二维图像传感器时,我们经常需要确定图像传感器上快门的类型。快门主要有两种类型:

我相信我们都在网上看过这样的视频:直升机在飞行,但旋翼却似乎没有动。这是因为卷帘快门与卷帘上的旋翼叶片同步,导致旋翼叶片看起来静止不动。

对于大多数人来说,我们通过眼睛感知世界的方式是彩色视觉。然而,到目前为止,我们只讨论了像素、电荷的积累,以及电荷转化为电压,再转化为数字格式的过程。
所有波长的光子在一个像素上混合,并转换成代表图像的电压。如果我们在图像处理应用程序中按原样处理这些信息,结果将是一幅灰度图像。
灰度图像用于许多应用,因为它们提供亮度信息,这对于分析亮度、对比度、边缘、形状、轮廓、纹理、透视和阴影至关重要。
灰度运算也具有计算效率高的特点,因为只需处理单个通道的数据。此外,使用阈值处理很容易将灰度图像转换为二值图像,从而实现形态学运算。

为了获得彩色图像,需要在传感器上直接应用一个特定的光学滤镜。这个滤镜被称为拜耳掩模,它覆盖每个像素,只允许一种波长的光(红色、绿色或蓝色)通过。
每个像素仅捕获红色、绿色或蓝色光子。这些滤光片通常排列成 2x2 的网格,包含一个红色像素、一个蓝色像素和两个绿色像素。这种排列会突出绿色,因为它位于可见光谱的中间,而人眼对绿光更敏感。

拜耳掩模需要后期处理才能重建全彩图像。在 FPGA 中,我们可以处理像素流,对原始数据进行反拜耳处理,使用 2x2 网格将每个像素转换为 RGB 值。
此过程涉及网格中相邻像素之间的插值。虽然有效,但由于需要插值来填充缺失的颜色信息,可能会导致图像分辨率略有损失。
如果我们决定处理 RGB 图像,还需要考虑颜色空间。通常,我们从 RGB 颜色空间开始。假设每个颜色通道(R、G、B)8 位,则每个像素需要 24 位。
在 FPGA 中,可以轻松实现任意总线大小。但是,由于 24 位格式,将这些数据存储在内存(例如 DDR3 或 DDR4)中效率不高。
为了提高内存效率,我们可以使用更紧凑的色彩空间,例如 YUV,它将亮度 (Y) 和色度(U 和 V)通道分开。在 YUV 色彩空间中,两个像素可以共享 U 和 V 通道,从而将存储需求降低到每像素 16 位,从而提高效率。
此外,较窄的总线宽度简化了 FPGA 内的布线,使实现更容易且可能更具成本效益。

现在我们了解了图像传感器的工作原理,我们可以探索传感器如何与 FPGA 连接。
主要有两种方法:
无论采用何种方法,FPGA I/O 都是多功能的,能够与摄像机和传感器连接。
我们先来看一下一些常用的接口标准:
对于分辨率更高的图像,其性能可能超出高清 (HD) 存储体的性能,可以使用千兆收发器。HDMI 通过三个差分通道(分别用于红、绿、蓝)和一个用于时钟信号的附加通道传输视频数据。
支持的分辨率:范围从标准清晰度(SD-SDI)到超高清(12G-SDI)。
应用:由于其高质量、低延迟性能和对长电缆传输的支持,非常适合广播和现场制作。
FPGA 支持:当与 AMD FPGA 或 SoC 连接时,SDI 使用千兆收发器。
SDI 视频信号通过专用硬件 IP 核进行处理,可提供:
支持各种SDI标准。诸如视频缩放、色彩空间转换和多路复用等功能。
这些核心能够实现与专业视频工作流程的强大而灵活的集成。


协议层:MIPI 跨各种 OSI 模型层运行,最低的是 DPHY 层。
DPHY 定义了通道数、时钟以及差分信号 (SLVS) 与单端信号 (LVCMOS) 之间的转换。
该组合支持CSI-2(摄像机串行接口)和DSI(显示器串行接口)等协议的高带宽数据传输。
低速通信允许以较低的功率水平有效传输控制信息。
表现:
每个 MIPI DPHY 链路可支持 1 至 4 个高速串行通道,每通道运行速度高达 2.5 Gbps,或四通道运行速度高达 10 Gbps。
数据传输以两倍数据速率进行,与时钟通道同步。
FPGA支持:
AMD UltraScale+ 设备和 Versal™ 自适应 SoC 原生支持 MIPI DPHY。
对于 AMD 7 系列 FPGA 和 UltraScale 设备,可以使用外部电阻网络或定制 DPHY 电路实现 MIPI DPHY。
FPGA的核心理念是尽可能多地利用 Vivado™ 设计套件和 Vitis™ 平台 IP 库中的现有 IP(知识产权)。这些库提供了丰富的预设计组件,能够高效地实现复杂的功能。
视频处理流水线中的大多数接口都使用 AXI Stream 协议在模块之间传输视频流。AXI Stream 的核心操作主要使用以下信号:
视频数据标记
对于视频流,需要额外的标记来指示帧的开始和行的结束,以便可以构建和处理完整的 2D 图像。
为了实现这一目标,AXI Stream 引入了:
这些标记确保视频数据的正确同步和重建,使 AXI Stream 协议非常适合处理 FPGA 中的 2D 图像和视频处理。
了解了 AXI Stream 的概念及其在图像处理流传输中的应用后,我们现在可以探索如何利用 FPGA 的并行特性。一种有效的方法是将多个像素包含在单个 AXI Stream 数据流中。
通过每个时钟周期传输多个像素,可以显著提高图像处理管道的吞吐量。

了解了 AXI Stream 的概念及其在图像处理流传输中的应用后,我们现在可以探索如何利用 FPGA 的并行特性。一种有效的方法是将多个像素包含在单个 AXI Stream 数据流中。
通过每个时钟周期传输多个像素,可以显著提高图像处理管道的吞吐量。
通常,AXI Stream 可以配置为每个时钟周期传输 1、2 或 4 个像素,具体取决于应用和系统要求。例如,当每个时钟周期输出 4 个像素时,整体数据速率和处理效率会显著提高,如下例所示。
这种并行性不仅增强了基于 FPGA 的图像处理流水线的性能,而且还确保了高分辨率和高帧率视频流的无缝处理。

在可编程逻辑中实现图像处理流水线时,需要考虑两种主要架构:
在直接架构中,输入直接连接到处理阶段和输出,具有最少的缓冲并且没有帧缓冲。

帧缓冲架构利用内存来缓冲一个或多个帧。
优点:在以下情况下使用此方法:
需要向相关处理系统中的处理器提供该图像。
需要修改视频流的输出时间(例如,为了同步或与其他组件兼容)。
用例:帧缓冲架构在灵活性和时间调整比延迟问题更重要的应用中很常见。

无论选择哪种图像处理架构(直接还是帧缓冲),设计中使用的 IP 核都需要通过 AXI Lite 进行正确的配置。
AXI Lite 配置:
对于此应用程序,我们将创建一个使用直接方法实现的示例图像处理管道。
这意味着从输入到输出无需帧缓冲,从而确保从输入帧到输出帧的最小延迟。为了实现这一点,我们必须最大限度地减少整个流水线的缓冲。
该设计的目标设备是 AMD Kintex™ 7 FPGA,具体使用 Digilent Genesys 2 开发板,其特点是:
该设计将利用 Vivado,并可分为两个主要部分:
该管道将:
管道和相关的视频时序生成器将由基于 RISC-V 指令集架构的 AMD MicroBlaze V 处理器控制。
与之前使用 VDMA(视频直接内存访问)的示例不同,此应用程序将不使用 VDMA 来确保输入和输出之间的最低延迟。
图像处理流水线将使用以下IP核:
AMD MicroBlaze V 处理器控制器子系统配置为微控制器。此配置支持 AXI 外设数据和指令接口,并通过 AXI 互连连接到:


设备利用率如下所示:

该软件运行在AMD Vitis平台开发的AMD MicroBlaze V处理器上。
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xvtc.h"
#include "xgpio.h"
#include "vga.h"
#include "xparameters.h"
XVtc VtcInst;
XVtc_Config *vtc_config ;
XGpio hpd_in;
XVtc_SourceSelect SourceSelect;
int main()
{
u16 result;
VideoMode video;
XVtc_Timing vtcTiming;
init_platform();
printf("Setting up VTC\n\r");
vtc_config = XVtc_LookupConfig(XPAR_XVTC_0_BASEADDR);
XVtc_CfgInitialize(&VtcInst, vtc_config, vtc_config->BaseAddress);
//configure and assert the HPD
XGpio_Initialize(&hpd_in, XPAR_XGPIO_0_BASEADDR);
XGpio_DiscreteWrite(&hpd_in,1,0x1);
sleep(20);
XGpio_DiscreteWrite(&hpd_in,2,0x1); ///needs time here
video = VMODE_1280x720;
vtcTiming.HActiveVideo = video.width; /**< Horizontal Active Video Size */
vtcTiming.HFrontPorch = video.hps - video.width; /**< Horizontal Front Porch Size */
vtcTiming.HSyncWidth = video.hpe - video.hps; /**< Horizontal Sync Width */
vtcTiming.HBackPorch = video.hmax - video.hpe + 1; /**< Horizontal Back Porch Size */
vtcTiming.HSyncPolarity = video.hpol; /**< Horizontal Sync Polarity */
vtcTiming.VActiveVideo = video.height; /**< Vertical Active Video Size */
vtcTiming.V0FrontPorch = video.vps - video.height; /**< Vertical Front Porch Size */
vtcTiming.V0SyncWidth = video.vpe - video.vps; /**< Vertical Sync Width */
vtcTiming.V0BackPorch = video.vmax - video.vpe + 1;; /**< Horizontal Back Porch Size */
vtcTiming.V1FrontPorch = video.vps - video.height; /**< Vertical Front Porch Size */
vtcTiming.V1SyncWidth = video.vpe - video.vps; /**< Vertical Sync Width */
vtcTiming.V1BackPorch = video.vmax - video.vpe + 1;; /**< Horizontal Back Porch Size */
vtcTiming.VSyncPolarity = video.vpol; /**< Vertical Sync Polarity */
vtcTiming.Interlaced = 0;
memset((void *)&SourceSelect, 0, sizeof(SourceSelect));
SourceSelect.VBlankPolSrc = 1;
SourceSelect.VSyncPolSrc = 1;
SourceSelect.HBlankPolSrc = 1;
SourceSelect.HSyncPolSrc = 1;
SourceSelect.ActiveVideoPolSrc = 1;
SourceSelect.ActiveChromaPolSrc= 1;
SourceSelect.VChromaSrc = 1;
SourceSelect.VActiveSrc = 1;
SourceSelect.VBackPorchSrc = 1;
SourceSelect.VSyncSrc = 1;
SourceSelect.VFrontPorchSrc = 1;
SourceSelect.VTotalSrc = 1;
SourceSelect.HActiveSrc = 1;
SourceSelect.HBackPorchSrc = 1;
SourceSelect.HSyncSrc = 1;
SourceSelect.HFrontPorchSrc = 1;
SourceSelect.HTotalSrc = 1;
XVtc_RegUpdateEnable(&VtcInst);
XVtc_SetGeneratorTiming(&VtcInst, &vtcTiming);
XVtc_SetSource(&VtcInst, &SourceSelect);
XVtc_EnableGenerator(&VtcInst);
XVtc_Enable(&VtcInst);
XVtc_EnableDetector(&VtcInst);
XVtc_Enable(&VtcInst);
xil_printf("Video Mode = %i ", result);
xil_printf("\n\r");
printf("VTC Set Up\n\r");
cleanup_platform();
return 0;
}
上面只是开胃菜,最后推荐一个【图像处理白皮书】:
https://github.com/suisuisi/FPGATechnologyGroup/blob/main/Adiuvo_Image_Processing_with_Programmable_Logic_WhitePaper(%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86%E7%99%BD%E7%9A%AE%E4%B9%A6).pdf