使用 MATLAB 实现帧间差分法运动目标检测的完整代码,包含详细注释和可视化界面。
function motion_detection_fd()% 基于帧间差分法的运动目标检测 MATLAB 实现% 创建主界面fig = figure('Name', '帧间差分法运动检测', 'NumberTitle', 'off', ...'Position', [100, 100, 1200, 700], 'MenuBar', 'none');% 创建控制面板controlPanel = uipanel('Title', '控制面板', 'Position', [0.02, 0.02, 0.3, 0.96], ...'BackgroundColor', [0.9, 0.9, 0.9]);% 创建参数控件uicontrol(controlPanel, 'Style', 'text', 'String', '阈值:', ...'Position', [20, 550, 80, 20], 'HorizontalAlignment', 'left');thresholdSlider = uicontrol(controlPanel, 'Style', 'slider', ...'Min', 1, 'Max', 100, 'Value', 30, ...'Position', [100, 550, 150, 20]);thresholdText = uicontrol(controlPanel, 'Style', 'text', ...'String', '30', 'Position', [260, 550, 40, 20]);uicontrol(controlPanel, 'Style', 'text', 'String', '最小区域:', ...'Position', [20, 500, 80, 20], 'HorizontalAlignment', 'left');minAreaSlider = uicontrol(controlPanel, 'Style', 'slider', ...'Min', 10, 'Max', 2000, 'Value', 500, ...'Position', [100, 500, 150, 20]);minAreaText = uicontrol(controlPanel, 'Style', 'text', ...'String', '500', 'Position', [260, 500, 40, 20]);uicontrol(controlPanel, 'Style', 'text', 'String', '形态学操作:', ...'Position', [20, 450, 80, 20], 'HorizontalAlignment', 'left');morphOps = {'无', '开运算', '闭运算', '开闭运算'};morphPopup = uicontrol(controlPanel, 'Style', 'popupmenu', ...'String', morphOps, 'Value', 4, ...'Position', [100, 450, 150, 20]);uicontrol(controlPanel, 'Style', 'pushbutton', 'String', '加载视频', ...'Position', [20, 400, 100, 30], 'Callback', @loadVideo);uicontrol(controlPanel, 'Style', 'pushbutton', 'String', '开始检测', ...'Position', [130, 400, 100, 30], 'Callback', @startDetection);uicontrol(controlPanel, 'Style', 'pushbutton', 'String', '暂停', ...'Position', [20, 350, 100, 30], 'Callback', @pauseDetection);uicontrol(controlPanel, 'Style', 'pushbutton', 'String', '停止', ...'Position', [130, 350, 100, 30], 'Callback', @stopDetection);uicontrol(controlPanel, 'Style', 'pushbutton', 'String', '保存结果', ...'Position', [20, 300, 210, 30], 'Callback', @saveResults);% 创建结果显示区域resultPanel = uipanel('Title', '检测结果', 'Position', [0.34, 0.52, 0.64, 0.46], ...'BackgroundColor', 'white');ax1 = subplot(2, 3, 1, 'Parent', resultPanel);title(ax1, '原始帧');ax2 = subplot(2, 3, 2, 'Parent', resultPanel);title(ax2, '灰度图像');ax3 = subplot(2, 3, 3, 'Parent', resultPanel);title(ax3, '帧间差分');ax4 = subplot(2, 3, 4, 'Parent', resultPanel);title(ax4, '二值图像');ax5 = subplot(2, 3, 5, 'Parent', resultPanel);title(ax5, '形态学处理');ax6 = subplot(2, 3, 6, 'Parent', resultPanel);title(ax6, '检测结果');% 创建状态栏statusBar = uicontrol('Style', 'text', 'String', '就绪', ...'Position', [0.34, 0.02, 0.64, 0.04], ...'HorizontalAlignment', 'left', ...'BackgroundColor', [0.8, 0.8, 0.8]);% 初始化变量videoObj = [];currentFrame = [];prevFrame = [];isPlaying = false;timerObj = [];results = struct('original', {}, 'gray', {}, 'diff', {}, ...'thresh', {}, 'morph', {}, 'result', {});frameIndex = 0;% 加载视频回调函数function loadVideo(~, ~)[filename, pathname] = uigetfile({'*.avi;*.mp4;*.mov', '视频文件 (*.avi, *.mp4, *.mov)'}, ...'选择视频文件');if isequal(filename, 0)return;endvideoPath = fullfile(pathname, filename);tryvideoObj = VideoReader(videoPath);set(statusBar, 'String', ['已加载视频: ' filename]);% 读取第一帧prevFrame = readFrame(videoObj);frameIndex = 1;% 显示第一帧axes(ax1);imshow(prevFrame);title(ax1, '原始帧');axes(ax2);grayImg = rgb2gray(prevFrame);imshow(grayImg, []);title(ax2, '灰度图像');catch MEerrordlg(['无法读取视频文件: ' ME.message], '错误');endend% 开始检测回调函数function startDetection(~, ~)if isempty(videoObj)errordlg('请先加载视频文件', '错误');return;endif isPlayingreturn;endisPlaying = true;set(statusBar, 'String', '检测中...');% 获取参数值threshold = get(thresholdSlider, 'Value');minArea = get(minAreaSlider, 'Value');morphOp = get(morphPopup, 'Value');% 创建定时器处理视频帧timerObj = timer('ExecutionMode', 'fixedRate', 'Period', 0.05, ...'TimerFcn', @processFrame);start(timerObj);end% 暂停检测回调函数function pauseDetection(~, ~)if isPlayingisPlaying = false;if ~isempty(timerObj) && isvalid(timerObj)stop(timerObj);endset(statusBar, 'String', '已暂停');endend% 停止检测回调函数function stopDetection(~, ~)isPlaying = false;if ~isempty(timerObj) && isvalid(timerObj)stop(timerObj);delete(timerObj);timerObj = [];endframeIndex = 0;results = struct('original', {}, 'gray', {}, 'diff', {}, ...'thresh', {}, 'morph', {}, 'result', {});set(statusBar, 'String', '已停止');end% 保存结果回调函数function saveResults(~, ~)if isempty(results)errordlg('没有可保存的结果', '错误');return;end[filename, pathname] = uiputfile('motion_detection_results.avi', '保存结果视频');if isequal(filename, 0)return;endoutputPath = fullfile(pathname, filename);trywriterObj = VideoWriter(outputPath, 'Motion JPEG AVI');writerObj.FrameRate = videoObj.FrameRate;open(writerObj);for i = 1:length(results)writeVideo(writerObj, results(i).result);endclose(writerObj);set(statusBar, 'String', ['结果已保存到: ' outputPath]);catch MEerrordlg(['保存失败: ' ME.message], '错误');endend% 处理视频帧function processFrame(~, ~)if ~hasFrame(videoObj)stopDetection();return;end% 读取当前帧currentFrame = readFrame(videoObj);frameIndex = frameIndex + 1;% 获取参数值threshold = get(thresholdSlider, 'Value');minArea = get(minAreaSlider, 'Value');morphOp = get(morphPopup, 'Value');% 转换为灰度图像grayPrev = rgb2gray(prevFrame);grayCurr = rgb2gray(currentFrame);% 计算帧间差分diffImg = imabsdiff(grayCurr, grayPrev);% 二值化处理threshImg = imbinarize(diffImg, threshold/255);% 形态学处理se = strel('rectangle', [5, 5]);switch morphOpcase 1 % 无morphImg = threshImg;case 2 % 开运算morphImg = imopen(threshImg, se);case 3 % 闭运算morphImg = imclose(threshImg, se);case 4 % 开闭运算temp = imopen(threshImg, se);morphImg = imclose(temp, se);end% 标记运动目标labeledImg = bwlabel(morphImg);stats = regionprops(labeledImg, 'BoundingBox', 'Area');% 在原图上绘制检测结果resultImg = currentFrame;for i = 1:length(stats)if stats(i).Area > minAreabbox = stats(i).BoundingBox;resultImg = insertShape(resultImg, 'Rectangle', bbox, ...'LineWidth', 2, 'Color', 'green');resultImg = insertText(resultImg, [bbox(1), bbox(2)-20], ...'运动目标', 'FontSize', 12, ...'BoxColor', 'yellow', 'TextColor', 'black');endend% 更新结果显示axes(ax1);imshow(currentFrame);title(ax1, sprintf('原始帧 (%d/%d)', frameIndex, videoObj.NumFrames));axes(ax2);imshow(grayCurr, []);title(ax2, '灰度图像');axes(ax3);imshow(diffImg, []);title(ax3, '帧间差分');axes(ax4);imshow(threshImg);title(ax4, '二值图像');axes(ax5);imshow(morphImg);title(ax5, '形态学处理');axes(ax6);imshow(resultImg);title(ax6, '检测结果');% 保存结果results(end+1) = struct('original', currentFrame, ...'gray', grayCurr, ...'diff', diffImg, ...'thresh', threshImg, ...'morph', morphImg, ...'result', resultImg);% 更新前一帧prevFrame = currentFrame;% 更新状态栏set(statusBar, 'String', sprintf('处理中: 帧 %d/%d, 检测到 %d 个目标', ...frameIndex, videoObj.NumFrames, length(stats)));end% 滑块回调函数function thresholdSliderCallback(src, ~)value = round(get(src, 'Value'));set(thresholdText, 'String', num2str(value));endset(thresholdSlider, 'Callback', @thresholdSliderCallback);function minAreaSliderCallback(src, ~)value = round(get(src, 'Value'));set(minAreaText, 'String', num2str(value));endset(minAreaSlider, 'Callback', @minAreaSliderCallback);
end
使用说明
功能特点
- 完整的GUI界面:提供直观的用户界面,包含所有必要的控制选项
- 参数可调: 阈值:控制运动检测的灵敏度(1-100) 最小区域:过滤小面积噪声(10-2000像素) 形态学操作:选择不同的后处理方法(无、开运算、闭运算、开闭运算)
- 实时显示:同时显示原始帧、灰度图像、帧间差分、二值图像、形态学处理结果和最终检测结果
- 视频处理:支持加载和处理AVI、MP4、MOV等常见视频格式
- 结果保存:可将检测结果保存为视频文件
使用步骤
- 运行程序:
motion_detection_fd() - 点击"加载视频"按钮,选择要处理的视频文件
- 调整参数: 阈值:根据场景光照和运动速度调整 最小区域:根据目标大小调整 形态学操作:通常选择"开闭运算"效果最佳
- 点击"开始检测"按钮开始处理视频
- 处理过程中可随时暂停或停止
- 处理完成后,点击"保存结果"将检测结果保存为视频文件
算法原理
-
帧间差分:计算相邻两帧图像的绝对差值
\(D(x,y)=∣I_{k+1}(x,y)−I_k(x,y)∣\)
-
二值化:设定阈值T,将差分图像转换为二值图像
\(B_t(x, y) = \begin{cases} 255, & \text{if } |I_t(x, y) - I_{t-1}(x, y)| > T \\ 0, & \text{otherwise} \end{cases}\)
-
形态学处理:使用开运算和闭运算去除噪声和填充空洞
-
目标标记:标记连通区域,过滤小面积区域,绘制边界框
参数调整建议
- 阈值: 低光照场景:降低阈值(10-20) 高对比度场景:提高阈值(30-50) 快速运动目标:提高阈值减少噪声
- 最小区域: 小型目标(行人):100-300像素 中型目标(车辆):500-1000像素 大型目标(卡车):1000-2000像素
- 形态学操作: 噪声较多:使用开运算 目标有空洞:使用闭运算 一般情况:使用开闭运算
参考代码 基于帧间差分法的运动目标检测 www.3dddown.com/cnb/83299.html
扩展功能
如需进一步增强检测效果,可考虑:
- 添加背景建模(如高斯混合模型)
- 实现三帧差分法
- 结合光流法进行运动估计
- 添加目标跟踪功能
- 集成深度学习目标检测器
