基于帧间差分法的运动目标检测 MATLAB 实现

使用 MATLAB 实现帧间差分法运动目标检测的完整代码,包含详细注释和可视化界面。

matlab 复制代码
function motion_detection_fd()
    % 基于帧间差分法的运动目标检测 MATLAB 实现

    % 日期: 2023-10-15
    
    % 创建主界面
    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;
        end
        
        videoPath = fullfile(pathname, filename);
        try
            videoObj = 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 ME
            errordlg(['无法读取视频文件: ' ME.message], '错误');
        end
    end

    % 开始检测回调函数
    function startDetection(~, ~)
        if isempty(videoObj)
            errordlg('请先加载视频文件', '错误');
            return;
        end
        
        if isPlaying
            return;
        end
        
        isPlaying = 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 isPlaying
            isPlaying = false;
            if ~isempty(timerObj) && isvalid(timerObj)
                stop(timerObj);
            end
            set(statusBar, 'String', '已暂停');
        end
    end

    % 停止检测回调函数
    function stopDetection(~, ~)
        isPlaying = false;
        if ~isempty(timerObj) && isvalid(timerObj)
            stop(timerObj);
            delete(timerObj);
            timerObj = [];
        end
        frameIndex = 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;
        end
        
        outputPath = fullfile(pathname, filename);
        try
            writerObj = VideoWriter(outputPath, 'Motion JPEG AVI');
            writerObj.FrameRate = videoObj.FrameRate;
            open(writerObj);
            
            for i = 1:length(results)
                writeVideo(writerObj, results(i).result);
            end
            
            close(writerObj);
            set(statusBar, 'String', ['结果已保存到: ' outputPath]);
        catch ME
            errordlg(['保存失败: ' ME.message], '错误');
        end
    end

    % 处理视频帧
    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 morphOp
            case 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 > minArea
                bbox = 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');
            end
        end
        
        % 更新结果显示
        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));
    end
    set(thresholdSlider, 'Callback', @thresholdSliderCallback);
    
    function minAreaSliderCallback(src, ~)
        value = round(get(src, 'Value'));
        set(minAreaText, 'String', num2str(value));
    end
    set(minAreaSlider, 'Callback', @minAreaSliderCallback);
end

使用说明

功能特点

  1. 完整的GUI界面:提供直观的用户界面,包含所有必要的控制选项
  2. 参数可调: 阈值:控制运动检测的灵敏度(1-100) 最小区域:过滤小面积噪声(10-2000像素) 形态学操作:选择不同的后处理方法(无、开运算、闭运算、开闭运算)
  3. 实时显示:同时显示原始帧、灰度图像、帧间差分、二值图像、形态学处理结果和最终检测结果
  4. 视频处理:支持加载和处理AVI、MP4、MOV等常见视频格式
  5. 结果保存:可将检测结果保存为视频文件

使用步骤

  1. 运行程序:motion_detection_fd()
  2. 点击"加载视频"按钮,选择要处理的视频文件
  3. 调整参数: 阈值:根据场景光照和运动速度调整 最小区域:根据目标大小调整 形态学操作:通常选择"开闭运算"效果最佳
  4. 点击"开始检测"按钮开始处理视频
  5. 处理过程中可随时暂停或停止
  6. 处理完成后,点击"保存结果"将检测结果保存为视频文件

算法原理

  1. 帧间差分:计算相邻两帧图像的绝对差值

    D(x,y)=∣Ik+1(x,y)−Ik(x,y)∣D(x,y)=∣I_{k+1}(x,y)−I_k(x,y)∣D(x,y)=∣Ik+1(x,y)−Ik(x,y)∣

  2. 二值化:设定阈值T,将差分图像转换为二值图像

    Bt(x,y)={255,if ∣It(x,y)−It−1(x,y)∣>T0,otherwiseB_t(x, y) = \begin{cases} 255, & \text{if } |I_t(x, y) - I_{t-1}(x, y)| > T \\ 0, & \text{otherwise} \end{cases}Bt(x,y)={255,0,if ∣It(x,y)−It−1(x,y)∣>Totherwise

  3. 形态学处理:使用开运算和闭运算去除噪声和填充空洞

  4. 目标标记:标记连通区域,过滤小面积区域,绘制边界框

参数调整建议

  1. 阈值: 低光照场景:降低阈值(10-20) 高对比度场景:提高阈值(30-50) 快速运动目标:提高阈值减少噪声
  2. 最小区域: 小型目标(行人):100-300像素 中型目标(车辆):500-1000像素 大型目标(卡车):1000-2000像素
  3. 形态学操作: 噪声较多:使用开运算 目标有空洞:使用闭运算 一般情况:使用开闭运算

参考代码 基于帧间差分法的运动目标检测 www.3dddown.com/csa/83299.html

扩展功能

如需进一步增强检测效果,可考虑:

  1. 添加背景建模(如高斯混合模型)
  2. 实现三帧差分法
  3. 结合光流法进行运动估计
  4. 添加目标跟踪功能
  5. 集成深度学习目标检测器
相关推荐
aini_lovee2 小时前
基于Jousselme距离改进D-S证据理论matlab实现
开发语言·算法·matlab
roman_日积跬步-终至千里3 小时前
【计算机视觉(19)】语义理解-CNN应用_目标检测_语义分割
目标检测·计算机视觉·cnn
迪菲赫尔曼3 小时前
YAML2ModelGraph【v1.0】:一键生成 Ultralytics 模型结构图
人工智能·yolo·目标检测·yolov5·yolov8·yolo11·结构图
这张生成的图像能检测吗4 小时前
(论文速读)LCT:用于RGB-D突出物体检测的轻型跨模态变压器
图像处理·目标检测·计算机视觉·深度估计·轻量化模型·跨模态融合·rgb-d
Lun3866buzha5 小时前
【深度学习】【目标检测】改进YOLOv11香烟包装识别与分类_CSP-PTB优化
深度学习·yolo·目标检测
AI浩6 小时前
MMOT:首个面向无人机多光谱多目标跟踪的挑战性基准
人工智能·目标跟踪·无人机
简简单单做算法7 小时前
基于PSO优化CNN-BiLSTM网络模型的多输入单输出回归预测算法matlab仿真
matlab·回归·cnn·回归预测·cnn-bilstm·pso-cnn-bilstm
Loacnasfhia97 小时前
YOLOv8-CSFCN风力发电机叶片表面缺陷检测与分类实现详解
yolo·目标跟踪·分类
超龄超能程序猿7 小时前
YOLOv8中分类与目标检测模型训练的对比
yolo·目标检测·分类