opencv:基于ORB特征点匹配及图像拼接

#ORB简述
ORB(Oriented FAST and Rotated BRIEF)是一种快速特征点提取和描述的算法。ORB算法分为两部分,分别是特征点提取和特征点描述。特征提取是由FAST(Features from Accelerated Segment Test)算法发展来的,特征点描述是根据BRIEF(Binary Robust IndependentElementary Features)特征描述算法改进的。ORB特征是将FAST特征点的检测方法与BRIEF特征描述子结合起来,并在它们原来的基础上做了改进与优化。

下图是我在高数课本上进行的测试:

alt

#特征点匹配

例子里采集了3000个点吧对比一下,opencv3.3以后好像用上了智能指针Ptr对象,对象的创建只能用ptr模板创建,大概是因为图像再处理的过程中防止异常错误正常抛出吧。
两张原图:

alt
alt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Mat image_src1,image_src2;
Mat image_gray1,image_gray2;
image_src1=imread("D:\\yy.jpg");
cvtColor(image_src1,image_gray1,COLOR_BGR2GRAY);//转为灰度图
image_src2=imread("D:\\zz.jpg");
cvtColor(image_src2,image_gray2,COLOR_BGR2GRAY);//转为灰度图

Ptr<Feature2D> oDetectors=ORB::create(3000);//创建orb对象,采集特征点
vector<KeyPoint> KeyPoints1,KeyPoints2;//关键点数组

oDetectors->detect(image_gray1,KeyPoints1);//采集左图特征点
oDetectors->detect(image_gray2,KeyPoints2);//采集右图特征点

Mat imageDec1,imageDec2;
Ptr<Feature2D> ptor=ORB::create();//创建一个新的oeb对象,当然也可以用上面那个
ptor->compute(image_gray1,KeyPoints1,imageDec1);//描述特征点
ptor->compute(image_gray2,KeyPoints2,imageDec2);//描述特征点

flann::Index flannIndex(imageDec1,LshIndexParams(12,20,2),
cvflann::FLANN_DIST_HAMMING);

vector<DMatch> goodMatchPoints;
Mat matchIndex(imageDec2.rows,2,CV_32SC1);
Mat matchDistance(imageDec2.rows,2,CV_32FC1);

flannIndex.knnSearch(imageDec2,matchIndex,matchDistance,2,flann::SearchParams());//采用k近似值 将匹配的索引和距离计算出来

for(int i=0;i < matchDistance.rows;i++)
{
float t=matchDistance.at<float>(i,0);
float s=matchDistance.at<float>(i,1);
if(t<s*0.46)//相似度大约为0.46就算相符
{
DMatch part_matches(i,matchIndex.at<int>(i,0),t);
goodMatchPoints.push_back(part_matches);
}

}
Mat first_matching;
drawMatches(image_src2,KeyPoints2,image_src1,KeyPoints1,
goodMatchPoints,first_matching);
imshow("Match",first_matching);

如图,匹配完成:
alt

#拼接
记录相似点,将另一图像适度旋转调整,然后拼接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
vector<Point2f> Conners(4);// 左上角 左下角 右上角 右下角

void calcCorners(const Mat& homo,const Mat& input)
{
double v2[]={0,0,1};
double v1[3];//改变后值
Mat V2=Mat(3,1,CV_64FC1,v2);
Mat V1=Mat(3,1,CV_64FC1,v1);
//下面都是重复代码计算看懂一个即可
V1=homo*V2;
Conners[0].x=v1[0]/v1[2];
Conners[0].y=v1[1]/v1[2];

v2[0]=0;
v2[1]=input.rows;
v2[2]=1;
V2=Mat(3,1,CV_64FC1,v2);
V1=Mat(3,1,CV_64FC1,v1);
V1=homo*V2;
Conners[1].x=v1[0]/v1[2];
Conners[1].y=v1[1]/v1[2];

v2[0]=input.cols;
v2[1]=0;
v2[2]=1;
V2=Mat(3,1,CV_64FC1,v2);
V1=Mat(3,1,CV_64FC1,v1);
V1=homo*V2;
Conners[2].x=v1[0]/v1[2];
Conners[2].y=v1[1]/v1[2];

v2[0]=input.cols;
v2[1]=input.rows;
v2[2]=1;
V2=Mat(3,1,CV_64FC1,v2);
V1=Mat(3,1,CV_64FC1,v1);
V1=homo*V2;
Conners[3].x=v1[0]/v1[2];
Conners[3].y=v1[1]/v1[2];

}

main:://此处接着标题二的代码

vector<Point2f> image_points1,image_points2;

for(auto x:goodMatchPoints)
{
image_points2.push_back(KeyPoints2[x.queryIdx].pt);
image_points1.push_back(KeyPoints1[x.trainIdx].pt);
}

Mat homo=findHomography(image_points1,image_points2,CV_RANSAC);

cout<<"homo matrix:"<<homo<<endl;

calcCorners(homo,image_src1);
Mat imageTransform1,imageTransform2;
warpPerspective(image_src1,imageTransform1,homo,Size(max(Conners[2].x,
Conners[3].x),image_src2.rows));
imshow("12",imageTransform1);

int dst_width=imageTransform1.cols;
int dst_height=image_src2.rows;

Mat dst(dst_height,dst_width,CV_8UC3);
dst.setTo(0);
imageTransform1.copyTo(dst(Rect(0,0,imageTransform1.cols,imageTransform1.rows)));

image_src2.copyTo(dst(Rect(0,0,image_src2.cols,image_src2.rows)));
imshow("pin",dst);

拼接完,如图:
alt

#优化拼接处
此处是借鉴别人的,因为自己也不可能凭空想到,哈哈,修复拼接处的线条问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void optimizated(Mat &src,Mat &trans,Mat &dst)
{
int start=min(Conners[0].x,Conners[1].x);
double processWidth=src.cols-start;

int rows=dst.rows;
int cols=src.cols;
double alpha=1;

for(int i=0;i<rows;++i)
{
uchar *p=src.ptr<uchar>(i);
uchar *t=trans.ptr<uchar>(i);
uchar *d=dst.ptr<uchar>(i);
for(int j=start;j<cols;++j)
{
int k=j*3;
if(t[k]==0&&t[k+1]==0&&t[k+2]==0) alpha=1;
else alpha=(processWidth-j+start)/processWidth;

d[k]=p[k]*alpha+t[k]*(1-alpha);
d[k+1]=p[k+1]*alpha+t[k+1]*(1-alpha);
d[k+2]=p[k+2]*alpha+t[k+2]*(1-alpha);
}
}
}

成品图效果如下:

alt