BRISK算法
Brisk(Binary Robust Invariant Scalable Keypoints)特征 相比于 SURF SIFT 有些步骤是相同的
- 构建尺度空间
- 特征点检测
- FAST9-16寻找特征 连续 9-16 个点小于或大于当前值,就把其当作特征点的候选者
- 特征点定位
- 关键点描述子
特征点检测
BRISK算法主要利用FAST9-16进行特征点检测(为什么是主要?因为用到一次FAST5-8),可参见博客:FAST特征点检测。要解决尺度不变性,就必须在尺度空间进行特征点检测,于是BRISK算法中构造了图像金字塔进行多尺度表达。
建立尺度空间
构造n个octave层(用ci表示)和n个intra-octave层(用di表示),文章中n=4,i={0,1,...,n-1}。假设有图像img,octave层的产生:c0层就是img原图像,c1层是c0层的2倍下采样,c2层是c1层的2倍下采样,以此类推。intra-octave层的产生:d0层是img的1.5倍下采样,d1层是d0层的2倍下采样(即img的2*1.5倍下采样),d2层是d1层的2倍下采样,以此类推。则ci、di层与原图像的尺度关系用t表示为:
ci、di层与原图像大小关系为:
由于n=4,所以一共可以得到8张图,octave层之间尺度(缩放因子)是2倍关系,intra-octave层之间尺度(缩放因子)也是2倍关系。
特征点检测
对这8张图进行FAST9-16角点检测,得到具有角点信息的8张图,对原图像img进行一次FAST5-8角点检测(当做d(-1)层,虚拟层),总共会得到9幅有角点信息的图像。
非极大值抑制
对这9幅图像,进行空间上的非极大值抑制(同SIFT算法的非极大值抑制):特征点在位置空间(8邻域点)和尺度空间(上下层2x9个点),共26个邻域点的FAST的得分值要最大,否则不能当做特征点;此时得到的极值点还比较粗糙,需要进一步精确定位。
亚像素插值
进过上面步骤,得到了图像特征点的位置和尺度,在极值点所在层及其上下层所对应的位置,对FAST得分值(共3个)进行二维二次函数插值(x、y方向),得到真正意义上的得分极值点及其精确的坐标位置(作为特征点位置);再对尺度方向进行一维插值,得到极值点所对应的尺度(作为特征点尺度)。
特征点描述
高斯滤波
现在,我们得到了特征点的位置和尺度(t)后,要对特征点赋予其描述符。均匀采样模式:以特征点为中心,构建不同半径的同心圆,在每个圆上获取一定数目的等间隔采样点(所有采样点包括特征点,一共N个),由于这种邻域采样模式会引起混叠效应,所以需要对同心圆上的采样点进行高斯滤波。
采样模式如下图,蓝圈表示;以采样点为中心,为方差进行高斯滤波,滤波半径大小与高斯方差的大小成正比,红圈表示。最终用到的N个采样点是经过高斯平滑后的采样点。下图是t=1时的。(文章中:N=60)
局部梯度计算
由于有N个采样点,则采样点两两组合成一对,共有N(N-1)/2钟组合方式,所有组合方式的集合称作采样点对,用集合表示,其中像素分别是、,δ表示尺度。用表示特征点局部梯度集合,则有:
定义短距离点对子集、长距离点对子集(L个):
其中,,,t是特征点所在的尺度。
现在要利用上面得到的信息,来计算特征点的主方向(注意:此处只用到了长距离子集),如下:
特征描述符
要解决旋转不变性,则需要对特征点周围的采样区域进行旋转到主方向,旋转后得到新的采样区域,采样模式同上。BRISK描述子是二进制的特征,由采样点集合可得到N(N-1)/2对采样点对,就可以得到N(N-1)/2个距离的集合(包含长、短距离子集),考虑其中短距离子集中的512个短距离点对,进行二进制编码,判断方式如下:
其中,带有上标,表示经过旋转a角度后的,新的采样点。由此可得到,512Bit的二进制编码,也就是64个字节(BRISK64)。
匹配方法
汉明距离进行比较,与其他二进制描述子的匹配方式一样。
实例(opencv2):
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
int main()
{
//Load Image
Mat c_src1 = imread( "1.png");
Mat c_src2 = imread("2.png");
Mat src1 = imread( "1.png", CV_LOAD_IMAGE_GRAYSCALE);
Mat src2 = imread( "2.png", CV_LOAD_IMAGE_GRAYSCALE);
if( !src1.data || !src2.data )
{
cout<< "Error reading images " << std::endl;
return -1;
}
//feature detect
BRISK detector;
vector kp1, kp2;
double start = GetTickCount();
detector.detect( src1, kp1 );
detector.detect( src2, kp2 );
//cv::BRISK extractor;
Mat des1,des2;//descriptor
detector.compute(src1, kp1, des1);
detector.compute(src2, kp2, des2);
Mat res1,res2;
int drawmode = DrawMatchesFlags::DRAW_RICH_KEYPOINTS;
drawKeypoints(c_src1, kp1, res1, Scalar::all(-1), drawmode);//画出特征点
drawKeypoints(c_src2, kp2, res2, Scalar::all(-1), drawmode);
cout<<"size of description of Img1: "< matches;
matcher.match(des1, des2, matches);
double end = GetTickCount();
cout<<"耗时:"<<(end - start) <<"ms"<
from:https://www.cnblogs.com/welen/articles/6088639.html
实例(opencv3):
#include
#include
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
Mat img1 = imread("D:/cv400/data/box.png", 0);
Mat img2 = imread("D:/cv400/data/box_in_scene.png", 0);
if (img1.empty() || img2.empty())
{
cout << "Load image error..." << endl;
return -1;
}
imshow("object image", img1);
imshow("object in scene", img2);
// surf featurs extraction
double t1 = (double)getTickCount();
//int minHessian = 400;
Ptr detector = BRISK::create();
vector keypoints_obj;
vector keypoints_scene;
Mat descriptor_obj, descriptor_scene;
detector->detectAndCompute(img1, Mat(), keypoints_obj, descriptor_obj);
detector->detectAndCompute(img2, Mat(), keypoints_scene, descriptor_scene);
// matching
BFMatcher matcher(NORM_L2);
vector matches;
matcher.match(descriptor_obj, descriptor_scene, matches);
double t2 = (double)getTickCount();
double t = (t2 - t1) / getTickFrequency();
cout << "spend time : " << t << "s" << endl;
//求匹配点最近距离
double minDist = 1000;
for (int i = 0; i < descriptor_obj.rows; i++)
{
double dist = matches[i].distance;
if (dist < minDist)
minDist = dist;
}
cout<<"min distance : "<< minDist< goodMatches;
for (int i = 0; i < descriptor_obj.rows; i++)
{
double dist = matches[i].distance;
if (dist < max(2* minDist, 0.02))
goodMatches.push_back(matches[i]);
}
//寻找匹配上的关键点的变换
vector obj; //目标特征点
vector objInScene; //场景中目标特征点
for (size_t t = 0; t < goodMatches.size(); t++)
{
obj.push_back(keypoints_obj[goodMatches[t].queryIdx].pt);
objInScene.push_back(keypoints_scene[goodMatches[t].trainIdx].pt);
}
Mat imgBH = findHomography(obj, objInScene, RANSAC);
//映射点
vector obj_corners(4);
vector scene_corners(4);
obj_corners[0] = Point(0, 0);
obj_corners[1] = Point(img1.cols, 0);
obj_corners[2] = Point(img1.cols, img1.rows);
obj_corners[3] = Point(0, img1.rows);
perspectiveTransform(obj_corners, scene_corners, imgBH);
//四个点之间画线
Mat dst;
cvtColor(img2, dst, COLOR_GRAY2BGR);
for(int i=0;i<4;i++)
line(dst, scene_corners[i%4], scene_corners[(i+1)%4], Scalar(0, 0, 255), 2, 8, 0);
imshow("find object in sence", dst);
waitKey(0);
return 0;
}
from:https://blog.csdn.net/andylanzhiyong/article/details/84865807