主要内容

动态的多个对象跟踪

这个例子展示了如何执行自动检测和动态跟踪移动物体的视频从一个静止的摄像机。

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

  1. 每一帧中的运动对象检测

  2. 关联检测对应同一个对象

移动物体的检测使用一个基于高斯混合背景减法算法模型。应用形态学操作产生的前景掩模来消除噪声。最后,blob分析检测组连接像素,可能对应于移动对象。

检测到同一个对象的协会是完全基于运动。每个轨道的运动由卡尔曼滤波估计。过滤器是用来预测跟踪的位置在每一帧,并确定每个检测被分配给每个追踪的可能性。

跟踪维护变成了这个例子的一个重要方面。在任何给定的框架,一些检测可能会分配给追踪,而其他探测和跟踪可能仍未赋值的。分配的使用对应的检测跟踪更新。未赋值的跟踪是无形的。一个未赋值的检测开始一个新的轨道。

每个跟踪记录的连续帧的数量,它仍未赋值的。如果数量超过指定阈值,这个例子假设对象离开视野,它删除了。

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

这个例子是一个函数的主体顶部和辅助例程的形式嵌套函数。

函数MotionBasedMultiObjectTrackingExample ()

%用于创建系统对象阅读视频,检测运动物体,%和显示结果。obj = setupSystemObjects ();跟踪= initializeTracks ();%的创建一个空数组。nextId = 1;%的ID下一个轨道%检测运动物体,并在视频帧跟踪它们。hasFrame (obj.reader)帧= readFrame (obj.reader);[重心,bboxes,面具]= detectObjects(框架);predictNewLocationsOfTracks ();(作业、unassignedTracks unassignedDetections] =detectionToTrackAssignment ();updateAssignedTracks ();updateUnassignedTracks ();deleteLostTracks ();createNewTracks ();displayTrackingResults ();结束

创建系统对象

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

函数obj = setupSystemObjects ()%初始化视频I / O%为阅读视频从一个文件中创建对象,跟踪%每一帧中的对象,和玩视频。%创建一个电子阅读器。obj。读者= VideoReader (“atrium.mp4”);%创建两个视频播放器,一个显示视频,%和一个显示前台面具。obj。maskPlayer = vision.VideoPlayer (“位置”,740,400,700,400);obj。放像机= vision.VideoPlayer (“位置”,400,700,400);%为前景检测和blob分析创建系统对象%前台探测器是用来段移动对象%的背景。它输出二进制掩模,像素值% 1的值对应于前景和0对应%的背景。obj.detector = vision.ForegroundDetector (“NumGaussians”3,“NumTrainingFrames”现年40岁的“MinimumBackgroundRatio”,0.7);%连接组的前景像素可能对应于运动%的对象。blob分析系统对象用于发现这些组织%(称为“blob”或“连接组件”),并计算他们%的特征,如面积、质心和边界框。obj。blobAnalyser = vision.BlobAnalysis (“BoundingBoxOutputPort”,真的,“AreaOutputPort”,真的,“CentroidOutputPort”,真的,“MinimumBlobArea”,400);结束

初始化跟踪

initializeTracks函数创建的数组,其中每个跟踪是一个结构代表一个移动对象的视频。结构的目的是保持跟踪对象的状态。国家由信息用于检测跟踪任务,跟踪终止,并显示。

该结构包含以下字段:

  • id:整数ID的轨道

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

  • kalmanFilter:一个卡尔曼滤波器用于动态跟踪对象

  • 年龄:帧的数量,因为跟踪被检测到

  • totalVisibleCount:帧总数的跟踪检测(可见的)

  • consecutiveInvisibleCount:连续帧的跟踪的数量没有检测到(看不见的)。

噪声检测往往导致短暂的痕迹。出于这个原因,这个例子只显示一个对象跟踪后的帧数。发生这种情况时totalVisibleCount超过指定阈值。

当没有检测与跟踪连续几帧相关联,这个例子假设对象已经离开了视野和删除。发生这种情况时consecutiveInvisibleCount超过指定阈值。跟踪可能也被删除噪音如果是跟踪一段时间,和无形的帧。

函数跟踪= initializeTracks ()%的创建一个空数组跟踪=结构(“id”{},“bbox”{},“kalmanFilter”{},“年龄”{},“totalVisibleCount”{},“consecutiveInvisibleCount”,{});结束

检测对象

detectObjects函数返回检测到的质心和边界框对象。它还返回二进制掩模,具有相同的大小作为输入帧。像素值对应于前台,和像素值对应的背景。

使用前景探测器函数执行运动分割。然后执行形态学操作得到的二进制掩模去除噪声像素,来填补这个洞剩余的斑点。

函数[重心,bboxes,面具]= detectObjects(框架)%检测前景。掩码= obj.detector.step(框架);%应用形态学操作来消除噪音和填补漏洞。掩码= imopen(面具,strel (“矩形”[3 3]));掩码= imclose(面具,strel (“矩形”[15、15]));= imfill(面具,面具“黑洞”);%执行团分析找到连接组件。[~,重心,bboxes] = obj.blobAnalyser.step(面具);结束

预测现有轨道上的新位置

使用卡尔曼滤波器预测当前帧中的每个记录的质心,并相应地更新它的边界框。

函数predictNewLocationsOfTracks ()i = 1:长度(跟踪)bbox =跟踪(我).bbox;%预测的当前位置跟踪。predictedCentroid =预测(跟踪(i) .kalmanFilter);%将边界框,其中心%预计的位置。predictedCentroid = int32 (predictedCentroid)——bbox (3:4) / 2;跟踪(i)。bbox= [predictedCentroid, bbox(3:4)];结束结束

分配检测跟踪

分配对象在当前帧检测现有的跟踪是通过最小化成本。的成本被定义为负对数似检测相应的跟踪。

该算法包括两个步骤:

步骤1:计算的成本分配每一个检测每个跟踪使用距离的方法vision.KalmanFilter系统对象™。考虑成本预测质心之间的欧几里得距离的质心跟踪和检测。它还包括预测的信心,这是由卡尔曼滤波器。结果存储在一个麦根矩阵,M是跟踪的数量,N是检测的数量。

步骤2:解决分配问题表示为矩阵使用成本assignDetectionsToTracks函数。成本函数矩阵和不分配任何检测的成本跟踪。

的值没有分配检测跟踪成本取决于返回的值的范围距离的方法vision.KalmanFilter。这个值必须调整实验。设置过低会增加创建一个新的跟踪的可能性,并可能导致跟踪的碎片。设置过高可能导致一个轨道对应于一系列单独的移动对象。

assignDetectionsToTracks函数使用Munkres的版本的匈牙利算法来计算一个最小化总成本分配。它返回一个M x 2的矩阵包含相应的指标分配跟踪和检测两列。它还返回跟踪和检测的指标,仍未赋值的。

函数(作业、unassignedTracks unassignedDetections] =detectionToTrackAssignment () nTracks =长度(跟踪);nDetections =大小(质心,1);%计算的成本分配每个检测跟踪。成本= 0 (nTracks nDetections);i = 1: nTracks成本(我,:)=距离(跟踪(i)。kalmanFilter、质心);结束%解决分配问题。costOfNonAssignment = 20;(作业、unassignedTracks unassignedDetections] =assignDetectionsToTracks(成本、costOfNonAssignment);结束

更新指定的跟踪

updateAssignedTracks功能更新每个分配跟踪与相应的检测。它调用正确的的方法vision.KalmanFilter正确的位置估计。接下来,它存储新边界框,增加跟踪的年龄,可见总数的1。最后,函数将无形的计数设置为0。

函数updateAssignedTracks () numAssignedTracks =大小(作业,1);i = 1: numAssignedTracks trackIdx =作业(我,1);detectionIdx =作业(我,2);质心=质心(detectionIdx:);bbox = bboxes (detectionIdx:);%的正确估计对象的位置%使用新的检测。正确的(跟踪(trackIdx)。kalmanFilter、质心);%替代预测与检测到的边界框%边界框。跟踪(trackIdx)。bbox = bbox;%更新追踪的年龄。跟踪(trackIdx)。年龄= tracks(trackIdx).age + 1;%更新的可见性。跟踪(trackIdx)。totalVisibleCount=跟踪(trackIdx)。totalVisibleCount+ 1; tracks(trackIdx).consecutiveInvisibleCount = 0;结束结束

更新未赋值的跟踪

每个未赋值的跟踪标记为不可见,它的年龄增加1。

函数updateUnassignedTracks ()i = 1:长度(unassignedTracks)印第安纳= unassignedTracks(我);跟踪(印第安纳州)。年龄= tracks(ind).age + 1; tracks(ind).consecutiveInvisibleCount =跟踪(印第安纳州)。consecutiveInvisibleCount + 1;结束结束

删除了跟踪

deleteLostTracks函数删除跟踪被无形的连续多帧。删除最近创建跟踪,已经看不见太多的总体框架。

函数deleteLostTracks ()如果isempty(跟踪)返回;结束invisibleForTooLong = 20;ageThreshold = 8;%计算分数的跟踪的年龄这是可见的。年龄=(跟踪(:).age);totalVisibleCounts =(跟踪(:).totalVisibleCount);可见性= totalVisibleCounts。/年龄;%找到《迷失》的指数跟踪。lostInds =(年龄< ageThreshold & < 0.6) |的可见性(跟踪(:)。consecutiveInvisibleCount] > = invisibleForTooLong;%删除失去了踪迹。跟踪=跟踪(~ lostInds);结束

创建新的跟踪

创建新的追踪未赋值的检测。假设任何未赋值的检测是一个开始一个新的轨道。在实践中,您可以使用其他线索来消除噪声检测,如大小、位置,或外观。

函数createNewTracks()质心=质心(unassignedDetections:);bboxes = bboxes (unassignedDetections:);i = 1:尺寸(质心,1)重心=质心(我:);bbox = bboxes(我);%创建一个卡尔曼滤波器对象。kalmanFilter = configureKalmanFilter (“ConstantVelocity”,重心,200,,100,25,100);%创建一个新的轨道。newTrack =结构(“id”nextId,“bbox”bbox,“kalmanFilter”kalmanFilter,“年龄”,1“totalVisibleCount”,1“consecutiveInvisibleCount”,0);%将它添加到数组的痕迹。跟踪(结束+ 1)= newTrack;%增加下一个id。nextId = nextId + 1;结束结束

显示跟踪结果

displayTrackingResults函数将一个边界框和标签ID为每个跟踪在视频帧和前台的面具。然后,它显示了框架和各自的视频播放器的面具。

函数displayTrackingResults ()%转换框架和uint8 RGB的面具。= im2uint8帧(帧);掩码= uint8 (repmat(面具,(1,1,3)))* 255;minVisibleCount = 8;如果~ isempty(跟踪)%的检测往往导致短暂的痕迹。%只显示跟踪已超过可见%最小的帧数。reliableTrackInds =(跟踪(:)。totalVisibleCount] > minVisibleCount;reliableTracks =跟踪(reliableTrackInds);%显示对象。如果一个对象没有被发现%在本帧,显示其预测的边界框。如果~ isempty (reliableTracks)%得到边界框。reliableTracks.bbox bboxes =猫(1);%得到id。id = int32 ([reliableTracks (:) .id]);%为对象创建标签的指示%,我们预测,而不是实际的显示%的位置。标签= cellstr (int2str (ids));predictedTrackInds =[reliableTracks (:)。consecutiveInvisibleCount] > 0;isPredicted =细胞(大小(标签));isPredicted (predictedTrackInds) = {“预测”};标签= strcat(标签、isPredicted);%画出对象的框架。= insertObjectAnnotation帧(帧,“矩形”,bboxes、标签);%画出对象的面具。= insertObjectAnnotation(面具,面具“矩形”,bboxes、标签);结束结束%显示面具和框架。obj.maskPlayer.step(面具);obj.videoPlayer.step(框架);结束

总结

这个示例创建了一个动态的系统的多运动目标检测和跟踪。尝试使用不同的视频,看看你能够探测和跟踪对象。试着修改参数检测、分配和删除步骤。

跟踪在这个例子仅仅是基于运动的假设所有对象与等速直线移动。当一个对象的运动明显偏离了这个模型,示例可能产生跟踪错误。注意到错误跟踪标签# 12的人,当他被挡住的树。

跟踪错误的可能性可以减少通过使用一个更复杂的运动模型,比如加速度恒定,或者通过使用多个卡尔曼滤波器对每一个对象。也可以将其他线索关联检测随着时间的推移,如大小、形状和颜色。

结束