基于光流算法的运动物体检测与光流场分析(MATLAB实现)

MATLAB 代码,使用 Lucas-Kanade 光流法 计算视频序列中运动物体的光流场,并输出每个像素点的运动状态(速度矢量、方向角、运动幅度)。


一、算法原理概述

光流法通过连续帧间的像素强度变化估计运动场。Lucas-Kanade 法假设局部窗口内运动一致,通过最小二乘法求解光流方程:

Ixu+Iyv+It=0I_x u + I_y v + I_t = 0Ixu+Iyv+It=0

其中 Ix,IyI_x, I_yIx,Iy 为空间梯度,ItI_tIt为时间梯度,u,vu, vu,v为光流矢量。


二、 MATLAB 代码

2.1 主函数:optical_flow_demo.m

matlab 复制代码
%% 光流法检测运动物体并输出每个像素的运动状态
clear; clc; close all;

%% 1. 读取视频或图像序列
% 方式一:读取视频文件
vidObj = VideoReader('motion.avi'); % 替换为你的视频文件
% 方式二:生成模拟运动图像(用于演示)
% 这里使用内置的 shuttle 视频
if ~exist('vidObj','var')
    vidObj = VideoReader('shuttle.avi'); % MATLAB自带示例
end

% 读取前两帧
frame1 = readFrame(vidObj);
frame2 = readFrame(vidObj);

% 转为灰度图
if size(frame1,3)==3
    gray1 = rgb2gray(frame1);
    gray2 = rgb2gray(frame2);
else
    gray1 = frame1;
    gray2 = frame2;
end

%% 2. 计算光流(Lucas-Kanade 法)
% 参数设置
windowSize = 15;    % 局部窗口大小(奇数)
sigma = 2;          % 高斯平滑标准差
threshold = 0.01;   % 特征值阈值(过滤低纹理区域)

% 计算光流
[u, v] = lucas_kanade_flow(double(gray1), double(gray2), windowSize, sigma, threshold);

%% 3. 计算每个像素的运动状态
% 运动幅度(速度大小)
magnitude = sqrt(u.^2 + v.^2);
% 运动方向(弧度,范围 -π ~ π)
direction = atan2(v, u);
% 转换为角度(度)
direction_deg = rad2deg(direction);

%% 4. 可视化光流场
figure('Position',[100 100 1400 650]);

% 原图
subplot(2,3,1);
imshow(gray1); title('当前帧');

% 光流矢量图(稠密采样)
subplot(2,3,2);
imshow(gray1); hold on;
step = 8; % 每隔 step 像素显示一个箭头
[X, Y] = meshgrid(1:step:size(gray1,2), 1:step:size(gray1,1));
quiver(X, Y, u(1:step:end,1:step:end), v(1:step:end,1:step:end), ...
       2, 'r', 'LineWidth', 1);
title('光流矢量场');

% 运动幅度图
subplot(2,3,3);
imagesc(magnitude); axis image; colorbar;
colormap(jet); title('运动幅度');

% 运动方向图
subplot(2,3,4);
imagesc(direction_deg); axis image; colorbar;
colormap(hsv); title('运动方向(°)');

% 运动物体分割(幅度阈值)
subplot(2,3,5);
threshold_mag = 0.5; % 根据实际情况调整
moving_mask = magnitude > threshold_mag;
imshow(moving_mask); title('运动区域检测');

% 叠加显示
subplot(2,3,6);
imshow(gray1); hold on;
overlay = imoverlay(gray1, moving_mask, [1 0 0]);
imshow(overlay); title('运动物体标记(红色)');

sgtitle('Lucas-Kanade 光流分析','FontSize',14,'FontWeight','bold');

%% 5. 输出每个像素的运动状态(示例:前10×10区域)
fprintf('前10×10像素的运动状态(部分):\n');
fprintf('   (x,y)   |  u (pix/frame) |  v (pix/frame) |  速度幅值  |  方向角(°)\n');
for i = 1:min(10,size(u,1))
    for j = 1:min(10,size(u,2))
        fprintf('(%3d,%3d) | %12.4f | %12.4f | %10.4f | %8.2f\n', ...
                j, i, u(i,j), v(i,j), magnitude(i,j), direction_deg(i,j));
    end
end

2.2 Lucas-Kanade 光流函数:lucas_kanade_flow.m

matlab 复制代码
function [u, v] = lucas_kanade_flow(im1, im2, windowSize, sigma, threshold)
% Lucas-Kanade 稠密光流计算
% 输入:
%   im1, im2    - 连续两帧灰度图像(double类型)
%   windowSize  - 局部窗口大小(奇数,默认15)
%   sigma       - 高斯平滑标准差(默认2)
%   threshold   - 特征值阈值(默认0.01)
% 输出:
%   u, v        - 水平/垂直光流分量

if nargin < 3, windowSize = 15; end
if nargin < 4, sigma = 2; end
if nargin < 5, threshold = 0.01; end

% 高斯平滑预处理
h = fspecial('gaussian', max(3,ceil(2*sigma)), sigma);
im1 = imfilter(im1, h, 'replicate');
im2 = imfilter(im2, h, 'replicate');

% 计算空间梯度 Ix, Iy 和时间梯度 It
fx = [-1 1; -1 1]; % Sobel-like
fy = [-1 -1; 1 1];
ft = [1 1; 1 1];

Ix = imfilter(im1, fx, 'replicate') + imfilter(im2, fx, 'replicate');
Iy = imfilter(im1, fy, 'replicate') + imfilter(im2, fy, 'replicate');
It = imfilter(im1, ft, 'replicate') - imfilter(im2, ft, 'replicate');

% 初始化光流
u = zeros(size(im1));
v = zeros(size(im1));

half = floor(windowSize/2);

% 遍历每个像素
for i = 1+half : size(im1,1)-half
    for j = 1+half : size(im1,2)-half
        % 提取窗口内的梯度
        winIx = Ix(i-half:i+half, j-half:j+half);
        winIy = Iy(i-half:i+half, j-half:j+half);
        winIt = It(i-half:i+half, j-half:j+half);
        
        % 构建矩阵 A 和向量 b
        A = [winIx(:), winIy(:)];
        b = -winIt(:);
        
        % 求解最小二乘:A'*A * [u;v] = A'*b
        AtA = A' * A;
        Atb = A' * b;
        
        % 检查特征值(避免病态)
        eigenvalues = eig(AtA);
        if min(eigenvalues) > threshold
            flow = AtA \ Atb;
            u(i,j) = flow(1);
            v(i,j) = flow(2);
        else
            u(i,j) = 0;
            v(i,j) = 0;
        end
    end
end
end

2.3 辅助函数:imoverlay.m(用于叠加显示)

matlab 复制代码
function overlay = imoverlay(baseImg, mask, color)
% 将二值掩膜以指定颜色叠加到灰度图上
if nargin < 3, color = [1 0 0]; end % 默认红色
if size(baseImg,3)==1
    baseImg = repmat(baseImg, [1 1 3]);
end
overlay = baseImg;
for c = 1:3
    channel = overlay(:,:,c);
    channel(mask) = color(c) * 255;
    overlay(:,:,c) = channel;
end
overlay = uint8(overlay);
end

三、运行结果说明

3.1 输出内容

  • 光流矢量图:红色箭头表示运动方向和速度大小
  • 运动幅度图:颜色越亮表示运动越快
  • 运动方向图:HSV色环表示方向(0°红,90°黄,180°青,270°紫)
  • 运动区域检测:二值掩膜,白色为运动区域
  • 运动物体标记:红色覆盖在原图上
  • 命令行输出:前100个像素的详细运动状态(u, v, 幅值, 方向角)

3.2 参数调优建议

参数 作用 建议值
windowSize 局部窗口大小 11~21(运动剧烈用小窗口,平滑运动用大窗口)
sigma 高斯平滑程度 1~3(噪声大时增大)
threshold 特征值阈值 0.001~0.1(纹理丰富区域可设小,平坦区域设大)
threshold_mag 运动分割阈值 根据运动幅度动态调整(可用Otsu自动阈值)

四、扩展功能

4.1 使用 Horn-Schunck 全局光流法(备选)

若需要全局平滑约束,可替换为 Horn-Schunck 算法:

matlab 复制代码
function [u, v] = horn_schunck_flow(im1, im2, alpha, iterations)
% alpha: 平滑权重(越大越平滑)
% iterations: 迭代次数
% ...(实现略,可参考标准算法)

4.2 运动状态统计分析

matlab 复制代码
% 统计运动区域的平均速度和方向
moving = magnitude > threshold_mag;
mean_speed = mean(magnitude(moving));
mean_dir = mean(direction_deg(moving));
fprintf('运动区域平均速度: %.2f pix/frame\n', mean_speed);
fprintf('运动区域平均方向: %.2f°\n', mean_dir);

4.3 光流轨迹追踪

matlab 复制代码
% 使用累积光流追踪特征点轨迹
% 可结合 KLT 特征点追踪

参考代码 基于光流算法,检测运动物体的光流场 www.youwenfan.com/contentcsv/80977.html

五、注意事项

  1. 图像预处理:建议先进行直方图均衡化或光照归一化,减少光照变化影响。
  2. 多尺度处理 :对大运动(>1像素/帧),应采用金字塔 Lucas-Kanade 法(cv2.calcOpticalFlowPyrLK 类似)。
  3. 边缘处理:窗口靠近图像边界时,会自动缩小窗口(上述代码已处理边界)。
  4. 性能优化 :对于大尺寸图像,可先用 imresize 降采样再计算光流。
相关推荐
开源Z1 小时前
LeetCode 42 · 接雨水:从暴力到双指针的三步优化
算法·leetcode
旖-旎1 小时前
《LeetCode 695 岛屿的最大面积 FloodFill DFS 解法》
c++·算法·力扣·深度优先遍历·floodfill
syagain_zsx2 小时前
STL 之 vector 讲练结合
c++·算法
聚名网2 小时前
域名net,com,cn有区别吗?有哪些不同呢?
服务器·开发语言·php
牛油果子哥q2 小时前
STL set与map底层精讲,红黑树适配原理、有序去重特性、迭代器遍历、API实战与面试核心考点全解
开发语言·数据结构·c++·面试
foundbug9992 小时前
直流电机 PID 速度控制 MATLAB 仿真程序
开发语言·matlab
MartinYeung53 小时前
[论文学习]DP2Unlearning:高效且具保证的大型语言模型遗忘框架(基于差分隐私的 LLM Unlearning 方法)
学习·算法·语言模型
Tian_Hang3 小时前
C++原型模式(Protype)
开发语言·c++·算法
bIo7lyA8v3 小时前
算法复杂度的渐进分析与实际运行时间的差异的技术8
算法