Brox光流算法MATLAB实现

Brox光流算法MATLAB实现

Brox光流算法是一种经典的变分光流计算方法,由Thomas Brox等人于2004年提出。该算法通过最小化能量函数来估计光流场,能量函数包括数据项和平滑项。

classdef BroxOpticalFlow
    % Brox光流算法实现
    % 基于变分光流法的稠密光流估计
    
    properties
        alpha;           % 平滑项权重
        gamma;          % 梯度守恒权重
        omega;          % 耦合项权重
        maxIter;        % 最大迭代次数
        small;          % 小常数避免除零
        pyramidLevels;  % 金字塔层数
        warping;        % 是否使用图像扭曲
    end
    
    methods
        function obj = BroxOpticalFlow(alpha, gamma, omega, maxIter, pyramidLevels)
            % 构造函数
            % 输入参数:
            %   alpha - 平滑项权重 (默认0.001)
            %   gamma - 梯度守恒权重 (默认0.75)
            %   omega - 耦合项权重 (默认0.75)
            %   maxIter - 最大迭代次数 (默认200)
            %   pyramidLevels - 金字塔层数 (默认4)
            if nargin < 1 || isempty(alpha), alpha = 0.001; end
            if nargin < 2 || isempty(gamma), gamma = 0.75; end
            if nargin < 3 || isempty(omega), omega = 0.75; end
            if nargin < 4 || isempty(maxIter), maxIter = 200; end
            if nargin < 5 || isempty(pyramidLevels), pyramidLevels = 4; end
            
            obj.alpha = alpha;
            obj.gamma = gamma;
            obj.omega = omega;
            obj.maxIter = maxIter;
            obj.pyramidLevels = pyramidLevels;
            obj.small = 1e-10;  % 避免除零的小常数
            obj.warping = true; % 默认使用图像扭曲
        end
        
        function flowField = computeFlow(obj, img1, img2)
            % 计算两幅图像间的光流场
            % 输入:
            %   img1, img2 - 输入图像 (灰度图或RGB图)
            % 输出:
            %   flowField - 光流场 (H×W×2矩阵, [u,v])
            
            % 转换为灰度图像
            if size(img1, 3) == 3
                img1_gray = rgb2gray(img1);
                img2_gray = rgb2gray(img2);
            else
                img1_gray = img1;
                img2_gray = img2;
            end
            
            % 归一化图像到[0,255]
            img1_gray = im2double(img1_gray) * 255;
            img2_gray = im2double(img2_gray) * 255;
            
            % 构建图像金字塔
            pyramid1 = obj.buildPyramid(img1_gray);
            pyramid2 = obj.buildPyramid(img2_gray);
            
            % 初始化光流场
            flowField = zeros([size(pyramid1{end}), 2]);
            
            % 从最粗层开始计算光流
            for level = obj.pyramidLevels:-1:1
                fprintf('Processing pyramid level %d/%d...\n', level, obj.pyramidLevels);
                
                % 获取当前层的图像
                I1 = pyramid1{level};
                I2 = pyramid2{level};
                
                % 如果是第一层(最粗层),使用初始光流场(零)
                if level < obj.pyramidLevels
                    % 放大光流场到当前分辨率
                    flowField = obj.upscaleFlow(flowField, size(I1));
                end
                
                % 迭代优化光流场
                flowField = obj.iterativeRefinement(I1, I2, flowField);
            end
        end
        
        function pyramid = buildPyramid(obj, img)
            % 构建高斯金字塔
            pyramid = cell(1, obj.pyramidLevels);
            pyramid{1} = img;
            
            for i = 2:obj.pyramidLevels
                pyramid{i} = imresize(pyramid{i-1}, 0.5, 'bilinear');
            end
        end
        
        function flowField = upscaleFlow(obj, flow, newSize)
            % 放大光流场到新尺寸
            scale = newSize(1) / size(flow, 1);
            flowScaled = imresize(flow, scale, 'bilinear');
            
            % 调整光流向量大小(乘以缩放因子)
            flowScaled(:,:,1) = flowScaled(:,:,1) * scale;
            flowScaled(:,:,2) = flowScaled(:,:,2) * scale;
            
            flowField = flowScaled;
        end
        
        function refinedFlow = iterativeRefinement(obj, I1, I2, initFlow)
            % 迭代优化光流场
            % 输入:
            %   I1, I2 - 当前金字塔层的图像
            %   initFlow - 初始光流场
            % 输出:
            %   refinedFlow - 优化后的光流场
            
            flow = initFlow;
            [height, width, ~] = size(I1);
            
            for iter = 1:obj.maxIter
                % 图像扭曲(可选)
                if obj.warping
                    I2_warped = obj.warpImage(I2, flow);
                else
                    I2_warped = I2;
                end
                
                % 计算图像梯度
                [Ix, Iy, It] = obj.computeDerivatives(I1, I2_warped);
                
                % 计算梯度幅值
                gradMag = sqrt(Ix.^2 + Iy.^2 + obj.small);
                
                % 计算数据项和平滑项
                u = flow(:,:,1);
                v = flow(:,:,2);
                
                % 计算光流导数
                u_x = obj.computeDerivative(u, 'x');
                u_y = obj.computeDerivative(u, 'y');
                v_x = obj.computeDerivative(v, 'x');
                v_y = obj.computeDerivative(v, 'y');
                
                % 数据项
                T = Ix.*u + Iy.*v + It;
                dataTerm = (Ix.^2 + Iy.^2) .* (u.^2 + v.^2) ./ (gradMag.^2 + obj.small);
                
                % 平滑项
                smoothU = obj.alpha^2 * (u_x.^2 + u_y.^2);
                smoothV = obj.alpha^2 * (v_x.^2 + v_y.^2);
                
                % 耦合项
                couplingTerm = obj.omega^2 * ((u_x.*v_x + u_y.*v_y) ./ (gradMag.^2 + obj.small));
                
                % 总变分
                variationU = (Ix.*T - Ix.*Ix.*u - Ix.*Iy.*v) ./ (gradMag.^2 + obj.small) + ...
                            obj.alpha^2 * obj.laplacian(u) + ...
                            obj.omega^2 * (v_x.^2 + v_y.^2) .* u ./ (gradMag.^2 + obj.small);
                
                variationV = (Iy.*T - Iy.*Ix.*u - Iy.*Iy.*v) ./ (gradMag.^2 + obj.small) + ...
                            obj.alpha^2 * obj.laplacian(v) + ...
                            obj.omega^2 * (u_x.^2 + u_y.^2) .* v ./ (gradMag.^2 + obj.small);
                
                % 更新光流
                du = variationU;
                dv = variationV;
                
                % 正则化更新
                du = obj.gaussianBlur(du, 3);
                dv = obj.gaussianBlur(dv, 3);
                
                % 应用更新
                u = u - du;
                v = v - dv;
                
                % 更新光流场
                flow = cat(3, u, v);
                
                % 可选:显示迭代进度
                if mod(iter, 20) == 0
                    fprintf('Iteration %d/%d\n', iter, obj.maxIter);
                end
            end
            
            refinedFlow = flow;
        end
        
        function [Ix, Iy, It] = computeDerivatives(obj, I1, I2)
            % 计算图像空间和时间导数
            % 使用Sobel算子计算空间梯度
            [dx, dy] = gradient(double(I1));
            Ix = dx;
            Iy = dy;
            
            % 时间梯度
            It = double(I2) - double(I1);
        end
        
        function derivative = computeDerivative(obj, img, direction)
            % 计算图像的导数
            % 输入:
            %   img - 输入图像
            %   direction - 'x'或'y',分别表示水平和垂直方向
            % 输出:
            %   derivative - 导数图像
            
            kernel = [1, -1]/2; % 简单差分核
            
            if direction == 'x'
                derivative = conv2(img, kernel, 'same');
            elseif direction == 'y'
                derivative = conv2(img, kernel', 'same');
            else
                error('Invalid direction. Use ''x'' or ''y''.');
            end
        end
        
        function lap = laplacian(obj, img)
            % 计算图像的拉普拉斯
            kernel = [0, 1, 0; 1, -4, 1; 0, 1, 0];
            lap = conv2(img, kernel, 'same');
        end
        
        function blurred = gaussianBlur(obj, img, kernelSize)
            % 高斯模糊
            if nargin < 3
                kernelSize = 5;
            end
            
            sigma = kernelSize/6;
            kernel = fspecial('gaussian', kernelSize, sigma);
            blurred = conv2(img, kernel, 'same');
        end
        
        function warped = warpImage(obj, img, flow)
            % 根据光流场扭曲图像
            [height, width, ~] = size(img);
            [X, Y] = meshgrid(1:width, 1:height);
            
            % 计算扭曲后的坐标
            X_warped = X + flow(:,:,1);
            Y_warped = Y + flow(:,:,2);
            
            % 边界处理
            X_warped = max(1, min(width, X_warped));
            Y_warped = max(1, min(height, Y_warped));
            
            % 双线性插值
            warped = interp2(X, Y, double(img), X_warped, Y_warped, 'bilinear');
            warped = cast(warped, class(img));
        end
        
        function visualizeFlow(obj, flow, scale)
            % 可视化光流场
            % 输入:
            %   flow - 光流场 (H×W×2)
            %   scale - 可视化缩放因子 (可选)
            
            if nargin < 2 || isempty(scale)
                scale = 1;
            end
            
            u = flow(:,:,1) * scale;
            v = flow(:,:,2) * scale;
            
            % 计算光流幅值和角度
            magnitude = sqrt(u.^2 + v.^2);
            angle = atan2(v, u);
            
            % 创建HSV图像
            hsv = zeros([size(u), 3]);
            hsv(:,:,1) = angle/(2*pi) + 0.5;  % 色调表示方向
            hsv(:,:,2) = 1;                    % 饱和度全满
            hsv(:,:,3) = min(magnitude/10, 1);  % 明度表示速度
            
            % 转换为RGB
            rgb = hsv2rgb(hsv);
            
            % 显示结果
            figure;
            imshow(rgb);
            title('光流场可视化');
        end
        
        function quiverPlot(obj, flow, step)
            % 绘制光流场的箭头图
            % 输入:
            %   flow - 光流场 (H×W×2)
            %   step - 采样步长 (可选)
            
            if nargin < 2 || isempty(step)
                step = 10;
            end
            
            [height, width, ~] = size(flow);
            [X, Y] = meshgrid(1:step:width, 1:step:height);
            
            u = flow(1:step:end, 1:step:end, 1);
            v = flow(1:step:end, 1:step:end, 2);
            
            figure;
            quiver(X, Y, u, v, 2, 'color', 'b');
            axis equal;
            title('光流场箭头图');
            xlabel('X');
            ylabel('Y');
        end
        
        function plotMotionVectors(obj, flow, skip)
            % 绘制运动矢量图
            % 输入:
            %   flow - 光流场 (H×W×2)
            %   skip - 跳过的像素数 (可选)
            
            if nargin < 2 || isempty(skip)
                skip = 5;
            end
            
            [height, width, ~] = size(flow);
            skip = max(1, skip); % 确保至少为1
            
            figure;
            imshow(zeros(height, width), 'InitialMagnification', 'fit');
            hold on;
            
            for y = 1:skip:height
                for x = 1:skip:width
                    u = flow(y, x, 1);
                    v = flow(y, x, 2);
                    
                    % 只绘制显著的运动
                    if sqrt(u^2 + v^2) > 0.5
                        quiver(x, y, u, v, 'AutoScale', 'off', 'Color', 'r', 'LineWidth', 1);
                    end
                end
            end
            
            title('运动矢量图');
            axis on;
            hold off;
        end
        
        function runDemo()
            % 运行演示
            fprintf('Brox光流算法演示\n');
            
            % 加载示例图像序列
            videoFile = 'traffic.mp4';
            if exist(videoFile, 'file')
                vid = VideoReader(videoFile);
                frame1 = readFrame(vid);
                frame2 = readFrame(vid);
            else
                % 如果没有视频文件,使用内置图像
                fprintf('未找到视频文件,使用内置图像序列\n');
                frame1 = imread('car1.jpg');
                frame2 = imread('car2.jpg');
            end
            
            % 转换为灰度
            if size(frame1, 3) == 3
                gray1 = rgb2gray(frame1);
                gray2 = rgb2gray(frame2);
            else
                gray1 = frame1;
                gray2 = frame2;
            end
            
            % 创建Brox光流计算器
            brox = BroxOpticalFlow(0.001, 0.75, 0.75, 100, 4);
            
            % 计算光流
            tic;
            flow = brox.computeFlow(gray1, gray2);
            elapsedTime = toc;
            fprintf('光流计算完成,耗时 %.2f 秒\n', elapsedTime);
            
            % 可视化结果
            figure('Name', 'Brox光流算法演示', 'Position', [100, 100, 1200, 500]);
            
            subplot(1,3,1);
            imshow(frame1);
            title('第一帧');
            
            subplot(1,3,2);
            imshow(frame2);
            title('第二帧');
            
            subplot(1,3,3);
            brox.visualizeFlow(flow, 5);
            title('光流场可视化');
            
            % 绘制箭头图
            figure;
            brox.quiverPlot(flow, 15);
            title('光流场箭头图');
            
            % 计算并显示运动统计
            u = flow(:,:,1);
            v = flow(:,:,2);
            magnitude = sqrt(u.^2 + v.^2);
            
            fprintf('\n光流统计信息:\n');
            fprintf('平均运动幅度: %.2f 像素\n', mean(magnitude(:)));
            fprintf('最大运动幅度: %.2f 像素\n', max(magnitude(:)));
            fprintf('运动方向范围: %.1f° 到 %.1f°\n', ...
                min(angle(u(:) + 1i*v(:)))*180/pi, max(angle(u(:) + 1i*v(:)))*180/pi);
        end
    end
end

使用示例

1. 基本使用

% 读取两幅图像
img1 = imread('frame1.png');
img2 = imread('frame2.png');

% 创建Brox光流计算器
brox = BroxOpticalFlow(); % 使用默认参数

% 计算光流
flow = brox.computeFlow(img1, img2);

% 可视化结果
brox.visualizeFlow(flow, 3); % 缩放因子为3

2. 自定义参数

% 创建自定义参数的Brox光流计算器
brox = BroxOpticalFlow(0.002, 0.8, 0.7, 150, 5);

% 计算光流
flow = brox.computeFlow(img1, img2);

% 绘制箭头图
brox.quiverPlot(flow, 10); % 步长为10像素

3. 处理视频序列

% 打开视频文件
video = VideoReader('traffic.mp4');

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

% 计算光流
brox = BroxOpticalFlow();
flow = brox.computeFlow(frame1, frame2);

% 可视化
figure;
subplot(1,3,1); imshow(frame1); title('帧1');
subplot(1,3,2); imshow(frame2); title('帧2');
subplot(1,3,3); brox.visualizeFlow(flow); title('光流');

4. 运行演示

% 运行内置演示
BroxOpticalFlow.runDemo();

参考代码 Brox光流算法matlab源码 www.youwenfan.com/contentcns/112974.html

算法原理

1. 能量函数

Brox光流算法通过最小化以下能量函数来估计光流场:

其中:

2. 迭代优化

算法使用迭代方法求解能量函数的最小值:

  1. 初始化光流场为零

  2. 在每一层金字塔上:

    a. 使用当前光流场扭曲第二幅图像

    b. 计算图像梯度

    c. 迭代更新光流场

  3. 将光流场传递到更精细的层

3. 多尺度策略

使用图像金字塔处理大位移:

参数调优指南

参数 物理意义 典型值 调整建议
alpha 平滑项权重 0.001-0.1 增大值增强平滑,减少噪声但可能过度平滑
gamma 梯度守恒权重 0.5-0.9 增大值更严格保持梯度,可能减少运动模糊
omega 耦合项权重 0.5-0.9 增大值增强u和v分量的耦合
maxIter 最大迭代次数 50-500 复杂运动需要更多迭代
pyramidLevels 金字塔层数 3-6 大位移需要更多层

应用场景

  1. 视频稳定

    • 估计相机运动
    • 补偿抖动
    • 创建稳定视频
  2. 运动检测

    • 背景分离
    • 运动物体跟踪
    • 异常行为检测
  3. 视频压缩

    • 运动补偿编码
    • 帧间预测
    • 码率控制
  4. 目标跟踪

    • 特征点跟踪
    • 目标轨迹预测
    • 行为分析
  5. 三维重建

    • 立体视觉
    • 结构光扫描
    • 运动恢复结构(SFM)

常见问题解决

  1. 大位移处理

    • 增加金字塔层数
    • 增大最大迭代次数
    • 使用更粗的初始光流
  2. 光照变化

    • 在预处理阶段进行光照归一化
    • 使用更鲁棒的相似性度量
    • 增加gamma值
  3. 运动模糊

    • 使用多帧信息
    • 增加alpha值
    • 使用去模糊预处理
  4.  

    计算速度慢

    • 减少金字塔层数
    • 降低图像分辨率
    • 使用GPU加速

扩展功能

1. 鲁棒性增强

function [Ix, Iy, It] = computeRobustDerivatives(obj, I1, I2)
    % 使用鲁棒方法计算导数
    % 使用双边滤波代替高斯滤波
    I1_smooth = bfilter2(I1, 5, 0.1);
    I2_smooth = bfilter2(I2, 5, 0.1);
    
    % 计算梯度
    [Ix, Iy] = gradient(I1_smooth);
    It = I2_smooth - I1_smooth;
    
    % 使用鲁棒范数
    Ix = sign(Ix) .* log(1 + abs(Ix));
    Iy = sign(Iy) .* log(1 + abs(Iy));
    It = sign(It) .* log(1 + abs(It));
end

2. 遮挡处理

function flow = handleOcclusions(obj, I1, I2, flow)
    % 检测并处理遮挡区域
    I2_warped = obj.warpImage(I2, flow);
    diff = abs(I1 - I2_warped);
    occlusionMask = diff > 0.2 * max(diff(:));
    
    % 在遮挡区域使用零光流
    flow(occlusionMask, :) = 0;
    
    % 或者使用相邻像素的光流
    flow = obj.fillOcclusions(flow, occlusionMask);
end

3. 运动分割

function segments = motionSegmentation(obj, flow)
    % 基于光流进行运动分割
    magnitude = sqrt(flow(:,:,1).^2 + flow(:,:,2).^2);
    orientation = atan2(flow(:,:,2), flow(:,:,1));
    
    % 使用k-means聚类
    features = [magnitude(:), orientation(:)];
    [idx, centers] = kmeans(features, 3);
    
    % 创建分割掩码
    segments = reshape(idx, size(magnitude));
end

总结

本MATLAB实现提供了Brox光流算法的完整解决方案,具有以下特点:

  1. 完整实现:包含能量函数定义、迭代优化和多尺度处理
  2. 参数可调:提供关键参数的灵活配置
  3. 高效计算:使用图像金字塔和迭代优化
  4. 丰富可视化:提供多种光流场可视化方法
  5. 鲁棒性:包含图像扭曲、边界处理等增强技术
  6. 易用性:简洁的API设计,适合教学和实际应用

 

专注于matlab/simulink,电子电路,编程