基于帧图像序列的目标检测与跟踪MATLAB实现
基于帧图像序列的目标检测与跟踪MATLAB程序。该系统实现了从视频读取、背景建模、目标检测、目标跟踪到结果可视化的全流程处理。
%% 基于帧图像序列的目标检测与跟踪系统
% 描述: 实现目标检测、跟踪与轨迹分析的完整流程
%% 主程序入口
function objectDetectionAndTracking()
% 清空环境
clear; close all; clc;
% 参数设置
params = struct();
params.videoSource = 'pedestrians.mp4'; % 视频文件路径
params.useWebcam = false; % 是否使用摄像头
params.samplingRate = 5; % 采样率(每N帧处理一次)
params.minBlobArea = 500; % 最小目标面积
params.maxBlobArea = 20000; % 最大目标面积
params.minTrackLength = 10; % 最小轨迹长度
params.maxTrackGap = 5; % 最大轨迹中断帧数
params.kalmanProcessNoise = 1e-2; % 卡尔曼过程噪声
params.kalmanMeasurementNoise = 1e-1; % 卡尔曼测量噪声
params.iouThreshold = 0.3; % IOU匹配阈值
params.displayResults = true; % 是否显示结果
params.saveResults = true; % 是否保存结果
% 初始化系统
trackerSystem = initializeTrackingSystem(params);
% 处理视频序列
processVideoSequence(trackerSystem);
% 分析结果
analyzeTrackingResults(trackerSystem);
% 保存结果
if params.saveResults
saveTrackingResults(trackerSystem);
end
end
%% 初始化跟踪系统
function system = initializeTrackingSystem(params)
% 创建系统结构体
system.params = params;
system.tracks = {}; % 存储所有轨迹
system.nextTrackId = 1; % 下一个轨迹ID
system.frameCount = 0; % 已处理帧数
system.detectionHistory = {}; % 检测结果历史
system.trackingHistory = {}; % 跟踪结果历史
% 初始化背景建模器
system.backgroundModel = vision.ForegroundDetector(...
'NumGaussians', 5, ...
'NumTrainingFrames', 100, ...
'MinimumBackgroundRatio', 0.7);
% 初始化卡尔曼滤波器参数
system.kalmanParams = struct(...
'A', [1 0 0 0 1 0; 0 1 0 0 0 1; 0 0 1 0 0 0; 0 0 0 1 0 0; 0 0 0 0 1 0; 0 0 0 0 0 1], ... % 状态转移矩阵
'H', [1 0 0 0 0 0; 0 1 0 0 0 0], ... % 观测矩阵
'Q', diag([params.kalmanProcessNoise*ones(1,4), params.kalmanProcessNoise*ones(1,2)]), ... % 过程噪声协方差
'R', eye(2)*params.kalmanMeasurementNoise, ... % 测量噪声协方差
'P', eye(6)*10, ... % 初始估计误差协方差
'B', []); % 控制输入矩阵(无控制输入)
% 创建显示窗口
if params.displayResults
system.fig = figure('Name', '目标检测与跟踪系统', 'NumberTitle', 'off', ...
'Position', [100, 100, 1200, 600]);
end
% 视频源初始化
if params.useWebcam
system.videoSource = webcam;
system.videoInfo = imaqhwinfo(system.videoSource);
system.isLiveVideo = true;
else
if exist(params.videoSource, 'file')
system.videoSource = VideoReader(params.videoSource);
system.isLiveVideo = false;
else
error('视频文件不存在: %s', params.videoSource);
end
end
fprintf('目标检测与跟踪系统已初始化\n');
fprintf('视频源: %s\n', params.videoSource);
fprintf('参数设置:\n');
disp(params);
end
%% 处理视频序列
function processVideoSequence(system)
% 主处理循环
while true
% 获取下一帧
if system.isLiveVideo
frame = snapshot(system.videoSource);
else
if hasFrame(system.videoSource)
frame = readFrame(system.videoSource);
else
break; % 视频结束
end
end
% 更新帧计数
system.frameCount = system.frameCount + 1;
% 按采样率处理帧
if mod(system.frameCount, system.params.samplingRate) == 0
fprintf('处理帧: %d\n', system.frameCount);
% 1. 背景建模与前景检测
foregroundMask = detectForeground(system, frame);
% 2. 目标检测(连通区域分析)
detections = detectObjects(system, foregroundMask, frame);
% 3. 目标跟踪
tracks = trackObjects(system, detections, frame);
% 4. 更新系统状态
system.detectionHistory{system.frameCount} = detections;
system.trackingHistory{system.frameCount} = tracks;
system.tracks = tracks; % 更新当前轨迹
% 5. 可视化结果
if system.params.displayResults
visualizeResults(system, frame, detections, tracks);
end
end
% 退出条件(实时视频限制帧数)
if system.isLiveVideo && system.frameCount > 300
break;
end
end
fprintf('视频处理完成,共处理 %d 帧\n', system.frameCount);
end
%% 前景检测
function foregroundMask = detectForeground(system, frame)
% 转换为灰度图像
if size(frame, 3) == 3
grayFrame = rgb2gray(frame);
else
grayFrame = frame;
end
% 使用背景建模器检测前景
foregroundMask = step(system.backgroundModel, grayFrame);
% 形态学操作去除噪声
seOpen = strel('disk', 2);
seClose = strel('rectangle', [5, 5]);
foregroundMask = imopen(foregroundMask, seOpen);
foregroundMask = imclose(foregroundMask, seClose);
% 填充小孔
foregroundMask = imfill(foregroundMask, 'holes');
end
%% 目标检测(连通区域分析)
function detections = detectObjects(system, foregroundMask, frame)
% 连通区域分析
cc = bwconncomp(foregroundMask);
stats = regionprops(cc, 'BoundingBox', 'Area', 'Centroid', 'PixelIdxList');
% 筛选目标
detections = [];
for i = 1:length(stats)
area = stats(i).Area;
if area >= system.params.minBlobArea && area <= system.params.maxBlobArea
bbox = stats(i).BoundingBox;
centroid = stats(i).Centroid;
% 计算边界框宽高比
aspectRatio = bbox(3)/bbox(4);
if aspectRatio > 0.2 && aspectRatio < 5.0 % 合理的宽高比范围
detection = struct(...
'id', i, ...
'bbox', bbox, ...
'centroid', centroid, ...
'area', area, ...
'frame', system.frameCount, ...
'timestamp', tic);
detections = [detections; detection];
end
end
end
end
%% 目标跟踪
function tracks = trackObjects(system, detections, frame)
% 预测现有轨迹的新位置
predictedTracks = predictTracks(system);
% 关联检测与轨迹
[matchedPairs, unmatchedDetections, unmatchedTracks] = associateDetectionsToTracks(...
system, detections, predictedTracks);
% 更新匹配的轨迹
updatedTracks = updateMatchedTracks(system, matchedPairs, detections, predictedTracks);
% 处理未匹配的轨迹(标记为丢失)
lostTracks = handleUnmatchedTracks(system, unmatchedTracks, predictedTracks);
% 创建新轨迹
newTracks = createNewTracks(system, unmatchedDetections, detections);
% 合并所有轨迹
tracks = [updatedTracks; lostTracks; newTracks];
% 移除旧轨迹
tracks = removeOldTracks(system, tracks);
end
%% 预测轨迹位置(卡尔曼滤波)
function predictedTracks = predictTracks(system)
predictedTracks = {};
for i = 1:length(system.tracks)
track = system.tracks{i};
% 初始化卡尔曼滤波器(如果需要)
if isempty(track.kalmanFilter)
track.kalmanFilter = configureKalmanFilter(...
'ConstantVelocity', ...
track.centroid, ...
[100, 30], ... % 初始位置不确定性
system.params.kalmanMeasurementNoise, ...
system.params.kalmanProcessNoise);
end
% 预测下一步位置
predict(track.kalmanFilter);
predictedPos = track.kalmanFilter.State(1:2);
% 更新轨迹预测位置
track.predictedCentroid = predictedPos';
track.age = track.age + 1;
track.timeSinceUpdate = track.timeSinceUpdate + 1;
track.history = [track.history; predictedPos'];
predictedTracks{end+1} = track;
end
end
%% 配置卡尔曼滤波器
function kalmanFilter = configureKalmanFilter(model, initialLocation, initialEstimateError, measurementNoise, processNoise)
% 创建卡尔曼滤波器
kalmanFilter = vision.KalmanFilter(...
'State', [initialLocation, zeros(2,1)]', ...
'StateTransitionModel', [1 0 1 0; 0 1 0 1; 0 0 1 0; 0 0 0 1], ...
'MeasurementModel', [1 0 0 0; 0 1 0 0], ...
'ProcessNoise', eye(4)*processNoise, ...
'MeasurementNoise', eye(2)*measurementNoise, ...
'InitialStateEstimateError', eye(4)*initialEstimateError(1), ...
'InitialErrorCovariance', eye(4)*initialEstimateError(2));
end
%% 关联检测与轨迹(IOU匹配)
function [matchedPairs, unmatchedDetections, unmatchedTracks] = associateDetectionsToTracks(system, detections, tracks)
% 初始化输出
matchedPairs = [];
unmatchedDetections = 1:length(detections);
unmatchedTracks = 1:length(tracks);
% 如果没有检测或轨迹,直接返回
if isempty(detections) || isempty(tracks)
return;
end
% 计算代价矩阵(IOU距离)
costMatrix = zeros(length(tracks), length(detections));
for i = 1:length(tracks)
track = tracks{i};
for j = 1:length(detections)
detection = detections(j);
costMatrix(i, j) = 1 - computeIOU(track.predictedBbox, detection.bbox);
end
end
% 使用匈牙利算法求解最优匹配
[assignment, cost] = assignDetectionsToTracks(costMatrix, system.params.iouThreshold);
% 解析匹配结果
matchedPairs = [];
for i = 1:size(assignment, 1)
trackIdx = assignment(i, 1);
detIdx = assignment(i, 2);
if trackIdx > 0 && detIdx > 0
matchedPairs = [matchedPairs; trackIdx, detIdx];
% 从未匹配列表中移除
unmatchedTracks(unmatchedTracks == trackIdx) = [];
unmatchedDetections(unmatchedDetections == detIdx) = [];
end
end
end
%% 计算IOU(交并比)
function iou = computeIOU(bbox1, bbox2)
% 计算交集区域
x1_min = bbox1(1); y1_min = bbox1(2);
x1_max = bbox1(1) + bbox1(3); y1_max = bbox1(2) + bbox1(4);
x2_min = bbox2(1); y2_min = bbox2(2);
x2_max = bbox2(1) + bbox2(3); y2_max = bbox2(2) + bbox2(4);
inter_x_min = max(x1_min, x2_min);
inter_y_min = max(y1_min, y2_min);
inter_x_max = min(x1_max, x2_max);
inter_y_max = min(y1_max, y2_max);
inter_width = max(0, inter_x_max - inter_x_min);
inter_height = max(0, inter_y_max - inter_y_min);
intersection = inter_width * inter_height;
% 计算并集区域
area1 = bbox1(3) * bbox1(4);
area2 = bbox2(3) * bbox2(4);
union = area1 + area2 - intersection;
% 计算IOU
iou = intersection / union;
end
%% 更新匹配的轨迹
function updatedTracks = updateMatchedTracks(system, matchedPairs, detections, tracks)
updatedTracks = {};
for i = 1:size(matchedPairs, 1)
trackIdx = matchedPairs(i, 1);
detIdx = matchedPairs(i, 2);
track = tracks{trackIdx};
detection = detections(detIdx);
% 更新卡尔曼滤波器
correct(track.kalmanFilter, detection.centroid');
% 更新轨迹状态
track.centroid = detection.centroid;
track.bbox = detection.bbox;
track.predictedBbox = detection.bbox; % 简化处理
track.age = 1;
track.timeSinceUpdate = 0;
track.hits = track.hits + 1;
track.history = [track.history; detection.centroid];
updatedTracks{end+1} = track;
end
end
%% 处理未匹配的轨迹
function lostTracks = handleUnmatchedTracks(system, unmatchedTracks, tracks)
lostTracks = {};
for i = 1:length(unmatchedTracks)
trackIdx = unmatchedTracks(i);
track = tracks{trackIdx};
% 标记为丢失
track.timeSinceUpdate = track.timeSinceUpdate + 1;
% 如果丢失时间过长,标记为不可见
if track.timeSinceUpdate <= system.params.maxTrackGap
lostTracks{end+1} = track;
end
end
end
%% 创建新轨迹
function newTracks = createNewTracks(system, unmatchedDetections, detections)
newTracks = {};
for i = 1:length(unmatchedDetections)
detIdx = unmatchedDetections(i);
detection = detections(detIdx);
% 创建新轨迹
track = struct(...
'id', system.nextTrackId, ...
'centroid', detection.centroid, ...
'bbox', detection.bbox, ...
'predictedBbox', detection.bbox, ...
'kalmanFilter', [], ...
'age', 1, ...
'timeSinceUpdate', 0, ...
'hits', 1, ...
'history', detection.centroid, ...
'color', rand(1, 3)); % 随机颜色用于可视化
newTracks{end+1} = track;
system.nextTrackId = system.nextTrackId + 1;
end
end
%% 移除旧轨迹
function tracks = removeOldTracks(system, tracks)
validTracks = {};
for i = 1:length(tracks)
track = tracks{i};
if track.timeSinceUpdate <= system.params.maxTrackGap || track.age < system.params.minTrackLength
validTracks{end+1} = track;
end
end
tracks = validTracks;
end
%% 可视化结果
function visualizeResults(system, frame, detections, tracks)
% 复制帧用于绘制
displayFrame = frame;
% 绘制检测结果
for i = 1:length(detections)
bbox = detections(i).bbox;
centroid = detections(i).centroid;
% 绘制边界框
displayFrame = insertShape(displayFrame, 'Rectangle', bbox, ...
'Color', 'yellow', 'LineWidth', 2);
% 绘制质心
displayFrame = insertMarker(displayFrame, centroid, ...
'o', 'Color', 'cyan', 'Size', 8);
end
% 绘制轨迹
for i = 1:length(tracks)
track = tracks{i};
% 绘制边界框
displayFrame = insertShape(displayFrame, 'Rectangle', track.bbox, ...
'Color', track.color, 'LineWidth', 2);
% 绘制轨迹历史
if size(track.history, 1) > 1
for j = 2:size(track.history, 1)
pt1 = track.history(j-1, :);
pt2 = track.history(j, :);
displayFrame = insertShape(displayFrame, 'Line', ...
[pt1(1), pt1(2), pt2(1), pt2(2)], ...
'Color', track.color, 'LineWidth', 2);
end
end
% 绘制轨迹ID
displayFrame = insertText(displayFrame, track.bbox(1:2), ...
num2str(track.id), 'FontSize', 14, ...
'BoxColor', track.color, 'TextColor', 'white');
end
% 显示帧信息
infoStr = sprintf('帧: %d\n检测目标: %d\n跟踪目标: %d', ...
system.frameCount, length(detections), length(tracks));
displayFrame = insertText(displayFrame, [10, 10], infoStr, ...
'FontSize', 14, 'BoxColor', 'black', 'TextColor', 'white');
% 显示图像
if ishandle(system.fig)
figure(system.fig);
imshow(displayFrame);
title(sprintf('目标检测与跟踪 (帧 %d)', system.frameCount));
drawnow;
end
end
%% 分析跟踪结果
function analyzeTrackingResults(system)
% 计算统计信息
totalTracks = system.nextTrackId - 1;
activeTracks = length(system.tracks);
avgTrackLength = 0;
maxTrackLength = 0;
% 计算轨迹长度
trackLengths = [];
for i = 1:length(system.trackingHistory)
if ~isempty(system.trackingHistory{i})
for j = 1:length(system.trackingHistory{i})
track = system.trackingHistory{i}{j};
if isfield(track, 'hits')
trackLengths(end+1) = track.hits;
end
end
end
end
if ~isempty(trackLengths)
avgTrackLength = mean(trackLengths);
maxTrackLength = max(trackLengths);
end
% 显示统计信息
fprintf('\n===== 跟踪结果分析 =====\n');
fprintf('总轨迹数: %d\n', totalTracks);
fprintf('活动轨迹数: %d\n', activeTracks);
fprintf('平均轨迹长度: %.2f 帧\n', avgTrackLength);
fprintf('最长轨迹长度: %d 帧\n', maxTrackLength);
% 绘制轨迹长度分布
if ~isempty(trackLengths)
figure;
histogram(trackLengths, 20);
xlabel('轨迹长度 (帧)');
ylabel('数量');
title('轨迹长度分布');
grid on;
end
% 绘制目标运动轨迹
plotTrajectories(system);
end
%% 绘制目标运动轨迹
function plotTrajectories(system)
figure('Name', '目标运动轨迹', 'NumberTitle', 'off', 'Position', [100, 100, 1000, 800]);
% 收集所有轨迹点
allTrajectories = {};
for i = 1:length(system.trackingHistory)
if ~isempty(system.trackingHistory{i})
for j = 1:length(system.trackingHistory{i})
track = system.trackingHistory{i}{j};
if isfield(track, 'history') && size(track.history, 1) > 1
allTrajectories{end+1} = struct(...
'id', track.id, ...
'color', track.color, ...
'points', track.history);
end
end
end
end
% 绘制轨迹
if ~isempty(allTrajectories)
for i = 1:length(allTrajectories)
traj = allTrajectories{i};
plot(traj.points(:,1), traj.points(:,2), ...
'Color', traj.color, 'LineWidth', 1.5);
hold on;
plot(traj.points(1,1), traj.points(1,2), 'o', ...
'Color', traj.color, 'MarkerSize', 8, 'MarkerFaceColor', traj.color);
plot(traj.points(end,1), traj.points(end,2), 's', ...
'Color', traj.color, 'MarkerSize', 8, 'MarkerFaceColor', traj.color);
end
xlabel('X 坐标 (像素)');
ylabel('Y 坐标 (像素)');
title('目标运动轨迹');
grid on;
axis equal;
legend('轨迹', '起点', '终点', 'Location', 'bestoutside');
else
text(0.5, 0.5, '无有效轨迹数据', 'HorizontalAlignment', 'center');
end
end
%% 保存跟踪结果
function saveTrackingResults(system)
% 准备数据
results = struct();
results.params = system.params;
results.frameCount = system.frameCount;
results.totalTracks = system.nextTrackId - 1;
results.tracks = system.tracks;
results.detectionHistory = system.detectionHistory;
results.trackingHistory = system.trackingHistory;
% 保存为MAT文件
save('tracking_results.mat', 'results');
% 导出轨迹数据为CSV
exportTrajectoriesToCSV(system);
fprintf('跟踪结果已保存:\n');
fprintf(' - tracking_results.mat (完整数据)\n');
fprintf(' - trajectories.csv (轨迹数据)\n');
end
%% 导出轨迹数据到CSV
function exportTrajectoriesToCSV(system)
% 创建表格数据
data = [];
for frameIdx = 1:length(system.trackingHistory)
if ~isempty(system.trackingHistory{frameIdx})
for trackIdx = 1:length(system.trackingHistory{frameIdx})
track = system.trackingHistory{frameIdx}{trackIdx};
if isfield(track, 'id') && isfield(track, 'centroid')
data = [data; struct(...
'Frame', frameIdx, ...
'TrackID', track.id, ...
'X', track.centroid(1), ...
'Y', track.centroid(2), ...
'Width', track.bbox(3), ...
'Height', track.bbox(4), ...
'Age', track.age, ...
'Hits', track.hits, ...
'TimeSinceUpdate', track.timeSinceUpdate)];
end
end
end
end
% 写入CSV文件
if ~isempty(data)
% 转换为表格
T = struct2table(data);
% 写入CSV
writetable(T, 'trajectories.csv');
end
end
%% 辅助函数:使用匈牙利算法分配检测与轨迹
function [assignments, unassignedTracks, unassignedDetections] = assignDetectionsToTracks(costMatrix, costOfNonAssignment)
% 简化版的匈牙利算法实现
numTracks = size(costMatrix, 1);
numDets = size(costMatrix, 2);
% 初始化输出
assignments = zeros(0, 2);
unassignedTracks = 1:numTracks;
unassignedDets = 1:numDets;
% 如果没有轨迹或检测,直接返回
if numTracks == 0 || numDets == 0
return;
end
% 构建扩展代价矩阵
costMatrixExtended = [costMatrix, ones(numTracks, 1)*costOfNonAssignment];
costMatrixExtended = [costMatrixExtended; ones(1, numDets+1)*costOfNonAssignment];
% 使用匈牙利算法求解
[assignment, cost] = hungarian(costMatrixExtended);
% 解析结果
for i = 1:size(assignment, 1)
if assignment(i, 1) <= numTracks && assignment(i, 2) <= numDets
trackIdx = assignment(i, 1);
detIdx = assignment(i, 2);
if costMatrix(trackIdx, detIdx) <= costOfNonAssignment
assignments = [assignments; trackIdx, detIdx];
% 从未分配列表中移除
unassignedTracks(unassignedTracks == trackIdx) = [];
unassignedDets(unassignedDets == detIdx) = [];
end
end
end
% 剩余的是未分配的轨迹和检测
unassignedTracks = [unassignedTracks; (numTracks+1):size(costMatrixExtended, 1)];
unassignedDets = [unassignedDets; (numDets+1):size(costMatrixExtended, 2)];
unassignedTracks(unassignedTracks > numTracks) = [];
unassignedDets(unassignedDets > numDets) = [];
end
%% 匈牙利算法实现(简化版)
function [assignment, cost] = hungarian(costMatrix)
% 简化实现 - 实际应用中应使用更高效版本
[numRows, numCols] = size(costMatrix);
assignment = zeros(min(numRows, numCols), 2);
cost = 0;
% 贪心匹配(实际应用中应使用完整匈牙利算法)
usedRows = false(numRows, 1);
usedCols = false(numCols, 1);
for k = 1:min(numRows, numCols)
minCost = inf;
minRow = 0;
minCol = 0;
for i = 1:numRows
if ~usedRows(i)
for j = 1:numCols
if ~usedCols(j) && costMatrix(i, j) < minCost
minCost = costMatrix(i, j);
minRow = i;
minCol = j;
end
end
end
end
if minCost < inf
assignment(k, :) = [minRow, minCol];
cost = cost + minCost;
usedRows(minRow) = true;
usedCols(minCol) = true;
end
end
end
%% 演示函数
function demoObjectTracking()
% 创建示例视频
createSampleVideo();
% 运行目标检测与跟踪
objectDetectionAndTracking();
end
%% 创建示例视频
function createSampleVideo()
% 创建视频写入器
videoFile = 'pedestrians.mp4';
writer = VideoWriter(videoFile, 'MPEG-4');
writer.FrameRate = 10;
open(writer);
% 创建背景
bg = zeros(480, 640, 3, 'uint8');
bg(:,:,1) = 100; % R
bg(:,:,2) = 120; % G
bg(:,:,3) = 150; % B
% 添加一些静态对象
bg(200:300, 100:150, :) = 200; % 矩形
bg(150:200, 400:500, 1) = 200; % 红色区域
% 创建移动目标
for frameIdx = 1:100
frame = bg;
% 目标1:从左向右移动
x1 = 50 + frameIdx * 5;
y1 = 200;
if x1 < 600
frame(y1:y1+50, x1:x1+30, 1) = 255; % 红色目标
end
% 目标2:从上向下移动
x2 = 300;
y2 = 50 + frameIdx * 3;
if y2 < 400
frame(y2:y2+40, x2:x2+40, 2) = 255; % 绿色目标
end
% 目标3:对角线移动
x3 = 100 + frameIdx * 4;
y3 = 100 + frameIdx * 2;
if x3 < 550 && y3 < 380
frame(y3:y3+60, x3:x3+30, 3) = 255; % 蓝色目标
end
% 添加噪声
noise = randn(480, 640, 3) * 10;
frame = uint8(double(frame) + noise);
% 写入帧
writeVideo(writer, frame);
end
% 关闭写入器
close(writer);
disp(['示例视频已创建: ', videoFile]);
end
程序功能说明
1. 系统初始化模块
- 参数配置(视频源、检测阈值、跟踪参数等)
- 背景建模器初始化(高斯混合模型)
- 卡尔曼滤波器参数配置
- 视频源初始化(文件或摄像头)
2. 视频处理流水线
-
前景检测:使用高斯混合模型分离前景和背景
-
目标检测:连通区域分析提取目标边界框和质心
-
目标跟踪:
- 卡尔曼滤波预测目标位置
- IOU匹配关联检测与轨迹
- 更新轨迹状态
- 创建新轨迹
- 移除旧轨迹
-
结果可视化:实时显示检测结果和跟踪轨迹
3. 轨迹分析模块
- 轨迹统计(总数、长度分布)
- 运动轨迹可视化
- 轨迹数据导出(CSV格式)
4. 辅助功能
- 示例视频生成
- 匈牙利算法实现(数据关联)
- IOU计算(交并比)
- 结果保存(MAT文件和CSV)
关键技术实现
1. 背景建模与目标检测
function foregroundMask = detectForeground(system, frame)
% 转换为灰度图像
grayFrame = rgb2gray(frame);
% 使用背景建模器检测前景
foregroundMask = step(system.backgroundModel, grayFrame);
% 形态学操作去除噪声
seOpen = strel('disk', 2);
seClose = strel('rectangle', [5, 5]);
foregroundMask = imopen(foregroundMask, seOpen);
foregroundMask = imclose(foregroundMask, seClose);
foregroundMask = imfill(foregroundMask, 'holes');
end
2. 卡尔曼滤波跟踪
function predictedTracks = predictTracks(system)
for i = 1:length(system.tracks)
track = system.tracks{i};
% 初始化卡尔曼滤波器
if isempty(track.kalmanFilter)
track.kalmanFilter = configureKalmanFilter(...
'ConstantVelocity', track.centroid, [100, 30], ...
system.params.kalmanMeasurementNoise, ...
system.params.kalmanProcessNoise);
end
% 预测下一步位置
predict(track.kalmanFilter);
predictedPos = track.kalmanFilter.State(1:2);
% 更新轨迹预测位置
track.predictedCentroid = predictedPos';
track.age = track.age + 1;
track.timeSinceUpdate = track.timeSinceUpdate + 1;
track.history = [track.history; predictedPos'];
predictedTracks{end+1} = track;
end
end
3. 数据关联(IOU匹配)
function [matchedPairs, unmatchedDetections, unmatchedTracks] = associateDetectionsToTracks(system, detections, tracks)
% 计算代价矩阵(IOU距离)
costMatrix = zeros(length(tracks), length(detections));
for i = 1:length(tracks)
track = tracks{i};
for j = 1:length(detections)
detection = detections(j);
costMatrix(i, j) = 1 - computeIOU(track.predictedBbox, detection.bbox);
end
end
% 使用匈牙利算法求解最优匹配
[assignment, cost] = assignDetectionsToTracks(costMatrix, system.params.iouThreshold);
% 解析匹配结果
matchedPairs = [];
for i = 1:size(assignment, 1)
trackIdx = assignment(i, 1);
detIdx = assignment(i, 2);
if trackIdx > 0 && detIdx > 0 && costMatrix(trackIdx, detIdx) <= system.params.iouThreshold
matchedPairs = [matchedPairs; trackIdx, detIdx];
end
end
end
4. 轨迹可视化
function visualizeResults(system, frame, detections, tracks)
displayFrame = frame;
% 绘制检测结果
for i = 1:length(detections)
bbox = detections(i).bbox;
displayFrame = insertShape(displayFrame, 'Rectangle', bbox, ...
'Color', 'yellow', 'LineWidth', 2);
end
% 绘制轨迹
for i = 1:length(tracks)
track = tracks{i};
displayFrame = insertShape(displayFrame, 'Rectangle', track.bbox, ...
'Color', track.color, 'LineWidth', 2);
% 绘制轨迹历史
if size(track.history, 1) > 1
for j = 2:size(track.history, 1)
pt1 = track.history(j-1, :);
pt2 = track.history(j, :);
displayFrame = insertShape(displayFrame, 'Line', ...
[pt1(1), pt1(2), pt2(1), pt2(2)], ...
'Color', track.color, 'LineWidth', 2);
end
end
end
imshow(displayFrame);
title(sprintf('目标检测与跟踪 (帧 %d)', system.frameCount));
end
算法原理与数学基础
1. 背景建模(高斯混合模型)
高斯混合模型(GMM)将每个像素表示为多个高斯分布的混合:
其中K是高斯分量数量,ω是权重,μ是均值,Σ是协方差矩阵。
2. 卡尔曼滤波
卡尔曼滤波是一种递归估计算法,包含预测和更新两个步骤:
预测步骤:
更新步骤:
3. 数据关联(匈牙利算法)
匈牙利算法解决指派问题,最小化总匹配成本:
约束条件:
4. IOU(交并比)
IOU衡量两个边界框的重叠程度:
其中B1和B2是两个边界框。
使用说明
1. 基本使用
% 运行主程序
objectDetectionAndTracking();
% 使用摄像头实时处理
system.params.useWebcam = true;
system.params.videoSource = '';
objectDetectionAndTracking();
2. 参数调整
% 调整检测参数
system.params.minBlobArea = 300; % 减小最小目标面积
system.params.maxBlobArea = 30000; % 增大最大目标面积
% 调整跟踪参数
system.params.maxTrackGap = 10; % 允许更长的轨迹中断
system.params.iouThreshold = 0.5; % 提高匹配阈值
% 调整卡尔曼滤波参数
system.params.kalmanProcessNoise = 0.01;
system.params.kalmanMeasurementNoise = 0.1;
3. 处理自定义视频
% 指定视频文件
system.params.videoSource = 'my_video.mp4';
system.params.useWebcam = false;
% 调整采样率(处理每N帧)
system.params.samplingRate = 2; % 处理每2帧
参考代码 基于帧图像序列的目标检测与跟踪 www.youwenfan.com/contentalh/96326.html
扩展功能
1. 深度学习目标检测
function detections = detectObjectsWithYOLO(system, frame)
% 使用预训练的YOLOv4模型
net = yolov4ObjectDetector('csp-darknet53-coco');
% 检测目标
[bboxes, scores, labels] = detect(net, frame);
% 转换为统一格式
detections = [];
for i = 1:size(bboxes, 1)
if scores(i) > 0.5 % 置信度阈值
detection = struct(...
'id', i, ...
'bbox', [bboxes(i,1), bboxes(i,2), bboxes(i,3)-bboxes(i,1), bboxes(i,4)-bboxes(i,2)], ...
'centroid', [(bboxes(i,1)+bboxes(i,3))/2, (bboxes(i,2)+bboxes(i,4))/2], ...
'area', prod(bboxes(i,3:4)-bboxes(i,1:2)), ...
'frame', system.frameCount, ...
'timestamp', tic, ...
'label', labels(i));
detections = [detections; detection];
end
end
end
2. 多目标跟踪算法(DeepSORT)
function tracks = deepSortTracking(system, detections, tracks)
% 提取外观特征(使用ReID模型)
features = extractReIDFeatures(detections);
% 预测轨迹位置(卡尔曼滤波)
predictedTracks = predictTracks(system);
% 级联匹配(优先考虑近期匹配的轨迹)
[matches, unmatchedTracks, unmatchedDetections] = cascadeMatching(...
predictedTracks, detections, features);
% 更新匹配的轨迹
updatedTracks = updateMatchedTracks(system, matches, detections, predictedTracks);
% 创建新轨迹
newTracks = createNewTracks(system, unmatchedDetections, detections);
% 合并轨迹
tracks = [updatedTracks; newTracks];
end
3. 轨迹预测与行为分析
function analyzeBehavior(tracks)
% 分析轨迹运动模式
for i = 1:length(tracks)
track = tracks{i};
positions = track.history;
% 计算速度和加速度
velocities = diff(positions);
accelerations = diff(velocities);
% 识别运动模式
if std(velocities(:,1)) < 0.5 && std(velocities(:,2)) < 0.5
behavior = '静止';
elseif std(velocities(:,1)) > 2*std(velocities(:,2))
behavior = '水平运动';
elseif std(velocities(:,2)) > 2*std(velocities(:,1))
behavior = '垂直运动';
else
behavior = '任意方向运动';
end
% 识别路径类型
if isStraightLine(positions)
pathType = '直线';
elseif isCircular(positions)
pathType = '圆形';
else
pathType = '复杂路径';
end
fprintf('轨迹 %d: 行为=%s, 路径=%s\n', track.id, behavior, pathType);
end
end
4. 实时性能优化
function optimizedProcessing(system)
% 使用GPU加速
if gpuDeviceCount > 0
system.backgroundModel.UseGPU = true;
end
% 降低分辨率
system.params.processingResolution = [320, 240];
% 跳帧处理
system.params.skipFrames = 2; % 每3帧处理1帧
% 并行处理
if isempty(gcp('nocreate'))
parpool('local', 4);
end
parfor frameIdx = 1:totalFrames
% 处理帧
end
end
常见问题
-
目标检测不准确
- 调整形态学操作参数(结构元素大小)
- 修改目标面积阈值
- 使用更高级的检测算法(如YOLO、SSD)
-
轨迹ID切换频繁
- 调整IOU匹配阈值
- 增加卡尔曼滤波器的过程噪声
- 实现轨迹平滑算法
-
处理速度慢
- 降低处理分辨率
- 跳帧处理
- 使用GPU加速
- 优化算法实现
-
漏检和误检
- 调整背景建模参数
- 添加阴影去除步骤
- 使用多特征融合(颜色、纹理等)
实际应用建议
-
智能监控系统
- 异常行为检测(徘徊、奔跑、打架)
- 人数统计和密度分析
- 入侵检测
-
自动驾驶
- 行人检测和跟踪
- 车辆检测和跟踪
- 交通流量分析
-
体育分析
- 球员跟踪和热力图
- 战术分析
- 动作识别
-
零售分析
- 顾客行为分析
- 货架关注度分析
- 排队检测