图像拼接(十二):OpenCV SeamFinder+GraphCut+最佳拼接缝寻找

您所在的位置:网站首页 拼图有缝隙怎么消除 图像拼接(十二):OpenCV SeamFinder+GraphCut+最佳拼接缝寻找

图像拼接(十二):OpenCV SeamFinder+GraphCut+最佳拼接缝寻找

2024-07-12 20:25| 来源: 网络整理| 查看: 265

很多情况下,使用一个全局单应变换并不能准确对齐图像,需要一些后处理来削弱拼接的痕迹,比如寻找最佳拼接缝。

使用全局单应变换的对齐结果,实现代码参考图像拼接(六):OpenCV单应变换模型拼接两幅图像:

这里写图片描述

仔细观察,在拼缝的下方出现了没对齐的问题。

寻找最佳拼接缝算法中,Graph Cut很经典。它将计算机视觉问题和网络流联系在一起。寻找最佳拼接缝等价于求网络流的最小割。 在网络流问题中,最小割和最大流相等。这些概念可能会使你困惑,了解一些概念,可参考百度文库里图文并茂的PPT-最大流问题

不了解这些这些概念也没关系,因为在这里我也没打算自己造轮子实现。

OpenCV stitching模块里有相关的函数,实现里基于最小图割的最佳拼接缝寻找算法。官方文档见这里:cv::detail::GraphCutSeamFinder Class Reference

void cv::detail::GraphCutSeamFinder::find ( const std::vector< UMat > & src, const std::vector< Point > & corners, std::vector< UMat > & masks )

具体怎么操作呢?

先输入两幅用全局单应变换配准后的图像,要统一坐标系。在这里图像宽为原始图像的两倍。

left 这里写图片描述

right 这里写图片描述

find函数第一个参数是输入源图像集合;第二个参数corners的是图像左上角坐标的集合;第三个参数会返回更新的拼接缝掩码。

按这种思路,两幅图像左上角都是(0,0),输入图像完全是重合的。但测试发现,这样使用函数并不能返回预期的结果。个人猜测,可能是因为完全重合,算法找不到分割的起点和终点,或者是因为图像中有大面积的黑色。

最佳的使用情形是输入图像间有部分重合区域。

既然这样,先剪切,需要注意不要忘记图像的配准信息。

从中间剪,其实是原图。 这里写图片描述

从1/4和3/4位置剪,(隐含了图像间有一半重复的假设) 这里写图片描述

这样,左图的corner是(0,0),右图的corner是(0.5*width,0)。

使用find函数返回得到表现拼接缝位置的掩码:

左图掩码: 这里写图片描述

右图掩码: 这里写图片描述

咦?为啥拼接缝这么竖直?不清楚内部原理的我,内心也有些许忐忑,担心效果。

最后的效果说明结果是合理的,根据掩码拼合图像:

这里写图片描述

以上对函数的说明,属于个人理解,如有错误,多谢指正帮助。

代码:

#include #include #include #include using namespace cv; using namespace cv::detail; int main() { //统一坐标下的两幅图 // 此例中canvas.width=2*src.width Mat canvas1 = imread("left.jpg"); Mat canvas2 = imread("right.jpg"); //将两幅图剪切出来,剪切位置包含了配准(两幅图像的相对位置)信息 Mat image1 = canvas1(Range::all(), Range(0, canvas1.cols/2)); Mat image2 = canvas2(Range::all(), Range(canvas2.cols/4, canvas2.cols*3/4));//假设大概1/2重复区域 image1.convertTo(image1, CV_32FC3); image2.convertTo(image2, CV_32FC3); image1 /= 255.0; image2 /= 255.0; //在找拼缝的操作中,为了减少计算量,用image_small Mat image1_small; Mat image2_small; Size small_size1 = Size(image1.cols / 2, image1.rows / 2); Size small_size2 = Size(image2.cols / 2, image2.rows / 2); resize(image1, image1_small, small_size1); resize(image2, image2_small, small_size2); // 左图的左上角坐标 cv::Point corner1; corner1.x = 0; corner1.y = 0; //右图的左上角坐标 cv::Point corner2; corner2.x = image2_small.cols/2; corner2.y = 0; std::vector corners; corners.push_back(corner1); corners.push_back(corner2); std::vector masks; Mat imageMask1(small_size1, CV_8U); Mat imageMask2(small_size2, CV_8U); imageMask1 = Scalar::all(255); imageMask2 = Scalar::all(255); masks.push_back(imageMask1); masks.push_back(imageMask2); std::vector sources; sources.push_back(image1_small); sources.push_back(image2_small); Ptr seam_finder = new cv::detail::GraphCutSeamFinder(GraphCutSeamFinderBase::COST_COLOR); seam_finder->find(sources, corners, masks); //将mask恢复放大 resize(masks[0], imageMask1, image1.size()); resize(masks[1], imageMask2, image2.size()); Mat canvas(image1.rows,image1.cols*3/2,CV_32FC3); image1.copyTo(canvas(Range::all(), Range(0, canvas.cols*2/3)), imageMask1); image2.copyTo(canvas(Range::all(), Range(canvas.cols / 3, canvas.cols)), imageMask2); /*canvas *= 255; canvas.convertTo(canvas, CV_8UC3);*/ imshow("canvas",canvas); imshow("Mask1",masks[0]); imshow("Mask2", masks[1]); imshow("src1", sources[0]); imshow("src2", sources[1]); waitKey(0); return 0; }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3