From: Vadim Pisarevsky Date: Tue, 31 Jul 2012 13:17:58 +0000 (+0400) Subject: added FAST<5/8> & FAST<7/12> (by Vincent Rabaud) X-Git-Tag: accepted/2.0/20130307.220821~364^2~281 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=640408ebe77ea263a980fb414692ceedc4555087;p=profile%2Fivi%2Fopencv.git added FAST<5/8> & FAST<7/12> (by Vincent Rabaud) --- diff --git a/modules/features2d/include/opencv2/features2d/features2d.hpp b/modules/features2d/include/opencv2/features2d/features2d.hpp index d8cd235..a191ca2 100644 --- a/modules/features2d/include/opencv2/features2d/features2d.hpp +++ b/modules/features2d/include/opencv2/features2d/features2d.hpp @@ -473,12 +473,18 @@ protected: //! detects corners using FAST algorithm by E. Rosten CV_EXPORTS void FAST( InputArray image, CV_OUT vector& keypoints, - int threshold, bool nonmaxSupression=true ); + int threshold, bool nonmaxSupression=true, int type = 2 ); class CV_EXPORTS_W FastFeatureDetector : public FeatureDetector { public: - CV_WRAP FastFeatureDetector( int threshold=10, bool nonmaxSuppression=true ); + enum + { + TYPE_5_8 = 0, TYPE_7_12 = 1, TYPE_9_16 = 2 + }; + + CV_WRAP FastFeatureDetector( int threshold=10, bool nonmaxSuppression=true); + CV_WRAP FastFeatureDetector( int threshold, bool nonmaxSuppression, int type); AlgorithmInfo* info() const; protected: @@ -486,6 +492,7 @@ protected: int threshold; bool nonmaxSuppression; + int type; }; diff --git a/modules/features2d/perf/perf_fast.cpp b/modules/features2d/perf/perf_fast.cpp index da75f9d..28f0ccc 100644 --- a/modules/features2d/perf/perf_fast.cpp +++ b/modules/features2d/perf/perf_fast.cpp @@ -22,9 +22,13 @@ PERF_TEST_P(fast, detectForORB, testing::Values(FAST_IMAGES)) declare.in(frame); - FastFeatureDetector fd(20, true); + FastFeatureDetector fd(20, true, FastFeatureDetector::TYPE_5_8); vector points; TEST_CYCLE() fd.detect(frame, points); + fd = FastFeatureDetector(20, true, FastFeatureDetector::TYPE_7_12); + TEST_CYCLE() fd.detect(frame, points); + fd = FastFeatureDetector(20, true, FastFeatureDetector::TYPE_9_16); + TEST_CYCLE() fd.detect(frame, points); } diff --git a/modules/features2d/src/fast.cpp b/modules/features2d/src/fast.cpp index 9495d35..f496de3 100644 --- a/modules/features2d/src/fast.cpp +++ b/modules/features2d/src/fast.cpp @@ -46,27 +46,93 @@ The references are: namespace cv { -static void makeOffsets(int pixel[], int row_stride) +static void makeOffsets(int pixel[], int row_stride, int patternSize) { - pixel[0] = 0 + row_stride * 3; - pixel[1] = 1 + row_stride * 3; - pixel[2] = 2 + row_stride * 2; - pixel[3] = 3 + row_stride * 1; - pixel[4] = 3 + row_stride * 0; - pixel[5] = 3 + row_stride * -1; - pixel[6] = 2 + row_stride * -2; - pixel[7] = 1 + row_stride * -3; - pixel[8] = 0 + row_stride * -3; - pixel[9] = -1 + row_stride * -3; - pixel[10] = -2 + row_stride * -2; - pixel[11] = -3 + row_stride * -1; - pixel[12] = -3 + row_stride * 0; - pixel[13] = -3 + row_stride * 1; - pixel[14] = -2 + row_stride * 2; - pixel[15] = -1 + row_stride * 3; + switch(patternSize) { + case 16: + pixel[0] = 0 + row_stride * 3; + pixel[1] = 1 + row_stride * 3; + pixel[2] = 2 + row_stride * 2; + pixel[3] = 3 + row_stride * 1; + pixel[4] = 3 + row_stride * 0; + pixel[5] = 3 + row_stride * -1; + pixel[6] = 2 + row_stride * -2; + pixel[7] = 1 + row_stride * -3; + pixel[8] = 0 + row_stride * -3; + pixel[9] = -1 + row_stride * -3; + pixel[10] = -2 + row_stride * -2; + pixel[11] = -3 + row_stride * -1; + pixel[12] = -3 + row_stride * 0; + pixel[13] = -3 + row_stride * 1; + pixel[14] = -2 + row_stride * 2; + pixel[15] = -1 + row_stride * 3; + break; + case 12: + pixel[0] = 0 + row_stride * 2; + pixel[1] = 1 + row_stride * 2; + pixel[2] = 2 + row_stride * 1; + pixel[3] = 2 + row_stride * 0; + pixel[4] = 2 + row_stride * -1; + pixel[5] = 1 + row_stride * -2; + pixel[6] = 0 + row_stride * -2; + pixel[7] = -1 + row_stride * -2; + pixel[8] = -2 + row_stride * -1; + pixel[9] = -2 + row_stride * 0; + pixel[10] = -2 + row_stride * 1; + pixel[11] = -1 + row_stride * 2; + break; + case 8: + pixel[0] = 0 + row_stride * 1; + pixel[1] = 1 + row_stride * 1; + pixel[2] = 1 + row_stride * 0; + pixel[3] = 1 + row_stride * -1; + pixel[4] = 0 + row_stride * -1; + pixel[5] = -1 + row_stride * -1; + pixel[6] = 0 + row_stride * 0; + pixel[7] = 1 + row_stride * 1; + break; + } } -static int cornerScore(const uchar* ptr, const int pixel[], int threshold) +/*static void testCorner(const uchar* ptr, const int pixel[], int K, int N, int threshold) { + // check that with the computed "threshold" the pixel is still a corner + // and that with the increased-by-1 "threshold" the pixel is not a corner anymore + for( int delta = 0; delta <= 1; delta++ ) + { + int v0 = std::min(ptr[0] + threshold + delta, 255); + int v1 = std::max(ptr[0] - threshold - delta, 0); + int c0 = 0, c1 = 0; + + for( int k = 0; k < N; k++ ) + { + int x = ptr[pixel[k]]; + if(x > v0) + { + if( ++c0 > K ) + break; + c1 = 0; + } + else if( x < v1 ) + { + if( ++c1 > K ) + break; + c0 = 0; + } + else + { + c0 = c1 = 0; + } + } + CV_Assert( (delta == 0 && std::max(c0, c1) > K) || + (delta == 1 && std::max(c0, c1) <= K) ); + } +}*/ + +template +int cornerScore(const uchar* ptr, const int pixel[], int threshold); + +template<> +int cornerScore<16>(const uchar* ptr, const int pixel[], int threshold) { const int K = 8, N = 16 + K + 1; int k, v = ptr[0]; @@ -150,50 +216,170 @@ static int cornerScore(const uchar* ptr, const int pixel[], int threshold) #endif #if 0 - // check that with the computed "threshold" the pixel is still a corner - // and that with the increased-by-1 "threshold" the pixel is not a corner anymore - for( int delta = 0; delta <= 1; delta++ ) + testCorner(ptr, pixel, K, N, threshold); +#endif + return threshold; +} + +template<> +int cornerScore<12>(const uchar* ptr, const int pixel[], int threshold) +{ + const int K = 6, N = 12 + K + 1; + int k, v = ptr[0]; + short d[N]; + for( k = 0; k < N; k++ ) + d[k] = (short)(v - ptr[pixel[k]]); + +#if CV_SSE2 + __m128i q0 = _mm_set1_epi16(-1000), q1 = _mm_set1_epi16(1000); + for( k = 0; k < 16; k += 8 ) { - int v0 = std::min(ptr[0] + threshold + delta, 255); - int v1 = std::max(ptr[0] - threshold - delta, 0); - int c0 = 0, c1 = 0; + __m128i v0 = _mm_loadu_si128((__m128i*)(d+k+1)); + __m128i v1 = _mm_loadu_si128((__m128i*)(d+k+2)); + __m128i a = _mm_min_epi16(v0, v1); + __m128i b = _mm_max_epi16(v0, v1); + v0 = _mm_loadu_si128((__m128i*)(d+k+3)); + a = _mm_min_epi16(a, v0); + b = _mm_max_epi16(b, v0); + v0 = _mm_loadu_si128((__m128i*)(d+k+4)); + a = _mm_min_epi16(a, v0); + b = _mm_max_epi16(b, v0); + v0 = _mm_loadu_si128((__m128i*)(d+k+5)); + a = _mm_min_epi16(a, v0); + b = _mm_max_epi16(b, v0); + v0 = _mm_loadu_si128((__m128i*)(d+k+6)); + a = _mm_min_epi16(a, v0); + b = _mm_max_epi16(b, v0); + v0 = _mm_loadu_si128((__m128i*)(d+k)); + q0 = _mm_max_epi16(q0, _mm_min_epi16(a, v0)); + q1 = _mm_min_epi16(q1, _mm_max_epi16(b, v0)); + v0 = _mm_loadu_si128((__m128i*)(d+k+7)); + q0 = _mm_max_epi16(q0, _mm_min_epi16(a, v0)); + q1 = _mm_min_epi16(q1, _mm_max_epi16(b, v0)); + } + q0 = _mm_max_epi16(q0, _mm_sub_epi16(_mm_setzero_si128(), q1)); + q0 = _mm_max_epi16(q0, _mm_unpackhi_epi64(q0, q0)); + q0 = _mm_max_epi16(q0, _mm_srli_si128(q0, 4)); + q0 = _mm_max_epi16(q0, _mm_srli_si128(q0, 2)); + threshold = (short)_mm_cvtsi128_si32(q0) - 1; +#else + int a0 = threshold; + for( k = 0; k < 12; k += 2 ) + { + int a = std::min((int)d[k+1], (int)d[k+2]); + if( a <= a0 ) + continue; + a = std::min(a, (int)d[k+3]); + a = std::min(a, (int)d[k+4]); + a = std::min(a, (int)d[k+5]); + a = std::min(a, (int)d[k+6]); + a0 = std::max(a0, std::min(a, (int)d[k])); + a0 = std::max(a0, std::min(a, (int)d[k+7])); + } - for( int k = 0; k < N; k++ ) - { - int x = ptr[pixel[k]]; - if(x > v0) - { - if( ++c0 > K ) - break; - c1 = 0; - } - else if( x < v1 ) - { - if( ++c1 > K ) - break; - c0 = 0; - } - else - { - c0 = c1 = 0; - } - } - CV_Assert( (delta == 0 && std::max(c0, c1) > K) || - (delta == 1 && std::max(c0, c1) <= K) ); + int b0 = -a0; + for( k = 0; k < 12; 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]); + if( b >= b0 ) + continue; + b = std::max(b, (int)d[k+5]); + b = std::max(b, (int)d[k+6]); + + b0 = std::min(b0, std::max(b, (int)d[k])); + b0 = std::min(b0, std::max(b, (int)d[k+7])); } + + threshold = -b0-1; +#endif + +#if 0 + testCorner(ptr, pixel, K, N, threshold); #endif return threshold; } +template<> +int cornerScore<8>(const uchar* ptr, const int pixel[], int threshold) +{ + const int K = 4, N = 8 + K + 1; + int k, v = ptr[0]; + short d[N]; + for( k = 0; k < N; k++ ) + d[k] = (short)(v - ptr[pixel[k]]); + +#if CV_SSE2 + __m128i q0 = _mm_set1_epi16(-1000), q1 = _mm_set1_epi16(1000); + for( k = 0; k < 16; k += 8 ) + { + __m128i v0 = _mm_loadu_si128((__m128i*)(d+k+1)); + __m128i v1 = _mm_loadu_si128((__m128i*)(d+k+2)); + __m128i a = _mm_min_epi16(v0, v1); + __m128i b = _mm_max_epi16(v0, v1); + v0 = _mm_loadu_si128((__m128i*)(d+k+3)); + a = _mm_min_epi16(a, v0); + b = _mm_max_epi16(b, v0); + v0 = _mm_loadu_si128((__m128i*)(d+k+4)); + a = _mm_min_epi16(a, v0); + b = _mm_max_epi16(b, v0); + v0 = _mm_loadu_si128((__m128i*)(d+k)); + q0 = _mm_max_epi16(q0, _mm_min_epi16(a, v0)); + q1 = _mm_min_epi16(q1, _mm_max_epi16(b, v0)); + v0 = _mm_loadu_si128((__m128i*)(d+k+5)); + q0 = _mm_max_epi16(q0, _mm_min_epi16(a, v0)); + q1 = _mm_min_epi16(q1, _mm_max_epi16(b, v0)); + } + q0 = _mm_max_epi16(q0, _mm_sub_epi16(_mm_setzero_si128(), q1)); + q0 = _mm_max_epi16(q0, _mm_unpackhi_epi64(q0, q0)); + q0 = _mm_max_epi16(q0, _mm_srli_si128(q0, 4)); + q0 = _mm_max_epi16(q0, _mm_srli_si128(q0, 2)); + threshold = (short)_mm_cvtsi128_si32(q0) - 1; +#else + int a0 = threshold; + for( k = 0; k < 8; k += 2 ) + { + int a = std::min((int)d[k+1], (int)d[k+2]); + if( a <= a0 ) + continue; + a = std::min(a, (int)d[k+3]); + a = std::min(a, (int)d[k+4]); + a0 = std::max(a0, std::min(a, (int)d[k])); + a0 = std::max(a0, std::min(a, (int)d[k+5])); + } + + int b0 = -a0; + for( k = 0; k < 12; k += 2 ) + { + int b = std::max((int)d[k+1], (int)d[k+2]); + b = std::max(b, (int)d[k+3]); + if( b >= b0 ) + continue; + b = std::max(b, (int)d[k+4]); + + b0 = std::min(b0, std::max(b, (int)d[k])); + b0 = std::min(b0, std::max(b, (int)d[k+5])); + } + + threshold = -b0-1; +#endif + +#if 0 + testCorner(ptr, pixel, K, N, threshold); +#endif + return threshold; +} -void FAST(InputArray _img, std::vector& keypoints, int threshold, bool nonmax_suppression) +template +void FAST_t(InputArray _img, std::vector& keypoints, int threshold, bool nonmax_suppression) { Mat img = _img.getMat(); - const int K = 8, N = 16 + K + 1; - int i, j, k, pixel[N]; - makeOffsets(pixel, (int)img.step); - for(k = 16; k < N; k++) - pixel[k] = pixel[k - 16]; + const int K = patternSize/2, N = patternSize + K + 1, quarterPatternSize = patternSize/4; + int i, j, k, pixel[25]; + makeOffsets(pixel, (int)img.step, patternSize); + for(k = patternSize; k < 25; k++) + pixel[k] = pixel[k - patternSize]; keypoints.clear(); @@ -235,9 +421,9 @@ void FAST(InputArray _img, std::vector& keypoints, int threshold, bool v0 = _mm_xor_si128(_mm_adds_epu8(v0, t), delta); __m128i x0 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[0])), delta); - __m128i x1 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[4])), delta); - __m128i x2 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[8])), delta); - __m128i x3 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[12])), delta); + __m128i x1 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[quarterPatternSize])), delta); + __m128i x2 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[2*quarterPatternSize])), delta); + __m128i x3 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[3*quarterPatternSize])), delta); m0 = _mm_and_si128(_mm_cmpgt_epi8(x0, v0), _mm_cmpgt_epi8(x1, v0)); m1 = _mm_and_si128(_mm_cmpgt_epi8(v1, x0), _mm_cmpgt_epi8(v1, x1)); m0 = _mm_or_si128(m0, _mm_and_si128(_mm_cmpgt_epi8(x1, v0), _mm_cmpgt_epi8(x2, v0))); @@ -279,7 +465,7 @@ void FAST(InputArray _img, std::vector& keypoints, int threshold, bool { cornerpos[ncorners++] = j+k; if(nonmax_suppression) - curr[j+k] = (uchar)cornerScore(ptr+k, pixel, threshold); + curr[j+k] = (uchar)cornerScore(ptr+k, pixel, threshold); } } #endif @@ -317,7 +503,7 @@ void FAST(InputArray _img, std::vector& keypoints, int threshold, bool { cornerpos[ncorners++] = j; if(nonmax_suppression) - curr[j] = (uchar)cornerScore(ptr, pixel, threshold); + curr[j] = (uchar)cornerScore(ptr, pixel, threshold); break; } } @@ -339,7 +525,7 @@ void FAST(InputArray _img, std::vector& keypoints, int threshold, bool { cornerpos[ncorners++] = j; if(nonmax_suppression) - curr[j] = (uchar)cornerScore(ptr, pixel, threshold); + curr[j] = (uchar)cornerScore(ptr, pixel, threshold); break; } } @@ -375,19 +561,36 @@ void FAST(InputArray _img, std::vector& keypoints, int threshold, bool } } - +void FAST(InputArray _img, std::vector& keypoints, int threshold, bool nonmax_suppression, int type) +{ + switch(type) { + case FastFeatureDetector::TYPE_5_8: + FAST_t<8>(_img, keypoints, threshold, nonmax_suppression); + break; + case FastFeatureDetector::TYPE_7_12: + FAST_t<12>(_img, keypoints, threshold, nonmax_suppression); + break; + case FastFeatureDetector::TYPE_9_16: + FAST_t<16>(_img, keypoints, threshold, nonmax_suppression); + break; + } +} /* * FastFeatureDetector */ FastFeatureDetector::FastFeatureDetector( int _threshold, bool _nonmaxSuppression ) -: threshold(_threshold), nonmaxSuppression(_nonmaxSuppression) + : threshold(_threshold), nonmaxSuppression(_nonmaxSuppression), type(FastFeatureDetector::TYPE_9_16) {} +FastFeatureDetector::FastFeatureDetector( int _threshold, bool _nonmaxSuppression, int _type ) +: threshold(_threshold), nonmaxSuppression(_nonmaxSuppression), type(_type) +{} + void FastFeatureDetector::detectImpl( const Mat& image, vector& keypoints, const Mat& mask ) const { Mat grayImage = image; if( image.type() != CV_8U ) cvtColor( image, grayImage, CV_BGR2GRAY ); - FAST( grayImage, keypoints, threshold, nonmaxSuppression ); + FAST( grayImage, keypoints, threshold, nonmaxSuppression, type ); KeyPointsFilter::runByPixelsMask( keypoints, mask ); } diff --git a/modules/features2d/src/features2d_init.cpp b/modules/features2d/src/features2d_init.cpp index 0d884ef..6ecffeb 100644 --- a/modules/features2d/src/features2d_init.cpp +++ b/modules/features2d/src/features2d_init.cpp @@ -58,7 +58,8 @@ CV_INIT_ALGORITHM(BriefDescriptorExtractor, "Feature2D.BRIEF", CV_INIT_ALGORITHM(FastFeatureDetector, "Feature2D.FAST", obj.info()->addParam(obj, "threshold", obj.threshold); - obj.info()->addParam(obj, "nonmaxSuppression", obj.nonmaxSuppression)); + obj.info()->addParam(obj, "nonmaxSuppression", obj.nonmaxSuppression); + obj.info()->addParam(obj, "type", obj.type, FastFeatureDetector::TYPE_9_16)); /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/modules/features2d/test/test_fast.cpp b/modules/features2d/test/test_fast.cpp index d991a29..671e66d 100644 --- a/modules/features2d/test/test_fast.cpp +++ b/modules/features2d/test/test_fast.cpp @@ -58,6 +58,7 @@ CV_FastTest::~CV_FastTest() {} void CV_FastTest::run( int ) { + for(int type=0; type <= 2; ++type) { Mat image1 = imread(string(ts->get_data_path()) + "inpaint/orig.jpg"); Mat image2 = imread(string(ts->get_data_path()) + "cameracalibration/chess9.jpg"); string xml = string(ts->get_data_path()) + "fast/result.xml"; @@ -74,8 +75,8 @@ void CV_FastTest::run( int ) vector keypoints1; vector keypoints2; - FAST(gray1, keypoints1, 30); - FAST(gray2, keypoints2, 30); + FAST(gray1, keypoints1, 30, type); + FAST(gray2, keypoints2, 30, type); for(size_t i = 0; i < keypoints1.size(); ++i) { @@ -109,17 +110,21 @@ void CV_FastTest::run( int ) read( fs["exp_kps2"], exp_kps2, Mat() ); fs.release(); + // We only have testing data for 9_16 but it actually works equally well for 7_12 + if ((type==1) || (type==2)){ if ( 0 != norm(exp_kps1, kps1, NORM_L2) || 0 != norm(exp_kps2, kps2, NORM_L2)) { ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH); return; } + } - /* cv::namedWindow("Img1"); cv::imshow("Img1", image1); + /*cv::namedWindow("Img1"); cv::imshow("Img1", image1); cv::namedWindow("Img2"); cv::imshow("Img2", image2); cv::waitKey(0);*/ + } - ts->set_failed_test_info(cvtest::TS::OK); + ts->set_failed_test_info(cvtest::TS::OK); } TEST(Features2d_FAST, regression) { CV_FastTest test; test.safe_run(); }