上一篇文章简单介绍过什么是Homographies,点击链接
这里总结一下Homographies:
假设我们有两个相同平面对象的图像,它们之间有一些重叠(存在对应点)。让P1成为第一图像中的点的像素坐标[P1x,P1y,1 ],P2是第二图像中的点的像素坐标[P2X,P2Y,1 ]。我们将第一图像称为目的图像,第二图像称为源图像。单应性1H2是将第二图像中的每个像素坐标与第一图像中的像素坐标映射关系。
P1 = 1H2 * P2
获取对应点
可以使用特征点检测,如 SIFT,SURF,ORB等,这里,我们使用sift,虽然慢,但是相比于其他,这个是最准确的。
1,两张有重叠部分的图
2 如果我们想把源图像的像素粘贴在一个大的画布上,我们需要考虑在更大的画布上的目标图像。
int offsetx = 800;
int offsety = 1000;
Mat trans_mat = (Mat_<double>(2, 3) << 1, 0, offsetx, 0, 1, offsety);
warpAffine(im1, im1, trans_mat, Size(3 * im1.cols, 3 * im1.rows));
结果
SIFT特征检测
cv::Ptr<Feature2D> f2d = xfeatures2d::SIFT::create();
// Step 1: Detect the keypoints:
std::vector<KeyPoint> keypoints_1, keypoints_2;
f2d->detect( im_1, keypoints_1 );
f2d->detect( im_2, keypoints_2 );
// Step 2: Calculate descriptors (feature vectors)
Mat descriptors_1, descriptors_2;
f2d->compute( im_1, keypoints_1, descriptors_1 );
f2d->compute( im_2, keypoints_2, descriptors_2 );
// Step 3: Matching descriptor vectors using BFMatcher :
BFMatcher matcher;
std::vector< DMatch > matches;
matcher.match( descriptors_1, descriptors_2, matches );
// Keep 200 best matches only.
// We sort distance between descriptor matches
Mat index;
int nbMatch = int(matches.size());
Mat tab(nbMatch, 1, CV_32F);
for (int i = 0; i < nbMatch; i++)
tab.at<float>(i, 0) = matches[i].distance;
sortIdx(tab, index, SORT_EVERY_COLUMN + SORT_ASCENDING);
vector<DMatch> bestMatches;
for (int i = 0; i < 200; i++)
bestMatches.push_back(matches[index.at < int > (i, 0)]);
匹配对应点
计算单应性矩阵
// 1st image is the destination image and the 2nd image is the src image
std::vector<Point2f> dst_pts; //1st
std::vector<Point2f> source_pts; //2nd
for (vector<DMatch>::iterator it = bestMatches.begin(); it != bestMatches.end(); ++it) {
//-- Get the keypoints from the good matches
dst_pts.push_back( keypoints_1[ it->queryIdx ].pt );
source_pts.push_back( keypoints_2[ it->trainIdx ].pt );
}
Mat H = findHomography( source_pts, dst_pts, CV_RANSAC );
cout << H << endl;
输出H为
[0.119035947337248, -0.04651626756941147, 1700.852494625838;
-0.5652916039380339, 0.9340629651977271, 1045.011078408947;
-0.0004251711674909509, 1.783961055570689e-05, 1]
现在我们有单应性。我们只需要将ima2上的透视扭曲应用于IMA1大小的空白黑色画布上。
Mat wim_2;
warpPerspective(im_2, wim_2, H, im_1.size());
im_2经过H 变换,变为wim_2,
效果图如下:
大部分事情现在都完成了。我们只需要把这两个图像合并在一起。这一粗暴和简单的方法是:复制所有这些像素的wim_2到im_1是黑色的im_1。
// We can do this since im_1 and wim_2 have the same size.
for (int i = 0; i < im_1.cols; i++)
for (int j = 0; j < im_1.rows; j++) {
Vec3b color_im1 = im_1.at<Vec3b>(Point(i, j));
Vec3b color_im2 = wim_2.at<Vec3b>(Point(i, j));
if (norm(color_im1) == 0)
im_1.at<Vec3b>(Point(i, j)) = color_im2;
}
展示im_1:
更多详情请关注个人工作号: