opencv实现一种改进的Fast特征检测算法

Posted Wiley_Li

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv实现一种改进的Fast特征检测算法相关的知识,希望对你有一定的参考价值。

引言

  之前了解了Fast算法之后使用opencv自己实现了下,具体见http://www.cnblogs.com/Wiley-hiking/p/6898049.html。不过算法也有缺点,主要就是对边缘点和噪点的抗干扰能力不强。在《基于FAST改进的快速角点探测算法》文章中作者提出一种改进的Fast角点算法,提高算法的稳定性和抗干扰能力。自己读完后使用opencv实现了该算法,这里将学习过程进行一个记录。

 

1.原始Fast检测算法

    有关原始Fast检测算法,自己写的小结在http://www.cnblogs.com/Wiley-hiking/p/6898049.html。

 

2.改进的Fast检测算法

    文章中指出,原始Fast检测算法优点是计算量小,缺点是(1)算法阈值需要人工设定,适应性不好(2)原始算法会提取得到很多边缘点或者局部非极大值点。针对上述问题,作者提出改进的Fast检测算法,主要步骤包括:

(1)根据图像性质自适应确定Fast检测算法中的阈值;作者提出使用图像中像素灰度值前i个最大值和前i个最小值差值和,乘以一个系数结果作为阈值。

 

 

(2)使用(1)得到的阈值进行Fast检测角点得到候选点

(3)对于(2)得到的候选点,利用Hessian矩阵去除边缘点

(4)使用拉普拉斯极值排除局部非极大值点

  现在详细说明各步骤的具体实现。

2.1自适应确定阈值

    这里需要得到图像像素中前10个最大值和前10个最小值,直观感受就是——这是一个排序问题呀。从网上搜索一些有关排序的文章温习下,确定使用下述方法实现

 (1)得到前10个最大值;首先读取图像中前10个像素灰度值保存到数组并进行降序排列(升序排列也可以,使用改进的冒泡法实现,参考http://blog.csdn.net/cbs612537/article/details/8294960/);之后依次读取剩下的像素,每读入一个像素,做如下操作:将该像素灰度值与前面10个像素组合并重新降序排列并将最小值剔除,得到新的降序排列数组(仍然包含10个像素);重复上述步骤直至读取完所有像素,最终的数组包含的10个像素灰度值就是前10个最大值

  (2)得到前10个最小值;步骤与上述类似,只不过将新的像素灰度值与前面10个像素组合重新排序后剔除最大值。

    这里我在将新像素合入数组再剔除最大值(最小值)的过程等效为新元素插入的过程。由于插入前数组就是有序的,我就使用了二分法进行插入并更新数组,提高算法效率。得到前10个最大值和前10个最小值之后按照上述方法得到Fast特征检测中需要用到的threshold。

2.2Fast特征检测

    带入2.1中得到的threshold进行计算,具体参考http://www.cnblogs.com/Wiley-hiking/p/6898049.html

2.3使用hessian矩阵去除边缘点

    有关hessian矩阵的介绍和应用,参考http://blog.csdn.net/lwzkiller/article/details/55050275。我们这里只需要知道,角点的Hessian矩阵两个特征值比较相近;而利用矩阵的特性我们可以在不求得特征值的情况下计算两个特征值的比值,这里直接给出应用结论如下。

  而我们是在离散的灰度值点上计算hessian矩阵,二阶导数的计算相对简化。有关离散函数二阶导数计算方面参考http://blog.csdn.net/xiaofengsheng/article/details/6023368

2.4使用拉普拉斯极值排除非局部极大值点

  由于Fast算法中可以利用膨胀与比较的组合实现非极大值的抑制,这里我就没有使用该步骤

 

3.opencv代码实现

   开发环境是vs2012+opencv2.4.13,源代码贴出来

  1 #include <iostream>
  2 #include <core/core.hpp>
  3 #include <highgui/highgui.hpp>
  4 #include <imgproc/imgproc.hpp>
  5 #include <features2d/features2d.hpp>
  6 #include <stdlib.h>
  7 
  8 using namespace std;
  9 using namespace cv;
 10 
 11 
 12 int getSum(uchar *p, int length)
 13 {
 14     int sum = 0;
 15     for(int i=0;i<length; i++)
 16     {
 17         sum += *(p+i);
 18     }
 19     return sum;
 20 }
 21 
 22 void bubbleSortUp(int *p)
 23 {
 24     uchar flag = 1;
 25     for(int i=0; i < 10 && flag; i++)
 26     {
 27         flag = 0;
 28         for(int j=9; j > i; j--)
 29         {
 30             if(p[j] < p[j-1])
 31             {
 32                 uchar tmp = p[j];
 33                 p[j] = p[j-1];
 34                 p[j-1] = tmp;
 35                 flag = 1;
 36             }
 37         }
 38     }
 39 }
 40 
 41 void bubbleSortDown(int *p)
 42 {
 43     uchar flag = 1;
 44     for(int i=0; i < 10 && flag; i++)
 45     {
 46         flag = 0;
 47         for(int j=9; j > i; j--)
 48         {
 49             if(p[j] > p[j-1])
 50             {
 51                 uchar tmp = p[j];
 52                 p[j] = p[j-1];
 53                 p[j-1] = tmp;
 54                 flag = 1;
 55             }
 56         }
 57     }
 58 }
 59 
 60 void printArray(int *p, int len)
 61 {
 62     for(int i=0; i<len; i++)
 63     {
 64         cout<<p[i]<<" ";
 65     }
 66     cout<<endl;
 67 }
 68 
 69 void binaryInsertUp(int *p, uchar value)
 70 {
 71     int left = 0;
 72     int right = 9;
 73     int mid;
 74     if(value < p[0])
 75         return;
 76     if(value > p[9])
 77     {
 78         for(int i=0; i<9; i++)
 79         {
 80             p[i] = p[i+1];
 81 
 82         }
 83         p[9] = value;
 84         return;
 85     }
 86 
 87     while(left < right)
 88     {
 89         mid = (left + right)/2;
 90         if(mid == left || mid == right)
 91             break;
 92         if(value < p[mid])
 93             right = mid;
 94         else
 95             left = mid;
 96     }
 97     for(int i = 0; i < left; i++)
 98     {
 99         p[i] = p[i+1];
100     }
101     p[left] = value;
102 }
103 
104 void binaryInsertDown(int *p, uchar value)
105 {
106     int left = 0;
107     int right = 9;
108     int mid;
109     if(value > p[0])
110         return;
111     if(value < p[9])
112     {
113         for(int i=0; i<9; i++)
114         {
115             p[i] = p[i+1];
116 
117         }
118         p[9] = value;
119         return;
120     }
121 
122     while(left < right)
123     {
124         mid = (left + right)/2;
125         if(mid == left || mid == right)
126             break;
127         if(value < p[mid])
128             left = mid;
129         else
130             right = mid;
131     }
132     for(int i = 0; i < left; i++)
133     {
134         p[i] = p[i+1];
135     }
136     p[left] = value;
137 }
138 
139 int main(int argc, char* argv[])
140 {
141     /* 1.读入图像 */
142     Mat image = imread("../church01.jpg", 0);
143     if(!image.data)
144         return 0;
145 
146     namedWindow("Original Image");
147     imshow("Original Image", image);
148 
149     Mat fastImg(image.size(), CV_8U, Scalar(0));//用于保存Fast检测的候选点
150     Mat fastScore(image.size(), CV_32F, Scalar(0));//用于计算候选点score
151     vector<Point> points;
152     int rows, cols, threshold;
153     rows = image.rows;
154     cols = image.cols;
155     threshold = 50;
156     int maxValues[10], minValues[10];
157 
158     /* 2.计算Fast算法中的阈值参数 */
159     Mat_<uchar>::const_iterator it = image.begin<uchar>();
160     int count = 0;
161     while(count < 10)
162     {
163         maxValues[count] = *it;
164         minValues[count] = *it;
165         count++;
166         it++;
167     }
168     bubbleSortUp(maxValues);
169     bubbleSortDown(minValues);
170 
171     while(it != image.end<uchar>())
172     {
173         int value = *it;
174         binaryInsertUp(maxValues, value);
175         binaryInsertDown(minValues, value);
176         it++;
177     }
178     printArray(maxValues, 10);
179     printArray(minValues, 10);
180 
181     int diff = 0;
182     for(int i = 0; i < 10; i++)
183     {
184         diff += maxValues[i] - minValues[i];
185     }
186     threshold = (int)(0.15f*diff/10.0);
187     
188     /* 3.使用Fast算法检测得到候选点 */
189     for(int x = 3; x < rows - 3; x++)
190     {
191         for(int y = 3; y < cols - 3; y++)
192         {
193             uchar delta[16] = {0};
194             uchar diff[16] = {0};
195             delta[0] = (diff[0] = abs(image.at<uchar>(x,y) - image.at<uchar>(x, y-3))) > threshold;
196             delta[8] = (diff[8] = abs(image.at<uchar>(x,y) - image.at<uchar>(x, y+3))) > threshold;
197             if(getSum(delta, 16) == 0)
198                 continue;
199             else
200             {
201                 delta[12] = (diff[12] = abs(image.at<uchar>(x,y) - image.at<uchar>(x-3, y))) > threshold;
202                 delta[4] = (diff[4] = abs(image.at<uchar>(x,y) - image.at<uchar>(x+3, y))) > threshold;
203                 if(getSum(delta, 16) < 3)
204                     continue;
205 
206                 else
207                 {
208                     delta[1] = (diff[1] = abs(image.at<uchar>(x,y) - image.at<uchar>(x+1, y-3))) > threshold;
209                     delta[2] = (diff[2] = abs(image.at<uchar>(x,y) - image.at<uchar>(x+2, y-2))) > threshold;
210                     delta[3] = (diff[3] = abs(image.at<uchar>(x,y) - image.at<uchar>(x+3, y-1))) > threshold;
211                                 
212                     delta[5] = (diff[5] = abs(image.at<uchar>(x,y) - image.at<uchar>(x+3, y+1))) > threshold;
213                     delta[6] = (diff[6] = abs(image.at<uchar>(x,y) - image.at<uchar>(x+2, y+2))) > threshold;
214                     delta[7] = (diff[7] = abs(image.at<uchar>(x,y) - image.at<uchar>(x+1, y+3))) > threshold;
215 
216                     delta[9] = (diff[9] = abs(image.at<uchar>(x,y) - image.at<uchar>(x-1, y+3))) > threshold;
217                     delta[10] = (diff[10] = abs(image.at<uchar>(x,y) - image.at<uchar>(x-2, y+2))) > threshold;
218                     delta[11] = (diff[11] = abs(image.at<uchar>(x,y) - image.at<uchar>(x-3, y+1))) > threshold;
219 
220                     delta[13] = (diff[13] = abs(image.at<uchar>(x,y) - image.at<uchar>(x-3, y-1))) > threshold;
221                     delta[14] = (diff[14] = abs(image.at<uchar>(x,y) - image.at<uchar>(x-2, y-2))) > threshold;
222                     delta[15] = (diff[15] = abs(image.at<uchar>(x,y) - image.at<uchar>(x-1, y-3))) > threshold;
223 
224                     if(getSum(delta, 16) >= 12)
225                     {
226                         points.push_back(Point(y,x));
227                         fastScore.at<float>(x,y) = getSum(diff, 16);
228                         fastImg.at<uchar>(x,y) = 255;
229                     }
230                 }
231             }
232         }
233 
234     }
235 
236     vector<Point>::const_iterator itp = points.begin();
237     while(itp != points.end())
238     {
239         circle(image, *itp, 3, Scalar(255), 1);
240         ++itp;
241     }
242     namedWindow("Fast Image");
243     imshow("Fast Image", image);//Fast检测候选点在原图中标记
244 
245     /* 4.局部非极大值抑制 */
246     image = imread("../church01.jpg", 0);
247     Mat dilated(fastScore.size(), CV_32F, Scalar(0));
248     Mat localMax;
249     //Mat element(7, 7, CV_8U, Scalar(1));
250     dilate(fastScore, dilated, Mat());
251     compare(fastScore, dilated, localMax, CMP_EQ);
252     bitwise_and(fastImg, localMax, fastImg);
253 
254     for(int x = 0;x < fastImg.cols; x++)
255     {
256         for(int y = 0; y < fastImg.rows; y++)
257         {
258             if(fastImg.at<uchar>(y,x))
259             {
260                 circle(image, Point(x,y), 3, Scalar(255), 1);
261             }
262         }
263     }
264     namedWindow("Fast Image2");
265     imshow("Fast Image2", image);//局部非极大值抑制后候选点在原图中标记
266     
267     /* 5.计算Hessian矩阵去除边缘点和不稳定点 */
268     image = imread("../church01.jpg", 0);
269     Mat cornerStrength(fastScore.size(), CV_64F, Scalar(0));
270     for(int x = 0;x < fastImg.rows; x++)
271     {
272         for(int y = 0; y < fastImg.cols; y++)
273         {
274             if(fastImg.at<uchar>(x,y))
275             {
276                 if((x>0) && (x<fastImg.rows-1)
277                     && (y>0) && (y<fastImg.cols-1))
278                 {
279                     double Ixx = image.at<uchar>(x+1,y) + image.at<uchar>(x-1,y)-2*image.at<uchar>(x,y);
280                     double Iyy = image.at<uchar>(x,y+1) + image.at<uchar>(x,y-1)-2*image.at<uchar>(x,y);
281                     double Ixy = image.at<uchar>(x+1,y+1) + image.at<uchar>(x,y)-image.at<uchar>(x,y+1)-image.at<uchar>(x+1,y);
282                     cornerStrength.at<double>(x,y) = (Ixx+Iyy)*(Ixx+Iyy)/(Ixx*Iyy-Ixy*Ixy);
283                 
284                 }
285             }
286         }
287     }
288     int t = 10;
289     Mat cornerMap;
290     cornerMap = cornerStrength > t;
291     image = imread("../church01.jpg", 0);
292 
293     for(int x = 0;x < cornerMap.cols; x++)
294     {
295         for(int y = 0; y < cornerMap.rows; y++)
296         {
297             if(cornerMap.at<uchar>(y,x))
298             {
299                 circle(image, Point(x,y), 3, Scalar(255), 1);
300 
301             }
302         }
303     }
304     namedWindow("improvedFast");
305     imshow("improvedFast", image);//最终检测得到的角点在原图中标记
306 
307     waitKey();
308     return 0;
309 }
View Code

 

4.算法效果

  图1是原始Fast检测出来的特征点,点数较多;图2是图1经过非极大值抑制后的结果,观察可发现局部非极大值点被剔除;图3是图2利用Hessian矩阵剔除边缘点后的结果,在图像下方边缘处效果比较明显。

 

 

5.参考文献

 [1]《基于FAST改进的快速角点探测算法》 燕鹏等

以上是关于opencv实现一种改进的Fast特征检测算法的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV 例程 300篇243. 特征检测之 FAST 算法

OpenCV 例程 300篇243. 特征检测之 FAST 算法

C# OpenCV FAST 特征检测

使用 OpenCV 改进特征点的匹配

OpenCV实战(17)——FAST特征点检测

OpenCV-Python教程:38.FAST角点检测算法