主要内容

跟踪行人在开动的汽车里

这个例子展示了如何使用一个摄像头跟踪行人安装在一个移动的汽车。

概述

这个例子展示了如何执行自动检测和跟踪的视频从一个移动相机。它展示了跟踪系统的灵活性适应移动相机,这是理想的汽车安全的应用程序。不像固定相机的例子中,动态的多个对象的跟踪,这个例子中包含几个额外的算法步骤。这些步骤包括检测、定制non-maximum抑制,和启发式识别和消除假警报的踪迹。有关更多信息,请参阅多个对象跟踪

这个例子是一个函数与顶部的主体和辅助例程的形式嵌套函数是什么?在下面。

函数PedestrianTrackingFromMovingCameraExample ()
%用于创建系统对象阅读视频,加载前提数据文件,检测行人,并显示结果。videoFile =“vippedtracking.mp4”;scaleDataFile =“pedScaleTable.mat”;%一个辅助文件,有助于确定一个行人在不同大小的像素位置。obj = setupSystemObjects (videoFile scaleDataFile);探测器= peopleDetectorACF (加州理工学院的);%的创建一个空数组。跟踪= initializeTracks ();%的ID下一个轨道。nextId = 1;%设置全局参数。选择。ROI= [40 95 400 140];%一个矩形(x, y, w h),限制了加工区域地面的位置。选择。scThresh = 0.3;%一个阈值来控制误差估计的规模的公差检测到行人。选择。gatingThresh = 0.9;%一个阈值拒绝候选人匹配检测和跟踪。选择。gatingCost = 100;%一个较大的值执行的作业成本矩阵候选匹配的拒绝。选择。costOfNonAssignment = 10;%调优参数来控制创建一个新的跟踪的可能性。选择。timeWindowSize = 16;%调优参数来指定所需要的帧数稳定跟踪的信心得分。选择。confidenceThresh = 2;%一个阈值来确定轨道是真阳性或假警报。选择。年龄Thresh = 8;%一个阈值来确定所需的最小长度跟踪真阳性。选择。visThresh = 0.6;%的阈值来确定最小能见度值跟踪真阳性。%探测和跟踪他们在视频帧。stopFrame = 1629;%站一个有趣的框架与几个行人fNum = 1: stopFrame帧= readFrame (obj.reader);(重心,bboxes分数)= detectPeople ();predictNewLocationsOfTracks ();(作业、unassignedTracks unassignedDetections] =detectionToTrackAssignment ();updateAssignedTracks ();updateUnassignedTracks ();deleteLostTracks ();createNewTracks ();displayTrackingResults ();%退出循环,如果图关闭视频播放器。如果~ isOpen (obj.videoPlayer)打破;结束结束

辅助输入和全局参数的跟踪系统

这种跟踪系统需要一个数据文件,其中包含的信息与图像的像素位置边界框的大小标志着行人的位置。这个先验知识存储在一个向量pedScaleTable。第n个条目pedScaleTable代表了估计一个成年人在像素的高度。该指数n引用的近似坐标行人的脚。

获得这样一个向量,一组训练图像取自相同的观点和类似的场景到测试环境。训练图像包含图像的行人距离不等的相机。使用图片标志应用,边界框的行人图像人工注释。边界框的高度和行人的位置在图像被用来生成规模数据文件通过回归。这是一个helper函数显示符合线性回归模型的算法步骤:helperTableOfScales.m

还有一组全局参数,可以调整优化跟踪性能。您可以使用下面的描述来了解这些参数影响跟踪性能。

  • ROI:的的形式[x, y, w h]。它限制了加工区域地面的位置。

  • scThresh规模:容忍阈值估计。当检测到的区别规模和预期的规模超过了公差,候选人检测被认为是不切实际的,从输出中删除。

  • gatingThresh:控制参数测量的距离。当匹配检测边界框的成本和预测的边界框超过阈值时,系统会删除该协会跟踪两个边界框的考虑。

  • gatingCost作业成本:值矩阵来阻止可能的跟踪检测任务。

  • costOfNonAssignment:作业成本矩阵的值不分配检测或跟踪。设置过低会增加创建一个新的跟踪的可能性,并可能导致跟踪的碎片。设置过高可能导致一个轨道对应于一系列单独的移动对象。

  • timeWindowSize:所需要的帧数估计的信心。

  • confidenceThresh:信心阈值来确定跟踪是一个真正的积极。

  • ageThresh:最小长度跟踪的一个真正的积极。

  • visThresh能见度:最小阈值来确定跟踪是一个真正的积极。

创建系统对象的跟踪系统初始化

setupSystemObjects函数创建系统对象用于读取和显示视频帧和加载数据文件。

pedScaleTable矢量,这是存储在数据文件,编码我们的目标和场景的先验知识。一旦你的回归量训练样本,你可以在每一个可能的坐标计算出期望高度的形象。这些值存储在向量。第n个条目pedScaleTable代表了我们的一个成年的人估计身高在像素。该指数n引用的近似坐标行人的脚。

函数obj = setupSystemObjects (videoFile scaleDataFile)%初始化视频I / O%为阅读视频从一个文件中创建对象,画的%探测和跟踪每一帧,和玩视频。%读者创建一个视频文件。obj。读者= VideoReader (videoFile);%创建一个视频播放器。obj。放像机= vision.VideoPlayer (“位置”,29岁,597、643、386);%加载数据文件ld =负载(scaleDataFile,“pedScaleTable”);obj。pedScaleTable = ld.pedScaleTable;结束

初始化跟踪

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

该结构包含以下字段:

  • id:一个整数ID的轨道。

  • 颜色:跟踪的颜色显示的目的。

  • bboxes:一个N-by-4矩阵来表示物体的边界框与当前框在最后一行。每一行有一个形式的(x, y,宽度、高度)。

  • 分数:一个n×1向量记录分类评分从人与当前检测探测器得分在最后一行。

  • kalmanFilter:一个卡尔曼滤波器用于动态跟踪对象。我们跟踪对象在图像的中心点;

  • 年龄:帧的数量自跟踪初始化。

  • totalVisibleCount:帧总数的对象检测(可见的)。

  • 信心:一对两个数字代表我们如何自信的信任。它存储的最大和平均检测成绩在过去在一个预定义的时间窗口。

  • predPosition:预测在接下来的帧边界框。

函数跟踪= initializeTracks ()%的创建一个空数组跟踪=结构(“id”{},“颜色”{},“bboxes”{},“分数”{},“kalmanFilter”{},“年龄”{},“totalVisibleCount”{},“信心”{},“predPosition”,{});结束

检测人

detectPeople函数返回的重心,边界框,分类检测到人民的分数。它执行过滤和non-maximum抑制的原始输出返回的探测器peopleDetectorACF

  • 重心:一个n除以2矩阵每一行的形式(x, y)。

  • bboxes:一个N-by-4矩阵每一行的形式(x, y,宽度、高度)。

  • 分数:一个n×1与每个元素分类评分向量对应的帧。

函数(重心,bboxes分数)= detectPeople ()%调整图像增加行人的分辨率。%这有助于检测人们远离相机。resizeRatio = 1.5;resizeRatio = imresize帧(帧,抗锯齿的、假);% ACF人探测器运行在一个感兴趣的区域%检测候选人。[bboxes,分数]=检测(option.ROI探测器,框架,“WindowStride”2,“NumScaleLevels”4“SelectStrongest”、假);%查找一个行人的估计高度根据脚的位置。身高= bboxes (:, 4) / resizeRatio;y = (bboxes (:, 2) 1) / resizeRatio + 1;yfoot = min(长度(obj.pedScaleTable)轮(y +高度));estHeight = obj.pedScaleTable (yfoot);%去除大小偏离预期大小的检测,%由刻度尺估计提供。无效的= abs (estHeight-height) > estHeight * option.scThresh;bboxes(无效的,:)= [];分数(无效的,:)= [];% non-maximum抑制适用于选择最强的边界框。[bboxes,分数]= selectStrongestBbox (bboxes,分数,“RatioType”,“最小值”,“OverlapThreshold”,0.6);%计算质心如果isempty (bboxes)质心= [];其他的质心= [(bboxes (: 1) + bboxes (:, 3) / 2),(bboxes (:, 2) + bboxes (:, 4) / 2)];结束结束

预测现有轨道上的新位置

使用卡尔曼滤波器预测当前帧中的每个记录的质心,并相应地更新它的边界框。我们把边界框的宽度和高度在前一帧作为我们目前的预测的大小。

函数predictNewLocationsOfTracks ()i = 1:长度(跟踪)%在这个方向上得到最后的边界框。bbox = (i)。bboxes (,);%预测的当前位置跟踪。predictedCentroid =预测(跟踪(i) .kalmanFilter);%转变边界框,其中心是在预测位置。跟踪(i)。predPosition = [predictedCentroid - bbox (3:4) / 2, bbox (3:4)];结束结束

分配检测跟踪

分配对象在当前帧检测现有的跟踪是通过最小化成本。计算使用成本bboxOverlapRatio函数,预测边界框之间的重叠率,检测到的边界框。在这个例子中,我们假设这个人将逐渐在连续帧视频的帧率高和低运动速度的一个人。

该算法包括两个步骤:

步骤1:计算的成本分配每一个检测每个跟踪使用bboxOverlapRatio衡量。随着人们转向或远离摄像机,他们的运动将不会准确地描述由质心点孤单。成本考虑在图像平面的距离以及边界框的规模。这可以防止分配检测远离相机追踪靠近相机,即使他们的重心一致。这个成本函数的选择将缓解计算而不是诉诸于一种更复杂的动态模型。结果存储在一个麦根矩阵,M是跟踪的数量,N是检测的数量。

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

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

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

函数(作业、unassignedTracks unassignedDetections] =detectionToTrackAssignment ()%计算比率预测盒和重叠%检测盒,每个检测和计算的成本分配%每个跟踪。预测bbox时成本最低%完全符合检测bbox(重叠率是其中之一)predBboxes =重塑([跟踪(:)。predPosition], []) ';成本= 1 - bboxOverlapRatio (predBboxes bboxes);%力忽略一些匹配的优化步骤%设置相关的大量成本。请注意,这%数量不同于下面的“costOfNonAssignment”。%浇注时这是有用(去除不切实际的匹配)%技术应用。成本(成本> option.gatingThresh) = 1 + option.gatingCost;%解决分配问题。(作业、unassignedTracks unassignedDetections] =assignDetectionsToTracks(成本、option.costOfNonAssignment);结束

更新指定的跟踪

updateAssignedTracks功能更新每个分配跟踪与相应的检测。它调用正确的的方法vision.KalmanFilter正确的位置估计。接下来,它存储的新边界框通过最近的平均大小()4盒,并增加跟踪的年龄,可见总数的1。最后,函数调整我们的信心得分基于前面的轨道检测分数。

函数updateAssignedTracks () numAssignedTracks =大小(作业,1);i = 1: numAssignedTracks trackIdx =作业(我,1);detectionIdx =作业(我,2);质心=质心(detectionIdx:);bbox = bboxes (detectionIdx:);%的正确估计对象的位置%使用新的检测。正确的(跟踪(trackIdx)。kalmanFilter、质心);%稳定边界框的大小的平均值最近(%)4盒在跑道上。T = min(大小(跟踪(trackIdx) .bboxes, 1), 4);w =意味着([跟踪(trackIdx)。bboxes (end-T + 1:最终,3);bbox (3)]);h =意味着([跟踪(trackIdx)。bboxes (end-T + 1:最终,4);bbox (4)]);跟踪(trackIdx)。bboxes(end+1, :) = [centroid - [w, h]/2, w, h];%更新追踪的年龄。跟踪(trackIdx)。年龄= tracks(trackIdx).age + 1;%更新跟踪分数的历史跟踪(trackIdx)。分数= [tracks(trackIdx).scores; scores(detectionIdx)];%更新的可见性。跟踪(trackIdx)。totalVisibleCount=跟踪(trackIdx)。totalVisibleCount+ 1;%调整跟踪信心得分基于最大检测%的分数在过去timeWindowSize帧。T = min(选项。timeWindowSize,length(tracks(trackIdx).scores)); score = tracks(trackIdx).scores(end-T+1:end); tracks(trackIdx).confidence = [max(score), mean(score)];结束结束

更新未赋值的跟踪

updateUnassignedTracks函数将每个未赋值的跟踪标记为不可见,增加其年龄1,附加跟踪预测的边界框。信心是设置为0,因为我们不确定为什么它没有分配到一个轨道。

函数updateUnassignedTracks ()i = 1:长度(unassignedTracks) idx = unassignedTracks(我);跟踪(idx)。年龄= tracks(idx).age + 1; tracks(idx).bboxes = [tracks(idx).bboxes; tracks(idx).predPosition]; tracks(idx).scores = [tracks(idx).scores; 0];%调整跟踪信心得分基于最大检测%的分数在过去timeWindowSize帧T = min(选项。timeWindowSize,length(tracks(idx).scores)); score = tracks(idx).scores(end-T+1:end); tracks(idx).confidence = [max(score), mean(score)];结束结束

删除了跟踪

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

噪声检测往往导致创建错误的轨道。对于这个示例,我们删除一个跟踪在以下情况下:

  • 对象是跟踪一段时间。这通常发生在一个错误的检测出现几帧,跟踪了。

  • 跟踪是无形的帧。

  • 未能获得强大的检测在过去几帧,即表示为最大检测信心得分。

函数deleteLostTracks ()如果isempty(跟踪)返回;结束%计算分数的跟踪的年龄这是可见的。年龄=(跟踪(:).age) ';totalVisibleCounts =(跟踪(:).totalVisibleCount) ';可见性= totalVisibleCounts。/年龄;%检查最大检测信心得分。信心=重塑([跟踪(:)。信心]2 [])';maxConfidence =信心(:1);%找到《迷失》的指数跟踪。lostInds =(年龄< =选项。ageThresh& visibility <= option.visThresh) |(maxConfidence < = option.confidenceThresh);%删除失去了踪迹。跟踪=跟踪(~ lostInds);结束

创建新的跟踪

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

函数createNewTracks () unassignedCentroids =质心(unassignedDetections:);unassignedBboxes = bboxes (unassignedDetections:);unassignedScores =分数(unassignedDetections);i = 1:尺寸(unassignedBboxes 1)重心= unassignedCentroids(我:);bbox = unassignedBboxes(我);分数= unassignedScores(我);%创建一个卡尔曼滤波器对象。kalmanFilter = configureKalmanFilter (“ConstantVelocity”,重心,(2,1),5个,5个,100);%创建一个新的轨道。newTrack =结构(“id”nextId,“颜色”,255 *兰德(1、3)“bboxes”bbox,“分数”分数,“kalmanFilter”kalmanFilter,“年龄”,1“totalVisibleCount”,1“信心”(得分,分数),“predPosition”,bbox);%将它添加到数组的痕迹。跟踪(结束+ 1)= newTrack;% #好< AGROW >%增加下一个id。nextId = nextId + 1;结束结束

显示跟踪结果

displayTrackingResults函数将一个颜色的边界框为每个视频帧跟踪。盒子的透明度一起显示的分数表明检测和跟踪的信心。

函数displayTrackingResults () displayRatio = 4/3;= imresize帧(帧,displayRatio);如果~ isempty(跟踪)年龄=[跟踪(:).age]”;信心=重塑([跟踪(:)。信心]2 [])';maxConfidence =信心(:1);avgConfidence =信心(:,2);不透明度=最小(0.5,最大(0.1,avgConfidence / 3));noDispInds =(年龄<选项。ageThresh& maxConfidence < option.confidenceThresh) |(年龄<选项。ageThresh / 2);i = 1:长度(跟踪)如果~ noDispInds(我)%的规模边界框显示bb = (i)。bboxes(end, :); bb(:,1:2) = (bb(:,1:2)-1)*displayRatio + 1; bb(:,3:4) = bb(:,3:4) * displayRatio; frame = insertShape(frame,“FilledRectangle”bb,“颜色”,跟踪.color(我),“不透明度”,透明度(i));= insertObjectAnnotation帧(帧,“矩形”bb,num2str (avgConfidence(我)),“颜色”,跟踪(i) .color);结束结束结束= insertShape帧(帧,“矩形”,选择。ROI* displayRatio,“颜色”(255,0,0),“线宽”3);步骤(obj。放像机、框架);结束
结束