发 帖  

[经验] 【教程】通俗讲法告诉你什么是光流Optical Flow,附算法

2015-6-2 17:18:41  44593 飞行器 陀螺仪 PID
收藏 17 收藏 推荐 0 推荐
分享
现在四轴飞行器越来越火,如何在室内进行定位呢?不同于传统四轴的姿态控制,电机驱动,室外定位,都有了一套完整的方案,室内定位还是没有完全成熟。,目前大四轴可以利用的GPS定高,小四轴比较成熟的也就是光流方案了。
先看一下光流的效果

这是一个挥动的手
1.jpg

虽然也有背景在动,但是因为他们的运动方向不一样,所以还是可以辨认出来前面那个是手,在前景和背景运动不统一的时候,还是可以辨认出来的。

2.jpg

那么光流(optic flow)是什么呢?

名字很专业,感觉很陌生从本质上说,光流就是你在这个运动着的世界里感觉到的明显的视觉运动。例如,当你坐在火车上,然后往窗外看。你可以看到树、地面、建筑等等,他们都在往后退。这个运动就是光流。而且,我们都会发现,他们的运动速度居然不一样?这就给我们提供了一个挺有意思的信息:通过不同目标的运动速度判断它们与我们的距离。一些比较远的目标,例如云、山,它们移动很慢,感觉就像静止一样。但一些离得比较近的物体,例如建筑和树,就比较快的往后退,然后离我们的距离越近,它们往后退的速度越快。一些非常近的物体,例如路面的标记啊,草地啊等等,快到好像在我们耳旁发出嗖嗖的声音。
      

        光流除了提供远近外,还可以提供角度信息。与咱们的眼睛正对着的方向成90度方向运动的物体速度要比其他角度的快,当小到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等人对多种光流计算技术进行了总结,按照理论基础与数学方法的区别把它们分成四种:基于梯度的方法、基于匹配的方法、基于能量的方法、基于相位的方法。近年来神经动力学方法也颇受学者重视。

还是回归应用吧,目前OpenCV中实现了不少的光流算法。

1. calcOpticalFlowPyrLK
通过金字塔Lucas-Kanade 光流方法计算某些点集的光流(稀疏光流)。理解的话,可以参考这篇论文:”Pyramidal Implementation of the Lucas Kanade Feature TrackerDescription of the algorithm”
2. calcOpticalFlowFarneback
用Gunnar Farneback 的算法计算稠密光流(即图像上所有像素点的光流都计算出来)。它的相关论文是:"Two-Frame Motion Estimation Based on PolynomialExpansion"
3. CalcOpticalFlowBM
通过块匹配的方法来计算光流。
4. CalcOpticalFlowHS
用Horn-Schunck 的算法计算稠密光流。相关论文好像是这篇:”Determining Optical Flow”
5. calcOpticalFlowSF
这一个是2012年欧洲视觉会议的一篇文章的实现:"SimpleFlow: A Non-iterative, Sublinear Optical FlowAlgorithm",工程网站是:http://graphics.berkeley.edu/papers/Tao-SAN-2012-05/  在OpenCV新版本中有引入。
还有他们的API的使用说明,我们直接参考OpenCV的官方手册就行:
http://www.opencv.org.cn/opencvdoc/2.3.2/html/modules/video/doc/motion_analysis_and_object_tracking.html#calcopticalflowfarneback

IJCV2011有一篇文章,《A Database and Evaluation Methodology for Optical Flow》里面对主流的光流算法做了简要的介绍和对不同算法进行了评估。网址是:http://vision.middlebury.edu/flow/

       感觉这个文章在光流算法的解说上非常好,条例很清晰。想了解光流的,推荐看这篇文章。另外,需要提到的一个问题是,光流场是图片中每个像素都有一个x方向和y方向的位移,所以在上面那些光流计算结束后得到的光流flow是个和原来图像大小相等的双通道图像。那怎么可视化呢?这篇文章用的是Munsell颜色系统来显示。

1363506969_4561.jpg

孟塞尔颜色系统的空间大致成一个圆柱形:
南北轴=明度(value),从全黑(1)到全白(10)。
经度=色相(hue)。把一周均分成五种主色调和五种中间色:红(R)、红黄(YR)、黄(Y)、黄绿(GY)、绿(G)、绿蓝(BG)、蓝(B)、蓝紫(PB)、紫(P)、紫红(RP)。相邻的两个位置之间再均分10份,共100份。
距轴的距离=色度(chroma),表示色调的纯度。其数值从中间(0)向外随着色调的纯度增加,没有理论上的上限(普通的颜色实际上限为10左右,反光、荧光等材料可高达30)。由于人眼对各种颜色的的敏感度不同,色度不一定与每个色调和明度组合相匹配。
具体颜色的标识形式为:色相+明度+色度。
       在上面的那个评估的网站有这个从flow到color显示的Matlab和C++代码。但是感觉C++代码分几个文件,有点乱,然后我自己整理成两个函数了,并配合OpenCV的Mat格式。
       下面的代码是用calcOpticalFlowFarneback来计算稠密光流并且用这个颜色系统来显示的。这个计算稠密光流的方法与其他几个相比还是比较快的,640x480的视频我的是200ms左右一帧,但其他的一般都需要一两秒以上。结果图中,不同颜色表示不同的运动方向,深浅就表示运动的快慢了。
void calcOpticalFlowFarneback(InputArray prevImg, InputArray nextImg,InputOutputArray flow, double pyrScale, int levels, int winsize, intiterations, int polyN, double polySigma, int flags)
大部分参数在论文中都有一套比较好的值的,直接采用他们的就好了。
  1.     // Farneback dense optical flow calculate and show in Munsell system of colors  
  2.     // Author : Zouxy  
  3.     // Date   : 2013-3-15  
  4.     // HomePage : http://blog.csdn.net/zouxy09  
  5.     // Email  : zouxy09@qq.com  
  6.       
  7.     // API calcOpticalFlowFarneback() comes from OpenCV, and this  
  8.     // 2D dense optical flow algorithm from the following paper:  
  9.     // Gunnar Farneback. "Two-Frame Motion Estimation Based on Polynomial Expansion".  
  10.     // And the OpenCV source code locate in ..\opencv2.4.3\modules\video\src\optflowgf.cpp  
  11.       
  12.     #include <iostream>  
  13.     #include "opencv2/opencv.hpp"  
  14.       
  15.     using namespace cv;  
  16.     using namespace std;  
  17.       
  18.     #define UNKNOWN_FLOW_THRESH 1e9  
  19.       
  20.     // Color encoding of flow vectors from:  
  21.     // http://members.shaw.ca/quadibloc/other/colint.htm  
  22.     // This code is modified from:  
  23.     // http://vision.middlebury.edu/flow/data/  
  24.     void makecolorwheel(vector<Scalar> &colorwheel)  
  25.     {  
  26.         int RY = 15;  
  27.         int YG = 6;  
  28.         int GC = 4;  
  29.         int CB = 11;  
  30.         int BM = 13;  
  31.         int MR = 6;  
  32.       
  33.         int i;  
  34.       
  35.         for (i = 0; i < RY; i++) colorwheel.push_back(Scalar(255,       255*i/RY,     0));  
  36.         for (i = 0; i < YG; i++) colorwheel.push_back(Scalar(255-255*i/YG, 255,       0));  
  37.         for (i = 0; i < GC; i++) colorwheel.push_back(Scalar(0,         255,      255*i/GC));  
  38.         for (i = 0; i < CB; i++) colorwheel.push_back(Scalar(0,         255-255*i/CB, 255));  
  39.         for (i = 0; i < BM; i++) colorwheel.push_back(Scalar(255*i/BM,      0,        255));  
  40.         for (i = 0; i < MR; i++) colorwheel.push_back(Scalar(255,       0,        255-255*i/MR));  
  41.     }  
  42.       
  43.     void motionToColor(Mat flow, Mat &color)  
  44.     {  
  45.         if (color.empty())  
  46.             color.create(flow.rows, flow.cols, CV_8UC3);  
  47.       
  48.         static vector<Scalar> colorwheel; //Scalar r,g,b  
  49.         if (colorwheel.empty())  
  50.             makecolorwheel(colorwheel);  
  51.       
  52.         // determine motion range:  
  53.         float maxrad = -1;  
  54.       
  55.         // Find max flow to normalize fx and fy  
  56.         for (int i= 0; i < flow.rows; ++i)   
  57.         {  
  58.             for (int j = 0; j < flow.cols; ++j)   
  59.             {  
  60.                 Vec2f flow_at_point = flow.at<Vec2f>(i, j);  
  61.                 float fx = flow_at_point[0];  
  62.                 float fy = flow_at_point[1];  
  63.                 if ((fabs(fx) >  UNKNOWN_FLOW_THRESH) || (fabs(fy) >  UNKNOWN_FLOW_THRESH))  
  64.                     continue;  
  65.                 float rad = sqrt(fx * fx + fy * fy);  
  66.                 maxrad = maxrad > rad ? maxrad : rad;  
  67.             }  
  68.         }  
  69.       
  70.         for (int i= 0; i < flow.rows; ++i)   
  71.         {  
  72.             for (int j = 0; j < flow.cols; ++j)   
  73.             {  
  74.                 uchar *data = color.data + color.step[0] * i + color.step[1] * j;  
  75.                 Vec2f flow_at_point = flow.at<Vec2f>(i, j);  
  76.       
  77.                 float fx = flow_at_point[0] / maxrad;  
  78.                 float fy = flow_at_point[1] / maxrad;  
  79.                 if ((fabs(fx) >  UNKNOWN_FLOW_THRESH) || (fabs(fy) >  UNKNOWN_FLOW_THRESH))  
  80.                 {  
  81.                     data[0] = data[1] = data[2] = 0;  
  82.                     continue;  
  83.                 }  
  84.                 float rad = sqrt(fx * fx + fy * fy);  
  85.       
  86.                 float angle = atan2(-fy, -fx) / CV_PI;  
  87.                 float fk = (angle + 1.0) / 2.0 * (colorwheel.size()-1);  
  88.                 int k0 = (int)fk;  
  89.                 int k1 = (k0 + 1) % colorwheel.size();  
  90.                 float f = fk - k0;  
  91.                 //f = 0; // uncomment to see original color wheel  
  92.       
  93.                 for (int b = 0; b < 3; b++)   
  94.                 {  
  95.                     float col0 = colorwheel[k0][b] / 255.0;  
  96.                     float col1 = colorwheel[k1][b] / 255.0;  
  97.                     float col = (1 - f) * col0 + f * col1;  
  98.                     if (rad <= 1)  
  99.                         col = 1 - rad * (1 - col); // increase saturation with radius  
  100.                     else  
  101.                         col *= .75; // out of range  
  102.                     data[2 - b] = (int)(255.0 * col);  
  103.                 }  
  104.             }  
  105.         }  
  106.     }  
  107.       
  108.     int main(int, char**)  
  109.     {  
  110.         VideoCapture cap;  
  111.         cap.open(0);  
  112.         //cap.open("test_02.wmv");  
  113.       
  114.         if( !cap.isOpened() )  
  115.             return -1;  
  116.       
  117.         Mat prevgray, gray, flow, cflow, frame;  
  118.         namedWindow("flow", 1);  
  119.       
  120.         Mat motion2color;  
  121.       
  122.         for(;;)  
  123.         {  
  124.             double t = (double)cvGetTickCount();  
  125.       
  126.             cap >> frame;  
  127.             cvtColor(frame, gray, CV_BGR2GRAY);  
  128.             imshow("original", frame);  
  129.       
  130.             if( prevgray.data )  
  131.             {  
  132.                 calcOpticalFlowFarneback(prevgray, gray, flow, 0.5, 3, 15, 3, 5, 1.2, 0);  
  133.                 motionToColor(flow, motion2color);  
  134.                 imshow("flow", motion2color);  
  135.             }  
  136.             if(waitKey(10)>=0)  
  137.                 break;  
  138.             std::swap(prevgray, gray);  
  139.       
  140.             t = (double)cvGetTickCount() - t;  
  141.             cout << "cost time: " << t / ((double)cvGetTickFrequency()*1000.) << endl;  
  142.         }  
  143.         return 0;  
  144.     }  
复制代码

相关经验

说的好像真的 2015-6-4 17:08:31
很专业,支持一下
回复

举报

ggghello 2015-6-4 17:44:47
回复

举报

zywplc 2015-6-5 08:46:34
谢谢分享                                                               
回复

举报

shanhe95 2015-6-8 16:49:36
有图有真相,谢谢分享
回复

举报

梦石~ 2015-6-8 17:14:12
恶补一下  谢谢
回复

举报

ly8023 2015-6-12 10:26:15
谢谢分享  
强烈赞一个
回复

举报

daziran168 2015-6-13 09:31:36
学习啦,感谢分享!!!
回复

举报

龙锐陈 2015-6-13 11:53:35
赞一个。不错的资料。
回复

举报

lydmom 2015-6-13 19:27:37
恶补知识点!!!谢谢
回复

举报

cds1999 2015-6-14 19:35:30
非常感谢楼主,本人也在研究光流传感器,希望能用上哈哈哈
回复

举报

lnxkxx 2015-6-25 12:43:03
有意思,这个有时间可以好好研究一下。。。
回复

举报

yanzhibo7554 2015-7-15 14:46:39
赞一个!!!!!!!!!!!
回复

举报

kuangle_6092 2015-7-25 16:32:13
最近在弄PX4flow的控制,楼主可以介绍一下他的位置控制思想吗?
回复

举报

chen1658137632 2015-11-19 20:15:45

楼主分享的资料很不错,很好,很感谢
回复

举报

Curry1992 2016-3-24 10:52:05
刚玩四轴,最近在考虑光流,不知道如何安装
回复

举报

wd88 2016-3-30 14:41:39
感谢楼主分享,讲的很通俗易懂。
回复

举报

lidianzi 2016-7-16 19:54:30
多谢啦!
回复

举报

猎国倾城 2016-8-15 11:13:44
顶,恒专业!!
回复

举报

Ljmf5 2016-12-20 17:33:31
想问问哪位看懂了,能否简单解释一下算法的内容
回复

举报

评论

高级模式
您需要登录后才可以回帖 登录 | 注册

12下一页
发经验
关闭

站长推荐 上一条 /9 下一条

快速回复 返回顶部 返回列表
-

推荐专区

技术干货集中营

专家问答

用户帮助┃咨询与建议┃版主议事

工程师杂谈

项目|工程师创意

招聘|求职}工程师职场

论坛电子赛事

社区活动专版

发烧友活动

-

嵌入式论坛

ARM技术论坛

Linux论坛

单片机/MCU论坛

MSP430技术论坛

FPGA|CPLD|ASIC论坛

STM32/STM8技术论坛

NXP MCU 技术论坛

DSP论坛

嵌入式系统论坛

-

电源技术论坛

电源技术论坛

无线充电技术

-

硬件设计论坛

电路设计论坛

电子元器件论坛

传感技术

总线技术|接口技术

-

测试测量论坛

LabVIEW论坛

Matlab论坛

测试测量技术专区

-

EDA设计论坛

multisim论坛

PCB设计论坛

PADS技术论坛

Protel|AD|DXP论坛

Allegro论坛

proteus论坛|仿真论坛

Orcad论坛

-

综合技术与应用

电机控制

工程资源中心

汽车电子技术论坛

医疗电子论坛

-

开源硬件

-

无线通信论坛

物联网技术

天线|RF射频|微波|雷达技术

-

IC设计论坛

芯片测试与失效分析

Analog/RF IC设计

设计与制造封装测试

-

厂商专区

TI论坛

TI Deyisupport社区

-

检测技术与质量

电磁兼容(EMC)设计与整改

安规知识论坛

检测与认证

-

消费电子论坛

手机技术论坛

平板电脑/mid论坛

音视/视频/机顶盒论坛

-

电子论坛综合区

聚丰众筹官方社区

新人报道区

聚丰供应链

-

论坛服务区

-

高校联盟

深圳大学电子协会

西藏民族学院电子设计协会

西安电子科技大学西电实验室

桂林电子科技大学机电工程协会

鉴鹰电子科技协会

广东海洋大学科技创新协会

福建农林大学电子科技创新协会

湖北理工学院电子技术协会

深圳职业技术学院电子精英训练营

浙江科技学院Zuster

湘潭大学电子科技协会

青岛理工大学琴岛学院天宏开发团队

河南理工大学大学生电器开发部

广西科技大学电气信息创新协会

浙江机电职业技术学院电子制作协会

郑州大学西亚斯国际学院爱电工作室

新乡学院电子创新工作室

洛阳理工学院SmartTeam

苏州工业职业技术学院创新工作室

浙江工业大学GC315团队

杭电-微芯大学生科技创新孵化器

西安工业大学电信科协

商洛学院科技创新协会

大连民族学院创新工作室

南京信息工程大学帆木工作室

-

供求信息发布

方案交易

供需广告

芯片求购|供应发布区