

♥
步骤1:创建系统对象
创建用于读取视频帧、检测前景对象和显示结果的系统对象。
function obj = setupSystemObjects()
% 读取视频文件
obj.reader = VideoReader('atrium.mp4');
% 创建两个视频播放器,一个用于显示视频,一个用于显示前景蒙版
obj.maskPlayer = vision.VideoPlayer('Position', [740, 400, 700, 400]);
obj.videoPlayer = vision.VideoPlayer('Position', [20, 400, 700, 400]);
% 创建用于前景检测和blob分析的系统对象
obj.detector = vision.ForegroundDetector('NumGaussians', 3, ...
'NumTrainingFrames', 40, 'MinimumBackgroundRatio', 0.7);
obj.blobAnalyser = vision.BlobAnalysis('BoundingBoxOutputPort', true, ...
'AreaOutputPort', true, 'CentroidOutputPort', true, ...
'MinimumBlobArea', 400);
end步骤2:初始化轨迹
function tracks = initializeTracks()
% 创建一个空的轨迹数组
tracks = struct(...
'id', {}, ...
'bbox', {}, ...
'kalmanFilter', {}, ...
'age', {}, ...
'totalVisibleCount', {}, ...
'consecutiveInvisibleCount', {});
end步骤3:检测对象
function [centroids, bboxes, mask] = detectObjects(frame)
% 检测前景
mask = obj.detector.step(frame);
% 应用形态学操作来去除噪声并填充孔洞
mask = imopen(mask, strel('rectangle', [3,3]));
mask = imclose(mask, strel('rectangle', [15, 15]));
mask = imfill(mask, 'holes');
% 执行blob分析以查找连接的组件
[~, centroids, bboxes] = obj.blobAnalyser.step(mask);
end步骤4:预测现有轨迹的新位置
使用卡尔曼滤波预测当前帧中每个轨迹的质心,并相应地更新其边界框。
function predictNewLocationsOfTracks()
for i = 1:length(tracks)
bbox = tracks(i).bbox;
% 预测轨迹的当前位置
predictedCentroid = predict(tracks(i).kalmanFilter);
% 移动边界框,使其中心位于预测的位置
predictedCentroid = int32(predictedCentroid) - bbox(3:4) / 2;
tracks(i).bbox = [predictedCentroid, bbox(3:4)];
end
end步骤5:将检测分配给轨迹
function [assignments, unassignedTracks, unassignedDetections] = ...
detectionToTrackAssignment()
nTracks = length(tracks);
nDetections = size(centroids, 1);
% 计算将每个检测分配给每个轨迹的成本
cost = zeros(nTracks, nDetections);
for i = 1:nTracks
cost(i, :) = distance(tracks(i).kalmanFilter, centroids);
end
% 解决分配问题
costOfNonAssignment = 20;
[assignments, unassignedTracks, unassignedDetections] = ...
assignDetectionsToTracks(cost, costOfNonAssignment);
end步骤6:更新分配的轨迹
function updateAssignedTracks()
numAssignedTracks = size(assignments, 1);
for i = 1:numAssignedTracks
trackIdx = assignments(i, 1);
detectionIdx = assignments(i, 2);
centroid = centroids(detectionIdx, :);
bbox = bboxes(detectionIdx, :);
% 更正对象位置的估计值
correct(tracks(trackIdx).kalmanFilter, centroid);
% 使用检测到的边界框替换预测的边界框
tracks(trackIdx).bbox = bbox;
% 更新age
tracks(trackIdx).age = tracks(trackIdx).age + 1;
% 更新visibility
tracks(trackIdx).totalVisibleCount = ...
tracks(trackIdx).totalVisibleCount + 1;
tracks(trackIdx).consecutiveInvisibleCount = 0;
end
end步骤7:更新未分配的轨迹
function updateUnassignedTracks()
for i = 1:length(unassignedTracks)
ind = unassignedTracks(i);
tracks(ind).age = tracks(ind).age + 1;
tracks(ind).consecutiveInvisibleCount = ...
tracks(ind).consecutiveInvisibleCount + 1;
end
end步骤8:删除丢失的轨迹
function deleteLostTracks()
if isempty(tracks)
return;
end
invisibleForTooLong = 20;
ageThreshold = 8;
% 计算轨迹visibility的age分数
ages = [tracks(:).age];
totalVisibleCounts = [tracks(:).totalVisibleCount];
visibility = totalVisibleCounts ./ ages;
% 查找“丢失”轨迹的索引
lostInds = (ages < ageThreshold & visibility < 0.6) | ...
[tracks(:).consecutiveInvisibleCount] >= invisibleForTooLong;
% 删除丢失的轨迹
tracks = tracks(~lostInds);
end步骤9:创建新轨迹
为从未分配的检测结果创建新的轨迹。
function createNewTracks()
centroids = centroids(unassignedDetections, :);
bboxes = bboxes(unassignedDetections, :);
for i = 1:size(centroids, 1)
centroid = centroids(i,:);
bbox = bboxes(i, :);
% 创建卡尔曼滤波器对象
kalmanFilter = configureKalmanFilter('ConstantVelocity', ...
centroid, [200, 50], [100, 25], 100);
% 创建新轨迹
newTrack = struct(...
'id', nextId, ...
'bbox', bbox, ...
'kalmanFilter', kalmanFilter, ...
'age', 1, ...
'totalVisibleCount', 1, ...
'consecutiveInvisibleCount', 0);
% 将其添加到轨迹阵列中
tracks(end + 1) = newTrack;
% 递增下一个id
nextId = nextId + 1;
end
end步骤10:显示跟踪结果
function displayTrackingResults()
% 将帧和mask转换为uint8 RGB。
frame = im2uint8(frame);
mask = uint8(repmat(mask, [1, 1, 3])) .* 255;
minVisibleCount = 8;
if ~isempty(tracks)
reliableTrackInds = ...
[tracks(:).totalVisibleCount] > minVisibleCount;
reliableTracks = tracks(reliableTrackInds);
% 显示对象
if ~isempty(reliableTracks)
% 获取边界框
bboxes = cat(1, reliableTracks.bbox);
% 获取ID
ids = int32([reliableTracks(:).id]);
% 为对象创建标签
labels = cellstr(int2str(ids'));
predictedTrackInds = ...
[reliableTracks(:).consecutiveInvisibleCount] > 0;
isPredicted = cell(size(labels));
isPredicted(predictedTrackInds) = {' predicted'};
labels = strcat(labels, isPredicted);
% 在frame上绘制对象
frame = insertObjectAnnotation(frame, 'rectangle', ...
bboxes, labels);
% 在mask上绘制对象
mask = insertObjectAnnotation(mask, 'rectangle', ...
bboxes, labels);
end
end
% 显示mask和frame
obj.maskPlayer.step(mask);
obj.videoPlayer.step(frame);
end

链接:https://ww2.mathworks.cn/help/vision/ug/motion-based-multiple-object-tracking.html?s_tid=srchtitle_site_search_1_Motion-Based+Multiple+Object+Tracking
本文分享自 图像处理与模式识别研究所 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!