matlab示例程序--Motion-Based Multiple Object Tracking--卡尔曼多目标跟踪程序--解读
静止背景下的卡尔曼多目标跟踪
最近学习了一下多目标跟踪,看了看MathWorks的关于Motion-Based Multiple Object Tracking的Documention。
程序来自matlab的CV工具箱Computer Vision System Toolbox。这种方法用于静止背景下的多目标检测与跟踪。
程序可以分为两部分,1.每一帧检测运动objects;
2.实时的将检测得到的区域匹配到相同一个物体;
检测部分,用的是基于高斯混合模型的背景剪除法;
参考链接:http://blog.pluskid.org/?p=39
所谓单高斯模型,就是用多维高斯分布概率来进行模式分类
其中μ用训练样本均值代替,Σ用样本方差代替,X为d维的样本向量。通过高斯概率公式就可以得出类别C属于正(负)样本的概率。
而混合高斯模型(GMM)就是数据从多个高斯分布中产生的。每个GMM由K个高斯分布线性叠加而成。
P(x)=Σp(k)*p(x|k) 相当于对各个高斯分布进行加权(权系数越大,那么这个数据属于这个高斯分布的可能性越大)
而在实际过程中,我们是在已知数据的前提下,对GMM进行参数估计,具体在这里即为图片训练一个合适的GMM模型。
那么在前景检测中,我们会取静止背景(约50帧图像)来进行GMM参数估计,进行背景建模。分类域值官网取得0.7,经验取值0.7-0.75可调。这一步将会分离前景和背景,输出为前景二值掩码。
然后进行形态学运算,并通过函数返回运动区域的centroids和bboxes,完成前景检测部分。
跟踪部分,用的是卡尔曼滤波。卡尔曼是一个线性估计算法,可以建立帧间bboxs的关系。
跟踪分为5种状态: 1,新目标出现 2,目标匹配 3,目标遮挡 4,目标分离 5,目标消失。
卡尔曼原理在这儿我就不贴了,网上很多。
状态方程: X(k+1)=A(K+1,K)X(K)+w(K) 其中 X(k)=[x(k),y(k),w(k),h(k),v(k)], x,y,w,h,分别表示bboxs的横纵坐标,长,宽。
观测方程: Z(k)=H(k)X(k)+v(k) w(k), v(k),不相关的高斯白噪声。
定义好了观测方程与状态方程之后就可以用卡尔曼滤波器实现运动目标的跟踪,步骤如下:
1)计算运动目标的特征信息(运动质心,以及外接矩形)。
2)用得到的特征信息初始化卡尔曼滤波器(开始时可以初始为0)。
3)用卡尔曼滤波器对下一帧中对应的目标区域进行预测,当下一帧到来时,在预测区域内进行目标匹配。
4)如果匹配成功,则更新卡尔曼滤波器
在匹配的过程中,使用的是匈牙利匹配算法,匈牙利算法在这里有很好的介绍:http://blog.csdn.net/pi9nc/article/details/11848327
匈牙利匹配算法在此处是将新一帧图片中检测到的运动物体匹配到对应的轨迹。匹配过程是通过最小化卡尔曼预测得到的质心与检测到的质心之间的欧氏距离之和实现的
具体可以分为两步:
1, 计算损失矩阵,大小为[M N],其中,M是轨迹数目,N是检测到的运动物体数目。
2, 求解损失矩阵
主要思路就是这么多,下面贴上matlab的demo,大家可以跑一跑。
function multiObjectTracking() % create system objects used for reading video, detecting moving objects,
% and displaying the results
obj = setupSystemObjects(); %初始化函数
tracks = initializeTracks(); % create an empty array of tracks %初始化轨迹对象 nextId = ; % ID of the next track % detect moving objects, and track them across video frames
while ~isDone(obj.reader)
frame = readFrame(); %读取一帧
[centroids, bboxes, mask] = detectObjects(frame); %前景检测
predictNewLocationsOfTracks(); %根据位置进行卡尔曼预测
[assignments, unassignedTracks, unassignedDetections] = ...
detectionToTrackAssignment(); %匈牙利匹配算法进行匹配 updateAssignedTracks();%分配好的轨迹更新
updateUnassignedTracks();%未分配的轨迹更新
deleteLostTracks();%删除丢掉的轨迹
createNewTracks();%创建新轨迹 displayTrackingResults();%结果展示
end %% Create System Objects
% Create System objects used for reading the video frames, detecting
% foreground objects, and displaying results. function obj = setupSystemObjects()
% Initialize Video I/O
% Create objects for reading a video from a file, drawing the tracked
% objects in each frame, and playing the video. % create a video file reader
obj.reader = vision.VideoFileReader('atrium.avi'); %读入视频 % create two video players, one to display the video,
% and one to display the foreground mask
obj.videoPlayer = vision.VideoPlayer('Position', [, , , ]); %创建两个窗口
obj.maskPlayer = vision.VideoPlayer('Position', [, , , ]); % Create system objects for foreground detection and blob analysis % The foreground detector is used to segment moving objects from
% the background. It outputs a binary mask, where the pixel value
% of corresponds to the foreground and the value of corresponds
% to the background. obj.detector = vision.ForegroundDetector('NumGaussians', , ... %GMM进行前景检测,高斯核数目为3,前40帧为背景帧,域值为0.7
'NumTrainingFrames', , 'MinimumBackgroundRatio', 0.7); % Connected groups of foreground pixels are likely to correspond to moving
% objects. The blob analysis system object is used to find such groups
% (called 'blobs' or 'connected components'), and compute their
% characteristics, such as area, centroid, and the bounding box. obj.blobAnalyser = vision.BlobAnalysis('BoundingBoxOutputPort', true, ... %输出质心和外接矩形
'AreaOutputPort', true, 'CentroidOutputPort', true, ...
'MinimumBlobArea', );
end %% Initialize Tracks
% The |initializeTracks| function creates an array of tracks, where each
% track is a structure representing a moving object in the video. The
% purpose of the structure is to maintain the state of a tracked object.
% The state consists of information used for detection to track assignment,
% track termination, and display.
%
% The structure contains the following fields:
%
% * |id| : the integer ID of the track
% * |bbox| : the current bounding box of the object; used
% for display
% * |kalmanFilter| : a Kalman filter object used for motion-based
% tracking
% * |age| : the number of frames since the track was first
% detected
% * |totalVisibleCount| : the total number of frames in which the track
% was detected (visible)
% * |consecutiveInvisibleCount| : the number of consecutive frames for
% which the track was not detected (invisible).
%
% Noisy detections tend to result in short-lived tracks. For this reason,
% the example only displays an object after it was tracked for some number
% of frames. This happens when |totalVisibleCount| exceeds a specified
% threshold.
%
% When no detections are associated with a track for several consecutive
% frames, the example assumes that the object has left the field of view
% and deletes the track. This happens when |consecutiveInvisibleCount|
% exceeds a specified threshold. A track may also get deleted as noise if
% it was tracked for a short time, and marked invisible for most of the of
% the frames. function tracks = initializeTracks()
% create an empty array of tracks
tracks = struct(...
'id', {}, ... %轨迹ID
'bbox', {}, ... %外接矩形
'kalmanFilter', {}, ...%轨迹的卡尔曼滤波器
'age', {}, ...%总数量
'totalVisibleCount', {}, ...%可视数量
'consecutiveInvisibleCount', {});%不可视数量
end %% Read a Video Frame
% Read the next video frame from the video file.
function frame = readFrame()
frame = obj.reader.step();%激活读图函数
end %% Detect Objects
% The |detectObjects| function returns the centroids and the bounding boxes
% of the detected objects. It also returns the binary mask, which has the
% same size as the input frame. Pixels with a value of correspond to the
% foreground, and pixels with a value of correspond to the background.
%
% The function performs motion segmentation using the foreground detector.
% It then performs morphological operations on the resulting binary mask to
% remove noisy pixels and to fill the holes in the remaining blobs. function [centroids, bboxes, mask] = detectObjects(frame) % detect foreground
mask = obj.detector.step(frame); % apply morphological operations to remove noise and fill in holes
mask = imopen(mask, strel('rectangle', [,]));%开运算
mask = imclose(mask, strel('rectangle', [, ])); %闭运算
mask = imfill(mask, 'holes');%填洞 % perform blob analysis to find connected components
[~, centroids, bboxes] = obj.blobAnalyser.step(mask);
end %% Predict New Locations of Existing Tracks
% Use the Kalman filter to predict the centroid of each track in the
% current frame, and update its bounding box accordingly. function predictNewLocationsOfTracks()
for i = :length(tracks)
bbox = tracks(i).bbox; % predict the current location of the track
predictedCentroid = predict(tracks(i).kalmanFilter);%根据以前的轨迹,预测当前位置 % shift the bounding box so that its center is at
% the predicted location
predictedCentroid = int32(predictedCentroid) - bbox(:) / ;
tracks(i).bbox = [predictedCentroid, bbox(:)];%真正的当前位置
end
end %% Assign Detections to Tracks
% Assigning object detections in the current frame to existing tracks is
% done by minimizing cost. The cost is defined as the negative
% log-likelihood of a detection corresponding to a track.
%
% The algorithm involves two steps:
%
% Step : Compute the cost of assigning every detection to each track using
% the |distance| method of the |vision.KalmanFilter| System object. The
% cost takes into account the Euclidean distance between the predicted
% centroid of the track and the centroid of the detection. It also includes
% the confidence of the prediction, which is maintained by the Kalman
% filter. The results are stored in an MxN matrix, where M is the number of
% tracks, and N is the number of detections.
%
% Step : Solve the assignment problem represented by the cost matrix using
% the |assignDetectionsToTracks| function. The function takes the cost
% matrix and the cost of not assigning any detections to a track.
%
% The value for the cost of not assigning a detection to a track depends on
% the range of values returned by the |distance| method of the
% |vision.KalmanFilter|. This value must be tuned experimentally. Setting
% it too low increases the likelihood of creating a new track, and may
% result in track fragmentation. Setting it too high may result in a single
% track corresponding to a series of separate moving objects.
%
% The |assignDetectionsToTracks| function uses the Munkres' version of the
% Hungarian algorithm to compute an assignment which minimizes the total
% cost. It returns an M x matrix containing the corresponding indices of
% assigned tracks and detections in its two columns. It also returns the
% indices of tracks and detections that remained unassigned. function [assignments, unassignedTracks, unassignedDetections] = ...
detectionToTrackAssignment() nTracks = length(tracks);
nDetections = size(centroids, ); % compute the cost of assigning each detection to each track
cost = zeros(nTracks, nDetections);
for i = :nTracks
cost(i, :) = distance(tracks(i).kalmanFilter, centroids);%损失矩阵计算
end % solve the assignment problem
costOfNonAssignment = ;
[assignments, unassignedTracks, unassignedDetections] = ...
assignDetectionsToTracks(cost, costOfNonAssignment);%匈牙利算法匹配
end %% Update Assigned Tracks
% The |updateAssignedTracks| function updates each assigned track with the
% corresponding detection. It calls the |correct| method of
% |vision.KalmanFilter| to correct the location estimate. Next, it stores
% the new bounding box, and increases the age of the track and the total
% visible count by . Finally, the function sets the invisible count to . function updateAssignedTracks()
numAssignedTracks = size(assignments, );
for i = :numAssignedTracks
trackIdx = assignments(i, );
detectionIdx = assignments(i, );
centroid = centroids(detectionIdx, :);
bbox = bboxes(detectionIdx, :); % correct the estimate of the object's location
% using the new detection
correct(tracks(trackIdx).kalmanFilter, centroid); % replace predicted bounding box with detected
% bounding box
tracks(trackIdx).bbox = bbox; % update track's age
tracks(trackIdx).age = tracks(trackIdx).age + ; % update visibility
tracks(trackIdx).totalVisibleCount = ...
tracks(trackIdx).totalVisibleCount + ;
tracks(trackIdx).consecutiveInvisibleCount = ;
end
end %% Update Unassigned Tracks
% Mark each unassigned track as invisible, and increase its age by . function updateUnassignedTracks()
for i = :length(unassignedTracks)
ind = unassignedTracks(i);
tracks(ind).age = tracks(ind).age + ;
tracks(ind).consecutiveInvisibleCount = ...
tracks(ind).consecutiveInvisibleCount + ;
end
end %% Delete Lost Tracks
% The |deleteLostTracks| function deletes tracks that have been invisible
% for too many consecutive frames. It also deletes recently created tracks
% that have been invisible for too many frames overall. function deleteLostTracks()
if isempty(tracks)
return;
end invisibleForTooLong = ;
ageThreshold = ; % compute the fraction of the track's age for which it was visible
ages = [tracks(:).age];
totalVisibleCounts = [tracks(:).totalVisibleCount];
visibility = totalVisibleCounts ./ ages; % find the indices of 'lost' tracks
lostInds = (ages < ageThreshold & visibility < 0.6) | ...
[tracks(:).consecutiveInvisibleCount] >= invisibleForTooLong; % delete lost tracks
tracks = tracks(~lostInds);
end %% Create New Tracks
% Create new tracks from unassigned detections. Assume that any unassigned
% detection is a start of a new track. In practice, you can use other cues
% to eliminate noisy detections, such as size, location, or appearance. function createNewTracks()
centroids = centroids(unassignedDetections, :);
bboxes = bboxes(unassignedDetections, :); for i = :size(centroids, ) centroid = centroids(i,:);
bbox = bboxes(i, :); % create a Kalman filter object
kalmanFilter = configureKalmanFilter('ConstantVelocity', ...
centroid, [, ], [, ], ); % create a new track
newTrack = struct(...
'id', nextId, ...
'bbox', bbox, ...
'kalmanFilter', kalmanFilter, ...
'age', , ...
'totalVisibleCount', , ...
'consecutiveInvisibleCount', ); % add it to the array of tracks
tracks(end + ) = newTrack; % increment the next id
nextId = nextId + ;
end
end %% Display Tracking Results
% The |displayTrackingResults| function draws a bounding box and label ID
% for each track on the video frame and the foreground mask. It then
% displays the frame and the mask in their respective video players. function displayTrackingResults()
% convert the frame and the mask to uint8 RGB
frame = im2uint8(frame);
mask = uint8(repmat(mask, [, , ])) .* ; minVisibleCount = ;
if ~isempty(tracks) % noisy detections tend to result in short-lived tracks
% only display tracks that have been visible for more than
% a minimum number of frames.
reliableTrackInds = ...
[tracks(:).totalVisibleCount] > minVisibleCount;
reliableTracks = tracks(reliableTrackInds); % display the objects. If an object has not been detected
% in this frame, display its predicted bounding box.
if ~isempty(reliableTracks)
% get bounding boxes
bboxes = cat(, reliableTracks.bbox); % get ids
ids = int32([reliableTracks(:).id]); % create labels for objects indicating the ones for
% which we display the predicted rather than the actual
% location
labels = cellstr(int2str(ids'));
predictedTrackInds = ...
[reliableTracks(:).consecutiveInvisibleCount] > ;
isPredicted = cell(size(labels));
isPredicted(predictedTrackInds) = {' predicted'};
labels = strcat(labels, isPredicted); % draw on the frame
frame = insertObjectAnnotation(frame, 'rectangle', ...
bboxes, labels); % draw on the mask
mask = insertObjectAnnotation(mask, 'rectangle', ...
bboxes, labels);
end
end % display the mask and the frame
obj.maskPlayer.step(mask);
obj.videoPlayer.step(frame);
end %% Summary
% This example created a motion-based system for detecting and
% tracking multiple moving objects. Try using a different video to see if
% you are able to detect and track objects. Try modifying the parameters
% for the detection, assignment, and deletion steps.
%
% The tracking in this example was solely based on motion with the
% assumption that all objects move in a straight line with constant speed.
% When the motion of an object significantly deviates from this model, the
% example may produce tracking errors. Notice the mistake in tracking the
% person labeled #, when he is occluded by the tree.
%
% The likelihood of tracking errors can be reduced by using a more complex
% motion model, such as constant acceleration, or by using multiple Kalman
% filters for every object. Also, you can incorporate other cues for
% associating detections over time, such as size, shape, and color. displayEndOfDemoMessage(mfilename)
end
matlab示例程序--Motion-Based Multiple Object Tracking--卡尔曼多目标跟踪程序--解读的更多相关文章
- Motion-Based Multiple Object Tracking
kalman filter tracking... %% Motion-Based Multiple Object Tracking % This example shows how to perfo ...
- Multiple Object Tracking using K-Shortest Paths Optimization简要
参考文献:Multiple Object Tracking using K-Shortest Paths Optimization 核心步骤: 两步:一.detection 二.link detect ...
- MATLAB 例子研究 Motion-Based Multiple Object Tracking
这个例子是用来识别视频中多个物体运动的.我要研究的是:搞清楚识别的步骤和相应的算法,识别出物体运动的轨迹. 详细参见官方帮助文档,总结如下: 移动物体的识别算法:a background subtra ...
- 多目标跟踪(MOT)论文随笔-POI: Multiple Object Tracking with High Performance Detection and Appearance Feature
网上已有很多关于MOT的文章,此系列仅为个人阅读随笔,便于初学者的共同成长.若希望详细了解,建议阅读原文. 本文是tracking by detection 方法进行多目标跟踪的文章,最大的特点是使用 ...
- 论文笔记-Deep Affinity Network for Multiple Object Tracking
作者: ShijieSun, Naveed Akhtar, HuanShengSong, Ajmal Mian, Mubarak Shah 来源: arXiv:1810.11780v1 项目:http ...
- [Object Tracking] Overview of Object Tracking
From: 目标跟踪方法的发展概述 From: 目标跟踪领域进展报告 通用目标的跟踪 经典目标跟踪方法 2010 年以前,目标跟踪领域大部分采用一些经典的跟踪方法,比如 Meanshift.Parti ...
- 论文笔记:Visual Object Tracking based on Adaptive Siamese and Motion Estimation Network
Visual Object Tracking based on Adaptive Siamese and Motion Estimation 本文提出一种利用上一帧目标位置坐标,在本帧中找出目标可能出 ...
- Object Tracking Benchmark
Abstract 问题: 1)evaluation is often not suffcient 2)biased for certain types of algorthms 3)datasets ...
- Online Object Tracking: A Benchmark 论文笔记(转)
转自:http://blog.csdn.net/lanbing510/article/details/40411877 有博主翻译了这篇论文:http://blog.csdn.net/roamer_n ...
随机推荐
- IIS7配置asp网站
An error occurred on the server when processing the URL. Please contact the system administrator. If ...
- @HTML.checkboxFor()用法
<%=Html.CheckBox("chk1",true) %> <%=Html.CheckBox("chk1", new { @class= ...
- 由ArrayList构造函数源码引出的问题
ArrayList应该用得很多了.最近看了看其源码,发现有很多细节,如果要我们自己来实现,估计会考虑不到.当然,这些细节跟jdk本身一些实现的bug有关,如果不去深挖,定然是不能发现.本文从Array ...
- linux后台运行和关闭、查看后台任务(转)
转自:http://www.cnblogs.com/kaituorensheng/p/3980334.html fg.bg.jobs.&.nohup.ctrl+z.ctrl+c 命令 一.&a ...
- 《Java中方法的重写》
//方法的重写 /* 注意:方法的重写要遵循“两同两小一大”规则 “两同”即方法名相同.形参列表相同: “两小”(1)指的是子类方法返回值类型比父类方法的返回值类型更小或相等,[什么意思?] (2)子 ...
- iOS求职之OC面试题
1.Objective-C的类可以多重继承么?可以采用多个协议么? 答:不可以多重继承,可以采用多个协议. 2.#import和#include的区别是什么?#import<> 跟 #im ...
- 当一个activity中按钮过多时怎么办?
这几天看极客学院的视频,跟视频中的老师学到的一些小技巧~~ .setOnClickListener(this) 通过重写this(我猜的是重写),下面有onClicked() package exam ...
- 数组第K小数问题 及其对于 快排和堆排 的相关优化比较
题目描述 给定一个整数数组a[0,...,n-1],求数组中第k小数 输入描述 首先输入数组长度n和k,其中1<=n<=5000, 1<=k<=n 然后输出n个整形元素,每个数 ...
- 2015百度之星1002 查找有序序列(RMQ+主席树模板水过)
题意:求在数列中能找到几个个长度为k 的区间,里面的 k 个数字排完序后是连续的. 思路:枚举范围,判断区间内是否有重复的数字(主席树),没有的话求区间最大-区间最小(RMQ),判断是否等于K,是的话 ...
- MVC中的自定义控件
MVC中的控件都是HtmlHelper的扩展方法(不了解扩展方?法请阅读扩展方法),比如@Html.ActionLink,F12可以看到它是这样写的: public static MvcHtmlStr ...