This commit is contained in:
Andrey Kamaev 2012-05-31 08:02:52 +00:00
parent 1a572c8e89
commit 9399394e6c
3 changed files with 178 additions and 131 deletions

View File

@ -16,8 +16,8 @@ are met:
notice, this list of conditions and the following disclaimer in the notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution. documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of *Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived its contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
@ -35,7 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/* /*
The references are: The references are:
* Machine learning for high-speed corner detection, * Machine learning for high-speed corner detection,
E. Rosten and T. Drummond, ECCV 2006 E. Rosten and T. Drummond, ECCV 2006
* Faster and better: A machine learning approach to corner detection * Faster and better: A machine learning approach to corner detection
E. Rosten, R. Porter and T. Drummond, PAMI, 2009 E. Rosten, R. Porter and T. Drummond, PAMI, 2009
@ -64,7 +64,7 @@ static void makeOffsets(int pixel[], int row_stride)
pixel[13] = -3 + row_stride * 1; pixel[13] = -3 + row_stride * 1;
pixel[14] = -2 + row_stride * 2; pixel[14] = -2 + row_stride * 2;
pixel[15] = -1 + row_stride * 3; pixel[15] = -1 + row_stride * 3;
} }
static int cornerScore(const uchar* ptr, const int pixel[], int threshold) static int cornerScore(const uchar* ptr, const int pixel[], int threshold)
{ {
@ -73,7 +73,7 @@ static int cornerScore(const uchar* ptr, const int pixel[], int threshold)
short d[N]; short d[N];
for( k = 0; k < N; k++ ) for( k = 0; k < N; k++ )
d[k] = (short)(v - ptr[pixel[k]]); d[k] = (short)(v - ptr[pixel[k]]);
#if CV_SSE2 #if CV_SSE2
__m128i q0 = _mm_set1_epi16(-1000), q1 = _mm_set1_epi16(1000); __m128i q0 = _mm_set1_epi16(-1000), q1 = _mm_set1_epi16(1000);
for( k = 0; k < 16; k += 8 ) for( k = 0; k < 16; k += 8 )
@ -128,7 +128,7 @@ static int cornerScore(const uchar* ptr, const int pixel[], int threshold)
a0 = std::max(a0, std::min(a, (int)d[k])); a0 = std::max(a0, std::min(a, (int)d[k]));
a0 = std::max(a0, std::min(a, (int)d[k+9])); a0 = std::max(a0, std::min(a, (int)d[k+9]));
} }
int b0 = -a0; int b0 = -a0;
for( k = 0; k < 16; k += 2 ) for( k = 0; k < 16; k += 2 )
{ {
@ -141,14 +141,14 @@ static int cornerScore(const uchar* ptr, const int pixel[], int threshold)
b = std::max(b, (int)d[k+6]); b = std::max(b, (int)d[k+6]);
b = std::max(b, (int)d[k+7]); b = std::max(b, (int)d[k+7]);
b = std::max(b, (int)d[k+8]); 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]));
b0 = std::min(b0, std::max(b, (int)d[k+9])); b0 = std::min(b0, std::max(b, (int)d[k+9]));
} }
threshold = -b0-1; threshold = -b0-1;
#endif #endif
#if 0 #if 0
// check that with the computed "threshold" the pixel is still a corner // 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 // and that with the increased-by-1 "threshold" the pixel is not a corner anymore
@ -157,7 +157,7 @@ static int cornerScore(const uchar* ptr, const int pixel[], int threshold)
int v0 = std::min(ptr[0] + threshold + delta, 255); int v0 = std::min(ptr[0] + threshold + delta, 255);
int v1 = std::max(ptr[0] - threshold - delta, 0); int v1 = std::max(ptr[0] - threshold - delta, 0);
int c0 = 0, c1 = 0; int c0 = 0, c1 = 0;
for( int k = 0; k < N; k++ ) for( int k = 0; k < N; k++ )
{ {
int x = ptr[pixel[k]]; int x = ptr[pixel[k]];
@ -184,7 +184,7 @@ static int cornerScore(const uchar* ptr, const int pixel[], int threshold)
#endif #endif
return threshold; return threshold;
} }
void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression) void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression)
{ {
@ -214,7 +214,7 @@ void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool
cpbuf[1] = cpbuf[0] + img.cols + 1; cpbuf[1] = cpbuf[0] + img.cols + 1;
cpbuf[2] = cpbuf[1] + img.cols + 1; cpbuf[2] = cpbuf[1] + img.cols + 1;
memset(buf[0], 0, img.cols*3); memset(buf[0], 0, img.cols*3);
for(i = 3; i < img.rows-2; i++) for(i = 3; i < img.rows-2; i++)
{ {
const uchar* ptr = img.ptr<uchar>(i) + 3; const uchar* ptr = img.ptr<uchar>(i) + 3;
@ -222,7 +222,7 @@ void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool
int* cornerpos = cpbuf[(i - 3)%3]; int* cornerpos = cpbuf[(i - 3)%3];
memset(curr, 0, img.cols); memset(curr, 0, img.cols);
int ncorners = 0; int ncorners = 0;
if( i < img.rows - 3 ) if( i < img.rows - 3 )
{ {
j = 3; j = 3;
@ -233,7 +233,7 @@ void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool
__m128i v0 = _mm_loadu_si128((const __m128i*)ptr); __m128i v0 = _mm_loadu_si128((const __m128i*)ptr);
__m128i v1 = _mm_xor_si128(_mm_subs_epu8(v0, t), delta); __m128i v1 = _mm_xor_si128(_mm_subs_epu8(v0, t), delta);
v0 = _mm_xor_si128(_mm_adds_epu8(v0, t), delta); 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 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 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 x2 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[8])), delta);
@ -256,24 +256,24 @@ void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool
ptr -= 8; ptr -= 8;
continue; continue;
} }
__m128i c0 = _mm_setzero_si128(), c1 = c0, max0 = c0, max1 = c0; __m128i c0 = _mm_setzero_si128(), c1 = c0, max0 = c0, max1 = c0;
for( k = 0; k < N; k++ ) for( k = 0; k < N; k++ )
{ {
__m128i x = _mm_xor_si128(_mm_loadu_si128((const __m128i*)(ptr + pixel[k])), delta); __m128i x = _mm_xor_si128(_mm_loadu_si128((const __m128i*)(ptr + pixel[k])), delta);
m0 = _mm_cmpgt_epi8(x, v0); m0 = _mm_cmpgt_epi8(x, v0);
m1 = _mm_cmpgt_epi8(v1, x); m1 = _mm_cmpgt_epi8(v1, x);
c0 = _mm_and_si128(_mm_sub_epi8(c0, m0), m0); c0 = _mm_and_si128(_mm_sub_epi8(c0, m0), m0);
c1 = _mm_and_si128(_mm_sub_epi8(c1, m1), m1); c1 = _mm_and_si128(_mm_sub_epi8(c1, m1), m1);
max0 = _mm_max_epu8(max0, c0); max0 = _mm_max_epu8(max0, c0);
max1 = _mm_max_epu8(max1, c1); max1 = _mm_max_epu8(max1, c1);
} }
max0 = _mm_max_epu8(max0, max1); max0 = _mm_max_epu8(max0, max1);
int m = _mm_movemask_epi8(_mm_cmpgt_epi8(max0, K16)); int m = _mm_movemask_epi8(_mm_cmpgt_epi8(max0, K16));
for( k = 0; m > 0 && k < 16; k++, m >>= 1 ) for( k = 0; m > 0 && k < 16; k++, m >>= 1 )
if(m & 1) if(m & 1)
{ {
@ -288,26 +288,26 @@ void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool
int v = ptr[0]; int v = ptr[0];
const uchar* tab = &threshold_tab[0] - v + 255; const uchar* tab = &threshold_tab[0] - v + 255;
int d = tab[ptr[pixel[0]]] | tab[ptr[pixel[8]]]; int d = tab[ptr[pixel[0]]] | tab[ptr[pixel[8]]];
if( d == 0 ) if( d == 0 )
continue; continue;
d &= tab[ptr[pixel[2]]] | tab[ptr[pixel[10]]]; d &= tab[ptr[pixel[2]]] | tab[ptr[pixel[10]]];
d &= tab[ptr[pixel[4]]] | tab[ptr[pixel[12]]]; d &= tab[ptr[pixel[4]]] | tab[ptr[pixel[12]]];
d &= tab[ptr[pixel[6]]] | tab[ptr[pixel[14]]]; d &= tab[ptr[pixel[6]]] | tab[ptr[pixel[14]]];
if( d == 0 ) if( d == 0 )
continue; continue;
d &= tab[ptr[pixel[1]]] | tab[ptr[pixel[9]]]; d &= tab[ptr[pixel[1]]] | tab[ptr[pixel[9]]];
d &= tab[ptr[pixel[3]]] | tab[ptr[pixel[11]]]; d &= tab[ptr[pixel[3]]] | tab[ptr[pixel[11]]];
d &= tab[ptr[pixel[5]]] | tab[ptr[pixel[13]]]; d &= tab[ptr[pixel[5]]] | tab[ptr[pixel[13]]];
d &= tab[ptr[pixel[7]]] | tab[ptr[pixel[15]]]; d &= tab[ptr[pixel[7]]] | tab[ptr[pixel[15]]];
if( d & 1 ) if( d & 1 )
{ {
int vt = v - threshold, count = 0; int vt = v - threshold, count = 0;
for( k = 0; k < N; k++ ) for( k = 0; k < N; k++ )
{ {
int x = ptr[pixel[k]]; int x = ptr[pixel[k]];
@ -325,11 +325,11 @@ void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool
count = 0; count = 0;
} }
} }
if( d & 2 ) if( d & 2 )
{ {
int vt = v + threshold, count = 0; int vt = v + threshold, count = 0;
for( k = 0; k < N; k++ ) for( k = 0; k < N; k++ )
{ {
int x = ptr[pixel[k]]; int x = ptr[pixel[k]];
@ -349,17 +349,17 @@ void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool
} }
} }
} }
cornerpos[-1] = ncorners; cornerpos[-1] = ncorners;
if( i == 3 ) if( i == 3 )
continue; continue;
const uchar* prev = buf[(i - 4 + 3)%3]; const uchar* prev = buf[(i - 4 + 3)%3];
const uchar* pprev = buf[(i - 5 + 3)%3]; const uchar* pprev = buf[(i - 5 + 3)%3];
cornerpos = cpbuf[(i - 4 + 3)%3]; cornerpos = cpbuf[(i - 4 + 3)%3];
ncorners = cornerpos[-1]; ncorners = cornerpos[-1];
for( k = 0; k < ncorners; k++ ) for( k = 0; k < ncorners; k++ )
{ {
j = cornerpos[k]; j = cornerpos[k];
@ -375,7 +375,7 @@ void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool
} }
} }
/* /*
* FastFeatureDetector * FastFeatureDetector
*/ */

View File

@ -53,31 +53,31 @@ static void
HarrisResponses(const Mat& img, vector<KeyPoint>& pts, int blockSize, float harris_k) HarrisResponses(const Mat& img, vector<KeyPoint>& pts, int blockSize, float harris_k)
{ {
CV_Assert( img.type() == CV_8UC1 && blockSize*blockSize <= 2048 ); CV_Assert( img.type() == CV_8UC1 && blockSize*blockSize <= 2048 );
size_t ptidx, ptsize = pts.size(); size_t ptidx, ptsize = pts.size();
const uchar* ptr00 = img.ptr<uchar>(); const uchar* ptr00 = img.ptr<uchar>();
int step = (int)(img.step/img.elemSize1()); int step = (int)(img.step/img.elemSize1());
int r = blockSize/2; int r = blockSize/2;
float scale = (1 << 2) * blockSize * 255.0f; float scale = (1 << 2) * blockSize * 255.0f;
scale = 1.0f / scale; scale = 1.0f / scale;
float scale_sq_sq = scale * scale * scale * scale; float scale_sq_sq = scale * scale * scale * scale;
AutoBuffer<int> ofsbuf(blockSize*blockSize); AutoBuffer<int> ofsbuf(blockSize*blockSize);
int* ofs = ofsbuf; int* ofs = ofsbuf;
for( int i = 0; i < blockSize; i++ ) for( int i = 0; i < blockSize; i++ )
for( int j = 0; j < blockSize; j++ ) for( int j = 0; j < blockSize; j++ )
ofs[i*blockSize + j] = (int)(i*step + j); ofs[i*blockSize + j] = (int)(i*step + j);
for( ptidx = 0; ptidx < ptsize; ptidx++ ) for( ptidx = 0; ptidx < ptsize; ptidx++ )
{ {
int x0 = cvRound(pts[ptidx].pt.x - r); int x0 = cvRound(pts[ptidx].pt.x - r);
int y0 = cvRound(pts[ptidx].pt.y - r); int y0 = cvRound(pts[ptidx].pt.y - r);
const uchar* ptr0 = ptr00 + y0*step + x0; const uchar* ptr0 = ptr00 + y0*step + x0;
int a = 0, b = 0, c = 0; int a = 0, b = 0, c = 0;
for( int k = 0; k < blockSize*blockSize; k++ ) for( int k = 0; k < blockSize*blockSize; k++ )
{ {
const uchar* ptr = ptr0 + ofs[k]; const uchar* ptr = ptr0 + ofs[k];
@ -98,13 +98,13 @@ static float IC_Angle(const Mat& image, const int half_k, Point2f pt,
const vector<int> & u_max) const vector<int> & u_max)
{ {
int m_01 = 0, m_10 = 0; int m_01 = 0, m_10 = 0;
const uchar* center = &image.at<uchar> (cvRound(pt.y), cvRound(pt.x)); const uchar* center = &image.at<uchar> (cvRound(pt.y), cvRound(pt.x));
// Treat the center line differently, v=0 // Treat the center line differently, v=0
for (int u = -half_k; u <= half_k; ++u) for (int u = -half_k; u <= half_k; ++u)
m_10 += u * center[u]; m_10 += u * center[u];
// Go line by line in the circular patch // Go line by line in the circular patch
int step = (int)image.step1(); int step = (int)image.step1();
for (int v = 1; v <= half_k; ++v) for (int v = 1; v <= half_k; ++v)
@ -120,7 +120,7 @@ static float IC_Angle(const Mat& image, const int half_k, Point2f pt,
} }
m_01 += v * v_sum; m_01 += v * v_sum;
} }
return fastAtan2((float)m_01, (float)m_10); return fastAtan2((float)m_01, (float)m_10);
} }
@ -134,10 +134,10 @@ static void computeOrbDescriptor(const KeyPoint& kpt,
//angle = cvFloor(angle/12)*12.f; //angle = cvFloor(angle/12)*12.f;
angle *= (float)(CV_PI/180.f); angle *= (float)(CV_PI/180.f);
float a = (float)cos(angle), b = (float)sin(angle); float a = (float)cos(angle), b = (float)sin(angle);
const uchar* center = &img.at<uchar>(cvRound(kpt.pt.y), cvRound(kpt.pt.x)); const uchar* center = &img.at<uchar>(cvRound(kpt.pt.y), cvRound(kpt.pt.x));
int step = (int)img.step; int step = (int)img.step;
#if 1 #if 1
#define GET_VALUE(idx) \ #define GET_VALUE(idx) \
center[cvRound(pattern[idx].x*b + pattern[idx].y*a)*step + \ center[cvRound(pattern[idx].x*b + pattern[idx].y*a)*step + \
@ -153,7 +153,7 @@ static void computeOrbDescriptor(const KeyPoint& kpt,
cvRound(center[iy*step + ix]*(1-x)*(1-y) + center[(iy+1)*step + ix]*(1-x)*y + \ cvRound(center[iy*step + ix]*(1-x)*(1-y) + center[(iy+1)*step + ix]*(1-x)*y + \
center[iy*step + ix+1]*x*(1-y) + center[(iy+1)*step + ix+1]*x*y)) center[iy*step + ix+1]*x*(1-y) + center[(iy+1)*step + ix+1]*x*y))
#endif #endif
if( WTA_K == 2 ) if( WTA_K == 2 )
{ {
for (int i = 0; i < dsize; ++i, pattern += 16) for (int i = 0; i < dsize; ++i, pattern += 16)
@ -175,7 +175,7 @@ static void computeOrbDescriptor(const KeyPoint& kpt,
val |= (t0 < t1) << 6; val |= (t0 < t1) << 6;
t0 = GET_VALUE(14); t1 = GET_VALUE(15); t0 = GET_VALUE(14); t1 = GET_VALUE(15);
val |= (t0 < t1) << 7; val |= (t0 < t1) << 7;
desc[i] = (uchar)val; desc[i] = (uchar)val;
} }
} }
@ -186,16 +186,16 @@ static void computeOrbDescriptor(const KeyPoint& kpt,
int t0, t1, t2, val; int t0, t1, t2, val;
t0 = GET_VALUE(0); t1 = GET_VALUE(1); t2 = GET_VALUE(2); t0 = GET_VALUE(0); t1 = GET_VALUE(1); t2 = GET_VALUE(2);
val = t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0); val = t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0);
t0 = GET_VALUE(3); t1 = GET_VALUE(4); t2 = GET_VALUE(5); t0 = GET_VALUE(3); t1 = GET_VALUE(4); t2 = GET_VALUE(5);
val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 2; val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 2;
t0 = GET_VALUE(6); t1 = GET_VALUE(7); t2 = GET_VALUE(8); t0 = GET_VALUE(6); t1 = GET_VALUE(7); t2 = GET_VALUE(8);
val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 4; val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 4;
t0 = GET_VALUE(9); t1 = GET_VALUE(10); t2 = GET_VALUE(11); t0 = GET_VALUE(9); t1 = GET_VALUE(10); t2 = GET_VALUE(11);
val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 6; val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 6;
desc[i] = (uchar)val; desc[i] = (uchar)val;
} }
} }
@ -211,7 +211,7 @@ static void computeOrbDescriptor(const KeyPoint& kpt,
if( t3 > t2 ) t2 = t3, v = 3; if( t3 > t2 ) t2 = t3, v = 3;
k = t0 > t2 ? u : v; k = t0 > t2 ? u : v;
val = k; val = k;
t0 = GET_VALUE(4); t1 = GET_VALUE(5); t0 = GET_VALUE(4); t1 = GET_VALUE(5);
t2 = GET_VALUE(6); t3 = GET_VALUE(7); t2 = GET_VALUE(6); t3 = GET_VALUE(7);
u = 0, v = 2; u = 0, v = 2;
@ -219,7 +219,7 @@ static void computeOrbDescriptor(const KeyPoint& kpt,
if( t3 > t2 ) t2 = t3, v = 3; if( t3 > t2 ) t2 = t3, v = 3;
k = t0 > t2 ? u : v; k = t0 > t2 ? u : v;
val |= k << 2; val |= k << 2;
t0 = GET_VALUE(8); t1 = GET_VALUE(9); t0 = GET_VALUE(8); t1 = GET_VALUE(9);
t2 = GET_VALUE(10); t3 = GET_VALUE(11); t2 = GET_VALUE(10); t3 = GET_VALUE(11);
u = 0, v = 2; u = 0, v = 2;
@ -227,7 +227,7 @@ static void computeOrbDescriptor(const KeyPoint& kpt,
if( t3 > t2 ) t2 = t3, v = 3; if( t3 > t2 ) t2 = t3, v = 3;
k = t0 > t2 ? u : v; k = t0 > t2 ? u : v;
val |= k << 4; val |= k << 4;
t0 = GET_VALUE(12); t1 = GET_VALUE(13); t0 = GET_VALUE(12); t1 = GET_VALUE(13);
t2 = GET_VALUE(14); t3 = GET_VALUE(15); t2 = GET_VALUE(14); t3 = GET_VALUE(15);
u = 0, v = 2; u = 0, v = 2;
@ -235,23 +235,23 @@ static void computeOrbDescriptor(const KeyPoint& kpt,
if( t3 > t2 ) t2 = t3, v = 3; if( t3 > t2 ) t2 = t3, v = 3;
k = t0 > t2 ? u : v; k = t0 > t2 ? u : v;
val |= k << 6; val |= k << 6;
desc[i] = (uchar)val; desc[i] = (uchar)val;
} }
} }
else else
CV_Error( CV_StsBadSize, "Wrong WTA_K. It can be only 2, 3 or 4." ); CV_Error( CV_StsBadSize, "Wrong WTA_K. It can be only 2, 3 or 4." );
#undef GET_VALUE #undef GET_VALUE
} }
static void initializeOrbPattern( const Point* pattern0, vector<Point>& pattern, int ntuples, int tupleSize, int poolSize ) static void initializeOrbPattern( const Point* pattern0, vector<Point>& pattern, int ntuples, int tupleSize, int poolSize )
{ {
RNG rng(0x12345678); RNG rng(0x12345678);
int i, k, k1; int i, k, k1;
pattern.resize(ntuples*tupleSize); pattern.resize(ntuples*tupleSize);
for( i = 0; i < ntuples; i++ ) for( i = 0; i < ntuples; i++ )
{ {
for( k = 0; k < tupleSize; k++ ) for( k = 0; k < tupleSize; k++ )
@ -545,7 +545,7 @@ static void makeRandomPattern(int patchSize, Point* pattern, int npoints)
} }
} }
static inline float getScale(int level, int firstLevel, double scaleFactor) static inline float getScale(int level, int firstLevel, double scaleFactor)
{ {
return (float)std::pow(scaleFactor, (double)(level - firstLevel)); return (float)std::pow(scaleFactor, (double)(level - firstLevel));
@ -570,8 +570,8 @@ int ORB::descriptorSize() const
int ORB::descriptorType() const int ORB::descriptorType() const
{ {
return CV_8U; return CV_8U;
} }
/** Compute the ORB features and descriptors on an image /** Compute the ORB features and descriptors on an image
* @param img the image to compute the features and descriptors on * @param img the image to compute the features and descriptors on
* @param mask the mask to apply * @param mask the mask to apply
@ -599,7 +599,7 @@ static void computeOrientation(const Mat& image, vector<KeyPoint>& keypoints,
keypoint->angle = IC_Angle(image, halfPatchSize, keypoint->pt, umax); keypoint->angle = IC_Angle(image, halfPatchSize, keypoint->pt, umax);
} }
} }
/** Compute the ORB keypoints on an image /** Compute the ORB keypoints on an image
* @param image_pyramid the image pyramid to compute the features and descriptors on * @param image_pyramid the image pyramid to compute the features and descriptors on
@ -614,11 +614,11 @@ static void computeKeyPoints(const vector<Mat>& imagePyramid,
{ {
int nlevels = (int)imagePyramid.size(); int nlevels = (int)imagePyramid.size();
vector<int> nfeaturesPerLevel(nlevels); vector<int> nfeaturesPerLevel(nlevels);
// fill the extractors and descriptors for the corresponding scales // fill the extractors and descriptors for the corresponding scales
float factor = (float)(1.0 / scaleFactor); float factor = (float)(1.0 / scaleFactor);
float ndesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - (float)pow((double)factor, (double)nlevels)); float ndesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - (float)pow((double)factor, (double)nlevels));
int sumFeatures = 0; int sumFeatures = 0;
for( int level = 0; level < nlevels-1; level++ ) for( int level = 0; level < nlevels-1; level++ )
{ {
@ -627,19 +627,19 @@ static void computeKeyPoints(const vector<Mat>& imagePyramid,
ndesiredFeaturesPerScale *= factor; ndesiredFeaturesPerScale *= factor;
} }
nfeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0); nfeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0);
// Make sure we forget about what is too close to the boundary // Make sure we forget about what is too close to the boundary
//edge_threshold_ = std::max(edge_threshold_, patch_size_/2 + kKernelWidth / 2 + 2); //edge_threshold_ = std::max(edge_threshold_, patch_size_/2 + kKernelWidth / 2 + 2);
// pre-compute the end of a row in a circular patch // pre-compute the end of a row in a circular patch
int halfPatchSize = patchSize / 2; int halfPatchSize = patchSize / 2;
vector<int> umax(halfPatchSize + 1); vector<int> umax(halfPatchSize + 1);
int v, v0, vmax = cvFloor(halfPatchSize * sqrt(2.f) / 2 + 1); int v, v0, vmax = cvFloor(halfPatchSize * sqrt(2.f) / 2 + 1);
int vmin = cvCeil(halfPatchSize * sqrt(2.f) / 2); int vmin = cvCeil(halfPatchSize * sqrt(2.f) / 2);
for (v = 0; v <= vmax; ++v) for (v = 0; v <= vmax; ++v)
umax[v] = cvRound(sqrt((double)halfPatchSize * halfPatchSize - v * v)); umax[v] = cvRound(sqrt((double)halfPatchSize * halfPatchSize - v * v));
// Make sure we are symmetric // Make sure we are symmetric
for (v = halfPatchSize, v0 = 0; v >= vmin; --v) for (v = halfPatchSize, v0 = 0; v >= vmin; --v)
{ {
@ -648,37 +648,37 @@ static void computeKeyPoints(const vector<Mat>& imagePyramid,
umax[v] = v0; umax[v] = v0;
++v0; ++v0;
} }
allKeypoints.resize(nlevels); allKeypoints.resize(nlevels);
for (int level = 0; level < nlevels; ++level) for (int level = 0; level < nlevels; ++level)
{ {
int nfeatures = nfeaturesPerLevel[level]; int nfeatures = nfeaturesPerLevel[level];
allKeypoints[level].reserve(nfeatures*2); allKeypoints[level].reserve(nfeatures*2);
vector<KeyPoint> & keypoints = allKeypoints[level]; vector<KeyPoint> & keypoints = allKeypoints[level];
// Detect FAST features, 20 is a good threshold // Detect FAST features, 20 is a good threshold
FastFeatureDetector fd(20, true); FastFeatureDetector fd(20, true);
fd.detect(imagePyramid[level], keypoints, maskPyramid[level]); fd.detect(imagePyramid[level], keypoints, maskPyramid[level]);
// Remove keypoints very close to the border // Remove keypoints very close to the border
KeyPointsFilter::runByImageBorder(keypoints, imagePyramid[level].size(), edgeThreshold); KeyPointsFilter::runByImageBorder(keypoints, imagePyramid[level].size(), edgeThreshold);
if( scoreType == ORB::HARRIS_SCORE ) if( scoreType == ORB::HARRIS_SCORE )
{ {
// Keep more points than necessary as FAST does not give amazing corners // Keep more points than necessary as FAST does not give amazing corners
KeyPointsFilter::retainBest(keypoints, 2 * nfeatures); KeyPointsFilter::retainBest(keypoints, 2 * nfeatures);
// Compute the Harris cornerness (better scoring than FAST) // Compute the Harris cornerness (better scoring than FAST)
HarrisResponses(imagePyramid[level], keypoints, 7, HARRIS_K); HarrisResponses(imagePyramid[level], keypoints, 7, HARRIS_K);
} }
//cull to the final desired level, using the new Harris scores or the original FAST scores. //cull to the final desired level, using the new Harris scores or the original FAST scores.
KeyPointsFilter::retainBest(keypoints, nfeatures); KeyPointsFilter::retainBest(keypoints, nfeatures);
float sf = getScale(level, firstLevel, scaleFactor); float sf = getScale(level, firstLevel, scaleFactor);
// Set the level of the coordinates // Set the level of the coordinates
for (vector<KeyPoint>::iterator keypoint = keypoints.begin(), for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint) keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)
@ -686,12 +686,12 @@ static void computeKeyPoints(const vector<Mat>& imagePyramid,
keypoint->octave = level; keypoint->octave = level;
keypoint->size = patchSize*sf; keypoint->size = patchSize*sf;
} }
computeOrientation(imagePyramid[level], keypoints, halfPatchSize, umax); computeOrientation(imagePyramid[level], keypoints, halfPatchSize, umax);
} }
} }
/** Compute the ORB decriptors /** Compute the ORB decriptors
* @param image the image to compute the features and descriptors on * @param image the image to compute the features and descriptors on
* @param integral_image the integral image of the image (can be empty, but the computation will be slower) * @param integral_image the integral image of the image (can be empty, but the computation will be slower)
@ -706,12 +706,12 @@ static void computeDescriptors(const Mat& image, vector<KeyPoint>& keypoints, Ma
CV_Assert(image.type() == CV_8UC1); CV_Assert(image.type() == CV_8UC1);
//create the descriptor mat, keypoints.size() rows, BYTES cols //create the descriptor mat, keypoints.size() rows, BYTES cols
descriptors = Mat::zeros((int)keypoints.size(), dsize, CV_8UC1); descriptors = Mat::zeros((int)keypoints.size(), dsize, CV_8UC1);
for (size_t i = 0; i < keypoints.size(); i++) for (size_t i = 0; i < keypoints.size(); i++)
computeOrbDescriptor(keypoints[i], image, &pattern[0], descriptors.ptr((int)i), dsize, WTA_K); computeOrbDescriptor(keypoints[i], image, &pattern[0], descriptors.ptr((int)i), dsize, WTA_K);
} }
/** Compute the ORB features and descriptors on an image /** Compute the ORB features and descriptors on an image
* @param img the image to compute the features and descriptors on * @param img the image to compute the features and descriptors on
* @param mask the mask to apply * @param mask the mask to apply
@ -725,21 +725,21 @@ void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _ke
{ {
bool do_keypoints = !useProvidedKeypoints; bool do_keypoints = !useProvidedKeypoints;
bool do_descriptors = _descriptors.needed(); bool do_descriptors = _descriptors.needed();
if( (!do_keypoints && !do_descriptors) || _image.empty() ) if( (!do_keypoints && !do_descriptors) || _image.empty() )
return; return;
//ROI handling //ROI handling
const int HARRIS_BLOCK_SIZE = 9; const int HARRIS_BLOCK_SIZE = 9;
int halfPatchSize = patchSize / 2; int halfPatchSize = patchSize / 2;
int border = std::max(edgeThreshold, std::max(halfPatchSize, HARRIS_BLOCK_SIZE/2))+1; int border = std::max(edgeThreshold, std::max(halfPatchSize, HARRIS_BLOCK_SIZE/2))+1;
Mat image = _image.getMat(), mask = _mask.getMat(); Mat image = _image.getMat(), mask = _mask.getMat();
if( image.type() != CV_8UC1 ) if( image.type() != CV_8UC1 )
cvtColor(_image, image, CV_BGR2GRAY); cvtColor(_image, image, CV_BGR2GRAY);
int nlevels = this->nlevels; int nlevels = this->nlevels;
if( !do_keypoints ) if( !do_keypoints )
{ {
// if we have pre-computed keypoints, they may use more levels than it is set in parameters // if we have pre-computed keypoints, they may use more levels than it is set in parameters
@ -756,7 +756,7 @@ void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _ke
nlevels = std::max(nlevels, std::max(_keypoints[i].octave, 0)); nlevels = std::max(nlevels, std::max(_keypoints[i].octave, 0));
nlevels++; nlevels++;
} }
// Pre-compute the scale pyramids // Pre-compute the scale pyramids
vector<Mat> imagePyramid(nlevels), maskPyramid(nlevels); vector<Mat> imagePyramid(nlevels), maskPyramid(nlevels);
for (int level = 0; level < nlevels; ++level) for (int level = 0; level < nlevels; ++level)
@ -766,49 +766,48 @@ void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _ke
Size wholeSize(sz.width + border*2, sz.height + border*2); Size wholeSize(sz.width + border*2, sz.height + border*2);
Mat temp(wholeSize, image.type()), masktemp; Mat temp(wholeSize, image.type()), masktemp;
imagePyramid[level] = temp(Rect(border, border, sz.width, sz.height)); imagePyramid[level] = temp(Rect(border, border, sz.width, sz.height));
if( !mask.empty() ) if( !mask.empty() )
{ {
masktemp = Mat(wholeSize, mask.type()); masktemp = Mat(wholeSize, mask.type());
maskPyramid[level] = masktemp(Rect(border, border, sz.width, sz.height)); maskPyramid[level] = masktemp(Rect(border, border, sz.width, sz.height));
} }
// Compute the resized image // Compute the resized image
if( level != firstLevel ) if( level != firstLevel )
{ {
if( level < firstLevel ) if( level < firstLevel )
{ {
resize(image, imagePyramid[level], sz, scale, scale, INTER_LINEAR); resize(image, imagePyramid[level], sz, 0, 0, INTER_LINEAR);
if (!mask.empty()) if (!mask.empty())
resize(mask, maskPyramid[level], sz, scale, scale, INTER_LINEAR); resize(mask, maskPyramid[level], sz, 0, 0, INTER_LINEAR);
copyMakeBorder(imagePyramid[level], temp, border, border, border, border,
BORDER_REFLECT_101+BORDER_ISOLATED);
} }
else else
{ {
resize(imagePyramid[level-1], imagePyramid[level], sz, resize(imagePyramid[level-1], imagePyramid[level], sz, 0, 0, INTER_LINEAR);
1./scaleFactor, 1./scaleFactor, INTER_LINEAR);
if (!mask.empty()) if (!mask.empty())
resize(maskPyramid[level-1], maskPyramid[level], sz, {
1./scaleFactor, 1./scaleFactor, INTER_LINEAR); resize(maskPyramid[level-1], maskPyramid[level], sz, 0, 0, INTER_LINEAR);
copyMakeBorder(imagePyramid[level], temp, border, border, border, border, threshold(maskPyramid[level], maskPyramid[level], 254, 0, THRESH_TOZERO);
BORDER_REFLECT_101+BORDER_ISOLATED); }
} }
copyMakeBorder(imagePyramid[level], temp, border, border, border, border,
BORDER_REFLECT_101+BORDER_ISOLATED);
if (!mask.empty())
copyMakeBorder(maskPyramid[level], masktemp, border, border, border, border,
BORDER_CONSTANT+BORDER_ISOLATED);
} }
else else
{ {
copyMakeBorder(image, temp, border, border, border, border, copyMakeBorder(image, temp, border, border, border, border,
BORDER_REFLECT_101); BORDER_REFLECT_101);
image.copyTo(imagePyramid[level]);
if( !mask.empty() ) if( !mask.empty() )
mask.copyTo(maskPyramid[level]); copyMakeBorder(mask, masktemp, border, border, border, border,
BORDER_CONSTANT+BORDER_ISOLATED);
} }
if( !mask.empty() )
copyMakeBorder(maskPyramid[level], masktemp, border, border, border, border,
BORDER_CONSTANT+BORDER_ISOLATED);
} }
// Pre-compute the keypoints (we keep the best over all scales, so this has to be done beforehand // Pre-compute the keypoints (we keep the best over all scales, so this has to be done beforehand
vector < vector<KeyPoint> > allKeypoints; vector < vector<KeyPoint> > allKeypoints;
if( do_keypoints ) if( do_keypoints )
@ -817,19 +816,19 @@ void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _ke
computeKeyPoints(imagePyramid, maskPyramid, allKeypoints, computeKeyPoints(imagePyramid, maskPyramid, allKeypoints,
nfeatures, firstLevel, scaleFactor, nfeatures, firstLevel, scaleFactor,
edgeThreshold, patchSize, scoreType); edgeThreshold, patchSize, scoreType);
// make sure we have the right number of keypoints keypoints // make sure we have the right number of keypoints keypoints
/*vector<KeyPoint> temp; /*vector<KeyPoint> temp;
for (int level = 0; level < n_levels; ++level) for (int level = 0; level < n_levels; ++level)
{ {
vector<KeyPoint>& keypoints = all_keypoints[level]; vector<KeyPoint>& keypoints = all_keypoints[level];
temp.insert(temp.end(), keypoints.begin(), keypoints.end()); temp.insert(temp.end(), keypoints.begin(), keypoints.end());
keypoints.clear(); keypoints.clear();
} }
KeyPoint::retainBest(temp, n_features_); KeyPoint::retainBest(temp, n_features_);
for (vector<KeyPoint>::iterator keypoint = temp.begin(), for (vector<KeyPoint>::iterator keypoint = temp.begin(),
keypoint_end = temp.end(); keypoint != keypoint_end; ++keypoint) keypoint_end = temp.end(); keypoint != keypoint_end; ++keypoint)
all_keypoints[keypoint->octave].push_back(*keypoint);*/ all_keypoints[keypoint->octave].push_back(*keypoint);*/
@ -838,19 +837,19 @@ void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _ke
{ {
// Remove keypoints very close to the border // Remove keypoints very close to the border
KeyPointsFilter::runByImageBorder(_keypoints, image.size(), edgeThreshold); KeyPointsFilter::runByImageBorder(_keypoints, image.size(), edgeThreshold);
// Cluster the input keypoints depending on the level they were computed at // Cluster the input keypoints depending on the level they were computed at
allKeypoints.resize(nlevels); allKeypoints.resize(nlevels);
for (vector<KeyPoint>::iterator keypoint = _keypoints.begin(), for (vector<KeyPoint>::iterator keypoint = _keypoints.begin(),
keypointEnd = _keypoints.end(); keypoint != keypointEnd; ++keypoint) keypointEnd = _keypoints.end(); keypoint != keypointEnd; ++keypoint)
allKeypoints[keypoint->octave].push_back(*keypoint); allKeypoints[keypoint->octave].push_back(*keypoint);
// Make sure we rescale the coordinates // Make sure we rescale the coordinates
for (int level = 0; level < nlevels; ++level) for (int level = 0; level < nlevels; ++level)
{ {
if (level == firstLevel) if (level == firstLevel)
continue; continue;
vector<KeyPoint> & keypoints = allKeypoints[level]; vector<KeyPoint> & keypoints = allKeypoints[level];
float scale = 1/getScale(level, firstLevel, scaleFactor); float scale = 1/getScale(level, firstLevel, scaleFactor);
for (vector<KeyPoint>::iterator keypoint = keypoints.begin(), for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),
@ -858,10 +857,10 @@ void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _ke
keypoint->pt *= scale; keypoint->pt *= scale;
} }
} }
Mat descriptors; Mat descriptors;
vector<Point> pattern; vector<Point> pattern;
if( do_descriptors ) if( do_descriptors )
{ {
int nkeypoints = 0; int nkeypoints = 0;
@ -874,19 +873,19 @@ void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _ke
_descriptors.create(nkeypoints, descriptorSize(), CV_8U); _descriptors.create(nkeypoints, descriptorSize(), CV_8U);
descriptors = _descriptors.getMat(); descriptors = _descriptors.getMat();
} }
const int npoints = 512; const int npoints = 512;
Point patternbuf[npoints]; Point patternbuf[npoints];
const Point* pattern0 = (const Point*)bit_pattern_31_; const Point* pattern0 = (const Point*)bit_pattern_31_;
if( patchSize != 31 ) if( patchSize != 31 )
{ {
pattern0 = patternbuf; pattern0 = patternbuf;
makeRandomPattern(patchSize, patternbuf, npoints); makeRandomPattern(patchSize, patternbuf, npoints);
} }
CV_Assert( WTA_K == 2 || WTA_K == 3 || WTA_K == 4 ); CV_Assert( WTA_K == 2 || WTA_K == 3 || WTA_K == 4 );
if( WTA_K == 2 ) if( WTA_K == 2 )
std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern)); std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));
else else
@ -895,7 +894,7 @@ void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _ke
initializeOrbPattern(pattern0, pattern, ntuples, WTA_K, npoints); initializeOrbPattern(pattern0, pattern, ntuples, WTA_K, npoints);
} }
} }
_keypoints.clear(); _keypoints.clear();
int offset = 0; int offset = 0;
for (int level = 0; level < nlevels; ++level) for (int level = 0; level < nlevels; ++level)
@ -903,15 +902,15 @@ void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _ke
// Get the features and compute their orientation // Get the features and compute their orientation
vector<KeyPoint>& keypoints = allKeypoints[level]; vector<KeyPoint>& keypoints = allKeypoints[level];
int nkeypoints = (int)keypoints.size(); int nkeypoints = (int)keypoints.size();
// Compute the descriptors // Compute the descriptors
if (do_descriptors) if (do_descriptors)
{ {
Mat desc; Mat desc;
if (!descriptors.empty()) if (!descriptors.empty())
{ {
desc = descriptors.rowRange(offset, offset + nkeypoints); desc = descriptors.rowRange(offset, offset + nkeypoints);
} }
offset += nkeypoints; offset += nkeypoints;
// preprocess the resized image // preprocess the resized image
@ -920,7 +919,7 @@ void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _ke
GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101); GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101);
computeDescriptors(workingMat, keypoints, desc, pattern, descriptorSize(), WTA_K); computeDescriptors(workingMat, keypoints, desc, pattern, descriptorSize(), WTA_K);
} }
// Copy to the output data // Copy to the output data
if (level != firstLevel) if (level != firstLevel)
{ {
@ -933,11 +932,11 @@ void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _ke
_keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end()); _keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end());
} }
} }
void ORB::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask) const void ORB::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask) const
{ {
(*this)(image, mask, keypoints, noArray(), false); (*this)(image, mask, keypoints, noArray(), false);
} }
void ORB::computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors) const void ORB::computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors) const
{ {

View File

@ -1091,3 +1091,51 @@ TEST( Features2d_DescriptorMatcher_FlannBased, regression )
CV_DescriptorMatcherTest test( "descriptor-matcher-flann-based", new FlannBasedMatcher, 0.04f ); CV_DescriptorMatcherTest test( "descriptor-matcher-flann-based", new FlannBasedMatcher, 0.04f );
test.safe_run(); test.safe_run();
} }
TEST(Features2D_ORB, _1996)
{
cv::Ptr<cv::FeatureDetector> fd = cv::FeatureDetector::create("ORB");
cv::Ptr<cv::DescriptorExtractor> de = cv::DescriptorExtractor::create("ORB");
Mat image = cv::imread(string(cvtest::TS::ptr()->get_data_path()) + "shared/lena.jpg");
ASSERT_FALSE(image.empty());
Mat roi(image.size(), CV_8UC1, Scalar(0));
Point poly[] = {Point(100, 20), Point(300, 50), Point(400, 200), Point(10, 500)};
fillConvexPoly(roi, poly, int(sizeof(poly) / sizeof(poly[0])), Scalar(255));
std::vector<cv::KeyPoint> keypoints;
fd->detect(image, keypoints, roi);
cv::Mat descriptors;
de->compute(image, keypoints, descriptors);
//image.setTo(Scalar(255,255,255), roi);
int roiViolations = 0;
for(std::vector<cv::KeyPoint>::const_iterator kp = keypoints.begin(); kp != keypoints.end(); ++kp)
{
int x = cvRound(kp->pt.x);
int y = cvRound(kp->pt.y);
ASSERT_LE(0, x);
ASSERT_LE(0, y);
ASSERT_GT(image.cols, x);
ASSERT_GT(image.rows, y);
// if (!roi.at<uchar>(y,x))
// {
// roiViolations++;
// circle(image, kp->pt, 3, Scalar(0,0,255));
// }
}
// if(roiViolations)
// {
// imshow("img", image);
// waitKey();
// }
ASSERT_EQ(0, roiViolations);
}