图像特征提取(四)——FAST算法解析

旧城等待, 2022-05-20 00:20 209阅读 0赞

文章转载自:http://blog.csdn[.NET][]/luoshixian099/article/details/48294967

https://blog.csdn.net/kezunhai/article/details/11290749

https://blog.csdn.net/hujingshuang/article/details/46898007

简介

  1. 在局部特征点检测快速发展的时候,人们对于特征的认识也越来越深入,近几年来许多学者提出了许许多多的特征检测算法及其改进算法,在众多的特征提取算法中,不乏涌现出佼佼者。
  2. 从最早期的Moravec,到Harris,再到**SIFTSUSANGLOHSURF**算法,可以说**特征提取算法**层出不穷。各种改进算法PCA-SIFTICA-SIFTP-ASURFR-ASURFRadon-SIFT等也是搞得如火如荼,不亦乐乎。上面的算法如SIFTSURF提取到的特征也是非常优秀(有较强的不变性),但是时间消耗依然很大,而在一个系统中,特征提取仅仅是一部分,还要进行诸如配准、提纯、融合等后续算法。这使得实时性不好,降系了统性能。
  3. Edward RostenTom Drummond两位大神经过研究,于2006年在《Machine learning for high-speed corner detection》中提出了一种FAST特征点,并在2010年稍作修改后发表了《Features From Accelerated Segment Test》,简称[FAST][]。由于不涉及尺度,梯度,等复杂运算,Fast检测器速度非常快。**它使用一定邻域内像元的灰度值与中心点比较大小去判断是否为一个角点。但它的缺点是不具有方向性,尺度不变性。**

注意:FAST只是一种特征点检测算法,并不涉及特征点的特征描述。

FAST详解

  1. FAST:选择的特征点***很接近角点 ***
  2. 比较中间点与半径3.4的圆经过的邻域上的连续的12/9个点的灰度值,若**有连续129个邻域像素值都大于或都小于中心像素点,则认为是候选特征点**;用ID3信息增益进行决策树的训练;通过定义特征点响应函数进行角点的非极大值抑制;配合金字塔模型,可以对不同尺度的图像进行特征点检测,结果总和为最终结果;

FAST特征的定义

  1. FAST的提出者Rosten等将FAST角点定义为:若某像素与其周围邻域内足够多的像素点相差较大,则该像素可能是角点。

FAST算法的步骤

FAST算法包含3个主要步骤:

  1. 1)对固定半径圆上的像素进行分割测试,通过逻辑测试可以去处大量的非特征候选点;
  2. 2)基于分类的角点特征检测,利用ID3 分类器根据16个特征判决候选点是否为角点特征,每个特征的状态为一101
  3. 3)利用非极大值抑制进行角点特征的验证。

下面我们对这3个步骤进行分析。

  1. **1)固定半径圆上的像素进行分割测试(Segment Test)**
  2. 在分割测试中,可以去除大量的非候选角点,这样就可以把可能的角点帅选出来。分割测试是通过对一固定半径的圆形模板的比较和计算进行的,在FAST角点检测算子中,一般是通过半径为3.4 pixel、外围16个像素的圆的作为模板,通过下图我们来具体分析下分割测试。

Fast角点提取步骤(以Fast-12-16为例):

  1. ![20150906221347280][]

1.以固定半径为圆的边上取16个像素点(图中白色框出的位置),与中心点像素值Ip做差。

  1. ![1333094316_3973.png][]

2.若边上存在连续的12(N>12,若为Fast-9,只需要N>9)个点满足 ( I(x)-I(p) )>threshold 或者 ( I(x)-I(p) ) < -threshold。(其中I(x)表示边上的像素值,I(p)为中心点像素值,threshold为设定的阈值。)则此点作为一个候选角点。为什么选择N =12点,因为通过测试,12点的角点检测性能最稳定、速度更快、效果也很好,当然有些文献指出9点的方式也很好。分割测试是怎么进行的呢?用下面的公式来说,便可一目了然。

SouthEast

  1. 上式中,t是一个阈值(默认取值为10,不同场景取值有差异),Ip表示的是中心像素的像素值,Ip->x表示的是圆形模板中的像素值上式的意思是:当中心像素的像素值Ip小于x处的像素值Ip->x+t时,则该像素属于darkerSp->x=d,其他两种情况分别表示亮些和相似。这样一个块(圆形)区域就可以分成dsb三种类型。这时候只要统计圆形区域中db的次数,只要db出现的次数大于n((当是12点分割测试角点检测时,n=12;当是9点时,则n=9),那么该点就被认为是候选角点。
  2. 在分割测试步骤中,为了加快速度,其实不需要对这些像素进行逐一的比较。简单来说:首先比较15913处点的像素值(也即水平方向和垂直方向上的4个点)与中心像素值的大小,如果这四个点中的像素值有3个或3个以上大于Ip+t或小于Ip-t,那么则认为该处是一个候选角点,否则就不可能是角点。

2)ID3决策树算法来训练角点检测

  1. 通过上式中比较,可以将模板内的像素分成三部分dsb,分别记为:PdPsPb。因此对于每个Sp->x都属于PdPsPb中的一个。另外,令Kptrue,如果p为角点,否则为false。通过ID3算法来选择具有最大信息增益的像素来判断一个像素是否为角点。Kp的熵用下式来计算:

SouthEast 1

某一像素的信息增益通过下式来表示:

SouthEast 2

对上述像素依次进行如上处理,选择像素增益最大的像素作为判断角点的依据,生成决策树,从而实现角点的正确分类。

3)非极大值抑制:排除不稳定角点

  1. 在上面的分割测试中,没有计算角点响应函数(Corner Response Function),非极大值抑制无法直接应用于提取的特征。因此,定义一个角点响应函数V,考虑到分割测试的特征以及计算速度的需要,角点响应函数的定义如下:
  2. ![20150906222532671][]

即一个角点强度值定义为中心点与边上的12个角点像素差值的绝对值累加和。定义了角点响应函数后,就可以采用常规的非极大值抑制方法对非角点进行排除了。

  1. **总结**:FAST角点检测算法是一种具有高计算效率(computational performance)、高可重复性(high repeatability)特征提取算子,在立体图像匹配、图像配准、目标识别、目标跟踪、场景重构等领域得到了广泛的应用,成为计算机视觉领域最流行的角点检测方法。但是,噪声对该算子的影响比较大,而且阈值**t**对算子的影响比较大。
  2. ![20150908190335527][]

OpenCV源码解析:

同上面原理部分不同,opencv中默认采用Fast-9-16(还包括Fast-5-8,Fast-7-12).即在周围取16个像素点,若超过连续9个点与中心点差值大于阈值即成为候选角点。

角点强度计算方法不采用上面的公式所描述,而是采用最小的差值(见代码分析)作为其角点强度值。

[cpp] view plain copy

  1. #include
  2. #include
  3. #include “cv.h”
  4. #include “opencv2/highgui/highgui.hpp”
  5. #include “opencv2/core/core.hpp”
  6. #include “opencv2/features2d/features2d.hpp”
  7. using namespace std;
  8. using namespace cv;
  9. int main( int argc, char** argv )
  10. {
  11. Mat img_1 = imread( “F:\\Picture\\hotel.jpg”);
  12. if( !img_1.data )
  13. {
  14. return -1; }
  15. FastFeatureDetector detector(50,true); //第一个参数为阈值,第二个采用非最大值抑制
  16. std::vector keypoints_1;
  17. detector.detect( img_1, keypoints_1 );//调用FAST_t函数检测,见下面详细解析
  18. drawKeypoints(img_1,keypoints_1,img_1,Scalar::all(255));
  19. imshow(“HOTEL”,img_1);
  20. waitKey(0);
  21. return 0;
  22. }

[cpp] view plain copy

  1. void FAST_t(InputArray _img, std::vector& keypoints, int threshold, bool nonmax_suppression)
  2. {
  3. Mat img = _img.getMat();
  4. const int K = patternSize/2, N = patternSize + K + 1;
  5. int i, j, k, pixel[25];
  6. makeOffsets(pixel, (int)img.step, patternSize);
  7. keypoints.clear();
  8. threshold = std::min(std::max(threshold, 0), 255);//保证阈值在0-255之间。
  9. uchar threshold_tab[512];
  10. for( i = -255; i <= 255; i++ )
  11. threshold_tab[i+255] = (uchar)(i < -threshold ? 1 : i > threshold ? 2 : 0); //分类成为darker、similar、brighter三种
  12. AutoBuffer _buf((img.cols+16)*3*(sizeof(int) + sizeof(uchar)) + 128);
  13. uchar* buf[3];
  14. buf[0] = _buf; buf[1] = buf[0] + img.cols; buf[2] = buf[1] + img.cols;//保存对应角点强度值,否则为0
  15. int* cpbuf[3];
  16. cpbuf[0] = (int*)alignPtr(buf[2] + img.cols, sizeof(int)) + 1;//保存角点位置,+1为了存储这一行的角点总数
  17. cpbuf[1] = cpbuf[0] + img.cols + 1;
  18. cpbuf[2] = cpbuf[1] + img.cols + 1;
  19. memset(buf[0], 0, img.cols*3);
  20. for(i = 3; i < img.rows-2; i++)
  21. {
  22. const uchar* ptr = img.ptr(i) + 3;
  23. uchar* curr = buf[(i - 3)%3];
  24. int* cornerpos = cpbuf[(i - 3)%3];
  25. memset(curr, 0, img.cols);
  26. int ncorners = 0;
  27. if( i < img.rows - 3 )
  28. {
  29. j = 3;
  30. /*采用9点分割测试,加快检测速度
  31. 检测任意一个直径两端的像素点,若同时与中心点相似,必定不是角点
  32. 因为至少要占一半的数量
  33. */
  34. for( ; j < img.cols - 3; j++, ptr++ )
  35. {
  36. int v = ptr[0];
  37. const uchar* tab = &threshold_tab[0] - v + 255;
  38. int d = tab[ptr[pixel[0]]] | tab[ptr[pixel[8]]];//
  39. if( d == 0 ) // 加快检测速度[0]与[8]两个点都与中心点灰度值相近,排除这个点
  40. continue;
  41. d &= tab[ptr[pixel[2]]] | tab[ptr[pixel[10]]];//直径两端两个点都相近,则为0
  42. d &= tab[ptr[pixel[4]]] | tab[ptr[pixel[12]]];//
  43. d &= tab[ptr[pixel[6]]] | tab[ptr[pixel[14]]];//每隔45度选取一个点
  44. if( d == 0 ) //
  45. continue;
  46. d &= tab[ptr[pixel[1]]] | tab[ptr[pixel[9]]];
  47. d &= tab[ptr[pixel[3]]] | tab[ptr[pixel[11]]];
  48. d &= tab[ptr[pixel[5]]] | tab[ptr[pixel[13]]];
  49. d &= tab[ptr[pixel[7]]] | tab[ptr[pixel[15]]];
  50. if( d & 1 ) // darker 中心值大,周围小的情况
  51. {
  52. int vt = v - threshold, count = 0;
  53. for( k = 0; k < N; k++ ) //且连续一半的像素点灰度差值( v-x > threshold )大于阈值
  54. {
  55. int x = ptr[pixel[k]];
  56. if(x < vt)
  57. {
  58. if( ++count > K )
  59. {
  60. cornerpos[ncorners++] = j;
  61. if(nonmax_suppression)//非最大值抑制
  62. curr[j] = (uchar)cornerScore(ptr, pixel, threshold);//计算角点的强度响应值,最小的差值(绝对值)
  63. break;
  64. }
  65. }
  66. else
  67. count = 0;
  68. }
  69. }
  70. if( d & 2 )//brighter 中心值小,周围值大的情况
  71. {
  72. int vt = v + threshold, count = 0;
  73. for( k = 0; k < N; k++ ) //连续一半的像素点灰度差值( x-v < threshold )大于阈值
  74. {
  75. int x = ptr[pixel[k]];
  76. if(x > vt)
  77. {
  78. if( ++count > K )
  79. {
  80. cornerpos[ncorners++] = j;
  81. if(nonmax_suppression)
  82. curr[j] = (uchar)cornerScore(ptr, pixel, threshold);//计算角点的强度响应值,最小的差值(绝对值)
  83. break;
  84. }
  85. }
  86. else
  87. count = 0;
  88. }
  89. }
  90. }
  91. }
  92. cornerpos[-1] = ncorners;//存储第i行上的角点总数量
  93. if( i == 3 )
  94. continue;
  95. /*与邻域的8个角点响应值做比较,非角点的响应值为0*/
  96. const uchar* prev = buf[(i - 4 + 3)%3]; //相邻的两行
  97. const uchar* pprev = buf[(i - 5 + 3)%3];//
  98. cornerpos = cpbuf[(i - 4 + 3)%3];//存储角点的列位置
  99. ncorners = cornerpos[-1]; //存储第i行上的角点总数量
  100. for( k = 0; k < ncorners; k++ )
  101. {
  102. j = cornerpos[k];
  103. int score = prev[j];
  104. if( !nonmax_suppression || //非极大值抑制,用角点强度值比较周围8个强度响应值
  105. (score > prev[j+1] && score > prev[j-1] &&
  106. score > pprev[j-1] && score > pprev[j] && score > pprev[j+1] &&
  107. score > curr[j-1] && score > curr[j] && score > curr[j+1]) )
  108. {
  109. keypoints.push_back(KeyPoint((float)j, (float)(i-1), 7.f, -1, (float)score));
  110. }
  111. }
  112. }
  113. }

角点的强度计算方法:若采用Fast-9-16,计算连续的9个位置与中心位置的差值的绝对值,取最小的一个差值作为其强度值。

[cpp] view plain copy

  1. int cornerScore<16>(const uchar* ptr, const int pixel[], int threshold)//角点强度计算
  2. {
  3. const int K = 8, N = K*3 + 1;
  4. int k, v = ptr[0];
  5. short d[N];
  6. for( k = 0; k < N; k++ ) //计算与周围16个像素点的差值,保存在d[k]中
  7. d[k] = (short)(v - ptr[pixel[k]]);
  8. int a0 = threshold;
  9. for( k = 0; k < 16; k += 2 ) //周围像素小于中心点像素
  10. {
  11. int a = std::min((int)d[k+1], (int)d[k+2]);
  12. a = std::min(a, (int)d[k+3]);
  13. if( a <= a0 )
  14. continue;
  15. a = std::min(a, (int)d[k+4]);
  16. a = std::min(a, (int)d[k+5]);
  17. a = std::min(a, (int)d[k+6]);
  18. a = std::min(a, (int)d[k+7]);
  19. a = std::min(a, (int)d[k+8]);
  20. a0 = std::max(a0, std::min(a, (int)d[k]));
  21. a0 = std::max(a0, std::min(a, (int)d[k+9]));
  22. }
  23. int b0 = -a0;
  24. for( k = 0; k < 16; k += 2 )//周围像素点大于中心像素点
  25. {
  26. int b = std::max((int)d[k+1], (int)d[k+2]);
  27. b = std::max(b, (int)d[k+3]);
  28. b = std::max(b, (int)d[k+4]);
  29. b = std::max(b, (int)d[k+5]);
  30. if( b >= b0 )
  31. continue;
  32. b = std::max(b, (int)d[k+6]);
  33. b = std::max(b, (int)d[k+7]);
  34. b = std::max(b, (int)d[k+8]);
  35. b0 = std::min(b0, std::max(b, (int)d[k]));
  36. b0 = std::min(b0, std::max(b, (int)d[k+9]));
  37. }
  38. threshold = -b0-1;
  39. return threshold;
  40. }

实验

opencv代码

  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. using namespace cv;
  7. using namespace std;
  8. int main()
  9. {
  10. Mat frame=imread(“lena.jpg”, 1);
  11. double t = getTickCount();//当前滴答数
  12. std::vector keyPoints;
  13. FastFeatureDetector fast(50); // 检测的阈值为50
  14. fast.detect(frame, keyPoints);
  15. drawKeypoints(frame, keyPoints, frame, Scalar(0,0,255), DrawMatchesFlags::DRAW_OVER_OUTIMG);
  16. t = ((double)getTickCount() - t)/getTickFrequency();
  17. cout<<”算法用时:”<<t<<”秒”<<endl;
  18. imshow(“FAST特征点”, frame);
  19. cvWaitKey(0);
  20. return 0;
  21. }

输出结果:

20150715212751657

MATLAB代码

再上一个自己编写的MATLAB代码,没有进行非极大值抑制,效果不及opencv,而且检测出的角点有一定的出入,应该是opencv内部做了一定的优化。

  1. clear all;
  2. close all;
  3. %%
  4. pic=imread(‘lena.jpg’);
  5. img=pic;
  6. [M N D]=size(pic);
  7. if D==3
  8. pic=rgb2gray(pic);
  9. end
  10. %%
  11. mask=[0 0 1 1 1 0 0;…
  12. 0 1 0 0 0 1 0;…
  13. 1 0 0 0 0 0 1;…
  14. 1 0 0 0 0 0 1;…
  15. 1 0 0 0 0 0 1;…
  16. 0 1 0 0 0 1 0;…
  17. 0 0 1 1 1 0 0];
  18. mask=uint8(mask);
  19. threshold=50;
  20. figure;imshow(img);title(‘FAST角点检测’);hold on;
  21. tic;
  22. for i=4:M-3
  23. for j=4:N-3%若I1、I9与中心I0的差均小于阈值,则不是候选点
  24. delta1=abs(pic(i-3,j)-pic(i,j))>threshold;
  25. delta9=abs(pic(i+3,j)-pic(i,j))>threshold;
  26. delta5=abs(pic(i,j+3)-pic(i,j))>threshold;
  27. delta13=abs(pic(i,j-3)-pic(i,j))>threshold;
  28. if sum([delta1 delta9 delta5 delta13])>=3
  29. block=pic(i-3:i+3,j-3:j+3);
  30. block=block.*mask;%提取圆周16个点
  31. pos=find(block);
  32. block1=abs(block(pos)-pic(i,j))/threshold;
  33. block2=floor(block1);
  34. res=find(block2);
  35. if size(res,1)>=12
  36. plot(j,i,’ro’);
  37. end
  38. end
  39. end
  40. end
  41. toc;
  42. %%

输出结果:

20150715213333604

参考文章

  1. Edward Rosten et.:Machine Learning for High-Speed Corner Detection

http://blog.csdn[.Net][.NET]/kezunhai/article/details/11290749

http://www.edwardrosten.com/work/fast.html

1、FAST Corner Detection — Edward Rosten:http://www.edwardrosten.com/work/fast.html

2、A Brief History of FAST corner detector:http://blog.csdn.net/anshan1984/article/details/8867653

3、AGAST Corner Detector: faster than FAST and even FAST-ER:http://www6.in.tum.de/Main/ResearchAgast

1、Machine learning for high-speed corner detection[J],2006.

2、Features From Accelerated Segment Test[J],2010.

3、基于自适应阈值的FAST特征点提取算法[J],2013

发表评论

表情:
评论列表 (有 0 条评论,209人围观)

还没有评论,来说两句吧...

相关阅读

    相关 图像局部特征提取

    图像特征可以包括颜色特征、纹理特征、形状特征以及局部特征点等。其中局部特点具有很好的稳定性,不容易受外界环境的干扰。图像特征提取是图像分析与图像识别的前提,它是将高维的图像数据

    相关 图像特征提取

    从本节开始, 我们将逐步从数字图像处理向图像识别过渡。 严格地说, 图像特征提取属于图像分析的范畴, 是数字图像处理的高级阶段, 同时也是图像识别的开始。 本文主要包括以下内

    相关 Matlab 图像特征提取

    在图像处理过程中,尤其是图像相似度的匹配,在图片量比较小的情况下,深度学习的效果往往达不到期望,所以需要利用传统图像处理的方法,对图像特征进行提取,常用的方法有lbp,hog,