主要内容

基于运动的多目标跟踪

此示例显示了如何从固定相机执行移动对象的自动检测和运动基于移动对象的跟踪。

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

  1. 检测每个帧中的移动物体

  2. 将对应的检测与随时间相应

移动物体的检测使用基于高斯混合模型的背景减法算法。形态学操作被施加到所得到的前景掩模以消除噪音。最后,BLOB分析检测了连接像素的组,其可能对应于移动对象。

检测到相同对象的关联仅基于运动。卡尔曼滤波器估计了每个轨道的运动。过滤器用于预测每个帧中的轨道的位置,并确定分配给每个轨道的每个检测的可能性。

跟踪维护成为此示例的一个重要方面。在任何给定的帧中,可以将一些检测分配给轨道,而其他检测和曲目可以保持未分配。使用相应的检测更新分配的曲目。未分配的曲目标记为隐形。未分配的检测开始成为一个新曲目。

每个轨道都会保持连续帧数的计数,在那里它仍然是未分配的。如果计数超过指定的阈值,则示例假定对象留下视野,删除轨道。

有关更多信息,请参阅多个对象跟踪

此示例是顶部和辅助程序的主体的函数,嵌套功能的形式。

功能motionbasedmultiobjectTrackingExample()

%创建用于读取视频的系统对象,检测移动对象,%并显示结果。obj = setupsystemobjects();曲目= initializetracks();%创建一个空的曲目数组。Nextid = 1;下一曲目的%ID%检测移动对象,并在视频帧中跟踪它们。尽管hasfame(obj.reader)frame = ReadFrame(obj.reader);[质心,虚拟机,掩码] =探测器(帧);predictnewlocationsoftracks();[作业,未分配的触发,未分配的detections] =......detectiontotrackassignment();updateassignedtracks();updateUnassignedTracks();deletelosttracks();createNewtracks();displaytrackingresults();结尾

创建系统对象

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

功能obj = setupsystemobjects()%初始化视频I / O.%创建用于从文件读取视频的对象,绘制跟踪每帧中的%对象,并播放视频。%创建视频阅读器。obj.reader = Videoreader('itrium.mp4');%创建两个视频播放器,一个用于显示视频,%和一个显示前景掩码。obj.maskplayer = Vision.videoplayer('位置',[740,400,700,400]);obj.videoplayer = Vision.videoplayer('位置',[20,400,700,400]);%为前台检测和BLOB分析创建系统对象%前景探测器用于将移动物体分段% 背景。它输出二进制掩码,其中像素值1的%值对应于前景,值0对应%到背景。obj.detector = Vision.ForeCloundDetector('numgaussians',3,......'numtringframes',40,'最小背景',0.7);%连接的前景像素组可能对应于移动%对象。blob分析系统对象用于查找这些组%(称为'blobs'或'连接的组件'),并计算它们%特征,如区域,质心和边界框。obj.blobanalyser = Vision.BlobanAlysis('bandingboxoutputport', 真的,......'AreaOutputport', 真的,'centroidoutputport', 真的,......'MiniplingBlobarea',400);结尾

初始化曲目

初始化条款函数创建一系列曲目,其中每个轨道是表示视频中移动对象的结构。结构的目的是保持跟踪物体的状态。该州包括用于检测的信息以跟踪分配,跟踪终止和显示。

该结构包含以下字段:

  • ID:曲目的整数ID

  • bbox.:对象的当前边界框;用于显示

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

  • 年龄:首次检测到曲线以来的帧数

  • totalvisiblecount.:检测到轨道的总数(可见)

  • 沟圈visiblecount.:未检测到轨道的连续帧的数量(不可见)。

嘈杂的检测往往会导致短暂的轨道。因此,该示例仅在跟踪某些帧之后显示对象。这是什么时候发生totalvisiblecount.超过指定的阈值。

当没有与几个连续帧的轨道相关联的检测时,示例假定对象留下了视野并删除了轨道。这是什么时候发生沟圈visiblecount.超过指定的阈值。如果在短时间内跟踪,则轨道也可以作为噪声删除,并为大多数帧标记不可见。

功能曲目= initializetracks()%创建一个空的曲目数组tracks = struct(......'ID',{},......'bbox',{},......'kalmanfilter',{},......'年龄',{},......'totalvisiblecount',{},......'沟圈visiblecount',{});结尾

检测对象

探测器函数返回检测到的对象的质心和边界框。它还返回二进制掩码,其具有与输入帧相同的大小。值为1的像素对应于前景,并且值为0的像素对应于背景。

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

功能[质心,虚拟机,掩码] =探测器(帧)%检测前景。mask = obj.detector.step(帧);%应用形态操作以消除噪音并填充孔。mask = imopen(面具,光线('长方形',[3,3]));mask = imclose(面具,光线('长方形',[15,15]));mask = Imfill(掩码,'洞');%执行blob分析以查找连接的组件。[〜,质心,bboxes] = obj.blobanalyser.step(mask);结尾

预测现有曲目的新位置

使用Kalman滤波器预测当前帧中的每个轨道的质心,并相应地更新其边界框。

功能predictnewlocationsoftracks()为了i = 1:长度(曲目)bbox = track(i).bbox;%预测轨道的当前位置。predgeedcentroid =预测(曲目(i).kalmanfilter);%换边框,以便其中心在%预测的位置。predgedcentroid = int32(预测中心) -  bbox(3:4)/ 2;曲目(i).bbox = [predgeedcentroid,bbox(3:4)];结尾结尾

分配检测以跟踪

通过最小化成本,将当前帧中的对象检测分配给现有轨道。成本被定义为对应于轨道的检测的负值 - 可能性。

该算法涉及两个步骤:

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

第2步:解决成本矩阵表示的赋值问题使用赋予DESTRIECTIONSTOTRACKS.功能。该功能采用成本矩阵和未分配对轨道的任何检测的成本。

未为轨道分配检测的成本的值取决于返回的值范围距离方法的方法Vision.KalmanFilter.。必须通过实验调整此值。将其设置得太低提高了创建新曲目的可能性,并且可能导致轨道碎片。将其设置得太高可能导致对应于一系列单独移动对象的单个轨道。

赋予DESTRIECTIONSTOTRACKS.函数使用Munkres'版本的匈牙利算法来计算分配,这最小化了总成本。它返回一个m x 2矩阵,其中包含两列分配的曲目的相应索引和检测。它还返回仍然未分配的曲目和检测的索引。

功能[作业,未分配的触发,未分配的detections] =......detectiontotrackassignment()ntracks = length(曲目);ndetections =尺寸(质心,1);%计算为每个轨道分配每个检测的成本。成本=零(Ntracks,Ndetections);为了i = 1:ntracks成本(i,:) =距离(曲目(i).kalmanfilter,质心);结尾%解决了分配问题。costofnonassignment = 20;[作业,未分配的触发,未分配的detections] =......DisplayDetectionStotracks(成本,CostofNoSasnamment);结尾

更新分配的曲目

updateassignedtracks.功能更新每个分配的轨道,相应的检测。它致电正确的的方法Vision.KalmanFilter.纠正位置估计。接下来,它存储新的边界框,并增加轨道的年龄,并且总可见计数到1.最后,该功能将无形计数设置为0。

功能updateassignedtracks()numassignedtracks = size(分配,1);为了i = 1:numassignedtracks trackIDX =作业(i,1);DetectionIdx =赋值(I,2);Centroid =质心(DetectionIdX,:);bbox = bboxes(detectionIdx,:);%校正对象位置的估计%使用新检测。正确(曲目(TrackIDX).kalmanfilter,质心);%替换预测的边界框检测到%边界框。曲目(trackIDX).bbox = bbox;%更新曲目的年龄。曲目(trackIDX).age =曲目(trackIDX).age + 1;%更新可见性。曲目(TrackIDX).totalvisiblecount =......曲目(TrackIDX).totalvisiblecount + 1;曲目(trackIDX).consechutivevisiblecount = 0;结尾结尾

更新未分配的曲目

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

功能updateUnassignedTracks()为了i = 1:长度(UnassignedTracks)Ind = UnassignedTracks(i);曲目(IND).age =曲目(IND).age + 1;曲目(IND).consechutiveInvisiBlecount =......曲目(ind).consechutivevisiblecount + 1;结尾结尾

删除丢失的曲目

deletelosttracks.函数删除对于太多连续帧来说是不可见的曲目。它还删除最近创建的曲目,这对于太多帧来说是整体上的太多。

功能deletelosttracks()如果Isempty(轨道)返回;结尾InvisibleFortoolong = 20;Agethreshold = 8;%计算曲目年龄的分数,它是可见的。年龄= [曲目(:)。年龄];totalvisiblecounts = [tracks(:)。totalvisiblecount];可见性= TotalVisiBlecounts ./年代;%找到“丢失”轨道的指标。dosinds =(年龄......[曲目(:)。consechutiveInvisiblecount]> = invisiblefortoolong;%删除丢失的曲目。曲目=曲目(〜distinds);结尾

创建新曲目

从未分配的检测创建新曲目。假设任何未分配的检测都是新轨道的开始。在实践中,您可以使用其他提示来消除嘈杂的检测,例如尺寸,位置或外观。

功能createNewtracks()心针=质心(未分配的DETECTESS,:);bboxes = bboxes(未分配的ddetections,:);为了i = 1:大小(质心,1)Centroid =质心(我,:);bbox = bboxes(i,:);%创建一个Kalman过滤器对象。kalmanfilter = configurekalmanfilter('constantvelocity'......质心,[200,50],[100,25],100);%创建一个新曲目。newtrack = struct(......'ID',nextid,......'bbox',bbox,......'kalmanfilter',kalmanfilter,......'年龄',1,......'totalvisiblecount',1,......'沟圈visiblecount',0);%将其添加到轨道数组中。曲目(结束+ 1)= NewTrack;%递增下一个ID。nextid = Nextid + 1;结尾结尾

显示跟踪结果

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

功能displaytrackingresults()%将帧和蒙版转换为UINT8 RGB。框架= IM2UINT8(帧);蒙版= UINT8(REPMAT(掩码,[1,1,3]))。* 255;minvisiblecount = 8;如果〜Isempty(轨道)%嘈杂的检测往往会导致短暂的轨道。%仅显示超过的曲目超过%最小帧数。ReliacBletrackinds =......[曲目(:)。totalvisiblecount]> minvisiblecount;Reliablezracks =曲目(Reliablezrackinds);%显示对象。如果未检测到对象%在此帧中,显示其预测的边界框。如果〜Isempty(Reliablezracks)%获取边界框。Bboxes = Cat(1,Reliablezracks.Bbox);%获得ID。IDS = INT32([ReliaBlezracks(:)。ID]);%为指示oble的对象创建标签我们显示预测而不是实际的%% 地点。标签= CELLSTR(INT2STR(IDS'));predictedtrackinds =......[ReliaBlezracks(:)。贪婪invisiblecount]> 0;ispredicred = cell(大小(标签));ispreedicred(predictedtrackinds)= {' 预料到的'};标签= Strcat(标签,缺失);%绘制框架上的对象。Frame = InsertObjectAnnotation(帧,'长方形'......Bboxes,标签);%绘制掩码上的对象。mask = InsertObjectAnnotation(掩码,'长方形'......Bboxes,标签);结尾结尾%显示掩码和框架。obj.maskplayer.step(mask);obj.videoplayer.step(框架);结尾

概括

此示例创建了一种基于运动的系统,用于检测和跟踪多个移动对象。尝试使用其他视频查看您是否能够检测和跟踪对象。尝试修改检测,分配和删除步骤的参数。

该示例中的跟踪仅基于运动,假设所有对象以恒定速度的直线移动。当物体的运动显着地偏离该模型时,该示例可以产生跟踪误差。注意当他被树上封闭时,跟踪标有#12的人的错误。

通过使用更复杂的运动模型,例如恒定加速度,或者通过为每个对象使用多个Kalman滤波器,可以减少跟踪错误的可能性。此外,您可以包含其他提示,以随着时间的推移将检测相关联,例如尺寸,形状和颜色。

结尾