FAST角点检测原理及其C/C++实现

来源:互联网 发布:淘宝优惠券在哪领 编辑:程序博客网 时间:2024/05/22 17:44

1. Fast算法原理

        博客中已经介绍了很多图像特征检测算子,我们可以用LoG或者DoG检测图像中的Blobs(斑点检测),可以根据图像局部的自相关函数来求得Harris角点(Harris角点),后面又提到了两种十分优秀的特征点及它们的描述方法SIFT特征与SURF特征。SURF特征算是为了提高运算效率对SIFT特征的一种近似,虽然在有些实验环境中已经达到了实时,但是我们实践工程应用中,特征点的提取与匹配只是整个应用算法中的一部分,所以我们对于特征点的提取必须有更高的要求,从这一点来看前面介绍的的那些特征点方法都不可取。

        为了解决这个问题,Edward RostenTom Drummond2006年发表的“Machine learning for high-speed cornerdetection[1]”文章中提出了一种FAST特征,并在2010年对这篇论文作了小幅度的修改后重新发表[2]FAST的全称为Features From Accelerated Segment TestRosten等人将FAST角点定义为:若某像素点与其周围领域内足够多的像素点处于不同的区域,则该像素点可能为角点。也就是某些属性与众不同,考虑灰度图像,即若该点的灰度值比其周围领域内足够多的像素点的灰度值大或者小,则该点可能为角点。

2. FAST算法步骤

(1) 从图片中选取一个像素P,下面我们将判断它是否是一个特征点。我们首先把它的亮度值设为I_p

(2) 设定一个合适的阈值t

(3)考虑以该像素点为中心的一个半径等于3像素的离散化的Bresenham圆,这个圆的边界上有16个像素(如图所示)。


(4)现在,如果在这个大小为16个像素的圆上有n个连续的像素点,它们的像素值要么都比I_p + t大,要么都比I_p - t小,那么它就是一个角点。(如图中的白色虚线所示)。n的值可以设置为12或者9,实验证明选择9可能会有更好的效果。


        上面的算法中,对于图像中的每一个点,我们都要去遍历其邻域圆上的16个点的像素,效率较低。我们下面提出了一种高效的测试(high-speed test)来快速排除一大部分非角点的像素。该方法仅仅检查在位置19513四个位置的像素,首先检测位置1和位置9,如果它们都比阈值暗或比阈值亮,再检测位置5和位置13。如果$P$是一个角点,那么上述四个像素点中至少有3个应该必须都大于$I_p+t$或者小于$I_p-t$,因为若是一个角点,超过四分之三圆的部分应该满足判断条件。如果不满足,那么$p$不可能是一个角点。对于所有点做上面这一部分初步的检测后,符合条件的将成为候选的角点,我们再对候选的角点,做完整的测试,即检测圆上的所有点。

上面的算法效率实际上是很高的,但是有点一些缺点:

1.n<12时不能拒绝许多的候选点;

2.检测出来的角点不是最优的,这是因为它的效率取决于问题的排序与角点的分布;

3.  对于角点分析的结果被丢弃了;

4.  多个特征点容易挤在一起。

3.非极大值抑制

从邻近的位置选取了多个特征点是另一个问题,我们可以使用Non-Maximal Suppression来解决。

1.   为每一个检测到的特征点计算它的响应大小(score functionV。这里V定义为点p和它周围16个像素点的绝对偏差的和。

2.   考虑两个相邻的特征点,并比较它们的V值。

3.   V值较低的点将会被删除。

4. OpenCV中进行FAST特征检测

         在OpenCV中进行FAST特征提取的函数为FAST。它一共有4个参数,第一个参数是输入的图像,第二个是返回的特征点,第三个是定义的阈值,第四个决定是否使用非极大值抑制。


void FAST(InputArray image,vector<KeyPoint>& keypoints,int threshold,bool nonmaxSuppression=true )

#include <opencv2/core/core.hpp> 
#include <opencv2/highgui/highgui.hpp> 
#include <opencv2/imgproc/imgproc.hpp> 
#include <opencv2/features2d/features2d.hpp>
 
using namespace cv; 
using namespace std;
 
int main(int argc, char** argv) 
{ 
  Mat img = imread("box.png"); 
  std::vector<KeyPoint> keypoints; 
  FAST(img, keypoints, 20); 
  //-- Draw keypoints 
  Mat img_keypoints; 
  drawKeypoints(img, keypoints, img_keypoints, Scalar::all(-1), DrawMatchesFlags::DEFAULT); 
  //-- Show detected (drawn) keypoints 
  imshow("Keypoints", img_keypoints);
 
  waitKey(0); 
  return0; 
}
  

5. Fast角点检测的C/C++实现

#include "stdio.h"

#include <opencv2/opencv.hpp>

#include <opencv2/core/core.hpp> 

#include<opencv2/highgui/highgui.hpp> 

 

using namespace std;

using namespace cv;

 

 

struct Corner

{

    Pointcorner;

    intscore;

};

 

int main()

{

    intp0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16;

 

    intt = 50;    //阈值

 

    Matgray, image = imread("C:\\Users\\Administrator\\Pictures\\桌椅.jpg"),

    image1= imread("C:\\Users\\Administrator\\Pictures\\桌椅.jpg");

    cvtColor(image,gray, CV_BGR2GRAY);  //将图像转为单通道灰度图

 

    imshow("灰度图", gray);

    cvWaitKey(0);

 

    intn = 0;

    intcornersnum = 0;    //角点数

    //定义结构体数组,用来存储角点坐标及其得分(角点与其周围16点像素绝对差之和)

    Cornercorners[2000];

   //定义数组存储最终角点坐标  

    Pointcornerpoints[2000];

    for(int i = 3; i < gray.rows - 3; i++)

    {

        for(int j = 3; j < gray.cols - 3; j++)

        {

            uchar*data1 = gray.ptr<uchar>(i - 3);                       

//获取待测点及其领域16点的像素值

            p1= data1[j]; p2 = data1[j + 1]; p16 = data1[j - 1];

            uchar*data2 = gray.ptr<uchar>(i - 2);

            p3= data2[j + 2]; p15 = data2[j - 2];

            uchar*data3 = gray.ptr<uchar>(i - 1);

            p4= data3[j + 3]; p14 = data3[j - 2];

            uchar*data4 = gray.ptr<uchar>(i);

            p5= data4[j + 3]; p13 = data4[j - 3]; p0 = data4[j];

            uchar*data5 = gray.ptr<uchar>(i + 1);

            p6 = data5[j + 3]; p12 = data5[j - 3];

            uchar*data6 = gray.ptr<uchar>(i + 2);

            p7= data6[j + 2]; p11 = data6[j - 2];

            uchar*data7 = gray.ptr<uchar>(i + 3);

            p8= data7[j + 1]; p10 = data7[j - 1]; p9 = data7[j];

 

 

            //考察1、5、9、13四点,若有三点及三点以上满足角点条件(大于p0+ t或小于p0- t),则待测点可能是角点

 

            intm01, m02, m03, m04;

            inttu = p0 + t, tl = p0 - t;

 

            m01= ((p1 > tu) || (p1 < tl)); m02 = ((p5 > tu) || (p5 < tl));

            m03= ((p9 > tu) || (p9 < tl)); m04 = ((p13 > tu) || (p13 < tl));

 

            intm = m01 + m02 + m03 + m04;

 

            if(m>2) //满足条件则为待选角点,考察全部16点像素

            {

                intm1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16;

 

                m1= m01; m5 = m02; m9 = m03; m13 = m04;

                m2= ((p2 > tu) || (p2 < tl)); m3 = ((p3 > tu) || (p3 < tl));

                m4= ((p4 > tu) || (p4 < tl)); m6 = ((p6 > tu) || (p6 < tl));

                m7= ((p7 > tu) || (p7 < tl)); m8 = ((p8 > tu) || (p8 < tl));

                m10= ((p10 > tu) || (p10 < tl)); m11 = ((p11 > tu) || (p11 < tl));

                m12= ((p12 > tu) || (p12 < tl)); m14 = ((p14 > tu) || (p14 < tl));

                m15= ((p15 > tu) || (p15 < tl)); m16 = ((p16 > tu) || (p16 < tl));

 

                intmt1, mt2, mt3, mt4, mt5, mt6, mt7, mt8, mt9, mt10, mt11, mt12, mt13, mt14,mt15, mt16;

 

                mt1= m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9; mt2 = m10 + m2 + m3 + m4 + m5 +m6 + m7 + m8 + m9;

                mt3= m10 + m11 + m3 + m4 + m5 + m6 + m7 + m8 + m9; mt4 = m10 + m11 + m12 + m4 + m5+ m6 + m7 + m8 + m9;

                mt5= m10 + m11 + m12 + m13 + m5 + m6 + m7 + m8 + m9; mt6 = m10 + m11 + m12 + m13 +m14 + m6 + m7 + m8 + m9;

                mt7= m10 + m11 + m12 + m13 + m14 + m15 + m7 + m8 + m9; mt8 = m10 + m11 + m12 + m13+ m14 + m15 + m16 + m8 + m9;

                mt9= m10 + m11 + m12 + m13 + m14 + m15 + m16 + m1 + m9; mt10 = m10 + m11 + m12 +m13 + m14 + m15 + m16 + m1 + m2;

                mt11 = m3 + m11 + m12 + m13 + m14 + m15 +m16 + m1 + m2; mt12 = m3 + m4 + m12 + m13 + m14 + m15 + m16 + m1 + m2;

                mt13= m3 + m4 + m5 + m13 + m14 + m15 + m16 + m1 + m2; mt14 = m3 + m4 + m5 + m6 +m14 + m15 + m16 + m1 + m2;

                mt15= m3 + m4 + m5 + m6 + m7 + m15 + m16 + m1 + m2; mt16 = m3 + m4 + m5 + m6 + m7 +m8 + m16 + m1 + m2;

 

                //考察全部16点像素,若有连续9点满足条件,则可认为待测点为角点

 

                if((mt1 == 9) || (mt2 == 9) || (mt3 == 9) || (mt4 == 9) || (mt5 == 9) || (mt6 ==9) || (mt7 == 9) || (mt8 == 9) || (mt9 == 9) || (mt10 == 9) || (mt11 == 9) ||(mt12 == 9) || (mt13 == 9) || (mt14 == 9) || (mt15 == 9) || (mt16 == 9))

                {  

                    if(n < 2000)

                    {

                        cornersnum++;

                        intdet=0;

//计算得分值(角点与其周围16点像素绝对差之和)

                        det= abs(p0 - p1) + abs(p0 - p2) + abs(p0 - p3) + abs(p0 - p4) +

                            abs(p0- p5) + abs(p0 - p6) + abs(p0 - p7) + abs(p0 - p8) +

                            abs(p0- p9) + abs(p0 - p10) + abs(p0 - p11) + abs(p0 - p12) +

                            abs(p0- p13) + abs(p0 - p14) + abs(p0 - p15) + abs(p0 - p16);

                        corners[n].corner.x= j;

                        corners[n].corner.y = i;

                        corners[n].score= det;

                        n++;

//画出角点

                        circle(image,Point(j, i), 5, Scalar(0, 0, 255), 1, CV_AA, 0);

                    }

                }

 

 

            }

 

        }

    }

 

    imshow("角点检测结果", image);

    cvWaitKey(0);

 

    //非极大值抑制 取角点(2m-1)x(2m-1)的领域

    for(int i = 0; i < cornersnum; i++)

    {

        intloc = i;

       int  m = 60;

        intmaxdet = corners[i].score;

        for(int j = 0; j < cornersnum; j++)

        {

            intx1 = corners[i].corner.x + m, x2 = corners[i].corner.x - m;

            inty1 = corners[i].corner.y + m, y2 = corners[i].corner.y - m;

            if((corners[j].corner.x>x2) && (corners[j].corner.x<x1) &&(corners[j].corner.y>y2) && (corners[j].corner.y < y1))

            {

                if(corners[j].score>maxdet)

                {

                    maxdet= corners[j].score;

                    loc= j;

                }  

            }

        }

        circle(image1,corners[loc].corner, 5, Scalar(0, 0, 255), 1, CV_AA, 0);

      //取得分最高的角点为最终角点

        cornerpoints[i]= corners[loc].corner;

    }

 

    imshow("非极大值抑制后角点",image1);

    cvWaitKey(0);

 

    return0;

}

 

程序运行结果:






6. 总结

       FAST算法比其他已知的角点检测算法要快很多倍,但是当图片中的噪点较多时,它的健壮性并不好,而且算法的效果还依赖于一个阈值$t$。而且FAST不产生多尺度特征而且FAST特征点没有方向信息,这样就会失去旋转不变性。


原创粉丝点击