1. 项目概述:为什么我们需要一个又快又省的深度估计硬件?
深度估计,简单来说,就是让机器像人眼一样,通过两只“眼睛”(通常是两个平行放置的摄像头)看到的世界,来判断物体离我们有多远。这个距离信息,是自动驾驶汽车判断前车刹车距离、机器人规划避障路径、甚至手机实现人像虚化效果的核心。其基本原理,就是计算同一个物体在左右两张图像中水平位置的偏移量,也就是“视差”。物体越近,这个偏移量就越大;物体越远,偏移量就越小。
听起来原理很直接,但要在硬件上实时、准确地算出来,却是个不小的挑战。传统的软件算法在通用处理器(CPU)或图形处理器(GPU)上跑,虽然灵活,但功耗高、速度慢,很难满足嵌入式设备对实时性和能效的苛刻要求。比如,一辆自动驾驶汽车每秒需要处理几十甚至上百帧图像,每一帧的深度图计算都必须在几十毫秒内完成,任何延迟都可能带来风险。
因此,将深度估计算法用硬件“固化”下来,设计成专用的集成电路(ASIC)或在现场可编程门阵列(FPGA)上实现,就成了必然选择。FPGA因其可重构性和并行计算能力,成为算法验证和快速部署的理想平台。但硬件设计本身又有其铁律:资源(逻辑单元、内存、计算单元)是有限的,功耗和成本是必须控制的,而速度和精度则是要拼命追求的。这就像一个带着镣铐的舞蹈,我们需要在严格的约束下,跳出最优美的舞步。
我这次要拆解的,就是一篇来自IEEE Access 2022的论文,它提出了一种基于“视差融合方法”的高速低成本深度估计硬件实现方案。这个方案的核心目标非常明确:在FPGA上,用更少的计算量,跑出更快的速度,同时保持甚至提升深度图的质量。这对于将深度感知能力集成到消费级电子产品或车载系统中,具有实实在在的工程价值。
2. 核心思路拆解:视差融合与连续平面优化
在深入电路细节之前,我们必须先吃透这篇论文的两个核心创新点:视差融合(Disparity Fusion, DF)和连续平面优化(Continuous Plane Refinement, CPR)。理解了它们,就抓住了整个设计的灵魂。
2.1 视差融合:用一半的力气,干全部的活
传统立体匹配算法为了找到一个像素点的最佳视差值,通常需要在完整的视差搜索范围(例如0到63)内,逐个计算匹配代价。这意味着对于每个像素点,我们需要计算64次代价,生成64张代价图,然后从中选出代价最小的那个视差值。计算量和内存占用都非常大。
这篇论文提出了一个大胆的假设:在图像平滑区域,真实视差附近的代价应该也是平滑变化的。也就是说,如果一个点的真实视差是k,那么即使我们只计算了k-1, k, k+1这几个视差,我们也能通过它们之间的代价关系,推断出k很可能就是最优解。
基于这个假设,作者设计了一套巧妙的“分而治之”策略:
- 左图只算偶数视差(2, 4, 6, ..., 64),生成32张左代价图。
- 右图只算奇数视差(1, 3, 5, ..., 63),生成32张右代价图。
- 这样,总的代价图数量从128张(左右各64张)直接降到了64张,计算量砍半。
注意:这里有个关键细节。左右图的计算是独立的,但它们的视差搜索方向是相反的。左图是在右图中向右搜索对应点,而右图是在左图中向左搜索。所以“左图算偶数视差”意味着左图的每个像素只在右图中偏移偶数个像素的位置进行匹配。
那么,只算了一半的视差,如何得到完整的0-63视差图呢?这就是“视差融合”算法的妙处。它通过一个左右一致性检查来恢复丢失的视差信息。具体规则如下(以左图某个像素点视差值为偶数k为例):
- 情况一:检查右图中
(i - (k-1), j)位置的视差值是否等于k-1。若相等,则该点最终视差为k-1。 - 情况二:检查右图中
(i - k, j)位置的视差值。若其值为k-1或k+1(即与k的绝对差为1),则该点最终视差为k。 - 情况三:检查右图中
(i - (k+1), j)位置的视差值是否等于k+1。若相等,则该点最终视差为k+1。 - 如果以上三种情况都不满足,则该点标记为“不一致点”,留待后续优化步骤处理。
这个方法的精妙之处在于,它利用左右图视差图的相互校验,不仅恢复了完整的视差范围,还顺便完成了一次初步的误匹配剔除(左右一致性检查),一举两得。
2.2 连续平面优化:给深度图“磨皮”和“补妆”
经过视差融合,我们得到了一张初步的视差图,但其中还存在一些“不一致点”和噪声。特别是在物体边缘、弱纹理区域或受光照变化影响的地方,匹配容易出错。这时就需要“连续平面优化”出场了。
它的核心思想非常符合直觉:在三维空间中一个连续的表面(比如一面墙、一张桌面),投影到二维图像上,其视差值应该是相同或缓慢变化的。反过来说,图像中位于两个边缘之间的连续区域,其视差也应该基本一致。
CPR算法分为三步:
- 统计(Statistics):对于图像中的每一行,找到两个边缘之间的连续区域。统计该区域内所有“一致点”(即视差融合后确认有效的点)的视差分布,找出出现次数最多的那个视差值
d_ref。 - 优化(Refinement):如果该连续区域内“一致点”的总数超过阈值
τ_v,且d_ref的占比超过阈值τ_h,那么我们就认为这个区域是一个有效的连续平面。将该区域内所有“不一致点”的视差值,都用d_ref来填充。 - 填充(Filling):经过优化后,可能还有少数孤立的点未被处理(比如位于非常小的区域或不符合平面假设)。对于这些残留的“不一致点”,算法会简单地赋予它最近的“一致点”的视差值。
这个过程就像图像处理中的“区域生长”和“空洞填充”,但它是在视差域、并且结合了边缘信息进行的,因此能更智能地平滑噪声、修复错误,同时对图像边缘保持得比较好,避免过度平滑。
2.3 整体算法流程
结合以上两个核心思想,整个算法的流程就清晰了:
- 代价计算:输入左右RGB图像,分别计算颜色、梯度、Census变换编码三种代价,加权融合生成初步的代价图(仅计算一半视差)。
- 引导图像滤波:使用原始灰度图作为引导,对每一张代价图进行滤波,平滑噪声同时保持边缘。这一步能显著提升代价图的质量。
- 胜者为王:对滤波后的每一张代价图,为每个像素点选择代价最小的视差值,分别生成左视差图(仅含偶数视差)和右视差图(仅含奇数视差)。
- 视差融合:对左右视差图进行一致性检查与融合,生成一张覆盖完整视差范围的、初步的视差图,并标记出不一致点。
- 连续平面优化:基于边缘信息,对不一致点进行统计、优化和填充,得到优化后的视差图。
- 中值滤波:最后在垂直方向进行一次中值滤波,去除可能的椒盐噪声,输出最终的视差图。
这个流程在逻辑上层层递进,前一步的输出是后一步的输入,非常适合用流水线(Pipeline)的方式在硬件上实现,从而达到极高的吞吐率。
3. 硬件架构深度解析:如何把算法“烧”进FPGA?
纸上谈兵终觉浅,绝知此事要躬行。算法的巧妙必须通过高效的硬件架构才能转化为实实在在的性能。这篇论文的硬件设计充分体现了面向硬件优化的思想,下面我们分模块拆解。
3.1 信息计算单元:数据预处理流水线
任何图像处理硬件设计的第一步,都是高效的数据输入和预处理。论文中的梯度、Census变换与边缘信息计算单元承担了这个重任。
- 输入与缓存:RGB图像数据以像素流的形式输入。为了进行3x3的卷积操作(如Sobel算子和边缘检测核),需要缓存至少两行图像数据。设计中使用了FPGA的块RAM来构建行缓存,配合一组寄存器,形成一个3x3的滑动窗口。每个时钟周期,窗口移动一列,新的像素数据流入,最旧的数据移出。
- 并行计算:
- RGB转灰度:用于后续的梯度和Census计算。
- 梯度计算:使用Sobel算子(x方向和y方向)与3x3窗口进行卷积,得到每个像素的梯度幅值(实际论文中用的是x和y梯度差的绝对值之和)。这个计算需要两个卷积器。
- Census变换:这是一个非常硬件友好的局部特征描述子。以当前像素为中心,比较其与周围8个邻域像素的灰度值,如果邻域像素值更大则输出1,否则输出0,形成一个8位的二进制串。这个操作只需要比较器,无需乘法,非常适合硬件实现。
- 边缘检测:使用四个不同方向(0°, 45°, 90°, 135°)的3x3核对灰度图进行卷积,取四个结果中绝对值最大的作为边缘强度,再与阈值比较生成二值边缘图。这一步为后续的CPR提供了关键的“边界”信息。
实操心得:在FPGA图像处理流水线开头,设计一个稳定、高效的滑动窗口生成模块是基础。要特别注意行缓存的深度(与图像宽度相关)和初始化的延迟。通常需要等待前N行数据填满缓存后,才有有效的3x3窗口数据输出。这个“启动延迟”在系统时序设计中必须考虑进去。
3.2 代价计算单元:精打细算的硬件优化
代价计算单元需要为每个像素、每个视差(尽管只有一半)计算匹配代价。这是一个计算密集型模块,优化至关重要。
- 三部分代价的硬件实现:
- 颜色代价:计算左右图对应像素RGB三通道的绝对差之和。论文中为了节省硬件,将除以3的操作近似为右移2位(即除以4)。这是一个典型的面积换精度(或速度)的权衡。因为颜色代价在总代价中权重较低(α=0.1),这种近似带来的误差可以接受。
- 梯度代价:计算左右图对应像素x和y方向梯度的绝对差之和。这部分权重最高(β=0.7),因此计算需要保持相对较高的精度。
- Hamming距离代价:比较左右图对应像素的Census编码串。使用异或门逐位比较,然后用一个加法器计算“1”的个数(即汉明距离)。这是纯组合逻辑,速度极快。
- 代价融合与截断:将上述三项代价乘以各自的权重系数(α, β, γ)后相加。在硬件中,乘法常用移位相加来实现。例如,权重0.7(约等于11.2/16)可以通过
(值<<3) + (值<<2) + (值<<0)来近似。最后,每一项代价在相加前都会与一个阈值(Tc, Tg, Th)比较,取较小值。这个“截断”操作非常重要,可以防止个别异常值(如高光点)主导整个代价,同时也能控制数据的位宽,节省存储资源。
3.3 引导图像滤波单元:算法核心的硬件映射
引导图像滤波是提升匹配质量的关键步骤,但其计算涉及均值、方差、协方差等,在硬件上实现需要技巧。
- 均值滤波的优化:GIF中需要多次对整幅图像进行均值滤波(窗口半径r=4)。直接滑动窗口求和计算量巨大。论文采用了经典的积分图/行列和优化思想。
- 维护一个“列和存储器”,存储图像每一列在当前滑动窗口内的累加和。
- 当窗口向下滑动一行时,只需更新这个列和:加上新一行的该列值,减去滑出窗口的旧行该列值。
- 窗口的总和,就是这几列列和的总和。通过一个先入先出队列来管理这些列和,可以每个时钟周期高效地更新窗口总和。
- 求均值时,除以窗口面积(如81)的操作,再次用移位相加来近似(右移7、8、11位的结果相加,近似除以81)。
- 共享计算与近似除法:对于同一幅引导图像I,其均值
mean(I)和方差var(I)对于所有代价图c都是相同的。因此,硬件上可以设计一个共享的GIFU_I模块来计算这些公共量,然后广播给多个并行的GIFU_c模块(每个处理一张代价图)。GIFU_c模块计算mean(c)和cov(I, c)。 - 最棘手的除法:计算线性系数
a_k = cov(I, c) / (var(I) + ε)。除法在FPGA中是非常消耗资源的操作。论文采用了一个巧妙的近似:寻找(var(I) + ε)最接近的2的幂次方,然后用右移操作代替除法。例如,如果分母是9,则近似为8(2^3),那么除法就变成了cov(I, c) >> 3。虽然引入了误差,但在滤波的背景下,这种近似被证明是有效的,且极大地节省了硬件资源。
3.4 胜者为王与视差融合单元:决策与校验
- 胜者为王单元:这是一个经典的“求最小值”电路。输入是32个滤波后的代价(对应32个视差),输出是代价最小的那个视差值。硬件上通常采用比较树的结构。第一级,两两比较,产生16个胜出者和它们的代价;第二级,再两两比较,产生8个……以此类推,经过log2(32)=5级比较,就能得到全局最小值。这种结构延迟固定,且可以通过流水线化来提高吞吐率。
- 视差融合单元:这个模块执行算法3中的逻辑。输入是左视差图(串行输入)和右视差图(需要随机访问,因此设计为串行输入并行输出的缓冲模式)。对于左视差图的每一个值k,硬件需要同时读取右视差图中
(i-k-1, j),(i-k, j),(i-k+1, j)三个位置的视差值(这需要右视差图有足够的行缓存和寻址逻辑),然后并行进行三个条件的判断。根据判断结果,通过一个优先级选择器(通常条件一的优先级最高)输出最终的融合视差值和一致性标志。
3.5 连续平面优化与后处理单元:错误的修正
- 连续平面优化单元:这是算法中最复杂的控制逻辑部分。它需要按行扫描,识别边缘之间的区域,并进行统计和填充。
- 统计部分:需要一组计数器,来统计当前连续区域内,每个视差值出现的次数。当遇到边缘或行尾时,判断当前区域是否有效(点数超过阈值
τ_v,且最大占比视差超过阈值τ_h)。如果有效,则输出该区域的参考视差d_ref。 - 优化部分:在统计的同时,另一条路径根据当前像素的一致性标志和
d_ref的有效性,决定是输出原始视差、d_ref还是标记为待填充。 - 填充部分:对于最终残留的不一致点,需要一个查找逻辑,寻找其左右最近的一致点,用该点的视差值填充。这通常需要额外的缓存和搜索逻辑。
- 统计部分:需要一组计数器,来统计当前连续区域内,每个视差值出现的次数。当遇到边缘或行尾时,判断当前区域是否有效(点数超过阈值
- 中值滤波单元:最后一步是简单的垂直方向中值滤波,用于去除孤立的噪声点。中值滤波的硬件实现也有成熟方案,如使用比较器网络对窗口内的所有值进行排序,然后取中间值。
4. 关键设计抉择与工程化思考
读论文不能光看它做了什么,更要思考它为什么这么做,以及换做我们自己设计,会面临哪些抉择。
4.1 为什么选择GIF而不是ADSW或SGM?
在立体匹配领域,除了本文用的引导图像滤波,还有自适应支持权重和半全局匹配等流行方法。
- 自适应支持权重:效果通常更好,因为它根据颜色和距离为窗口内每个像素赋予不同的权重,能更好地处理边缘。但它的计算量巨大,需要为每个像素、每个视差、每个支持窗口内的像素都计算一次权重,内存访问和计算复杂度都是O(窗口面积 x 视差范围),硬件资源消耗极高。
- 半全局匹配:通过多路径聚合来优化代价,精度很高,但同样涉及复杂的迭代和大量的中间状态存储,不利于高速流水线实现。
- 引导图像滤波:它的优势在于,滤波权重是通过引导图像(这里是原图)线性回归得到的,这个计算过程可以分解为一系列可并行、可流水化的均值滤波操作。虽然一次GIF也需要多次遍历图像,但它的操作是全局统一的,非常适合用第3.3节所述的硬件优化方法来实现,最终在精度和硬件开销之间取得了很好的平衡。
结论:在追求高速、低成本的嵌入式硬件实现中,GIF因其计算规则、易于并行和硬件友好的特点,成为了更优的选择。
4.2 视差融合的“风险”与“收益”
只计算一半视差,最大风险在于假设不成立。在物体边缘、重复纹理或遮挡严重的区域,代价函数可能不是平滑的,真实视差附近的代价可能并非次优。这会导致视差融合步骤无法找到正确的匹配,产生错误。
论文通过两个手段来对冲这个风险:
- 代价滤波:在WTA之前进行GIF,平滑了代价平面,使得“局部最小值”的假设更容易成立。
- 后续优化:CPR模块专门处理这些由融合失败产生的“不一致点”。通过基于边缘的平面假设和填充,能够修复大部分错误。
这是一种典型的系统工程思维:在前级用一个快速但可能有瑕疵的方法获得一个“还不错”的初始解,然后在后级用一个更鲁棒但可能稍慢的方法去修正错误。总体效率远高于每一步都追求完美。
4.3 资源与速度的权衡艺术
通篇论文充满了这种权衡:
- 除法变移位:用精度换取了大量的DSP资源。
- 均值滤波的优化:用额外的行缓存和列和存储器,换取了O(1)时间复杂度的窗口求和,实现了极高的吞吐率。
- 并行度与面积:代价计算、GIF都可以高度并行。但并行32个GIFU_c模块会占用大量资源。设计中通过共享GIFU_I模块,以及可能采用分时复用部分计算单元的策略,来控制总面积。
- 数据位宽:每一步计算后,都需要谨慎地确定数据的位宽,防止溢出,同时避免不必要的位宽浪费。论文中提到的阈值截断,也起到了控制位宽的作用。
5. 性能评估与对比:用数据说话
论文在Kintex-7 FPGA上实现了整个设计,并使用权威的Middlebury立体匹配数据集进行了测试。
5.1 精度对比
作者主要在两个数据集上对比:
- Middlebury V2:包含“Tsukuba”, “Venus”, “Teddy”, “Cones”四组标准图像。评价指标是“坏点率”(Bad Pixel Percentage),即视差误差大于1个像素的点的比例。论文提出的方法在“所有区域”的平均坏点率为6.36%。
- 与早期FPGA设计相比(坏点率通常在10%-20%),精度有显著提升。
- 与同期其他基于GIF的FPGA设计相比,在相近或更少的资源消耗下,达到了最低的坏点率。
- Middlebury V3:包含更多复杂场景和光照变化。在更严格的评价指标下(误差大于阈值τ_d的点的平均绝对误差),该方法也展现出了优于其他对比方案的鲁棒性,证明其CPR模块对光照噪声有一定的耐受性。
5.2 速度与资源对比
这是硬件设计的核心指标:
- 帧率:在110MHz的工作频率下,处理分辨率约为450x375的图像,达到了118 FPS。这意味着处理一帧图像不到8.5毫秒,完全满足实时性要求。
- 吞吐率:达到了6960 MDE/s(每秒百万次视差估计)。这个指标综合了帧率和图像分辨率,是衡量立体匹配硬件性能的通用指标。
- 资源占用:在Kintex-7 XC7K325T FPGA上,主要资源占用如下表所示(与同类设计对比):
| 设计 | LUTs | 寄存器 | BRAMs | DSPs | 帧率 (FPS) | 坏点率 (%) |
|---|---|---|---|---|---|---|
| 本文设计 | ~42K | ~28K | ~120 | ~10 | 118 | 6.36 |
| 设计A (2019) | ~38K | ~26K | ~150 | ~50 | ~60 | ~6.5 |
| 设计B (2018) | ~25K | ~18K | ~90 | ~5 | ~90 | ~9.7 |
解读:可以看出,本文设计在资源使用上非常经济,尤其是DSP(用于乘法)用得很少,这得益于大量的移位和加法优化。在取得最高帧率的同时,保持了最低的坏点率,实现了“高速”与“低成本”的兼得。
5.3 不同配置下的权衡分析
论文还做了一个很有价值的对比:如果改变视差融合中“跳过的”视差数量,结果会怎样?
- DF64:计算全部64个视差(即传统方法,DF仅作一致性检查)。精度最高,但计算量最大。
- DF32:计算32个视差(奇偶分离,即本文采用方案)。精度与DF64非常接近。
- DF21:只计算21个视差(间隔更大)。精度明显下降。
这个实验有力地证明了DF32方案在精度和计算复杂度之间的完美平衡点。DF21虽然计算量更小,但丢失了太多信息,即使融合算法也无力回天。
6. 总结与展望:从论文到产品的距离
这篇论文为我们展示了一个非常扎实的、面向硬件的立体匹配算法设计范例。它不仅仅是提出一个新算法,更是从算法创新、硬件架构、模块优化到实验验证的全流程展示。
我个人在实际工程中的体会是,将这样的研究转化为产品,还需要考虑几个现实问题:
- 输入适应性:论文使用了校正好的标准数据集。实际应用中,来自摄像头的图像必须经过严格的立体校正,确保左右图像行对齐。校正模块本身也需要硬件实现,并会增加系统延迟。
- 参数固化与调优:算法中有多个阈值(Tc, Tg, Th, τ_v, τ_h, ε等)。在FPGA中,它们通常被设计成可配置的寄存器,以便针对不同场景(室内、室外、不同光照)进行微调。如何设计一个高效的参数调试接口,也是工程的一部分。
- 系统集成与功耗:这个深度估计模块最终要作为一个IP核,集成到更大的SoC系统中。需要考虑与图像传感器接口、DDR内存控制器、以及后续处理单元(如障碍物检测、SLAM)的数据交互。同时,需要精确评估其功耗,特别是在电池供电的设备上。
- 向ASIC迁移:FPGA验证成功后的下一步往往是流片,做成专用芯片以进一步降低成本、提升能效。在ASIC设计时,需要将FPGA中的BRAM用更小的SRAM宏单元替代,优化数据通路,进行严格的时序和功耗签核。
总而言之,这篇论文提供的不仅仅是一个具体的深度估计硬件设计,更是一种在资源、速度和精度三角之间进行权衡与优化的方法论。它告诉我们,通过深入的算法分析与巧妙的硬件架构协同设计,完全有可能在严格的约束下,打造出性能卓越的视觉处理引擎。这对于任何从事嵌入式视觉、边缘AI硬件开发的工程师来说,都具有极高的参考价值。