【特徵檢測】ORB特徵提取演算法
簡介
ORB的全稱是ORiented Brief,是文章ORB: an efficient alternative to SIFT or SURF中提出的一種新的角點檢測與特徵描述演算法。實際上,ORB演算法是將FAST角點檢測與BRIEF特徵描述結合並進行了改進。
ORB演算法
在上一篇文章《BRIEF特徵點描述演算法》中,指出了BRIEF的優缺點,ORB演算法就是針對BRIEF演算法的缺點1、2提出來的。ORB演算法分為兩個部分:FAST特徵點檢測、BRIEF特徵描述。
FAST特徵檢測
在文章《FAST特徵點檢測演算法》中,詳細闡述了FAST演算法。但該演算法僅僅確定了特徵點的位置,沒有得到其他任何資訊。在ORB演算法中,依然採用FAST來檢測特徵點的位置,但演算法進行了如下改動:(以FAST-9為例)
1、假設在影象中要提取N個特徵點,則降低FAST的閾值,使FAST演算法檢測到的特徵點大於N;
2、在特徵點位置處,計算特徵點的Harris響應值R,取前N個響應值大的點作為FAST特徵點(Harris角點響應計算:Harris角點檢測中的數學推導);
3、由於要解決BRIEF演算法的旋轉不變性,則需要計算特徵點的主方向。
ORB中利用重心來計算,如下(其中(x,y)是特徵鄰域內的點):
atan2表示反正切,得到的θ值就是FAST特徵點的主方向。
BRIEF特徵描述
在文章《BRIEF特徵點描述演算法》種,闡述了BRIEF演算法。該演算法速度優勢相當明顯,但存在三個致命的缺點。針對尺度不變性,可以像SIFT演算法一樣,子尺度空間構造影象金字塔解決,此處不再說明。ORB演算法主要解決前兩天缺點:噪聲敏感、旋轉不變性。
1、解決噪聲敏感問題
BRIEF中,採用了9x9的高斯運算元進行濾波,可以一定程度上解決噪聲敏感問題,但一個濾波顯然是不夠的。ORB中提出,利用積分影象來解決:在31x31的視窗中,產生一對隨機點後,以隨機點為中心,取5x5的子視窗,比較兩個子視窗內的畫素和的大小進行二進位制編碼,而非僅僅由兩個隨機點決定二進位制編碼。(這一步可有積分影象完成)
2、解決旋轉不變性
利用FAST中求出的特徵點的主方向θ,對特徵點鄰域進行旋轉,Calonder建議先將每個塊旋轉後,再進行BRIEF描述子的提取,但這種方法代價較大。ORB演算法採用的是:每一個特徵點處,對產生的256對隨機點(以256為例),將其進行旋轉,後進行判別,再二進位制編碼。如下:S表示隨機點位置(2xn的矩陣),Sθ表示旋轉後的隨機點的位置(2xn的矩陣),x1=(u1,v1)是一個座標向量,其餘雷同。n=256。
得到新的隨機點位置後,利用積分影象進行二進位制編碼,即可。
實驗
opencv程式碼
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/legacy/legacy.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
int main()
{
Mat img_1 = imread("beaver1.png");
Mat img_2 = imread("beaver2.png");
if (!img_1.data || !img_2.data)
{
cout << "error reading images " << endl;
return -1;
}
ORB orb;
vector<KeyPoint> keyPoints_1, keyPoints_2;
Mat descriptors_1, descriptors_2;
orb(img_1, Mat(), keyPoints_1, descriptors_1);
orb(img_2, Mat(), keyPoints_2, descriptors_2);
BruteForceMatcher<HammingLUT> matcher;
vector<DMatch> matches;
matcher.match(descriptors_1, descriptors_2, matches);
double max_dist = 0; double min_dist = 100;
//-- Quick calculation of max and min distances between keypoints
for( int i = 0; i < descriptors_1.rows; i++ )
{
double dist = matches[i].distance;
if( dist < min_dist ) min_dist = dist;
if( dist > max_dist ) max_dist = dist;
}
printf("-- Max dist : %f \n", max_dist );
printf("-- Min dist : %f \n", min_dist );
//-- Draw only "good" matches (i.e. whose distance is less than 0.6*max_dist )
//-- PS.- radiusMatch can also be used here.
std::vector< DMatch > good_matches;
for( int i = 0; i < descriptors_1.rows; i++ )
{
if( matches[i].distance < 0.6*max_dist )
{
good_matches.push_back( matches[i]);
}
}
Mat img_matches;
drawMatches(img_1, keyPoints_1, img_2, keyPoints_2,
good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
imshow( "Match", img_matches);
cvWaitKey();
return 0;
}
實驗結果
ORB原始碼
在:..\opencv\sources\modules\features2d\src\orb.c中,請自行檢視分析,這裡列出部分原始碼:static void//計算Harris角點響應
HarrisResponses(const Mat& img, vector<KeyPoint>& pts, int blockSize, float harris_k)
{
CV_Assert( img.type() == CV_8UC1 && blockSize*blockSize <= 2048 );
size_t ptidx, ptsize = pts.size();
const uchar* ptr00 = img.ptr<uchar>();
int step = (int)(img.step/img.elemSize1());
int r = blockSize/2;
float scale = (1 << 2) * blockSize * 255.0f;
scale = 1.0f / scale;
float scale_sq_sq = scale * scale * scale * scale;
AutoBuffer<int> ofsbuf(blockSize*blockSize);
int* ofs = ofsbuf;
for( int i = 0; i < blockSize; i++ )
for( int j = 0; j < blockSize; j++ )
ofs[i*blockSize + j] = (int)(i*step + j);
for( ptidx = 0; ptidx < ptsize; ptidx++ )
{
int x0 = cvRound(pts[ptidx].pt.x - r);
int y0 = cvRound(pts[ptidx].pt.y - r);
const uchar* ptr0 = ptr00 + y0*step + x0;
int a = 0, b = 0, c = 0;
for( int k = 0; k < blockSize*blockSize; k++ )
{
const uchar* ptr = ptr0 + ofs[k];
int Ix = (ptr[1] - ptr[-1])*2 + (ptr[-step+1] - ptr[-step-1]) + (ptr[step+1] - ptr[step-1]);
int Iy = (ptr[step] - ptr[-step])*2 + (ptr[step-1] - ptr[-step-1]) + (ptr[step+1] - ptr[-step+1]);
a += Ix*Ix;
b += Iy*Iy;
c += Ix*Iy;
}
pts[ptidx].response = ((float)a * b - (float)c * c -
harris_k * ((float)a + b) * ((float)a + b))*scale_sq_sq;
}
}
//計算FAST角點的主方向
static float IC_Angle(const Mat& image, const int half_k, Point2f pt,
const vector<int> & u_max)
{
int m_01 = 0, m_10 = 0;
const uchar* center = &image.at<uchar> (cvRound(pt.y), cvRound(pt.x));
// Treat the center line differently, v=0
for (int u = -half_k; u <= half_k; ++u)
m_10 += u * center[u];
// Go line by line in the circular patch
int step = (int)image.step1();
for (int v = 1; v <= half_k; ++v)
{
// Proceed over the two lines
int v_sum = 0;
int d = u_max[v];
for (int u = -d; u <= d; ++u)
{
int val_plus = center[u + v*step], val_minus = center[u - v*step];
v_sum += (val_plus - val_minus);
m_10 += u * (val_plus + val_minus);
}
m_01 += v * v_sum;
}
return fastAtan2((float)m_01, (float)m_10);
}
#define GET_VALUE(idx) \
(x = pattern[idx].x*a - pattern[idx].y*b, \ //計算旋轉後的位置
y = pattern[idx].x*b + pattern[idx].y*a, \
ix = cvRound(x), \
iy = cvRound(y), \
*(center + iy*step + ix) )
//判決,並二進位制編碼
for (int i = 0; i < dsize; ++i, pattern += 16)
{
int t0, t1, val;
t0 = GET_VALUE(0); t1 = GET_VALUE(1);
val = t0 < t1;
t0 = GET_VALUE(2); t1 = GET_VALUE(3);
val |= (t0 < t1) << 1;
t0 = GET_VALUE(4); t1 = GET_VALUE(5);
val |= (t0 < t1) << 2;
t0 = GET_VALUE(6); t1 = GET_VALUE(7);
val |= (t0 < t1) << 3;
t0 = GET_VALUE(8); t1 = GET_VALUE(9);
val |= (t0 < t1) << 4;
t0 = GET_VALUE(10); t1 = GET_VALUE(11);
val |= (t0 < t1) << 5;
t0 = GET_VALUE(12); t1 = GET_VALUE(13);
val |= (t0 < t1) << 6;
t0 = GET_VALUE(14); t1 = GET_VALUE(15);
val |= (t0 < t1) << 7;
desc[i] = (uchar)val;
}
//產生512個隨機點的座標位置
static void makeRandomPattern(int patchSize, Point* pattern, int npoints)
{
RNG rng(0x34985739); // we always start with a fixed seed,
// to make patterns the same on each run
for( int i = 0; i < npoints; i++ )
{
pattern[i].x = rng.uniform(-patchSize/2, patchSize/2+1);
pattern[i].y = rng.uniform(-patchSize/2, patchSize/2+1);
}
}
總結
ORB演算法利用了FAST檢測特徵點的快,BRIEF特徵描述子的簡單和快,二者結合並進行了改進,導致ORB演算法的又好又快。
參考文獻
1、ORB: an efficient alternative to SIFT or SURF[J],IEEE International Conference on Computer Vision,2011.
2、基於ORB和改進RANSAC演算法的影象拼接技術[J],2015.
3、基於ORB特徵的目標檢測與跟蹤的研究[碩士論文],2013.
4、基於背景差分與ORB演算法的運動目標檢測與跟蹤演算法研究[碩士論文],2014.