最近调研目标跟踪,看到一个光流法,测试了一下它的效果,挺好玩的,这里对找到的资料简单整理总结一下。
对于光流法的介绍,可以参看如下博客http://blog.csdn.net/zouxy09/article/details/8683859 :
光流(optic flow)是什么呢?名字很专业,感觉很陌生,但本质上,我们是最熟悉不过的了。因为这种视觉现象我们每天都在经历。从本质上说,光流就是你在这个运动着的世界里感觉到的明显的视觉运动(呵呵,相对论,没有绝对的静止,也没有绝对的运动)。例如,当你坐在火车上,然后往窗外看。你可以看到树、地面、建筑等等,他们都在往后退。这个运动就是光流。而且,我们都会发现,他们的运动速度居然不一样?这就给我们提供了一个挺有意思的信息:通过不同目标的运动速度判断它们与我们的距离。一些比较远的目标,例如云、山,它们移动很慢,感觉就像静止一样。但一些离得比较近的物体,例如建筑和树,就比较快的往后退,然后离我们的距离越近,它们往后退的速度越快。一些非常近的物体,例如路面的标记啊,草地啊等等,快到好像在我们耳旁发出嗖嗖的声音。
光流除了提供远近外,还可以提供角度信息。与咱们的眼睛正对着的方向成90度方向运动的物体速度要比其他角度的快,当小到0度的时候,也就是物体朝着我们的方向直接撞过来,我们就是感受不到它的运动(光流)了,看起来好像是静止的。当它离我们越近,就越来越大(当然了,我们平时看到感觉还是有速度的,因为物体较大,它的边缘还是和我们人眼具有大于0的角度的)。
呵呵,说了那么多,好像还没进入比较官方的,研究性的定义。那就贴上一个吧。
光流的概念是Gibson在1950年首先提出来的。它是空间运动物体在观察成像平面上的像素运动的瞬时速度,是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存在的对应关系,从而计算出相邻帧之间物体的运动信息的一种方法。一般而言,光流是由于场景中前景目标本身的移动、相机的运动,或者两者的共同运动所产生的。
当人的眼睛观察运动物体时,物体的景象在人眼的视网膜上形成一系列连续变化的图像,这一系列连续变化的信息不断“流过”视网膜(即图像平面),好像一种光的“流”,故称之为光流(optical flow)。光流表达了图像的变化,由于它包含了目标运动的信息,因此可被观察者用来确定目标的运动情况。
研究光流场的目的就是为了从图片序列中近似得到不能直接得到的运动场。运动场,其实就是物体在三维真实世界中的运动;光流场,是运动场在二维图像平面上(人的眼睛或者摄像头)的投影。
那通俗的讲就是通过一个图片序列,把每张图像中每个像素的运动速度和运动方向找出来就是光流场。那怎么找呢?咱们直观理解肯定是:第t帧的时候A点的位置是(x1, y1),那么我们在第t+1帧的时候再找到A点,假如它的位置是(x2,y2),那么我们就可以确定A点的运动了:(ux, vy) = (x2, y2) - (x1,y1)。
那怎么知道第t+1帧的时候A点的位置呢? 这就存在很多的光流计算方法了。
1981年,Horn和Schunck创造性地将二维速度场与灰度相联系,引入光流约束方程,得到光流计算的基本算法。人们基于不同的理论基础提出各种光流计算方法,算法性能各有不同。Barron等人对多种光流计算技术进行了总结,按照理论基础与数学方法的区别把它们分成四种:基于梯度的方法、基于匹配的方法、基于能量的方法、基于相位的方法。近年来神经动力学方法也颇受学者重视。
IJCV2011有一篇文章,《A Database and Evaluation Methodology for Optical Flow》里面对主流的光流算法做了简要的介绍和对不同算法进行了评估。网址是:http://vision.middlebury.edu/flow/
感觉这个文章在光流算法的解说上非常好,条例很清晰。想了解光流的,推荐看这篇文章。另外,需要提到的一个问题是,光流场是图片中每个像素都有一个x方向和y方向的位移,所以在上面那些光流计算结束后得到的光流flow是个和原来图像大小相等的双通道图像。那怎么可视化呢?这篇文章用的是Munsell颜色系统来显示。
博文后面还介绍了孟塞尔颜色系统,及光流法在OpenCV中的使用示例
图像约束方程可以写为I(x,y,z,t) = I(x+δx,y+δy,z+δz,t+δt),I(x,y,z,t)为在(x,y,z)位置的体素。我们假设移动足够的小,那么对图像约束方程使用泰勒公式,我们可以得到:
所以
Ix*Vx + Iy*Vy + Iz*Vz = −It
即
在Matlab中也有相关函数可以实现光流法,在机器视觉工具箱中它被封装成了一个类,使用方便。这里简单测试一下,看代码:
% 光流法测试--两张图片
clc
clear
close all
% 指定图片
refimg = 'car0.png'; % 参考图片
inputimg = 'car1.png'; % 供对比图片
refimg = 'frame10.png'; % 参考图片
inputimg = 'frame11.png'; % 供对比图片
% 读取图片
Iref = imread(refimg);
Iinput = imread(inputimg);
if size(Iref, 3) == 3
Irefg = rgb2gray(Iref);
Iinputg = rgb2gray(Iinput);
else
Irefg = Iref;
Iinputg = Iinput;
end
% 创建光流对象及类型转化对象
opticalFlow = vision.OpticalFlow('ReferenceFrameDelay', 1);
converter = vision.ImageDataTypeConverter;
% 修改光流对象的配置
opticalFlow.OutputValue = 'Horizontal and vertical components in complex form'; % 返回复数形式光流图
opticalFlow.ReferenceFrameSource = 'Input port'; % 对比两张图片,而不是视频流
if 1 % 使用的算法
opticalFlow.Method = 'Lucas-Kanade';
opticalFlow.NoiseReductionThreshold = 0.001; % 默认是0.0039
else
opticalFlow.Method = 'Horn-Schunck';
opticalFlow.Smoothness = 0.5; % 默认是1
end
% 调用光流对象计算两张图片的光流
Iinputg_c = step(converter, Iinputg);
Irefg_c = step(converter, Irefg);
of = step(opticalFlow, Iinputg_c, Irefg_c);
ofH = real(of);
ofV = imag(of);
% 将光流转化为彩色图显示
ofI = computeColor(ofH, ofV);
% 显示结果
figure
subplot(221)
imshow(Iref)
subplot(222)
imshow(Iinput)
subplot(223)
imshow(ofH, [])
subplot(224)
imshow(ofV, [])
figure
imshow(ofI)
程序中的测试图片可以从http://vision.middlebury.edu/flow/ ,这个网站提供了供光流法测试的很多测试图片,方便研究这方面算法的人对比效果,另外函数computeColor是将光流法结果转化为彩色图显示,代码也在这个网站中。运行程序结果如下
% 光流法测试--视频
clc
clear
close all
% 读取文件对象
videoReader = vision.VideoFileReader('viptraffic.avi','ImageColorSpace','Intensity','VideoOutputDataType','uint8');
% 类型转化对象
converter = vision.ImageDataTypeConverter;
% 光流对象
opticalFlow = vision.OpticalFlow('ReferenceFrameDelay', 1);
opticalFlow.OutputValue = 'Horizontal and vertical components in complex form';
if 0 % 使用的算法
opticalFlow.Method = 'Lucas-Kanade';
opticalFlow.NoiseReductionThreshold = 0.001; % 默认是0.0039
else
opticalFlow.Method = 'Horn-Schunck';
opticalFlow.Smoothness = 0.5; % 默认是1
end
% 显示对象
frame = step(videoReader);
figure
subplot(121)
himg = imshow(frame);
subplot(122)
hof = imshow(frame);
% 开始播放
while ~isDone(videoReader)
% 得到一帧
frame = step(videoReader);
% 格式转化
im = step(converter, frame);
% 计算光流
of = step(opticalFlow, im);
% 光流图转化
ofI = computeColor(real(of), imag(of));
% 显示
set(himg, 'cdata', frame)
set(hof, 'cdata', ofI)
drawnow
end
release(videoReader);
运行程序结果如下