主要内容

利用卡尔曼滤波进行目标跟踪

此示例显示如何使用卡曼过滤器对象和configureKalmanFilter用于跟踪对象的函数。

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

作用kalmanFilterForTracking

介绍

卡尔曼滤波器有许多用途,包括在控制、导航、计算机视觉和时间序列计量经济学中的应用。此示例说明了如何使用卡尔曼滤波器跟踪对象,并重点介绍了三个重要功能:

  • 目标未来位置的预测

  • 减少不准确检测带来的噪声

  • 促进将多个对象与其轨迹关联的过程

目标跟踪的挑战

在展示卡尔曼滤波器的使用之前,让我们先看看在视频中跟踪目标的挑战。下面的视频显示一个绿色的球在地板上从左向右移动。

showDetections ();

球上方的白色区域突出显示使用视觉前探地仪,它将移动对象与背景分离。由于球与地板之间的对比度较低,背景减法只能找到球的一部分。换句话说,检测过程并不理想,并且会引入噪声。

为了方便地可视化整个物体轨迹,我们将所有视频帧叠加到单个图像上。“+”标记表示使用blob分析计算的质心。

show轨迹();

可以看到两个问题:

  1. 该区域的中心通常与球的中心不同。换句话说,在测量球的位置时存在误差。

  2. 当球被盒子挡住时,球的位置不可用,即测量丢失。

这两个挑战都可以通过使用卡尔曼滤波器来解决。

使用卡尔曼滤波器跟踪单个目标

使用之前看到的视频trackSingleObject函数显示如何:

  • 创建卡曼过滤器利用configureKalmanFilter

  • 使用预测对的一系列消除跟踪系统中噪声的方法

  • 使用预测方法本身来估计球的位置时,它被框遮挡

卡尔曼滤波器参数的选择具有挑战性。这个configureKalmanFilter函数有助于简化此问题。关于这一点的更多细节可以在示例中进一步找到。

这个trackSingleObject函数包括嵌套的辅助函数。以下顶级变量用于在嵌套函数之间传输数据。

帧=[];%视频帧检测位置=[];%检测到的位置trackedLocation = [];%跟踪位置标签=''给球打标签公用事业= [];%用于处理视频的工具

跟踪单个对象的过程如下所示。

作用trackSingleObject(参数)%创建用于读取视频、检测移动对象的实用程序,%并显示结果。utilities=createUtilities(参数);isTrackInitialized=false;虽然hasFrame(utilities.videoReader)frame=readFrame(utilities.videoReader);发现球。[detectedLocation,isObjectDetected]=DetectedObject(帧);如果~Istrack已初始化如果等深线探测%当球移动时,通过创建卡尔曼滤波器初始化轨迹%首次检测到。initialLocation = computeInitialLocation(param, detectedLocation);kalmanFilter = configureKalmanFilter (param.motionModel,...initialLocation,param.initialEstimateError,...参数。motionNoise param.measurementNoise);isTrackInitialized = true;trackkedlocation = correct(kalmanFilter, detectedLocation);标签=“初始”其他的trackedLocation=[];标签=''结束其他的%使用卡尔曼滤波器跟踪球。如果等深线探测%球被发现了。%通过调用predict,然后调用%对。预测(卡尔曼滤波);trackedLocation=正确(kalmanFilter,detectedLocation);标签=“纠正”其他的%球不见了。预测球的位置。trackedLocation=预测(kalmanFilter);标签=“预测”结束结束annotateTrackedObject ();结束%,而show轨迹();结束

卡尔曼滤波器处理两种不同的情况:

  • 当检测到球时,卡尔曼滤波首先预测其在当前视频帧中的状态,然后利用新检测到的目标位置对其状态进行校正。这将生成一个过滤过的位置。

  • 当球丢失时,卡尔曼滤波器仅依靠其先前的状态来预测球的当前位置。

你可以通过叠加所有视频帧来看到球的轨迹。

param = getDefaultParameters ();%得到工作良好的卡尔曼配置%例如trackSingleObject(参数);%可视化结果

探索卡尔曼滤波器配置选项

配置卡尔曼滤波器是非常具有挑战性的。除了对卡尔曼滤波器有基本的了解外,为了得到一组合适的配置参数,通常还需要进行实验。这个trackSingleObject上面定义的函数可帮助您探索configureKalmanFilter函数。

这个configureKalmanFilter函数返回一个卡尔曼滤波对象。必须提供五个输入参数。

配置kalmanFilter (MotionModel, InitialLocation, InitialEstimateError, MotionNoise, MeasurementNoise)

这个运动模型设置必须与对象运动的物理特征相对应。可以将其设置为恒定速度或恒定加速度模型。以下示例说明了做出次优选择的后果。

param = getDefaultParameters ();获得工作良好的参数参数。motionModel =“康斯坦特维洛西蒂”%从恒定加速转换%, ConstantVelocity%切换运动模型后,降低噪音规格项%对应于加速度。param.initialEstimateError=param.initialEstimateError(1:2);param.motionNoise=param.motionNoise(1:2);trackSingleObject(param);%可视化结果

请注意,球出现在与预测位置完全不同的位置。从球释放时起,由于地毯的阻力,它会不断减速。因此,恒定加速度模型是更好的选择。如果保持恒定速度模型,跟踪结果将是次优的al,无论您为其他值选择了什么。

通常,你会设置初始位置输入对象第一次被检测到的位置。你也可以设置初始估计误差由于初始状态可能是非常嘈杂的,因为它是从单个检测得到的。下图显示了错误配置这些参数的影响。

param = getDefaultParameters ();获得工作良好的参数param.initialLocation=[0,0];%不基于实际检测的位置参数initialEstimateError=100*个(1,3);%使用相对较小的值trackSingleObject(参数);%可视化结果

对于配置错误的参数,在卡尔曼滤波器返回的位置与目标的实际轨迹对齐之前,需要进行几步。

价值观测量噪声应根据探测器的精度进行选择。对于精度较低的探测器,将测量噪声设置为较大值。以下示例说明了错误配置的分割阈值的噪声检测。增加测量噪声会导致卡尔曼滤波器更多地依赖其内部状态,而不是传入的测量值,从而补偿检测噪声。

param = getDefaultParameters ();参数。segmentationThreshold = 0.0005;%较小的值会导致噪声检测参数测量噪声=12500;%增加值以补偿%表示测量噪声增加trackSingleObject(参数);%可视化结果

通常,对象不会以恒定加速度或恒定速度移动MotionNoise指定与理想运动模型的偏差量。当增加运动噪声时,卡尔曼滤波器更依赖于传入的测量值,而不是其内部状态。请尝试使用MotionNoise参数以了解更多有关其效果的信息。

现在您已经熟悉了如何使用卡尔曼滤波器以及如何配置它,下一节将帮助您了解如何将其用于多目标跟踪。

注:为了简化上述示例中的配置过程,我们使用configureKalmanFilter函数。这个函数做了几个假设。有关详细信息,请参阅函数的文档。如果您需要对配置过程进行更大级别的控制,则可以使用卡曼过滤器直接对象。

利用卡尔曼滤波跟踪多目标

跟踪多个对象带来了几个额外的挑战:

  • 多个检测必须与正确的轨迹相关联

  • 必须处理场景中出现的新对象

  • 当多个对象合并为一个检测时,必须维护对象标识

这个卡曼过滤器对象与分配检测到跟踪函数可以帮助解决

  • 为轨道分配检测

  • 确定检测是否对应于新对象,换句话说,轨迹创建

  • 就像在单个物体被遮挡的情况下一样,预测可以用来帮助分离彼此接近的物体

要了解有关使用卡尔曼滤波器跟踪多个对象的更多信息,请参见标题为基于运动的多目标跟踪.

示例中使用的实用程序函数

效用函数用于检测对象和显示结果。本节说明示例如何实现这些函数。

获取用于创建卡尔曼过滤器和分割球的默认参数。

作用param=getDefaultParameters param.motionModel=“持续加速”;参数初始位置=“与第一次检测相同”;param.initialEstimateError=1E5*个(1,3);param.motionNoise=[25,10,1];param.measurementNoise=25;param.segmentationThreshold=0.05;结束

检测和注释视频中的球。

作用showDetections() param = getDefaultParameters();公用事业= createUtilities(参数);trackedLocation = [];idx = 0;虽然hasFrame(utilities.videoReader)frame=readFrame(utilities.videoReader);detectedLocation=detectObject(frame);显示当前视频帧的检测结果。annotateTrackedObject ();%为了突出测量噪声的影响,显示检测%结果为第40帧在一个单独的数字。idx=idx+1;如果idx==40组合图像=max(repmat(utilities.foregroundMask[1,1,3]),im2single(frame));图,imshow(组合图像);结束结束%,而%关闭用于显示单个视频帧的窗口。uiscopes.close (“全部”);结束

检测当前视频帧中的球。

作用[detection, isObjectDetected] = detectObject(frame) grayImage = rgb2gray(im2single(frame)); / /检测图像实用工具。foregroundMask =步骤(实用程序。foregroundDetector grayImage);检测=步骤(实用程序。blobAnalyzer utilities.foregroundMask);如果isempty(检测)isObjectDetected=假;其他的%为了简化跟踪过程,只使用第一个检测到的对象。检测=检测(1,:);isObjectDetected = true;结束结束

显示当前检测和跟踪结果。

作用accumulateResults annotateTrackedObject () ();将前景掩码与当前视频帧相结合,以便%显示检测结果。CombineImage=最大值(repmat(utilities.foregroundMask[1,1,3]),im2single(frame));如果~ isempty (trackedLocation)形状=“圆”;地区= trackedLocation;区域(:3)= 5;combinedImage = insertObjectAnnotation(combinedImage, shape,...地区,{标签},“颜色”,“红色”);结束步骤(实用程序.视频播放器,组合图像);结束

通过将所有视频帧相互叠加显示球的轨迹。

作用展示轨迹%关闭用于显示单个视频帧的窗口。uiscopes.close (“全部”);%创建一个图形来显示所有视频帧的处理结果。图;imshow (utilities.accumulatedImage / 2 + 0.5);持有;打印(实用程序。累计检测(:,1),...实用程序.累计检测(:,2),“k+”);如果~ isempty (utilities.accumulatedTrackings)情节(utilities.accumulatedTrackings (: 1),...公用设施.累计裂缝(:,2),“r-o”);传奇(“检测”,“追踪”);结束结束

累积视频帧、检测到的位置和跟踪的位置,以显示球的轨迹。

作用accumulateResults()公用事业。accumulatedImage = max(实用程序。accumulatedImage,框架);utilities.accumulatedDetections...=[实用程序.累计检测;检测位置];公用设施.累积裂缝...= [utilities.accumulatedTrackings;trackedLocation];结束

为了说明目的,选择卡尔曼滤波器使用的初始位置。

作用loc=计算初始分配(参数,检测位置)如果strcmp(参数初始位置,“与第一次检测相同”) loc = detectedLocation;其他的loc = param.initialLocation;结束结束

创建用于读取视频、检测移动对象和显示结果的实用程序。

作用公用事业= createUtilities(参数)%创建用于读取视频、显示视频和提取视频的系统对象%前台,并分析连接的组件。实用程序.videoReader=videoReader(“单球,mp4”);实用工具。放像机=愿景。放像机(“位置”[100100500400]);实用工具。foregroundDetector =愿景。ForegroundDetector (...“NumTrainingFrames”, 10,“InitialVariance”, param.segmentationThreshold);实用工具。blobAnalyzer =愿景。BlobAnalysis (“区域输出端口”假的,...“最小面积”, 70,“CentroidOutputPort”,真正的);实用工具。accumulatedImage = 0;utilities.accumulatedDetections= zeros(0, 2); utilities.accumulatedTrackings = zeros(0, 2);结束
结束