主要内容

基于运动的多目标跟踪

这个例子展示了如何执行自动检测和基于运动的跟踪运动对象的视频从一个固定的摄像机。

运动目标检测和基于运动的跟踪是许多计算机视觉应用的重要组成部分,包括活动识别、交通监控和汽车安全。基于运动的目标跟踪问题可分为两部分:

  1. 检测每一帧的移动目标

  2. 随着时间的推移,将相应于同一对象的检测关联起来

运动目标的检测采用基于高斯混合模型的背景减法算法。形态学操作被应用到产生的前景蒙版,以消除噪声。最后,blob分析检测连接的像素组,这些像素组很可能与移动对象相对应。

对同一物体的探测仅仅基于运动。通过卡尔曼滤波估计每个航迹的运动。该滤波器用于预测航迹在每一帧中的位置,并确定为每个航迹分配每个检测的可能性。

跟踪维护是本示例的一个重要方面。在任何给定帧中,一些检测可能被分配到轨道,而其他检测和轨道可能保持未分配。使用相应的检测更新指定的轨迹。未分配的轨道被标记为不可见。一个未分配的检测开始一个新的轨道。

每个轨迹都会记录连续帧的数量,其中未分配。如果计数超过指定的阈值,则该示例假定对象离开视野,并删除轨迹。

欲了解更多信息,请参见多个对象跟踪

这个例子是一个函数,主体位于顶部,助手例程以嵌套函数的形式出现。

函数motionbasedmultiobjectTrackingExample()

%创建系统对象,用于读取视频,检测移动对象,%并显示结果。obj = setupSystemObjects ();跟踪= initializeTracks ();%创建一个空的轨迹数组。Nextid = 1;%下一个曲目的ID%检测移动物体,并在视频帧间跟踪它们。hasfame(obj.reader)frame = ReadFrame(obj.reader);[质心,虚拟机,掩码] =探测器(帧);predictnewlocationsoftracks();[assignments, unassigndtracks, unassignddetections] =...detectionToTrackAssignment();updateAssignedTracks();updateUnassignedTracks();deleteLostTracks();createNewTracks();displayTrackingResults();结束

创建系统对象

创建用于读取视频帧、检测前景对象和显示结果的System对象。

函数obj=设置系统对象()%初始化视频I / O.创建对象从文件中读取视频,绘制跟踪%对象在每一帧,并播放视频。创建一个视频阅读器。obj。读者= VideoReader (“atrium.mp4”);%创建两个视频播放器,一个用于显示视频,%和1显示前景掩码。obj。maskPlayer =愿景。放像机('位置', [740, 400, 700, 400]);obj。放像机=愿景。放像机('位置', [20, 400, 700, 400]);%创建用于前景检测和blob分析的系统对象%前景检测器用于从图像中分割运动对象% 背景。它输出二进制掩码,其中像素值%值1对应于前景,值0对应于%背景。obj.detector =愿景。ForegroundDetector (“NumGaussians”3,...“NumTrainingFrames”现年40岁的“MinimumBackgroundRatio”, 0.7);%连接的前景像素组可能与移动相对应%对象。blob分析系统对象用于查找这些组%(称为“blob”或“connected components”),并计算它们的%特征,如面积、质心和边界框。obj。blobAnalyser =愿景。BlobAnalysis (“BoundingBoxOutputPort”, 真的,...“AreaOutputPort”, 真的,“CentroidOutputPort”, 真的,...'MiniplingBlobarea', 400);结束

初始化跟踪

这个初始化条款函数创建一个轨迹数组,其中每个轨迹都是表示视频中移动对象的结构。该结构的目的是保持跟踪对象的状态。状态由用于检测的信息组成,包括轨迹分配、轨迹终止和显示。

该结构包含以下字段:

  • id:轨道的整数ID

  • 口技:对象的当前包围框;用于显示

  • kalmanFilter:用于基于运动的跟踪的卡尔曼滤波对象

  • 年龄:自跟踪第一次被检测到以来的帧数

  • 总可视计数:检测到轨道的帧总数(可见)

  • consecutiveInvisibleCount:未检测到轨迹的连续帧数(不可见)。

噪声检测往往会导致短暂的轨迹。由于这个原因,该示例只显示跟踪了一定帧数的对象。发生这种情况时总可视计数超过指定阈值。

当连续几帧没有检测到与轨迹相关联时,本例假设物体已经离开视场并删除轨迹。发生这种情况时consecutiveInvisibleCount超过指定阈值。如果跟踪的时间很短,并且大多数帧都被标记为不可见,那么轨道也可能被删除为噪声。

函数跟踪= initializeTracks ()%创建一个空的曲目数组轨道=结构(...“id”{},...“bbox”{},...“卡尔曼过滤器”{},...'年龄'{},...“totalVisibleCount”{},...“consecutiveInvisibleCount”, {});结束

检测对象

这个detectObjects函数返回被检测对象的质心和边界框。它还返回与输入帧大小相同的二进制掩码。值为1的像素对应前景,值为0的像素对应背景。

该函数使用前景探测器执行运动分段。然后,它对产生的二进制掩模进行形态操作以去除噪声像素并填充剩余斑点中的孔。

函数[质心、B盒、遮罩]=检测对象(帧)%检测前景。掩码= obj.detector.step(框架);%应用形态学操作以消除噪声并填充孔。Mask = imopen(Mask, strel()“矩形”, [3,3])); 掩码=imclose(掩码,strel(“矩形”, [15, 15])); 掩码=imfill(掩码,“黑洞”);%执行blob分析以找到连接的组件。[~, centroids, bboxes] = obj.blobAnalyser.step(mask);结束

预测现有轨道的新位置

利用卡尔曼滤波来预测当前帧中每个轨迹的质心,并相应地更新其边界框。

函数predictNewLocationsOfTracks()I = 1:length(tracks) bbox = tracks(I).bbox;%预测轨道的当前位置。预测质心=预测(轨迹(i).Kalman滤波器);移动边界框,使其中心位于%预测的位置。predictedCentroid = int32(predictedCentroid) - bbox(3:4) / 2;跟踪(i)。口技= [predictedCentroid, bbox(3:4)];结束结束

为轨迹分配检测

将当前帧中的对象检测分配到现有轨道是通过最小化代价来完成的。代价定义为对应于轨迹的检测的负对数似然。

算法包括两个步骤:

步骤1:使用距离的方法卡曼过滤器System Object™。成本考虑了轨道预测质心与检测的质心之间的欧几里德距离。它还包括由卡尔曼滤波器维护的预测的置信度。结果存储在MXN矩阵中,其中M是轨道的数量,n是检测次数。

步骤2:用矩阵求解代价矩阵表示的分配问题assignDetectionsToTracks功能。该函数采用成本矩阵和不向轨迹分配任何检测的成本。

属性返回的值的范围决定了不将检测赋给轨道的代价值距离的方法卡曼过滤器.这个值必须通过实验来调整。设置太低会增加创造新轨道的可能性,并可能导致轨道分裂。设置太高可能会导致一个单独的轨道对应一系列独立的移动物体。

这个assignDetectionsToTracks函数使用Munkres版本的匈牙利算法来计算使总成本最小化的分配。它返回一个M x 2矩阵,在其两列中包含指定轨迹和检测的相应索引。它还返回未分配的轨迹和检测的索引。

函数[assignments, unassigndtracks, unassignddetections] =...detectiontotrackassignment()ntracks = length(曲目);ndetections =尺寸(质心,1);%计算为每个轨道分配每个检测的成本。成本=零(Ntracks,Ndetections);i=1:nTracks成本(i,:)=距离(轨迹(i)。Kalman滤波器,质心);结束解决分配问题。costofnonassignment = 20;[assignments, unassigndtracks, unassignddetections] =...assignDetectionsToTracks(成本、costOfNonAssignment);结束

更新指定的跟踪

这个updateAssignedTracks函数用相应的检测更新每个分配的轨道。它调用对的方法卡曼过滤器修正位置估计。接下来,它存储新的边界框,并将曲目的年龄和总可见计数增加1。最后,该函数将不可见计数设置为0。

函数updateAssignedTracks() numAssignedTracks = size(赋值,1);i=1:numAssignedTracks trackIdx=assignments(i,1);检测dx=分配(i,2);质心=质心(检测dx,:);bbox=bboxes(检测dx,:);%更正对对象位置的估计%使用新的检测。正确(曲目(TrackIDX).kalmanfilter,质心);将预测的边界框替换为检测到的%边界框。轨道(trackIdx).bbox=bbox;%更新曲目的年龄。曲目(trackIDX).age =曲目(trackIDX).age + 1;%更新的可见性。曲目(trackIdx).总可视次数=...轨道(trackIdx)。总可视计数+1;轨迹(轨迹IDX)。连续可视计数=0;结束结束

更新未分配的轨迹

将每个未分配的轨迹标记为不可见,并将其年龄增加1。

函数updateUnassignedTracks ()i=1:长度(未分配的齿条)ind=未分配的齿条(i);轨道(ind).年龄=轨道(ind).年龄+1;轨道(ind).连续可视计数=...轨道(ind)。连续可视计数+1;结束结束

删除丢失的曲目

这个删除LostTracks函数删除在过多连续帧中不可见的轨道。它还删除了最近创建的轨迹,这些轨迹在太多帧中都是不可见的。

函数deleteLostTracks ()如果Isempty(轨道)返回;结束invisibleForTooLong = 20;ageThreshold = 8;%计算轨迹的可见年龄部分。年龄= [曲目(:)。年龄];totalvisiblecounts = [tracks(:)。totalvisiblecount];可见性= TotalVisiBlecounts ./年代;%找到“丢失”轨道的指标。lostInds=(年龄<年龄阈值&能见度<0.6)|...(跟踪(:)。consecutiveInvisibleCount] > = invisibleForTooLong;删除丢失的轨迹。跟踪=跟踪(~ lostInds);结束

创建新曲目

根据未分配的检测创建新轨迹。假设任何未分配的检测都是新轨迹的开始。实际上,您可以使用其他提示来消除噪音检测,例如大小、位置或外观。

函数createNewTracks() centroids = centroids(unassignddetections,:);bboxes = bboxes(unassignddetections,:);I = 1:size(质心,1)Bbox = bboxes(i,:);%创建一个卡尔曼滤波器对象。kalmanFilter=配置kalmanFilter(“康斯坦特维洛西蒂”,...质心,[200,50],[100,25],100);创建一个新的轨道。newTrack=struct(...“id”,nextId,...“bbox”,bbox,...“卡尔曼过滤器”kalmanFilter,...'年龄', 1...“totalVisibleCount”, 1...“consecutiveInvisibleCount”,0);%将它添加到轨道数组中。track (end + 1) = newTrack;%增加下一个id。nextId = nextId + 1;结束结束

显示跟踪结果

这个displayTrackingResults函数为视频帧和前景掩码上的每个轨道绘制边界框和标签ID。然后在各自的视频播放器中显示帧和掩码。

函数displayTrackingResults()%转换帧和掩码的uint8 RGB。框架= IM2UINT8(帧);掩码= UINT8(REPMAT(掩码,[1,1,3]))。* 255;minvisiblecount = 8;如果~i空(轨道)%嘈杂的检测往往会导致短暂的轨道。%只显示可见时间超过的轨道%最小帧数。可靠轨道=...[跟踪(:).totalVisibleCount]>minVisibleCount;reliableTracks=轨道(reliableTrackInds);%显示对象。如果未检测到对象%在此帧中,显示其预测的边界框。如果~isempty(可靠轨道)%获取边界框。bboxes=类别(1,reliableTracks.bbox);%获得ID。id = int32 ([reliableTracks (:) .id]);%为对象创建标签%,我们显示的是预测值而不是实际值%地点。标签= cellstr (int2str (ids));predictedTrackInds =...[reliableTracks(:)。consecutiveInvisibleCount) > 0;isPredicted =细胞(大小(标签));isPredicted (predictedTrackInds) = {“预测”};标签= strcat(标签,isPredicted);%在框架上绘制对象。= insertObjectAnnotation帧(帧,“矩形”,...b盒子、标签);在蒙版上绘制对象。mask = InsertObjectAnnotation(掩码,“矩形”,...b盒子、标签);结束结束%显示掩码和框架。obj.maskPlayer.step(面具);obj.videoPlayer.step(框架);结束

概括

这个例子创建了一个基于运动的系统,用于检测和跟踪多个运动物体。尝试使用不同的视频,看看你是否能够检测和跟踪物体。尝试修改检测、分配和删除步骤的参数。

本例中的跟踪完全基于所有物体以匀速直线运动的假设。当一个物体的运动明显偏离这个模型时,这个例子可能会产生跟踪误差。注意跟踪标签为#12的人时的错误,他被树挡住了。

通过使用更复杂的运动模型(如恒定加速度)或对每个对象使用多个卡尔曼滤波器,可以降低跟踪误差的可能性。此外,您还可以合并其他提示,以便随时间关联检测,例如大小、形状和颜色。

结束