简介:本资源面向图像处理初学者与红外目标跟踪研究者,提供一套完整、可直接运行的弱小目标检测与跟踪MATLAB实现方案,聚焦于低信噪比红外图像中的目标识别与运动轨迹估计问题。压缩包共7个文件,含3个核心M函数(主程序main.m、检测模块target_detect.m、跟踪优化模块target_refine.m)、3张运行效果对比图(直观展示检测与跟踪结果)及1份详细说明文档(含参数解释与结果分析),整体仅28KB,轻量易部署。已有1909人学习下载,代码基于Matlab 2019b开发,经作者实测可用,小白用户替换自有数据即可快速复现;配套文档明确标注各模块功能分工,主函数调用逻辑清晰,避免冗余运行步骤,显著降低入门门槛。 如果你是做信号与信息处理或者红外成像工程方向的,大概率被这类需求围堵过:要在红外图像里找到三五个像素大小的目标,还要稳定跟住不丢。我最早拿到这类项目时,心里想的还是常规目标检测的思路——跑通了深度学习检测框、边缘检测、光流,结果在真实红外序列上全被教育了一遍。后来才把注意力放到“弱小目标检测与跟踪”这条专门的路径上:先单帧提候选,再靠序列信息确认真伪,最后用卡尔曼滤波做稳定跟踪。今天这篇就是把一套能跑的Matlab实现拆开,从头到尾把原理、代码、参数调优、常见坑一次说清。
这套源码面向的是红外图像中的弱小目标检测与跟踪,核心场景包括遥感探测、红外搜救、无人机载光电吊舱、安防监控等。它适合三类读者:一是刚接触红外小目标检测的学生,想找一套完整可复现的基线;二是工程上需要快速落地检测跟踪方案的开发者,想少走弯路;三是对“传统图像处理+卡尔曼滤波”这套组合感兴趣的研究人员。我会按照检测链路、跟踪链路、源码结构、参数调试、问题排查的顺序展开,尽量做到不仅让你跑通,还让你知道怎么改。
1. 红外弱小目标检测到底“难”在哪:问题界定与检测链路
很多人第一次接触红外弱小目标检测时,会觉得这不就是“找亮点”嘛,把图像二值化一下不就行了。真这么做,十有八九会被虚警淹没。红外弱小目标检测属于目标检测里一个非常特殊的子问题,它的难点不是“检测”这个动作本身,而是目标在成像后几乎失去了所有常规检测所需的“身份信息”。
1.1 弱小目标的“小”和“弱”到底是个什么量级
在工程实践中,所谓“弱小目标”通常有明确的数量级概念。“小”指的是目标在探测器上的成像尺寸小于9×9像素,极端情况只有3×3像素甚至单像素,总成像面积不过几个到几十个像素。这个尺寸下,目标的轮廓、纹理、形状全部不可用,检测算法面对的只是孤立的、比背景稍亮的点。
“弱”指的是目标与背景的对比度很低。常见的衡量指标是信噪比SNR,定义为目标峰值强度与背景噪声标准差的比值。弱目标的SNR可能只有2~5,也就是说目标灰度只比噪声大两三倍,肉眼几乎分辨不出来。灰度对比度低意味着阈值稍微设高一点目标就漏掉,设低一点噪声就会被成片检出来。
再加上红外图像本身质量堪忧:探测器非均匀性导致横竖条纹,坏元产生椒盐亮点,云层边缘、建筑物棱线、海天线又是天然的高亮区域。这些干扰在形态上和目标高度相似,都是“局部小亮点”或者“边缘小亮点”。这个背景下,单帧检测策略能做的,只是尽可能多地出候选,而不是直接出最终结果。
1.2 为什么常规目标检测方法在这里会失灵
先说说我自己踩过的弯路,给别人排雷。常规目标检测有几个主要流派,放到红外弱小目标场景下都会失手:
- 深度学习目标检测(YOLO、Faster R-CNN等)依赖目标的纹理、结构、语义信息。一个3×3像素的目标输入网络后几乎没有可学习的特征,而且低信噪比小目标的公开数据集非常少,训练难以收敛。就算用极端的数据增强,虚警率也压不下来。
- 边缘检测(Canny、Sobel)对梯度敏感,但云层边缘、地物边缘产生的梯度远强于目标,检测结果几乎就是一张背景边缘图。
- 背景差分法假设背景静止,一旦云层缓慢移动、平台姿态变化,背景模型立刻失效。
- 光流法依赖光流场和运动一致性,目标亮度弱、面积小,光流计算误差大,点目标的运动矢量基本是噪点。
换句话说,红外弱小目标检测的突破口不是“识别目标长什么样”,而是“找出图像里异常的、具有目标性的局部区域”,之后再靠序列信息来消除虚警。这是完全不同的思路。
1.3 完整检测链路:单帧候选提取、多帧确认、轨迹跟踪
经过了若干轮试错后,我最终稳定下来的处理链路是三个层面的串联:
单帧候选提取:对每一帧红外图像做预处理、背景抑制、自适应阈值分割,得到若干个候选目标点。这一步允许虚警,目标是尽量不遗漏真实目标。
多帧确认:真实目标在连续帧中会出现位置连续、灰度稳定的运动模式,而随机噪声点往往只在单帧出现,或者在帧间无规则跳变。利用序列信息剔除大量虚警。
轨迹跟踪:对确认后的目标建立轨迹,用卡尔曼滤波预测下一帧位置,用最近邻关联把新检测点匹配到已有轨迹上,输出稳定、平滑的跟踪结果。
这套链路的好处是每一层都能独立调参。虚警高就加强第二层和第三层,检测率不够就调整第一层的阈值。逻辑清晰,改起来也快。
2. 目标候选提取:单帧图像里把隐藏的小目标“抠”出来
单帧候选提取是整个系统的地基。地基本身不需要把虚警全部滤掉,但要保证真实目标能被稳定选出来。如果这一步目标就丢了,后面的跟踪什么都不用谈。我用的方法是经典且可靠的一条路线:预处理 → 形态学背景抑制 → 自适应阈值分割 → 连通域筛选。
2.1 预处理:归一化与去噪,降低后续处理的难度
红外图像原始数据往往存在亮度范围不稳定、局部过亮过暗的问题。第一步我习惯做一次灰度归一化,把像素范围统一到0~1或者0~255之间。这样后续的阈值系数、形态学参数在不同数据上才能保持一致性。
归一化公式很简单:
I = double(frame); I = (I - min(I(:))) / (max(I(:)) - min(I(:)));如果图像里脉冲噪声明显,比如坏元造成的固定亮点,可以在归一化之前加一个中值滤波。中值滤波对椒盐噪声有很好的抑制效果,而且对目标的削弱程度很小。注意卷积核不要选太大,3×3就够,太大容易把目标抹平。
另外,如果原始数据有明显横竖条纹,那多半是探测器非均匀性没有校正干净。最好在进入算法前先做两点校正,只靠后续的形态学滤波处理条纹会很费劲。这一点在第六章还会展开讲。
2.2 形态学Top-Hat变换:用小目标亮度突变做文章
背景抑制是整个检测流程的核心。红外图像的背景一般是低频的、空间相关的——云层是大片渐变的灰度,天空背景是平缓的亮度变化。而小目标是高频的、局部的亮点突变。形态学Top-Hat变换就是专门用来提取这种局部突变信息的。
原理很简单:先对图像做开运算(先腐蚀后膨胀),开运算会把比结构元素尺度小的明亮区域抹平,得到的图像就是“背景估计图”。用原图减去背景估计图,剩下的大多是直径小于结构元素的明亮目标。这个过程在Matlab里一行代码就能完成:
se = strel('disk', params.seRadius); bg = imopen(frame, se); fg = frame - bg;结构元素seRadius是核心参数,它决定了“多大尺寸的物体被认为是目标”。如果目标是3×3像素,结构元素半径选5~10就够了;如果目标是8×8像素,半径要加大到12~15。简单总结:结构元素尺寸一般取目标尺寸的2~3倍,太小会把背景纹理当成目标放大噪声,太大会把目标一起抹掉。
实际调试时有一个小技巧:先把fg用imshow显示出来,肉眼观察目标点是否比周围背景显著突出。如果连人工都能在fg图里看到稳定亮点,说明背景抑制是成功的。
2.3 自适应阈值分割:从增强图中提取候选亮点
背景抑制后,图像里目标的亮度通常已经是局部极大值,但整幅图仍然存在背景波动和残余噪声,需要阈值把它截出来。自适应阈值我推荐用最经典的“均值 + k倍标准差”:
mu = mean(fg(:)); sigma = std(fg(:)); th = mu + params.thresholdK * sigma; bw = fg > th;thresholdK是另一个重要参数,典型取值范围是3~8。k越大,检测越保守,虚警越小但容易漏检;k越小,检测越激进,真实目标不容易丢但噪声也进来了。在实际工程里,我会先把k设为4,然后根据虚警数量逐步调整。
为什么不直接用Otsu大津阈值?因为Otsu假设图像灰度分布是双峰的,但红外小目标图中的目标像素占比极低,灰度直方图几乎就是单峰背景分布,大津法在这种场景下不稳定。均值+k倍标准差的好处是直接刻画了“偏离背景统计特征”的程度,物理含义更清晰。
2.4 形态学过滤与连通域筛选:给候选目标加尺寸约束
二值化之后,图像里会有分散的白色区域。第一步先用bwareaopen去掉面积过小的点,那些大多数是像素级噪声:
bw = bwareaopen(bw, params.minArea);然后用一个小尺寸闭运算把可能断裂的目标区域连起来,避免同一个目标被拆成两个候选点:
bw = imclose(bw, strel('square', 2));最后用bwconncomp和regionprops提取连通域的质心、面积和包围框:
CC = bwconncomp(bw); stats = regionprops(CC, 'Centroid', 'BoundingBox', 'Area');筛选条件主要看面积:目标是几像素到几十像素的尺寸,所以超过一定面积上限的区域基本都是背景边缘或者大面积杂波,直接排除。这样每一帧输入都能输出一个候选点列表,每个候选点包含x坐标、y坐标和面积信息,供后续跟踪模块使用。
3. 目标跟踪与轨迹管理:从离散检测点变成连续运动轨迹
单帧检测的结果只是一堆离散的候选点,有真有假。要把“真目标”和“假目标”区分开,并把真目标的运动轨迹稳定输出,靠的是序列处理。这一步我通常叫它“从点到轨迹的跨越”。
3.1 单帧检测的局限与多帧确认的必要性
单帧检测的候选点里,真实目标往往只占一小部分,大量是杂波虚警。但虚警有个特点:它不太能“连续出现”。随机噪声点在下一帧可能就消失了;云层边缘的虚警会随着云层缓慢漂移,位置漂移规律往往不稳定。而真实目标在连续帧中会出现位置连续、亮度稳定的状态。
因此我采用的策略是:不急着把单帧检测结果直接当作最终目标输出,而是把它们全部送入轨迹管理模块。新检测点先建立“临时轨迹”,只有连续多帧命中(例如连续3帧都能找到关联检测点),才升级为“确认轨迹”。临时轨迹不输出,确认轨迹才输出。这个过程能滤除绝大多数单帧虚警。
3.2 数据关联:最近邻匹配的核心逻辑
有了轨迹之后,每一帧新来的检测点要和已有轨迹做关联。最实用的是最近邻关联:对每条轨迹,用运动模型预测它当前帧应该出现的位置;计算预测位置和所有检测点之间的欧氏距离;选择距离最近的检测点作为该轨迹的本帧观测。
Matlab里实现一个简化版非常直接:
function trackId = associatePredToDet(predPos, dets, gateDist) numDets = size(dets, 1); minDist = inf; trackId = -1; for j = 1:numDets dist = sqrt((predPos(1) - dets(j,1))^2 + (predPos(2) - dets(j,2))^2); if dist < minDist && dist <= gateDist minDist = dist; trackId = j; end end end门限gateDist的取值很关键。它必须大于目标单帧最大运动位移,比如目标在视频中每秒移动20像素,帧率25fps,那么单帧位移约0.8像素,门限取5~10像素就够。如果目标运动速度快,门限要相应放大,否则帧间位移超标就会关联失败。但门限过大会引入错误关联,容易把两条邻近轨迹搞混。
3.3 卡尔曼滤波:为什么单靠均值滤波不够
数据关联解决了“检测点属于哪条轨迹”的问题,下一步就是滤波平滑和下一帧位置预测。我常用的模型是匀速运动模型的卡尔曼滤波,状态向量定义为[x, y, vx, vy],观测向量为[x, y]。
卡尔曼滤波相比简单均值滤波的优势在于:它能输出下一帧的预测位置(均值滤波不能);它能根据过程噪声和测量噪声自适应调整平滑权重;它对目标短暂遮挡、闪烁丢失有更强的鲁棒性。在低信噪比下,检测点坐标本身带噪声,没有滤波的轨迹会抖动得没法看。
核心递推可以自己实现,不依赖任何工具箱:
% 状态转移矩阵 A A = [1 0 1 0; 0 1 0 1; 0 0 1 0; 0 0 0 1]; % 观测矩阵 H H = [1 0 0 0; 0 1 0 0]; % 预测 x_pred = A * x; P_pred = A * P * A' + Q; % 更新(有关联观测时) K = P_pred * H' * inv(H * P_pred * H' + R); x = x_pred + K * (z - H * x_pred); P = (eye(4) - K * H) * P_pred;Q是过程噪声协方差,表示对运动模型不确定性的估计;R是测量噪声协方差,表示检测点坐标的噪声水平。Q调大,滤波器更相信观测,跟随性好但平滑度差;R调大,滤波器更相信模型预测,轨迹更平滑但响应变慢。在低信噪比场景,我通常把R设得偏大一点,减少位置抖动;如果目标机动性强,再把Q适度增大。
3.4 轨迹管理的完整状态机
轨迹不是“建了就永远存在”,需要完整管理。我用结构体数组存储轨迹,每条轨迹包含:
- id:轨迹编号;
- x、P:卡尔曼状态与协方差;
- hitCount:连续关联成功的次数;
- missCount:连续关联失败的次数;
- isConfirmed:是否已确认;
- history:历史位置,用于绘制轨迹。
状态迁移逻辑是:
新检测点出现 → 创建临时轨迹,hitCount=1,isConfirmed=false;每帧成功关联后,hitCount加1;hitCount达到M_confirm(例如3)后,标记为确认轨迹;每帧失去关联后,missCount加1;missCount超过maxMiss(例如5),删除该轨迹;被确认的轨迹即使偶尔丢一两帧,只要missCount没超上限,仍然保留,这能有效应对目标闪烁或短暂遮挡。
我遇到过很多刚上手的人犯同一个错误:目标跟丢了1帧就删轨迹,结果目标下一帧重新出现时又被当成新目标处理,导致跟踪id不断切换,输出轨迹断成无数小节。设置missCount缓冲期的意义就在这。
4. Matlab代码主流程与核心模块解读
很多下载到的源码项目都存在同一个问题:代码能跑,但看不懂结构,更不知道怎么改。这一章我把这套源码的组织方式和主流程讲清楚,方便你把代码和原理对应起来。
4.1 主程序框架:从读序列到输出可视化
主程序的设计思路是“循环处理每一帧,模块各司其职”。伪代码如下:
% mainTracking.m clc; clear; close all; config = loadConfig(); % 读取所有参数 frameList = dir(fullfile(config.dataDir, '*.jpg')); tracks = []; % 轨迹集合 for i = 1:length(frameList) frame = imread(fullfile(config.dataDir, frameList(i).name)); dets = detectSmallTarget(frame, config); % 第2章的检测函数 [tracks, confirmedTracks] = trackUpdate(tracks, dets, config, i); visualize(frame, confirmedTracks, i); end每个模块都独立成函数,参数全部集中在一个config结构体里。这样换数据集或者调参时,不需要去代码里找数字改,直接改配置文件即可。
4.2 单帧检测函数detectSmallTarget的实现
完整实现见第2章,这里补充几个工程化细节。函数输出是一个N×3的矩阵,每行是[x, y, area],方便后续跟踪模块直接使用。另外,我建议在检测函数入口做一次通道判断,很多红外数据是灰度图,但也有存成RGB三通道的情况:
if size(frame,3) == 3 frame = rgb2gray(frame); end4.3 轨迹更新函数trackUpdate的实现
trackUpdate承担了关联、滤波、状态管理三件事。实现时要注意顺序:先预测所有已有轨迹的当前帧位置,再和检测点做关联,最后根据关联结果更新滤波状态和轨迹状态。下面给一个简化版:
function tracks = trackUpdate(tracks, dets, config, frameIdx) for i = 1:length(tracks) % 1. 预测 tracks(i).x = config.A * tracks(i).x; tracks(i).P = config.A * tracks(i).P * config.A' + config.Q; predPos = [tracks(i).x(1), tracks(i).x(2)]; % 2. 关联 detIdx = associatePredToDet(predPos, dets, config.gateDist); if detIdx > 0 tracks(i).x = kalmanUpdate(tracks(i).x, tracks(i).P, dets(detIdx, 1:2), config); tracks(i).hitCount = tracks(i).hitCount + 1; tracks(i).missCount = 0; else tracks(i).missCount = tracks(i).missCount + 1; end % 3. 轨迹状态 if tracks(i).hitCount >= config.M_confirm tracks(i).isConfirmed = true; end if tracks(i).missCount > config.maxMiss tracks(i).isAlive = false; end end % 删除失效轨迹,并创建新轨迹 tracks = removeDeadTracks(tracks); tracks = createNewTracks(tracks, dets, config, frameIdx); end这段代码里要注意一个细节:已经确认的轨迹和未确认的临时轨迹在处理上要区隔开。临时轨迹的关联失败不计入missCount,而确认轨迹的关联失败要计入missCount,否则虚假轨迹会因为短暂失败被提前删除,导致真实目标重新建轨。
4.4 可视化与结果输出:让结果可见、可查、可复盘
红外小目标结果显示和普通目标检测不太一样。因为目标太小,单纯画一个几像素的框几乎看不见,需要在目标位置画一个醒目的十字标记,再动态画轨迹线。我用下面的方式显示:
function visualize(frame, tracks, frameIdx) imshow(frame, []); hold on; for i = 1:length(tracks) if tracks(i).isConfirmed plot(tracks(i).x(1), tracks(i).x(2), 'r+', 'MarkerSize', 10, 'LineWidth', 1.5); plot(tracks(i).history(:,1), tracks(i).history(:,2), 'g-', 'LineWidth', 1); text(tracks(i).x(1)+8, tracks(i).x(2)+8, num2str(tracks(i).id), 'Color', 'yellow'); end end title(sprintf('Frame %d', frameIdx)); drawnow; end实际调试时,我还会把每帧的检测候选点以蓝色“.”叠加到图上。这样能一眼看出虚警分布和真实目标的位置关系,对调阈值非常有帮助。
5. 实测效果与调参经验:从“能跑通”到“看得准”
一套算法跑通很容易,跑得准才是难点。这里整理几组典型场景下的实测结果和对应的参数调节经验,均为我自己在实际数据上反复验证过的参考值。
5.1 三种典型场景的实测对照
不同背景条件下,同一个参数配置的表现差异很大。我用的测试数据大致分为三类:
天空云层背景:目标贴片灰度较亮,背景有云层边缘高亮,噪声中等。目标尺寸约3×3像素,SNR约4。单帧检测率能到0.9以上,但单帧虚警可能多达几十个;经过3帧确认后,虚警降到1~2个;跟踪阶段输出稳定,一次运行基本不会跟丢。
海天背景:目标位于海天线附近,背景有波浪纹理和轻微海杂波。因为海天线会形成强边缘,Top-Hat背景抑制后偶尔会把海天线上的亮斑误检为目标,需要把阈值系数调高到5~6,以及加大多帧确认帧数,虚警才能压住。
地物背景:背景包含城市建筑、树木、车辆等大量高亮结构。这种场景最棘手,单帧虚警很高,目标容易被亮背景淹没。处理策略是缩小结构元素半径,增强对过小噪声的过滤,增大确认帧数到5,并依赖跟踪轨迹做运动一致性约束。
5.2 核心参数怎么配:结构元素半径与阈值系数
参数之间是联动的,我的调试顺序固定是:先定结构元素半径,再定阈值系数,最后定轨迹门限和确认帧数。
结构元素半径的确定方法:选取一帧目标最清晰的红外图,量出目标的横向和纵向像素直径d,seRadius取d×1.5~3之间。3×3目标建议8~10;5×5目标建议10~12;8×8目标建议12~15。不要为了追求检测率把seRadius设得太小,否则背景纹理被大量选中,虚警率会失控。
阈值系数的确定方法:先设k=4,观察一帧检测结果。如果候选点遍布全图,目标点只是其中一环,k上调到5~6;如果目标点没有出现在候选列表里,k下调到3。这个调节过程一般两三轮就能收敛。
5.3 卡尔曼滤波参数:按运动特性设置
卡尔曼参数没有固定的万能值,要结合目标的运动特性。匀速巡航目标:Q设小一点,R设大一点,轨迹平滑漂亮,位置误差能压到亚像素级;机动目标:Q适当增大,R调小,让滤波器更信任检测点,否则转弯处轨迹会被拉直。
我给一个起始配置,大部分场景都能收敛:
Q = diag([0.1, 0.1, 0.5, 0.5]) * 0.01; R = diag([0.5, 0.5]) * 10;Q里的第3、4个元素对应速度噪声,可以理解为目标加速度扰动。如果实测发现目标突然加速跟丢了,先增大这两个值,而不是去动R。
5.4 运行速度与性能评估指标
在320×256的灰度图像上,Matlab原生实现大约0.1~0.2秒每帧,瓶颈在区域props和形态学运算。512×512图像约0.3秒每帧。如果做离线分析,这个速度完全够用;如果要实时处理,建议先用profile定位热点,把regionprops遍历循环改成矩阵化操作,再用codegen生成C代码,速度能提升一个数量级。
性能评估方面,建议记录四个指标:单帧检测率(真实目标被检测出的比例)、单帧虚警数、轨迹确认率、轨迹保持率。调参时以这四个指标为基准,不要只看“某一帧效果不错”,要跑完一整段序列再看统计结果。
6. 常见问题排查与扩展思路
就算参数都调顺了,实际使用中还是会遇到各种交叉问题。这里整理几个最典型的问题排查链路,都是我在代码调试过程中踩过的。
6.1 虚警太多,怎么精准定位问题环节
虚警多的时候,不要急着整体调参数。先判断虚警来自哪一层:把检测结果和跟踪输出分开统计。如果单帧检测候选点非常多,问题在背景抑制或阈值分割;如果单帧检测虚警不多,但跟踪阶段仍然输出大量假轨迹,问题在多帧确认逻辑。
单帧候选点很多时,优先处理顺序是:先检查图像是否做了归一化和坏元处理;再检查Top-Hat结构元素尺寸是否太小;最后把阈值系数往上调,观察虚警下降速度。如果阈值到7以上虚警还在增加,那大概率是图像有固定条纹或坏点,需要回到数据预处理环节。
6.2 目标跟丢或者轨迹断裂怎么办
目标跟丢最典型的原因是帧间位移超过了关联门限。先确认目标单帧最大位移,用这个值作为gateDist的参考。另一个常见原因是卡尔曼滤波在目标做快速转弯时预测位置偏差太大,导致关联匹配不到检测点,这时应该把Q调大,让滤波器更信任新测量。
还有一类情况是目标亮度闪烁,目标在某一帧的灰度降到阈值以下,检测候选点直接消失。这种情况单靠跟踪模块很难恢复,我通常的做法是:降低阈值系数(从5降到4),把候选目标的上限面积放宽,同时把maxMiss调大到10,给目标留出重新出现的窗口。
6.3 关于数据质量:两点校正与坏元处理
前面提过两次两点校正,这里把它说透。红外探测器每个像元的响应增益和偏置都不完全一致,于是均匀背景成像后会出现固定图案噪声,表现为横竖条纹和固定亮点。如果不做校正,这些固定亮点在Top-Hat变换后会形成每帧都出现的稳定候选点,而且它们运动特性高度一致,连多帧确认都能骗过。
两点校正的思路是:用均匀高温黑体和低温黑体分别采集标定图像,计算每个像元的增益校正系数和偏置校正系数:
calibGain = (mean(H.T(:)) - mean(L.T(:))) ./ (H.T - L.T); calibOffset = mean(H.T(:)) - calibGain .* H.T; corrected = calibGain .* raw + calibOffset;如果你的测试序列已经出现固定位置虚警,先检查一下是否做过两点校正,这比后续加任何滤波都管用。
6.4 进一步扩展方向:时域滤波、检测前跟踪与深度学习
这套传统方案已经能在大多数中低难度场景稳定工作,但如果你要处理更低信噪比或者高机动目标,还有几个扩展方向。
时域滤波法(TDLMS、三维中值滤波)把连续多帧堆叠成三维数据块,利用目标在时域的运动连续性做检测。这类方法对静止背景效果很好,但对相机运动敏感。检测前跟踪(Track-Before-Detect,TBD)则是把检测和跟踪融合起来,在低信噪比下逐帧累加目标能量,用动态规划或粒子滤波在时序上搜索目标轨迹,对SNR低于3的目标也能硬扛——代价是计算量翻倍。
这两年也有不少用深度学习做红外小目标检测的工作,主要思路是用注意力机制增强局部对比度,或者用生成对抗网络做背景抑制。但我的实际感受是:在数据量有限、目标尺度极小的工程场景下,传统方法依旧有不可替代的稳定性。深度学习更适合作为候选生成的前端,和传统方法串联使用。
最后再分享一点我这几年反复体会到的:红外弱小目标检测,真正拉开差距的地方往往不在算法有多前沿,而在数据预处理和参数配比这种“脏活累活”上。同一套代码,有人跑出满屏虚警,有人跑出稳定轨迹,差别就在那些不显眼的细节里。调试的时候多把中间结果画出来看,别一味相信数值指标,你会少走很多弯路。
本文还有配套的精品资源,点击获取