function multiObjectTracking()
%创建用于阅读视频,检测移动物体的系统对象, %并显示结果
obj = setupSystemObjects();
tracks = initializeTracks(); % create an empty array of tracks
nextId = 1; % ID of the next track
%检测移动物体,并在视频帧中跟踪它们
while ~isDone(obj.reader)
frame = readFrame();
[centroids, bboxes, mask] = detectObjects(frame);
predictNewLocationsOfTracks();
[assignments, unassignedTracks, unassignedDetections] = ...
detectionToTrackAssignment();
updateAssignedTracks();
updateUnassignedTracks();
deleteLostTracks();
createNewTracks();
displayTrackingResults();
end
%%创建系统对象 %创建用于读取视频帧的系统对象,进行检测 %前景对象,并显示结果。
function obj = setupSystemObjects()
%初始化视频I / O. %创建用于从文件中读取视频的对象,绘制跟踪的对象 每帧中有%对象,并播放视频。
vid = videoinput('winvideo', 1, 'YUY2_320x240');
src = getselectedsource(vid);
vid.FramesPerTrigger = 1;
%TriggerRepeat基于零,始终为1 小于触发次数的百分比。
vid.TriggerRepeat = 899;
preview(vid);
start(vid);
stoppreview(vid);
savedvideo = getdata(vid);
%创建视频文件阅读器
obj.reader = vision.VideoFileReader(savedvideo);
%创建两个视频播放器,一个用于显示视频, %和一个显示前景蒙版
obj.videoPlayer = vision.VideoPlayer('Position', [20, 400, 700, 400]);
obj.maskPlayer = vision.VideoPlayer('Position', [740, 400, 700, 400]);
obj.detector = vision.ForegroundDetector('NumGaussians', 3, ...
'NumTrainingFrames', 40, 'MinimumBackgroundRatio', 0.7);
obj.blobAnalyser = vision.BlobAnalysis('BoundingBoxOutputPort', true, ...
'AreaOutputPort', true, 'CentroidOutputPort', true, ...
'MinimumBlobArea', 400);
end
function tracks = initializeTracks()
%创建一个空的曲目数组
tracks = struct(...
'id', {}, ...
'bbox', {}, ...
'kalmanFilter', {}, ...
'age', {}, ...
'totalVisibleCount', {}, ...
'consecutiveInvisibleCount', {});
end
%%阅读视频帧 %从视频文件中读取下一个视频帧。
function frame = readFrame()
frame = obj.reader.step();
end
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
%%预测现有曲目的新位置 %使用卡尔曼滤波器预测每个轨道的质心 %当前帧,并相应地更新其边界框。
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
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
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;
%更新曲目的年龄
tracks(trackIdx).age = tracks(trackIdx).age + 1;
%更新可见性
tracks(trackIdx).totalVisibleCount = ...
tracks(trackIdx).totalVisibleCount + 1;
tracks(trackIdx).consecutiveInvisibleCount = 0;
end
end
%%更新未分配的曲目 %将每个未分配的曲目标记为不可见,并将其年龄增加1。
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
function deleteLostTracks()
if isempty(tracks)
return;
end
invisibleForTooLong = 10;
ageThreshold = 8;
%计算可见的赛道年龄的分数
ages = [tracks(:).age];
totalVisibleCounts = [tracks(:).totalVisibleCount];
visibility = totalVisibleCounts ./ ages;
%找到'丢失的指数'轨道
lostInds = (ages < ageThreshold & visibility < 0.6) | ...
[tracks(:).consecutiveInvisibleCount] >= invisibleForTooLong;
%删除丢失的曲目
tracks = tracks(~lostInds);
end
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
function displayTrackingResults()
%将帧和掩码转换为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);
%get ids
ids = int32([reliableTracks(:).id]);
%为对象指定标签的标签 我们显示预测而不是实际的百分比 %location
labels = cellstr(int2str(ids'));
predictedTrackInds = ...
[reliableTracks(:).consecutiveInvisibleCount] > 0;
isPredicted = cell(size(labels));
isPredicted(predictedTrackInds) = {' predicted'};
labels = strcat(labels, isPredicted);
%绘制框架
frame = insertObjectAnnotation(frame, 'rectangle', ...
bboxes, labels);
%在面具上绘制
mask = insertObjectAnnotation(mask, 'rectangle', ...
bboxes, labels);
end
end
%显示蒙版和框架
obj.maskPlayer.step(mask);
obj.videoPlayer.step(frame);
end
displayEndOfDemoMessage(mfilename)
end
答案 0 :(得分:0)
您的问题是在尝试从相机读取帧时尝试使用vision.VideoFileReader
。 vision.VideoFileReader
仅用于阅读视频文件。如果您从相机获取帧,则根本不需要它。您应该将videoinput
对象添加到obj
结构中,然后尝试在getsnapshot
内使用readFrame()
。
答案 1 :(得分:-1)
我尝试使用MATLAB中的这段代码进行实时对象检测,并做到了。对您来说已经很晚了,但我希望它可以为任何需要的人提供帮助。
function multiObjectTracking()
%创建用于读取视频,检测移动的System对象 对象,并显示结果。
obj = setupSystemObjects();
tracks = initializeTracks(); % Create an empty array of tracks.
nextId = 1; % ID of the next track
%检测运动物体,并在视频帧中跟踪它们。
for x=1:200
frame = readFrame();
[centroids, bboxes, mask] = detectObjects(frame);
predictNewLocationsOfTracks();
[assignments, unassignedTracks, unassignedDetections] = ...
detectionToTrackAssignment();
updateAssignedTracks();
updateUnassignedTracks();
deleteLostTracks();
createNewTracks();
displayTrackingResults();
end
function obj = setupSystemObjects()
%初始化视频I / O %创建对象以从文件中读取视频,绘制跟踪 每帧中有%个对象,并播放视频。 %创建一个视频文件阅读器。
obj.reader = imaq.VideoDevice('winvideo', 1, 'YUY2_320x240', ...
'ReturnedColorSpace', 'rgb');
%创建两个视频播放器,一个用于显示视频, %和一个显示前景遮罩。
obj.videoPlayer = vision.VideoPlayer('Position', [20, 400, 700, 400]);
obj.maskPlayer = vision.VideoPlayer('Position', [740, 400, 700, 400]);
%创建系统对象以进行前景检测和斑点分析 %前景检测器用于分割运动物体 % 的背景。它输出一个二进制掩码,其中像素值 1的%对应于前景,0的值对应 %的背景。
obj.detector = vision.ForegroundDetector('NumGaussians', 3, ...
'NumTrainingFrames', 40, 'MinimumBackgroundRatio', 0.7);
%所连接的前景像素组可能对应于 移动 %个对象。斑点分析系统对象用于查找此类组 %(称为“斑点”或“连接的组件”),并计算其 %特征,例如面积,质心和边界框。
obj.blobAnalyser = vision.BlobAnalysis('BoundingBoxOutputPort', true, ...
'AreaOutputPort', true, 'CentroidOutputPort', true, ...
'MinimumBlobArea', 400);
end
function tracks = initializeTracks()
% create an empty array of tracks
tracks = struct(...
'id', {}, ...
'bbox', {}, ...
'kalmanFilter', {}, ...
'age', {}, ...
'totalVisibleCount', {}, ...
'consecutiveInvisibleCount', {});
end
function frame = readFrame()
frame = step(obj.reader);
end
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');
%执行斑点分析以查找连接的组件。
[~, centroids, bboxes] = obj.blobAnalyser.step(mask);
end
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
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
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;
%更新曲目的年龄。
tracks(trackIdx).age = tracks(trackIdx).age + 1;
%更新可见性。
tracks(trackIdx).totalVisibleCount = ...
tracks(trackIdx).totalVisibleCount + 1;
tracks(trackIdx).consecutiveInvisibleCount = 0;
end
end
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
function deleteLostTracks()
if isempty(tracks)
return;
end
invisibleForTooLong = 20;
ageThreshold = 8;
%计算可见的曲目年龄比例。
ages = [tracks(:).age];
totalVisibleCounts = [tracks(:).totalVisibleCount];
visibility = totalVisibleCounts ./ ages;
%查找“丢失”曲目的索引。
lostInds = (ages < ageThreshold & visibility < 0.6) | ...
[tracks(:).consecutiveInvisibleCount] >= invisibleForTooLong;
%删除丢失的曲目。
tracks = tracks(~lostInds);
end
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
function displayTrackingResults()
%将框架和遮罩转换为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 = insertObjectAnnotation(frame, 'rectangle', ...
bboxes, labels);
%在遮罩上绘制对象。
mask = insertObjectAnnotation(mask, 'rectangle', ...
bboxes, labels);
end
end
%显示遮罩和边框。
obj.maskPlayer.step(mask);
obj.videoPlayer.step(frame);
end
end