最近一直在做视频实时拼接,然后就一步步来,先做了图像拼接,然后简单总结了下图像拼接的一般步骤。
1.特征点提取
主要算法:FAST、ORB、SURF、SIFT等算法。
SURF受SIFT启发,比SIFT快,健壮性比较好
ORB,基于FAST特征点和BREIF特征描述子,比SURF快10倍左右,比SIFT快100倍左右,综合效果最佳。
FAST主要做边角点检测,速度最快,但是进行特征描述,计算特征向量需要用SURF或者ORB的方法。
主要通过以下两个小的步骤来完成特征点提取的工作。
1).特征点描述,计算特征向量
//提取特征点
int minHessian = 700;//设置阈值
Ptr<SURF>detector = SURF::create(minHessian);
vector<KeyPoint> keyPoint1, keyPoint2;
detector->detect(image1, keyPoint1);
detector->detect(image2, keyPoint2);
//特征点描述,为下边的特征点匹配做准备
Ptr<SURF>extractor = SURF::create();
Mat descriptors1, descriptors2;
extractor->compute(image1, keyPoint1, descriptors1);
extractor->compute(image2, keyPoint2, descriptors2);
2).特征匹配(FlannBasedMatcher/knnMatch)
Opencv提供了两种匹配方法:BFMatcher和FlannBasedMatcher两种Matching方式。
区别在于BFMatcher总是尝试所有可能的匹配,从而使得它总能够找到最佳匹配,而FlannBasedMatcher算法更快但是找到的 是最近邻近似匹配,所以当我们需要找到一个相对好的匹配但是不需要最佳匹配的时候往往使用FlannBasedMatcher。
BFMatcher有match,knnMatch
FlannBasedMatcher有match,knnMatch,radiusMatch
FlannBasedMatcher matcher;
vector<vector<DMatch> > matchePoints;
vector<DMatch> GoodMatchePoints;
vector<Mat> train_desc(1, descriptors1);
matcher.add(train_desc);
matcher.train();
matcher.knnMatch(descriptors2, matchePoints, 2);
然后剔除筛选匹配点,获取优秀匹配点
// Lowe's algorithm,获取优秀匹配点
for (int i = 0; i < matchePoints.size(); i++)
{
//设置筛选的条件,即匹配点的距离
if (matchePoints[i][0].distance < 0.4 * matchePoints[i][1].distance)
{
GoodMatchePoints.push_back(matchePoints[i][0]);
}
}
vector<Point2f> imagePoints1, imagePoints2;
for (int i = 0; i<GoodMatchePoints.size(); i++)
{
imagePoints2.push_back(keyPoint2[GoodMatchePoints[i].queryIdx].pt);
imagePoints1.push_back(keyPoint1[GoodMatchePoints[i].trainIdx].pt);
}
2.图像配准
Findhomography() 单应矩阵: Method(RANSAC/0/LMEDS/RHO)
findFundamentalMat() 基础矩阵: Method(CV_FM_7POINT/CV_FM_8POINT/CV_FM_RANSAC/CV_FM_LMEDS)
对于一组图像数据集中的两幅图像,通过寻找一种空间变换把一幅图像映射到另一幅图像,使得两图中对应于空间同一位置的点一一对应起来,从而达到信息融合的目的
//获取图像1到图像2的投影映射矩阵 尺寸为3*3
Mat homo = findHomography(imagePoints1, imagePoints2, CV_RANSAC);
也可以使用getPerspectiveTransform方法获得透视变换矩阵,不过要求只能有4个点,效果稍差
//Mat homo=getPerspectiveTransform(imagePoints1,imagePoints2);
cout << "变换矩阵为:\n" << homo << endl << endl; //输出映射矩阵
CalcCorners(homo, newright, corners2);
//图像配准
//warpPerspective 函数 对图像进行透视变换,就是变形
Mat imageTransform1, imageTransform2;
warpPerspective(newright, imageTransform1, homo, Size(MAX(corners2.right_top.x, corners2.right_bottom.x), newleft.rows));
imshow("warp", imageTransform1);
3.将图像拷贝到另一幅图像的特定位置
imageTransform1.copyTo(dst(Rect(0, 0, imageTransform1.cols, imageTransform1.rows)));
newleft.copyTo(dst(Rect(0, 0, newleft.cols, newleft.rows)));
4.进行图像融合边界处理
//优化图像融合边界函数
OptimizeSeam(newleft, imageTransform1, dst);
总体上,图像拼接的基本步骤就是这样。通过做好这几步,可以完成图像的一般拼接工作的。如果大家需要图形拼接示例代码的话可以评论,看到了会给大家发代码的。