本文主要记录我在基于特征的图像配准方法方面的一点儿实验。
本文图像配准主要参考如下3个链接:
1.https://blog.csdn.net/qq_27737701/article/details/82289607
2.https://www.cnblogs.com/skyfsm/p/7411961.html
3.https://blog.csdn.net/czl389/article/details/65937864、https://blog.csdn.net/czl389/article/details/60572159
一、总结1、2,基于特征的图像配准融合方法实现流程如下:
步骤1:计算获得两幅图像的特征点;
步骤2:根据特征对两幅图像进行匹配;
步骤3:对步骤2中匹配的特征对进行筛选;
步骤4:根据匹配关系得到单应性矩阵;
步骤5:根据单应性矩阵使用warpPerspective调整两幅图像为相对应图像;
步骤6:对应位置按比例融合。
二、代码部分
本代码使用sift特征,根据距离对匹配对儿筛选。对图像1、图像2进行配准融合。(图1、图2来自上述第三个链接)
图像1 图像2
正如链接3所述,实现两幅图像的拼接,主要是使用了单应矩阵和warpPerspective()这个库函数。
warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize)
这个函数不灵活。一方面,目标投影的图像不能选取感兴趣区域,使得每拼接两幅都会产生一个结果图像;另一方面,投影参考原点为图像左上角,如果投影后的图像在左方,就不能显示出,所以需要左侧的图像为参考图像。因此,下面拼接图1、图2将图像2作为参考图像。(在链接3中也给出了以右边图像作为参考时,将图像显示出来的方法,需要添加位移变换,具体可参考链接3)
#include <opencv2\highgui\highgui.hpp>
#include<opencv2\nonfree\nonfree.hpp>
#include<opencv2\legacy\legacy.hpp>
using namespace std;
using namespace cv;
void sift_Registration_fusion(Mat img1, Mat img2, Mat&fuseimage)
{
Mat g1(img1, Rect(0, 0, img1.cols, img1.rows));
Mat g2(img2, Rect(0, 0, img2.cols, img2.rows));
cvtColor(g1, g1, CV_BGR2GRAY);
cvtColor(g2, g2, CV_BGR2GRAY);
vector<cv::KeyPoint> keypoints_roi, keypoints_img;
Mat descriptor_roi, descriptor_img;
BFMatcher matcher(NORM_L2);
vector<cv::DMatch> matches, good_matches;
SIFT sift;
int i, dist = 80;
sift(g1, Mat(), keypoints_roi, descriptor_roi); /* get keypoints of ROI image */
sift(g2, Mat(), keypoints_img, descriptor_img); /* get keypoints of the image */
matcher.match(descriptor_roi, descriptor_img, matches);
double max_dist = 0; double min_dist = 1000;
for (int i = 0; i < descriptor_roi.rows; i++)
{
double dist = matches[i].distance;
if (dist < min_dist) min_dist = dist;
if (dist > max_dist) max_dist = dist;
}
for (i = 0; i < descriptor_roi.rows; i++)
{
if (matches[i].distance < 3 * min_dist)
{
good_matches.push_back(matches[i]);
}
}
vector<Point2f> keypoints1, keypoints2;
for (i = 0; i<good_matches.size(); i++)
{
keypoints1.push_back(keypoints_img[good_matches[i].trainIdx].pt);
keypoints2.push_back(keypoints_roi[good_matches[i].queryIdx].pt);
}
Mat Perspectiveimg;
//计算单应矩阵
Mat H = findHomography(keypoints1, keypoints2, CV_RANSAC);
//将投影全部画出来,可以计算变换后的图大小,如链接2中计算方式,此处直接将其放在2倍原图的大小进行拼接
warpPerspective(img2, Perspectiveimg, H, cv::Size(img1.cols*2, img1.rows), INTER_LINEAR);
imwrite("Perspectiveimg.jpg", Perspectiveimg);
img1.copyTo(Perspectiveimg(Rect(0, 0, img1.cols, img1.rows)));
fuseimage=Perspectiveimg.clone();
}
int main()
{
Mat img1 = cv::imread("img2.png");
Mat img2 = cv::imread("img1.png");
resize(img2, img2, Size(img1.cols, img1.rows));
Mat imgfuse;
sift_Registration_fusion(img1, img2, imgfuse);
imwrite("result.jpg", imgfuse);
}
上述示例,为了展示结果方便,在配准前将两幅图像变换为尺寸相同的图像,也可以不变换(如链接1、链接2需要提前计算结果图的大小),结果如下:
img1在img2上的透视变换图 拼接融合图
此处融合只是简单的拼接,链接2中有去裂纹的融合方法,还有更多的融合方法尚未研究实验。。。。
文中若有错误以及不妥之处,还望指出,以便共同学习。