Lucas-Kanade光流 跟踪

本文目录:

      一.基于特征点的目标跟踪的一般方法

      二.光流法

      三.OpenCV中的光流法函数

      四.用类封装基于光流法的目标跟踪方法

      五.完整代码

      六.参考文献


一.基于特征点的目标跟踪的一般方法

      基于特征点的跟踪算法大致可以分为两个步骤:

      1)探测当前帧的特征点;

      2)通过当前帧和下一帧灰度比较,估计当前帧特征点在下一帧的位置;

      3)过滤位置不变的特征点,余下的点就是目标了。

      很显然,基于特征点的目标跟踪算法和1),2)两个步骤有关。特征点可以是Harris角点(见我的另外一篇博文),也可以是边缘点等等,而估计下一帧位置的方法也有不少,比如这里要讲的光流法,也可以是卡尔曼滤波法(咱是控制系的,上课经常遇到这个,所以看光流法看着看着就想到这个了)。

      本文中,用改进的Harris角点提取特征点(见我另一篇博文:http://blog.csdn.NET/crzy_sparrow/article/details/7391511),用Lucas-Kanade光流法实现目标跟踪。


二.光流法

      这一部分《learing opencv》一书的第10章Lucas-Kanade光流部分写得非常详细,推荐大家看书。我这里也粘帖一些选自书中的内容。

      另外我对这一部分附上一些个人的看法(谬误之处还望不吝指正):

      1.首先是假设条件:

       (1)亮度恒定,就是同一点随着时间的变化,其亮度不会发生改变。这是基本光流法的假定(所有光流法变种都必须满足),用于得到光流法基本方程;

       (2)小运动,这个也必须满足,就是时间的变化不会引起位置的剧烈变化,这样灰度才能对位置求偏导(换句话说,小运动情况下我们才能用前后帧之间单位位置变化引起的灰度变化去近似灰度对位置的偏导数),这也是光流法不可或缺的假定;

       (3)空间一致,一个场景上邻近的点投影到图像上也是邻近点,且邻近点速度一致。这是Lucas-Kanade光流法特有的假定,因为光流法基本方程约束只有一个,而要求x,y方向的速度,有两个未知变量。我们假定特征点邻域内做相似运动,就可以连立n多个方程求取x,y方向的速度(n为特征点邻域总点数,包括该特征点)。

      2.方程求解

      多个方程求两个未知变量,又是线性方程,很容易就想到用最小二乘法,事实上opencv也是这么做的。其中,最小误差平方和为最优化指标。

      3.好吧,前面说到了小运动这个假定,聪明的你肯定很不爽了,目标速度很快那这货不是二掉了。幸运的是多尺度能解决这个问题。首先,对每一帧建立一个高斯金字塔,最大尺度图片在最顶层,原始图片在底层。然后,从顶层开始估计下一帧所在位置,作为下一层的初始位置,沿着金字塔向下搜索,重复估计动作,直到到达金字塔的底层。聪明的你肯定发现了:这样搜索不仅可以解决大运动目标跟踪,也可以一定程度上解决孔径问题(相同大小的窗口能覆盖大尺度图片上尽量多的角点,而这些角点无法在原始图片上被覆盖)。




三.opencv中的光流法函数

      opencv2.3.1中已经实现了基于光流法的特征点位置估计函数(当前帧位置已知,前后帧灰度已知),介绍如下(摘自opencv2.3.1参考手册):

[cpp]  view plain copy
  1. calcOpticalFlowPyrLK  
  2. Calculates an optical flow for a sparse feature set using the iterative Lucas-Kanade method with pyramids.  
  3.   
  4. void calcOpticalFlowPyrLK(InputArray prevImg, InputArray nextImg, InputArray prevPts,  
  5. InputOutputArray nextPts, OutputArray status, OutputArray err,  
  6. Size winSize=Size(15,15), int maxLevel=3, TermCriteria crite-  
  7. ria=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01),  
  8. double derivLambda=0.5, int flags=0 )  
  9.   
  10. Parameters  
  11. prevImg – First 8-bit single-channel or 3-channel input image.  
  12. nextImg – Second input image of the same size and the same type as prevImg .  
  13. prevPts – Vector of 2D points for which the flow needs to be found. The point coordinates  
  14. must be single-precision floating-point numbers.  
  15. nextPts – Output vector of 2D points (with single-precision floating-point coordinates)  
  16. containing the calculated new positions of input features in the second image. When  
  17. OPTFLOW_USE_INITIAL_FLOW flag is passed, the vector must have the same size as in the  
  18. input.  
  19. status – Output status vector. Each element of the vector is set to 1 if the flow for the  
  20. corresponding features has been found. Otherwise, it is set to 0.  
  21. err – Output vector that contains the difference between patches around the original and  
  22. moved points.  
  23. winSize – Size of the search window at each pyramid level.  
  24.   
  25. maxLevel – 0-based maximal pyramid level number. If set to 0, pyramids are not used  
  26. (single level). If set to 1, two levels are used, and so on.  
  27. criteria – Parameter specifying the termination criteria of the iterative search algorithm  
  28. (after the specified maximum number of iterations criteria.maxCount or when the search  
  29. window moves by less than criteria.epsilon .  
  30. derivLambda – Not used.  
  31.   
  32. flags – Operation flags:  
  33. – OPTFLOW_USE_INITIAL_FLOW Use initial estimations stored in nextPts . If the  
  34. flag is not set, then prevPts is copied to nextPts and is considered as the initial estimate.  
[cpp]  view plain  copy
  1. calcOpticalFlowPyrLK  
  2. Calculates an optical flow for a sparse feature set using the iterative Lucas-Kanade method with pyramids.  
  3.   
  4. void calcOpticalFlowPyrLK(InputArray prevImg, InputArray nextImg, InputArray prevPts,  
  5. InputOutputArray nextPts, OutputArray status, OutputArray err,  
  6. Size winSize=Size(15,15), int maxLevel=3, TermCriteria crite-  
  7. ria=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01),  
  8. double derivLambda=0.5, int flags=0 )  
  9.   
  10. Parameters  
  11. prevImg – First 8-bit single-channel or 3-channel input image.  
  12. nextImg – Second input image of the same size and the same type as prevImg .  
  13. prevPts – Vector of 2D points for which the flow needs to be found. The point coordinates  
  14. must be single-precision floating-point numbers.  
  15. nextPts – Output vector of 2D points (with single-precision floating-point coordinates)  
  16. containing the calculated new positions of input features in the second image. When  
  17. OPTFLOW_USE_INITIAL_FLOW flag is passed, the vector must have the same size as in the  
  18. input.  
  19. status – Output status vector. Each element of the vector is set to 1 if the flow for the  
  20. corresponding features has been found. Otherwise, it is set to 0.  
  21. err – Output vector that contains the difference between patches around the original and  
  22. moved points.  
  23. winSize – Size of the search window at each pyramid level.  
  24.   
  25. maxLevel – 0-based maximal pyramid level number. If set to 0, pyramids are not used  
  26. (single level). If set to 1, two levels are used, and so on.  
  27. criteria – Parameter specifying the termination criteria of the iterative search algorithm  
  28. (after the specified maximum number of iterations criteria.maxCount or when the search  
  29. window moves by less than criteria.epsilon .  
  30. derivLambda – Not used.  
  31.   
  32. flags – Operation flags:  
  33. – OPTFLOW_USE_INITIAL_FLOW Use initial estimations stored in nextPts . If the  
  34. flag is not set, then prevPts is copied to nextPts and is considered as the initial estimate.  

四.用类封装基于光流法的目标跟踪方法

      废话少说,附上代码,包括特征点提取,跟踪特征点,标记特征点等。

[cpp]  view plain copy
  1. <SPAN style="FONT-SIZE: 18px">//帧处理基类   
  2. class FrameProcessor{  
  3.     public:  
  4.         virtual void process(Mat &input,Mat &ouput)=0;  
  5. };  
  6.   
  7. //特征跟踪类,继承自帧处理基类   
  8. class FeatureTracker :  public FrameProcessor{  
  9.     Mat gray;  //当前灰度图   
  10.     Mat gray_prev;  //之前的灰度图   
  11.     vector<Point2f> points[2];//前后两帧的特征点   
  12.     vector<Point2f> initial;//初始特征点   
  13.     vector<Point2f> features;//检测到的特征   
  14.     int max_count; //要跟踪特征的最大数目   
  15.     double qlevel; //特征检测的指标   
  16.     double minDist;//特征点之间最小容忍距离   
  17.     vector<uchar> status; //特征点被成功跟踪的标志   
  18.     vector<float> err; //跟踪时的特征点小区域误差和   
  19. public:  
  20.     FeatureTracker():max_count(500),qlevel(0.01),minDist(10.){}  
  21.     void process(Mat &frame,Mat &output){  
  22.         //得到灰度图   
  23.         cvtColor (frame,gray,CV_BGR2GRAY);  
  24.         frame.copyTo (output);  
  25.         //特征点太少了,重新检测特征点   
  26.         if(addNewPoint()){  
  27.             detectFeaturePoint ();  
  28.             //插入检测到的特征点   
  29.             points[0].insert (points[0].end (),features.begin (),features.end ());  
  30.             initial.insert (initial.end (),features.begin (),features.end ());  
  31.         }  
  32.         //第一帧   
  33.         if(gray_prev.empty ()){  
  34.                 gray.copyTo (gray_prev);  
  35.         }  
  36.         //根据前后两帧灰度图估计前一帧特征点在当前帧的位置   
  37.         //默认窗口是15*15   
  38.         calcOpticalFlowPyrLK (  
  39.                 gray_prev,//前一帧灰度图   
  40.                 gray,//当前帧灰度图   
  41.                 points[0],//前一帧特征点位置   
  42.                 points[1],//当前帧特征点位置   
  43.                 status,//特征点被成功跟踪的标志   
  44.                 err);//前一帧特征点点小区域和当前特征点小区域间的差,根据差的大小可删除那些运动变化剧烈的点   
  45.         int k = 0;  
  46.         //去除那些未移动的特征点   
  47.         for(int i=0;i<points[1].size ();i++){  
  48.             if(acceptTrackedPoint (i)){  
  49.                 initial[k]=initial[i];  
  50.                 points[1][k++] = points[1][i];  
  51.             }  
  52.         }  
  53.         points[1].resize (k);  
  54.         initial.resize (k);  
  55.         //标记被跟踪的特征点   
  56.         handleTrackedPoint (frame,output);  
  57.         //为下一帧跟踪初始化特征点集和灰度图像   
  58.         std::swap(points[1],points[0]);  
  59.         cv::swap(gray_prev,gray);  
  60.     }  
  61.   
  62.     void detectFeaturePoint(){  
  63.         goodFeaturesToTrack (gray,//输入图片   
  64.                                  features,//输出特征点   
  65.                                  max_count,//特征点最大数目   
  66.                                  qlevel,//质量指标   
  67.                                  minDist);//最小容忍距离   
  68.     }  
  69.     bool addNewPoint(){  
  70.         //若特征点数目少于10,则决定添加特征点   
  71.         return points[0].size ()<=10;  
  72.     }  
  73.   
  74.     //若特征点在前后两帧移动了,则认为该点是目标点,且可被跟踪   
  75.     bool acceptTrackedPoint(int i){  
  76.         return status[i]&&  
  77.                 (abs(points[0][i].x-points[1][i].x)+  
  78.                   abs(points[0][i].y-points[1][i].y) >2);  
  79.     }  
  80.   
  81.     //画特征点   
  82.     void  handleTrackedPoint(Mat &frame,Mat &output){  
  83.             for(int i=0;i<points[i].size ();i++){  
  84.                 //当前特征点到初始位置用直线表示   
  85.                 line(output,initial[i],points[1][i],Scalar::all (0));  
  86.                 //当前位置用圈标出   
  87.                 circle(output,points[1][i],3,Scalar::all(0),(-1));  
  88.             }  
  89.         }  
  90. };</SPAN>  
[cpp]  view plain  copy
  1. <span style="font-size:18px;">//帧处理基类  
  2. class FrameProcessor{  
  3.     public:  
  4.         virtual void process(Mat &input,Mat &ouput)=0;  
  5. };  
  6.   
  7. //特征跟踪类,继承自帧处理基类  
  8. class FeatureTracker :  public FrameProcessor{  
  9.     Mat gray;  //当前灰度图  
  10.     Mat gray_prev;  //之前的灰度图  
  11.     vector<Point2f> points[2];//前后两帧的特征点  
  12.     vector<Point2f> initial;//初始特征点  
  13.     vector<Point2f> features;//检测到的特征  
  14.     int max_count; //要跟踪特征的最大数目  
  15.     double qlevel; //特征检测的指标  
  16.     double minDist;//特征点之间最小容忍距离  
  17.     vector<uchar> status; //特征点被成功跟踪的标志  
  18.     vector<float> err; //跟踪时的特征点小区域误差和  
  19. public:  
  20.     FeatureTracker():max_count(500),qlevel(0.01),minDist(10.){}  
  21.     void process(Mat &frame,Mat &output){  
  22.         //得到灰度图  
  23.         cvtColor (frame,gray,CV_BGR2GRAY);  
  24.         frame.copyTo (output);  
  25.         //特征点太少了,重新检测特征点  
  26.         if(addNewPoint()){  
  27.             detectFeaturePoint ();  
  28.             //插入检测到的特征点  
  29.             points[0].insert (points[0].end (),features.begin (),features.end ());  
  30.             initial.insert (initial.end (),features.begin (),features.end ());  
  31.         }  
  32.         //第一帧  
  33.         if(gray_prev.empty ()){  
  34.                 gray.copyTo (gray_prev);  
  35.         }  
  36.         //根据前后两帧灰度图估计前一帧特征点在当前帧的位置  
  37.         //默认窗口是15*15  
  38.         calcOpticalFlowPyrLK (  
  39.                 gray_prev,//前一帧灰度图  
  40.                 gray,//当前帧灰度图  
  41.                 points[0],//前一帧特征点位置  
  42.                 points[1],//当前帧特征点位置  
  43.                 status,//特征点被成功跟踪的标志  
  44.                 err);//前一帧特征点点小区域和当前特征点小区域间的差,根据差的大小可删除那些运动变化剧烈的点  
  45.         int k = 0;  
  46.         //去除那些未移动的特征点  
  47.         for(int i=0;i<points[1].size ();i++){  
  48.             if(acceptTrackedPoint (i)){  
  49.                 initial[k]=initial[i];  
  50.                 points[1][k++] = points[1][i];  
  51.             }  
  52.         }  
  53.         points[1].resize (k);  
  54.         initial.resize (k);  
  55.         //标记被跟踪的特征点  
  56.         handleTrackedPoint (frame,output);  
  57.         //为下一帧跟踪初始化特征点集和灰度图像  
  58.         std::swap(points[1],points[0]);  
  59.         cv::swap(gray_prev,gray);  
  60.     }  
  61.   
  62.     void detectFeaturePoint(){  
  63.         goodFeaturesToTrack (gray,//输入图片  
  64.                                  features,//输出特征点  
  65.                                  max_count,//特征点最大数目  
  66.                                  qlevel,//质量指标  
  67.                                  minDist);//最小容忍距离  
  68.     }  
  69.     bool addNewPoint(){  
  70.         //若特征点数目少于10,则决定添加特征点  
  71.         return points[0].size ()<=10;  
  72.     }  
  73.   
  74.     //若特征点在前后两帧移动了,则认为该点是目标点,且可被跟踪  
  75.     bool acceptTrackedPoint(int i){  
  76.         return status[i]&&  
  77.                 (abs(points[0][i].x-points[1][i].x)+  
  78.                   abs(points[0][i].y-points[1][i].y) >2);  
  79.     }  
  80.   
  81.     //画特征点  
  82.     void  handleTrackedPoint(Mat &frame,Mat &output){  
  83.             for(int i=0;i<points[i].size ();i++){  
  84.                 //当前特征点到初始位置用直线表示  
  85.                 line(output,initial[i],points[1][i],Scalar::all (0));  
  86.                 //当前位置用圈标出  
  87.                 circle(output,points[1][i],3,Scalar::all(0),(-1));  
  88.             }  
  89.         }  
  90. };</span>  

五.完整代码
  完整的运行代码有300+行,粘上来太多了,大家去我传的资源里下载吧。

  下载地址:http://download.csdn.net/detail/crzy_sparrow/4183674

  运行结果:



六.参考文献

【1】The classic article by B. Lucas and T. Kanade, An iterative image registration technique with an application to stereo vision in Int. Joint Conference in Artificial Intelligence, pp. 674-679,1981, that describes the original feature point tracking algorithm.
【2】The article by J. Shi and C. Tomasi, Good Features to Track in IEEE Conference on Computer Vision and Pattern Recognition, pp. 593-600, 1994, that describes an improved version of the original feature point tracking algorithm.
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Lucas-Kanade光流跟踪算法、Kalman滤波和Horn-Schunck光流跟踪算法都是常用的目标跟踪算法,但是它们的成功率受多种因素影响,例如目标的运动模式、场景的复杂程度、图像的质量等等。 一般而言,Lucas-Kanade光流跟踪算法适用于目标运动较小,场景复杂度较低的情况,其成功率较高;Kalman滤波则可以通过对目标运动模型的建立,提高跟踪的稳定性和准确性,但需要对目标运动模型进行精细的建模;Horn-Schunck光流跟踪算法可以对场景中的所有像素进行跟踪,但对于复杂场景和非刚体运动的目标,其成功率可能会降低。 总的来说,这些跟踪算法的成功率并不能一概而论,需要根据具体的应用场景和算法参数进行选择和优化。 ### 回答2: Lucas-Kanade光流跟踪算法是一种基于局部窗口的光流估计方法。它通过考虑像素间灰度的差异以及他们在时域上的关系来估计运动场景中的光流Lucas-Kanade算法在追踪小范围运动目标方面表现出色,对于物体的局部运动能够提供相对准确的跟踪结果。然而,当目标存在较大的尺度变化、非刚性运动或者相机快速移动时,Lucas-Kanade算法的追踪效果可能会变差,成功率较低。 Kalman滤波是一种常用于追踪目标的滤波算法。它基于目标的运动模型和观测模型,通过不断更新目标状态的估计值,可以实现对目标的连续追踪。Kalman滤波算法在目标运动模型和观测模型已知且符合线性高斯分布的情况下,可以提供较高的成功率。然而,当目标存在非线性动力学模型或观测噪声的非高斯性质时,Kalman滤波算法的追踪效果可能会下降,成功率也会降低。 Horn-Schunck光流跟踪算法考虑了光流连续性约束,通过最小化运动场景的光流梯度的平方来进行光流估计。这种算法可以较好地处理光照变化以及物体表面运动的不连续性。Horn-Schunck算法在物体表面的显著变化和相机运动快速时表现较差,成功率相对较低。 综上所述,不同的追踪算法在不同的场景下具有不同的成功率。针对不同的目标运动模式和环境条件,选择合适的跟踪算法是保证成功率的关键。+ ### 回答3: Lucas-Kanade光流跟踪算法是一种基于局部邻域的光流估计方法。它将图像的像素值变化视为一个小的运动向量,并利用目标周围像素的灰度值变化对目标的运动进行估计。这种算法适用于文理丰富的目标,并具有较高的跟踪成功率。 Kalman滤波是一种递归滤波器,常用于估计目标的状态并对其运动进行预测。它通过引入过程噪声和测量噪声的模型来减少噪声的影响,从而提高目标跟踪的成功率。Kalman滤波在目标运动模型已知的情况下表现良好,但在目标运动模型未知或非线性时可能存在一定的限制。 Horn-Schunck光流跟踪算法是一种基于全局光流约束的方法。它假设图像整体上是平滑的,通过最小化目标图像区域的灰度差异和光流分量的平方差来优化光流场。相对于Lucas-Kanade算法,Horn-Schunck算法能够应对光照和噪声的变化,但对于目标存在快速运动或文理不平滑的情况,其成功率可能会降低。 总体而言,Lucas-Kanade光流跟踪算法在适应文理丰富的目标方面表现较好;Kalman滤波在已知运动模型下能够有效预测目标的运动;Horn-Schunck光流跟踪算法适用于处理光照和噪声变化,但对于快速运动或不平滑的目标可能有限。因此,根据具体的应用场景和目标特征,选择合适的算法可以提高跟踪的成功率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值