文章转载自:http://blog.csdn .NET /luoshixian099/article/details/48294967
在实时的视频流处理中,需要对每一帧特征提取,对算法 处理速度上有很高的要求,传统的SIFT,Harris等特征点提取很难满足。由此提出Fast (Features from Accelerated Segment Test),由于不涉及尺度,梯度,等复杂运算,Fast检测器速度非常快。它使用一定邻域内像元的灰度值与中心点比较大小去判断是否为一个角点。但它的缺点是不具有方向性,尺度不变性。
Fast角点提取步骤(以Fast-12-16为例):
1.以固定半径为圆的边上取16个像素点(图中白色框出的位置),与中心点像素值Ip做差。
2.若边上存在连续 的12(N>12,若为Fast-9,只需要N>9)个点满足 ( I(x)-I(p) )>threshold 或者 ( I(x)-I(p) ) < -threshold。(其中I(x)表示边上的像素值,I(p)为中心点像素值,threshold为设定的阈值。) 则此点作为一个候选角点。如图上的虚线连接的位置。通常为了加速计算,直接比较1,5,9,13位置的差值,超过三个即视为一个候选点(存在连续的12个像元的必要条件),否则直接排除。
3.非极大值抑制,排除不稳定角点。 采用强度响应函数:
即一个角点强度值定义为中心点与边上的12个角点像素差值的绝对值累加和。
OpenCV 源码解析:
同上面原理部分不同,opencv中默认采用 Fast-9-16 (还包括Fast-5-8,Fast-7-12) .即在周围取16个像素点,若超过连续9个点与中心点差值大于阈值即成为候选角点。
角点强度计算方法不采用上面的公式所描述,而是采用最小的差值(见代码分析)作为其角点强度值。
#include <stdio.h> #include <iostream> #include "cv.h" #include "opencv2/highgui/highgui.hpp" #include "opencv2/core/core.hpp" #include "opencv2/features2d/features2d.hpp" using namespace std; using namespace cv; int main( int argc, char ** argv ) { Mat img_1 = imread( "F:\\Picture\\hotel.jpg" ); if ( !img_1.data ) { return -1; } FastFeatureDetector detector(50,true ); std::vector<KeyPoint> keypoints_1; detector.detect( img_1, keypoints_1 ); drawKeypoints(img_1,keypoints_1,img_1,Scalar::all(255)); imshow("HOTEL" ,img_1); waitKey(0); return 0; }
void FAST_t(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression) { Mat img = _img.getMat(); const int K = patternSize/2, N = patternSize + K + 1; int i, j, k, pixel[25]; makeOffsets(pixel, (int )img.step, patternSize); keypoints.clear(); threshold = std::min(std::max(threshold, 0), 255); uchar threshold_tab[512]; for ( i = -255; i <= 255; i++ ) threshold_tab[i+255] = (uchar)(i < -threshold ? 1 : i > threshold ? 2 : 0); AutoBuffer<uchar> _buf((img.cols+16)*3*(sizeof ( int ) + sizeof (uchar)) + 128); uchar* buf[3]; buf[0] = _buf; buf[1] = buf[0] + img.cols; buf[2] = buf[1] + img.cols; int * cpbuf[3]; cpbuf[0] = (int *)alignPtr(buf[2] + img.cols, sizeof ( int )) + 1; cpbuf[1] = cpbuf[0] + img.cols + 1; cpbuf[2] = cpbuf[1] + img.cols + 1; memset(buf[0], 0, img.cols*3); for (i = 3; i < img.rows-2; i++) { const uchar* ptr = img.ptr<uchar>(i) + 3; uchar* curr = buf[(i - 3)%3]; int * cornerpos = cpbuf[(i - 3)%3]; memset(curr, 0, img.cols); int ncorners = 0; if ( i < img.rows - 3 ) { j = 3; for ( ; j < img.cols - 3; j++, ptr++ ) { int v = ptr[0]; const uchar* tab = &threshold_tab[0] - v + 255; int d = tab[ptr[pixel[0]]] | tab[ptr[pixel[8]]]; if ( d == 0 ) continue ; d &= tab[ptr[pixel[2]]] | tab[ptr[pixel[10]]]; d &= tab[ptr[pixel[4]]] | tab[ptr[pixel[12]]]; d &= tab[ptr[pixel[6]]] | tab[ptr[pixel[14]]]; if ( d == 0 ) continue ; d &= tab[ptr[pixel[1]]] | tab[ptr[pixel[9]]]; d &= tab[ptr[pixel[3]]] | tab[ptr[pixel[11]]]; d &= tab[ptr[pixel[5]]] | tab[ptr[pixel[13]]]; d &= tab[ptr[pixel[7]]] | tab[ptr[pixel[15]]]; if ( d & 1 ) { int vt = v - threshold, count = 0; for ( k = 0; k < N; k++ ) { int x = ptr[pixel[k]]; if (x < vt) { if ( ++count > K ) { cornerpos[ncorners++] = j; if (nonmax_suppression) curr[j] = (uchar)cornerScore<patternSize>(ptr, pixel, threshold); break ; } } else count = 0; } } if ( d & 2 ) { int vt = v + threshold, count = 0; for ( k = 0; k < N; k++ ) { int x = ptr[pixel[k]]; if (x > vt) { if ( ++count > K ) { cornerpos[ncorners++] = j; if (nonmax_suppression) curr[j] = (uchar)cornerScore<patternSize>(ptr, pixel, threshold); break ; } } else count = 0; } } } } cornerpos[-1] = ncorners; if ( i == 3 ) continue ; const uchar* prev = buf[(i - 4 + 3)%3]; const uchar* pprev = buf[(i - 5 + 3)%3]; cornerpos = cpbuf[(i - 4 + 3)%3]; ncorners = cornerpos[-1]; for ( k = 0; k < ncorners; k++ ) { j = cornerpos[k]; int score = prev[j]; if ( !nonmax_suppression || (score > prev[j+1] && score > prev[j-1] && score > pprev[j-1] && score > pprev[j] && score > pprev[j+1] && score > curr[j-1] && score > curr[j] && score > curr[j+1]) ) { keypoints.push_back(KeyPoint((float )j, ( float )(i-1), 7.f, -1, ( float )score)); } } } }
角点的强度计算方法:若采用Fast-9-16,计算连续的9个位置与中心位置的差值的绝对值,取最小的一个差值作为其强度值。
int cornerScore<16>( const uchar* ptr, const int pixel[], int threshold) { const int K = 8, N = K*3 + 1; int k, v = ptr[0]; short d[N]; for ( k = 0; k < N; k++ ) d[k] = (short )(v - ptr[pixel[k]]); int a0 = threshold; for ( k = 0; k < 16; k += 2 ) { int a = std::min(( int )d[k+1], ( int )d[k+2]); a = std::min(a, (int )d[k+3]); if ( a <= a0 ) continue ; a = std::min(a, (int )d[k+4]); a = std::min(a, (int )d[k+5]); a = std::min(a, (int )d[k+6]); a = std::min(a, (int )d[k+7]); a = std::min(a, (int )d[k+8]); a0 = std::max(a0, std::min(a, (int )d[k])); a0 = std::max(a0, std::min(a, (int )d[k+9])); } int b0 = -a0; for ( k = 0; k < 16; k += 2 ) { int b = std::max(( int )d[k+1], ( int )d[k+2]); b = std::max(b, (int )d[k+3]); b = std::max(b, (int )d[k+4]); b = std::max(b, (int )d[k+5]); if ( b >= b0 ) continue ; b = std::max(b, (int )d[k+6]); b = std::max(b, (int )d[k+7]); b = std::max(b, (int )d[k+8]); b0 = std::min(b0, std::max(b, (int )d[k])); b0 = std::min(b0, std::max(b, (int )d[k+9])); } threshold = -b0-1; return threshold; }
参考文章:Edward Rosten et.:Machine Learning for High-Speed Corner Detection
http://blog.csdn.Net /kezunhai/article/details/11290749
http://www.edwardrosten.com/work/fast.html