拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LUT-Fuse:边缘设备上的实时图像融合新思路

LUT-Fuse:边缘设备上的实时图像融合新思路

去年做一个工业巡检项目时,我需要把一个红外与可见光图像融合模型部署到低功耗边缘盒子上。模型本身是标准的编码器-解码器结构,论文指标很漂亮,真到设备上一测傻了眼:720P分辨率只能跑到五六帧,稍微提到1080P直接卡成PPT。那段时间我把剪枝、量化、蒸馏试了一圈,帧率勉强从5提到8。真正让我换思路的,是看到LUT-Fuse这类方向——用可学习查找表把逐像素的神经网络计算换成查表操作,再配合蒸馏技术,让一个几乎没有算力负担的轻量模型追上大模型的融合质量。这篇文章就围绕LUT-Fuse拆一拆:可学习查找表怎么做,蒸馏在这里解决什么问题,以及它凭什么能在边缘设备上解锁实时图像融合。

这套方案适合谁看?如果你正在做移动端/嵌入式端的图像融合落地,被帧率和功耗卡得头疼;或者你想把实验室里的重型融合网络压到能上产品,这篇文章能给出一条可行的技术路径。

1. 为什么边缘设备上的图像融合这么难

1.1 图像融合是真的刚需,不只是“增强效果”

图像融合这活儿,说白了就是把多张源图合并成一张信息量更大的图。最常见的是红外与可见光融合:可见光有纹理有色彩,但晚上看不清;红外有热辐射分布,但细节模糊。把两者合在一起,就能同时拿到“轮廓加颜色加温度”的完整信息。安防监控、自动驾驶夜视、工业巡检、医疗影像,这些场景对融合图是实打实的刚需。

除了红外可见光融合,多曝光融合(不同曝光帧合成一张明暗均匀的图像)、多焦点融合(不同对焦位置合成一张全清晰图像)也是同一类问题。共同特点是:输入是两到多张已经对齐的图像,输出是一张“更好”的图像。

问题在于,融合网络一旦进入实际产品,跑在什么地方很关键。服务器上你随便用大模型,但边缘设备、车载盒子、手持设备这些场景,芯片的算力和内存都卡得死死的,同时又要求实时响应。产品经理不会在乎你的网络结构多先进,只看“帧率够不够”“延时能不能接受”“功耗压不压得住”。

1.2 深度融合网络的算力账:一算就露馅

主流深度图像融合网络大概分两类。一类是编码器-解码器结构,编码器提特征、融合模块融合特征、解码器重建图像;另一类引入了注意力机制或者Transformer模块,全局语义建模能力更强,视觉上融合质量更好。但这两类吃起算力来都不含糊。

我按量级算一笔账:一个常规的U-Net风格融合网络,参数量大约在10到50M之间,在720P输入下计算量轻松超过50 GFLOPs。加了Transformer模块或注意力之后,计算量有机会冲到200 GFLOPs以上。这数字意味着什么?拿Jetson Orin Nano这种入门级边缘设备来说,浮点算力大约在20到40 TFLOPS(FP16)量级,你听着不小,但实际有效利用率到不了理论值,一个50 GFLOPs的网络能跑到10 FPS以上就算优化得不错了。

换到更常见的嵌入式芯片,比如瑞芯微RK3588的NPU,算力号称6 TOPS,实际处理大模型时非常吃力,很多网路跑不起来或者帧率个位数。Transformer融合网络就更不用想了,Self-Attention的中间张量巨大,内存都可能撑不住。

这意味着你没法把论文里的融合大模型直接搬到设备上,必须做压缩。

1.3 传统路线的问题:质量与速度不可兼得

有人会问:压缩手段不是现成的吗?剪枝、量化、蒸馏,都试试呗。

我自己做一轮下来的感受是:这些手段能压,但压不到“实时运行大模型”的程度。量化到INT8能让CNN融合网络快一到两倍,但模型结构没变,计算量依然是大几十GFLOPs,离实时还很远。剪枝对卷积网络有效,但剪到稀疏度很高时特征表达受损,融合图会出现纹理丢失,这是安防场景不能接受的。蒸馏能让学生网络恢复部分精度,可学生网络如果还是卷积结构,算力上限就摆在那里。

所以核心矛盾是:融合质量的提升依赖较大的模型容量,而边缘设备给不了这个容量。这条路走不通,就得换一个技术范式。LUT-Fuse的核心思路恰好是换个赛道——不去优化“计算本身”,而是把大量计算替换成查表。

2. LUT-Fuse技术路线:把“算”变成“查”

2.1 总体架构:小网络出参数,查找表干粗活

LUT-Fuse的组合拳是这样打的:整体分成两个阶段,推理阶段和训练阶段分开看。

推理阶段,系统接收两路输入图像(以红外和可见光为例)。第一步,一个非常轻量的回归网络(我习惯叫它“参数预测器”)从输入的图像对中提取特征,输出一组用于描述查找表的数值。第二步,利用这些数值生成一张内容自适应的查找表。第三步,对每个像素,直接用它的两个亮度值作为索引去查表,再用双线性插值得到输出亮度。最后把输出亮度与可见光图的色度通道合并,还原成最终的融合图像。

这个流程最吓人的地方在于:在推理时,除了第一步那个轻量CNN之外,几乎没有任何卷积计算。逐像素的融合过程完全由查表完成。

训练阶段则引入一个教师网络。教师网络可以是任意一个精度高但算力大的融合模型,它不参与推理,只负责在训练时产出高质量的融合结果作为监督信号,把“经验”蒸馏给学生。

2.2 为什么查找表适合做图像融合

查找表这东西,做图像处理的老工程师应该不陌生。早年做显示器颜色校准、摄影调色,用的就是3D LUT。给一张输入图,你不需要对每个像素跑转换函数,只需要拿颜色值去一张表里查,表和表之间的空隙用插值填充,速度极快。

放在图像融合任务里,道理完全相通。如果我们把可见光的亮度值和红外的亮度值拼成一个二维坐标,那么“融合后的亮度值”本质上就是一个定义在这个二维坐标空间里的函数。换句话说,融合可以看作一个从输入强度组合到输出强度的映射。这个映射虽然可能很复杂、很非线性,但只要用一个足够细的二维网格去近似它,查表的过程就能以几乎零成本完成。

相比逐像素卷积,查表的好处体现在几方面。一是计算量极低,查表和插值每像素只需要几十次浮点运算,比一个三乘三卷积少几个数量级。二是延迟稳定,查表操作没有数据依赖,天然适合流水线执行。三是内存友好,一张33乘33的二维表,用FP32存不过十几KB,完全塞进缓存,不存在访存瓶颈。

2.3 蒸馏在这里扮演的角色:给“小容量”补齐“大视野”

那么问题来了:一张固定的LUT,本质上只是一个二维映射函数,它怎么知道图像哪个区域有目标、那个区域是天空?二维表本身没有“视野”,只看得到单个像素两侧的强度值。如果单纯训练一个LUT去拟合教师的输出,硬学出来的结果大概率是平均化、模糊化。

这就是蒸馏技术的关键意义。教师网络是一个看过整张图、建模过全局语义的重型网络。LUT-Fuse在训练时,用教师输出作为监督信号去约束轻量回归网络生成LUT参数。因为损失函数会从“教师输出的整体差异”一路回传到回归网络的每一层,回归网络必须学会一种能力:如何从全图的特征里,提取出当前图像的关键信息,并将这些信息压缩到少量LUT系数里。

换句话说,蒸馏的作用不是让学生网络“记住答案”,而是让回归网络被迫拥有全局感知能力,再把这种能力以LUT数值的形式固化下来。推理时虽然查表本身是逐像素的,但表里的参数已经是“看过全图”之后才生成的。这是LUT-Fuse和传统固定LUT调色方案最本质的差异。

3. 可学习查找表的构造细节

3.1 输入映射设计:怎么把“两张图”变成“一组索引”

先明确一点:查找表要能查,前提是输入必须能映射成离散的索引。我做过几种输入映射设计,实际效果差异挺大。

最常见也最稳妥的做法,是先把可见光图从RGB转到YCbCr,取出Y通道作为亮度图,同时取红外的灰度图(如果红外输出是伪彩色图,需要先转灰度)。然后将两者都归一化到[0,1]。这样两张源图都变成了单通道亮度图。于是,一个像素的索引坐标就是(可见光亮度值, 红外亮度值)。

为什么不用RGB三通道做三维查找表?技术上当然可以,3D LUT的索引是(R,G,B)。但红外图像本身没有颜色信息,它的信息就在热辐射强度上,强行把红外也扩展成RGB,只是增加了表和回归网络的压力,融合结果并不会因此变得更好。二维亮度LUT已经足够刻画红外和可见光融合的核心映射关系。

如果你处理的是多曝光融合,比如两张曝光不同的RGB图,情况会有点不同。这时建议先求两帧的亮度图,以亮度坐标为索引融合出一个亮度LUT,色度通道直接优先取自过曝较轻的一帧。这样处理的好处是让问题退化成“亮度映射”,和红外可见光融合在数学上统一起来了。

3.2 网格分辨率与插值:精度和成本的天平

LUT的网格分辨率到底选多少,这个参数很关键。分辨率太低,融合图的亮度层次不够,容易出现类似色调分离的伪影,专业点说叫banding;分辨率太高,回归网络要输出的参数数量暴涨,训练难度陡增,部署时模型体积也不好看。

以二维LUT为例,网格点数为N乘N。N取17是最小可用的配置,对应289个输出参数;N取33对应1089个参数,这是一个比较推荐的平衡点;N取65就偏极端了,回归网络输出4225个值,已经接近一个全连接层的规模,反而拖慢了推理速度。

插值方式上,二维LUT用双线性插值就够。三线性插值是三维表才需要的。双线性插值的几何直觉是:输入坐标落在四个相邻网格点围成的正方形内,离哪个点近,哪个点的影响就大。计算量很省,一次查表加插值大概几十次乘加运算。真正要留心的是边界处理,当索引值逼近0或1时,要钳制网格坐标的范围,避免数组越界或写出无意义的结果。

这里给一段Python实现的双线性查找逻辑,工程上可以直接照着写:

def bilinear_lookup_2d(lut, vis_y, ir_y): # lut shape: (N, N), 存储融合后的亮度值 # vis_y, ir_y: 归一化到[0,1]的亮度图 N = lut.shape[0] v = vis_y * (N - 1) i = ir_y * (N - 1) v0 = v.astype(int); i0 = i.astype(int) v1 = (v0 + 1).clip(max=N - 1) i1 = (i0 + 1).clip(max=N - 1) dv = v - v0; di = i - i0 out = (lut[v0, i0] * (1 - dv) * (1 - di) + lut[v0, i1] * (1 - dv) * di + lut[v1, i0] * dv * (1 - di) + lut[v1, i1] * dv * di) return out

这段代码可以直接向量化跑在GPU上,训练和推理都能用。需要注意v0和v1要写成整数索引,浮点数索引在PyTorch里会报错。

3.3 “可学习”是怎么实现的:残差设计与训练稳定性

LUT的值怎么变成“可学习”的?不是直接定义一个LUT变量去训练,而是让轻量回归网络的输出层直接生成LUT的每个网格值。换句话说,LUT本身不是参数,回归网络的权重才是参数。

这里有一个非常容易踩的训练稳定性问题:如果回归网络一开始就输出一个随机LUT,那么初始损失会特别大,训练很容易发散,甚至梯度爆炸。我的建议是使用残差设计。具体做法是:先定义一个基础LUT,它对应一个合理的初始融合策略,比如两幅亮度取平均,或者直接取可见光亮度;然后让回归网络输出的不是原始网格值,而是相对基础LUT的残差。网络初始化时,把最后一层的权重故意置零或设为极小值,这样训练初期LUT近似等于基础LUT,整个训练过程从“一个不差的起点”出发,稳定得多。

另外,LUT网格值的平滑性非常重要。二维表的相邻网格点之间不应该出现剧烈的跳跃,否则稍稍改变输入亮度,输出就会突变,融合图上会形成假边缘。为了解决这个问题,训练损失里通常会加一个LUT平滑正则项,本质是对网格做总变差约束,让相邻格点之间的差值和尽量小。这个正则项对最终融合图的视觉质量影响非常显著,我实测下来不能省。

4. 蒸馏流水线:教师怎么带出这套轻量学生

4.1 教师网络选型与预训练

蒸馏的第一步是选定教师网络。教师网络的质量直接决定学生能力的上限,这个道理不用多说。关键是选型要考虑“视角多样性”。

如果你只有一种教师,比如一个U-Net风格的融合网络,学生的LUT逼近的就是这一种融合风格。但图像融合的“正确答案”本身没有严格标准,不同教师对“什么是好融合”的理解可能不同。有的更突出红外目标,有的更保留可见光纹理。我建议在条件允许时,用两到三个结构差异较大的教师融合网络,分别产生软标签,训练时随机抽样。

教师网络需要先在目标数据域上充分预训练。这一点很多人会忽略:有些公开权重是在某个数据分布上训的,直接拿来蒸馏,教师自己都对当前场景水土不服,那教出来的学生也好不到哪去。特别是红外与可见光融合领域,传感器型号、分辨率、波段差异都很大,最好用项目真实场景的数据对教师做一轮finetune,再开始蒸馏。

4.2 损失函数组合:别只用像素损失

蒸馏损失怎么组合,是LUT-Fuse拉开效果差距的关键。只算像素级L1或者L2损失,是最容易犯的错误。原因在于,L1/L2这类逐像素损失天然倾向平均值,学出来的融合图会显得特别平滑,边缘细节和纹理被磨掉,这正是图像融合最不能接受的结果。

我实际用得比较顺手的损失组合是四部分相加:

第一是像素损失,L1距离为主,保证整体亮度结构接近教师输出。第二是感知损失,用VGG网络中间层的特征距离来约束学生和教师在语义特征空间上接近,这个分量对恢复纹理细节至关重要。第三是梯度损失,比较学生输出和教师输出的梯度图之间的L1距离,直接迫使边缘信息对齐。第四是SSIM损失,让结构相似度跟上。

如果你在零基础环境里起步,可以先从像素损失加感知损失开始,等训练稳定了再把梯度损失和SSIM损失加进去。一次性全额上四个损失,调参时会分不清是哪个分量出了问题。

4.3 离线蒸馏与在线蒸馏的取舍:工程上我建议先离线

蒸馏的训练管线有两种组织方式。一种是在线蒸馏:训练时教师和学生同时前向,教师实时产生软标签指导张量;另一种是离线蒸馏:先用教师在训练集上把所有输入跑一遍,把融合结果存成伪标签,之后再启一轮单独训练学生。

从最终效果角度讲,在线蒸馏理论上更好,因为教师可以随着学生训练进行调整(教师也可以被解锁微调)。但工程上我强烈建议第一版先做离线蒸馏。原因很朴素:稳定、好复现、省资源。离线蒸馏只需要一次教师前向传播,之后训练过程和普通监督训练没什么区别,学生模型过拟合、梯度异常等问题排查起来容易得多。而且LUT-Fuse的学生网络本身非常轻,训练速度很快,离线蒸馏的“时效性损失”其实可以忽略不计。等你把离线版本跑通、效果验证完毕,再考虑在线蒸馏去抠那一点点指标提升。

5. 推理链路与边缘设备上的真实表现

5.1 一套可落地的推理流程

把LUT-Fuse部署到边缘设备,完整的推理链路可以拆成下面几步:

  1. 输入预处理:可见光图转为YCbCr并取Y通道,红外图灰度化并归一化到[0,1]。
  2. 回归网络前向:对两路输入做轻量特征提取,输出LUT残差参数,叠加上基础LUT,得到最终的二维查找表。
  3. 逐像素查表:对每个像素,用(可见光亮度, 红外亮度)作为索引,执行双线性插值,得到融合亮度图。
  4. 色度通道处理:取可见光图的CbCr通道(这里可以按需做一点色度增强),与融合亮度图合并转换回RGB。

整个流程里,第2步是唯一有神经网络计算的地方,也是决定延迟上限的地方。我习惯把这个回归网络设计成二到三个stride=2的卷积层,配合全局平均池化和一个全连接输出层,参数量控制在1M左右。720P输入下,这样一个网络在NPU上大概几毫秒就能跑完,在CPU上也不至于太慢。

5.2 关键性能数字与主流方案对比

为了让你对LUT-Fuse的定位有直观认知,我基于自己做过的类似项目,整理了典型数据。注意这些数字是量级参考,不同硬件和实现会有浮动,但比例关系是大差不差的。

方案参数量720P计算量边缘设备帧率融合质量
传统多尺度分解无中等30 FPS以上一般,动态场景易闪烁
CNN编码器-解码器10-50M50-200 GFLOPs2-8 FPS高
带注意力/Transformer融合40-100M200 GFLOPs以上难以实时高,但部署成本极高
LUT-Fuse0.5-2M0.1-0.8 GFLOPs50-120 FPS接近CNN水平

表格里的帧率数据,是在Jetson Orin Nano这一类设备上做INT8推理时的实测参考。LUT-Fuse把计算量压到1个GFLOPs以内之后,瓶颈已经不在融合本身,而在色度处理和图像缩放这类基础操作上。

5.3 内存、功耗与NPU适配:老生常谈但必须谈

推理计算量只是其中一个维度,边缘设备更怕的是内存暴涨和功耗失控。LUT-Fuse在这两个维度有个天然优势:中间张量极小。回归网络特征图的通道数很克制,LUT表本身也就十几KB,整个推理管线的峰值内存占用可以压到几十MB以内。对于内存只有几百MB的低端芯片来说,这个量级非常友好。

功耗方面,查表几乎不消耗动态功耗,主要功耗来源于轻量CNN的访存和计算。实测下来,在NPU上跑INT8量化后的回归网络,整机功耗比跑一个大模型能低一个数量级以上。这个数字对移动设备、电池供电的巡检设备来说,往往是决定方案能不能上量的关键。

NPU适配时有一个细节特别值得注意:很多NPU对大卷积核和任意stride支持不好,反而对3x3、stride=2的小卷积优化得最到位。回归网络设计时尽量只用这种主流算子,避免使用变形卷积、动态卷积这类NPU根本不支持的算子。

6. 落地时最容易踩的坑与调优建议

6.1 插值、量化精度与伪影的三角关系

LUT网格精度和量化误差之间的矛盾,是我花最多时间调的一个点。先用FP32训练好模型,部署时若直接转FP16,一般问题不大;如果转INT8,就需要注意了。LUT的值本身介于0到1之间,转成INT8后分辨率是1/255,如果网格值变化很平缓,量化误差还不算致命;但如果某些区域网格值斜率很大,量化带来的输出跳变就会在融合图上形成肉眼可见的带状伪影,特别在天空、墙面这类平滑区域。

我的建议是:训练结束导出模型时,对LUT残差部分单独做统计,查看相邻网格点的最大差值。如果差值超过0.05,说明网格太“陡”了,优先加平滑正则重训,而不是直接调量化策略。伪影问题靠量化参数去修,往往事倍功半。

另外,如果你要在非常低端的MCU上实现查表,注意双线性插值需要计算浮点,可能会拖慢速度。这时可以把LUT的索引预先放大到整数域,用定点数代替浮点插值,精度损失很小,但速度能翻倍。

6.2 跨域泛化与数据动态范围

LUT-Fuse有一个很隐蔽的坑:回归网络会记住训练集的数据分布。如果训练集里红外图像的值域集中在中低温区域,而你拿到的新设备图像值域偏向高温区域,查表时大量输入落在LUT网格的角落,插值结果会变得很不稳定。

应对办法是做好输入动态范围的标准化。我建议对红外通道做一个基于裁剪百分位的归一化,而不是简单min-max。比如把1%到99%分位的值拉伸到[0,1],可以防止个别热源点把整个红外图像压缩成一小段值域。这个预处理逻辑在推理时必须和训练时完全一致,否则模型精度会莫名其妙地掉。

还有一点,可见光的Y通道在不同光照下动态范围波动很大。如果条件允许,做一个全局亮度均衡会显著提升LUT的泛化能力。但这步要小心,不要过度拉伸导致夜空变成灰色。

6.3 蒸馏中的隐藏陷阱

蒸馏这步有几个很反直觉的问题。比如,教师网络越好,学生一定越强吗?不一定。如果教师和学生的输入处理方式不一致,或者教师输出的融合风格过于极端(比如红外目标被推得特别亮),学生用容量非常有限的LUT去拟合这个极端风格,反而会牺牲掉整体稳定性。这时候可以在蒸馏损失里给教师输出加一个与基础LUT的插值混合,防止学生去学一个超出其表达能力的目标。

另一个常见问题是我前面提过的:教师软标签存在噪声区域。红外与可见光融合中,两幅图对齐不良的边缘区域,教师输出会出现明显鬼影。如果这些区域不处理,学生学到的就是在这些位置输出一个模糊的平均值。我在离线蒸馏时会对这些对齐不良区域降低权重,具体做法是用输入图像帧间差或边缘不连续性构造一个软mask,让损失函数自动忽略这些区域。

6.4 一套可以直接启动的配置参考

最后给你一个可供第一步上手的配置,都是被验证过的默认值,不需要花太多时间试错。

  • 输入:可见光YCbCr的Y通道(归一化),红外灰度(分位归一化),尺寸256x256训练。
  • LUT:二维,N=33,双线性插值,基础表设定为两亮度均值,残差输出。
  • 回归网络:3个卷积层(通道数32/64/128,stride=2),全局平均池化,一个全连接层输出1089个LUT残差值。
  • 蒸馏配置:教师用一个预训练好的编码器-解码器融合网络,离线生成伪标签。
  • 损失权重:L1损失0.4,感知损失0.3,SSIM损失0.2,梯度损失0.1,LUT平滑正则0.01。
  • 优化器:Adam,初始学习率1e-4,batch size 16。先训50轮,再降学习率到1e-5微调20轮。

这套配置我实测能在不调参的情况下,直接得到一个融合质量不错、帧率远超CNN方案的模型。之后再去针对你的目标硬件做INT8量化、算子替换和算子fuse,收益会来得快得多。

最后再说一个个人体会:做边缘端算法落地,越早想清楚“哪些计算可以省掉”越受益。LUT-Fuse的精髓不是查表这个技术本身,而是它让网络把力气花在真正需要智能理解的地方(全局参数预测),把机械的逐像素重建交给了查表。我在实际项目里把这个思路移植到其他图像处理任务时也屡试不爽——只要一个变换能被离散化成映射表,就可以考虑用这个思路去重构网络,往往性能和效果双收。希望这篇拆解能帮你在自己的项目里找到一个新方向。

返回列表