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 }

4.算法效果

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

5.参考文献

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

时间: 2024-08-06 11:52:52

opencv实现一种改进的Fast特征检测算法的相关文章

Fast特征检测

一.Fast算法 1.基本原理 Fast特征点检测feature2D原理是在圆周上按顺时针方向从1到16的顺序对圆周像素点进行编号.如果在圆周上有N个连续的像素的亮度都比圆心像素的亮度Ip加上阈值t还要亮,或者比圆心像素的亮度减去阈值还要暗,则圆心像素被称为角点. 算法核心:利用周围像素比较的信息可以得到特征点,简单.高效. FAST特征检测算法来源于corner的定义,基于特征点周围的像素灰度值.检测候选特征点周围一圈的像素值,如果候选区域内像素点足够多且与候选点灰度值差值足够大,则认为一个特

改进的起泡排序算法--java

一.基本思路: 冒泡排序是一种简单的交换类排序.其基本思路是从头开始扫描待排序的元素,在扫描过程中依次对相邻元素进行比较,将关键字值大的元素后移.每经过一趟排序后,关键字值最大的元素将移到末尾,此时记下该元素的位置,下一趟排序只需要比较到此位置为止,直到所有元素都已有序排列. 一般地,对n个元素进行冒泡排序,总共需要进行n-1趟.第1趟需要比较n-1次,第2趟需要比较n-2次,......第i趟需要比较n-i次. 二.算法 2.1原始起泡排序算法 public static int[] bubb

两种改进的模拟退火算法求解大值域约束满足问题1.0

0引言 约束满足问题(Constraint Satisfaction Problem,CSP)是人工智能研究领域中一个非常重要的分支,现已成为理论计算机科学.数学和统计物理学等交叉学科研究中的热点问题.人工智能.计算机科学和自动控制等领域中的许多问题都可以归结为约束满足问题.同时,约束满足问题在实际问题如模式识别.决策支持.物流调度及资源分配等领域也有着非常广泛的应用. CSP由一个变量集合和一个约束集合组成.每个变量都有一个非空的可能值域,每个约束描述了一个变量子集与子集内各变量的相容赋值,所

一种改进的动力学处理方法

我们验证为一个反应提出的机理是否合理,常常利用动力学实验.通过对比实验动力学方程和依据机理推导出的动力学方程来验证机理的合理性,首先需要推导出合理的动力学方程.推到动力学方程的方法基本是稳态近似和平衡态近似,这两种方法各有各的局限,为此,胡英院士在他主编的教材<物理化学>中提出一种改进的处理方法,特分享如下:

OpenCV4Android释疑: 透析Android以JNI调OpenCV的三种方式(让OpenCVManager永不困扰)

前文曾具体探讨了关于OpenCV的使用,原本以为天下已太平.但不断有人反应依旧配不好OpenCV4Android,不能得心应手的在Android上使用OpenCV.大量的精力都浪费在摸索配置上.尤其是OpenCVManager诞生之后.更让人无语.大家第一个反应就是怎样才干不安装OpenCVManager.由于要多安装这个东西对客户来说体验太不好了. 咱家昨夜研究至两点,今早七点起床.最终把头绪理清了. 以下咱家以之前做过的一个基于OpenCV2.3.1.android通过jni调用opencv

两种改进的模拟退火算法求解大值域约束满足问题2.0

2    两种改进的模拟退火算法 模拟退火算法(Simulatedannealing algorithm)是一种通用的概率算法,其思想源于固体退火过程:当固体物质温度很高时,固体内部粒子运动杂乱无序:而当温度逐渐降低时粒子又渐渐趋于有序运动.模拟退火算法往往用来求解优化问题的最小值问题,算法过程中会不断地对变量的当前赋值进行扰动,以产生新的赋值.如果新的赋值使得目标函数值变小,则接受新的赋值为当前赋值.反之,则以概率接受新的赋值,其中T是当前温度,为新赋值目标函数值,为当前赋值目标函数值,重复上

一种改进的red5集群方案的应用、基于Red5服务器集群负载均衡调度算法研究

转自: 一种改进的red5集群方案的应用: http://wenku.baidu.com/link?url=jYQ1wNwHVBqJ-5XCYq0PRligp6Y5q6BYXyISUsF56My8DP8dc9CZ4pZvpPz1abxJn8fojMrL0IyfmMHStpvkotqC1RWlRMGnzVL1X4IPOa_ 基于Red5服务器集群负载均衡调度算法研究 http://www.doc88.com/p-0456863461331.html

VLBP基本算法和一种改进的算法IVLBP

VLBP ——variable learning rate 可变学习速率的BP VLBP算法基本思想 一种改进的可变学习速率的BP神经网络算法IVLBP https://wenku.baidu.com/view/c4723e3043323968011c9283.html 原文地址:https://www.cnblogs.com/rinroll/p/12167071.html

SIFT,SURF,ORB,FAST 特征提取算法比较

SIFT,SURF,ORB,FAST 特征提取算法比较 主要的特征检测方法有以下几种,在一般的图像处理库中(如opencv, VLFeat, Boofcv等)都会实现. FAST ,Machine Learning for High-speed Corner Detection, 2006 SIFT,Distinctive Image Features from Scale-Invariant Keypoints,2004, invariant to image translation, sca