C++ OpenCV实战之图像全景拼接

目录
  • 前言
  • 一、OpenCV Stitcher
    • 1.功能源码
    • 2.效果
  • 二、图像全景拼接
    • 1.特征检测
    • 2.计算单应性矩阵
    • 3.透视变换
    • 4.图像拼接
    • 5.功能源码
    • 6.效果
  • 三、源码
  • 总结

前言

本文将使用OpenCV C++ 进行图像全景拼接。目前使用OpenCV对两幅图像进行拼接大致可以分为两类。

一、使用OpenCV内置API Stitcher 进行拼接。

二、使用特征检测算法匹配两幅图中相似的点、计算变换矩阵、最后对其进行透视变换就可以了。

一、OpenCV Stitcher

imageA

imageB

原图如图所示。本案例的需求是将上述两幅图片拼接成一幅图像。首先使用OpenCV提供的Stitcher进行拼接。关于Stitcher的具体原理请大家自行查找相关资料。

1.功能源码

bool OpenCV_Stitching(Mat imageA, Mat imageB)
{
	vector<Mat>images;
	images.push_back(imageA);
	images.push_back(imageB);

	Ptr<Stitcher>stitcher = Stitcher::create();

	Mat result;
	Stitcher::Status status = stitcher->stitch(images, result);// 使用stitch函数进行拼接

	if (status != Stitcher::OK) return false;

	imshow("OpenCV图像全景拼接", result);

	return true;
}

2.效果

这就是使用OpenCV 内置Stitcher拼接出来的效果。

二、图像全景拼接

1.特征检测

使用方法二进行图像全景拼接。目前网上教程大致流程归为:

1、使用特征检测算子提取两幅图像的关键点,然后进行特征描述子匹配。我这里使用的是SURF算子。当然SIFT等其他特征检测算子也可以。

	//创建SURF特征检测器
	int Hessian = 800;
	Ptr<SURF>detector = SURF::create(Hessian);

	//进行图像特征检测、特征描述
	vector<KeyPoint>keypointA, keypointB;
	Mat descriptorA, descriptorB;
	detector->detectAndCompute(imageA, Mat(), keypointA, descriptorA);
	detector->detectAndCompute(imageB, Mat(), keypointB, descriptorB);

	//使用FLANN算法进行特征描述子的匹配
	FlannBasedMatcher matcher;
	vector<DMatch>matches;
	matcher.match(descriptorA, descriptorB, matches);

如图为使用FLANN算法进行特征描述子匹配的结果。我们需要把那些匹配程度高的关键点筛选出来用以下面计算两幅图像的单应性矩阵。

2、筛选出匹配程度高的关键点

	double Max = 0.0;
	for (int i = 0; i < matches.size(); i++)
	{
		//float distance –>代表这一对匹配的特征点描述符(本质是向量)的欧氏距离,数值越小也就说明两个特征点越相像。
		double dis = matches[i].distance;
		if (dis > Max)
		{
			Max = dis;
		}
	}

	//筛选出匹配程度高的关键点
	vector<DMatch>goodmatches;
	vector<Point2f>goodkeypointA, goodkeypointB;
	for (int i = 0; i < matches.size(); i++)
	{
		double dis = matches[i].distance;
		if (dis < 0.15*Max)
		{
			//int queryIdx –>是测试图像的特征点描述符(descriptor)的下标,同时也是描述符对应特征点(keypoint)的下标。
			goodkeypointA.push_back(keypointA[matches[i].queryIdx].pt);
			//int trainIdx –> 是样本图像的特征点描述符的下标,同样也是相应的特征点的下标。
			goodkeypointB.push_back(keypointB[matches[i].trainIdx].pt);
			goodmatches.push_back(matches[i]);
		}
	}

如图为imageA筛选出来的关键点。

如图为imageB筛选出来的关键点。

从上图可以看出,我们已经筛选出imageA,imageB共有的关键点部分。接下来,我们需要使用这两个点集计算两幅图的单应性矩阵。

2.计算单应性矩阵

计算单应性变换矩阵

    //获取图像A到图像B的投影映射矩阵,尺寸为3*3
    Mat H = findHomography(goodkeypointA, goodkeypointB, RANSAC);
    Mat M = (Mat_<double>(3, 3) << 1.0, 0, imageA.cols, 0, 1.0, 0, 0, 0, 1.0);
    Mat Homo = M * H;

3.透视变换

根据计算出来的单应性矩阵对imageA进行透视变换

    //进行透视变换
    Mat DstImg;
    warpPerspective(imageA, DstImg, Homo, Size(imageB.cols + imageA.cols, imageB.rows));
    imshow("透视变换", DstImg);

如图所示为imageA进行透视变换得到的结果。

4.图像拼接

根据上述操作,我们已经得到了经透视变换的imageA,接下来只需将imageA与imageB拼接起来就可以了。

    imageB.copyTo(DstImg(Rect(imageA.cols, 0, imageB.cols, imageB.rows)));
    imshow("图像全景拼接", DstImg);

5.功能源码

bool Image_Stitching(Mat imageA, Mat imageB, bool draw)
{
	//创建SURF特征检测器
	int Hessian = 800;
	Ptr<SURF>detector = SURF::create(Hessian);

	//进行图像特征检测、特征描述
	vector<KeyPoint>keypointA, keypointB;
	Mat descriptorA, descriptorB;
	detector->detectAndCompute(imageA, Mat(), keypointA, descriptorA);
	detector->detectAndCompute(imageB, Mat(), keypointB, descriptorB);

	//使用FLANN算法进行特征描述子的匹配
	FlannBasedMatcher matcher;
	vector<DMatch>matches;
	matcher.match(descriptorA, descriptorB, matches);

	double Max = 0.0;
	for (int i = 0; i < matches.size(); i++)
	{
		//float distance –>代表这一对匹配的特征点描述符(本质是向量)的欧氏距离,数值越小也就说明两个特征点越相像。
		double dis = matches[i].distance;
		if (dis > Max)
		{
			Max = dis;
		}
	}

	//筛选出匹配程度高的关键点
	vector<DMatch>goodmatches;
	vector<Point2f>goodkeypointA, goodkeypointB;
	for (int i = 0; i < matches.size(); i++)
	{
		double dis = matches[i].distance;
		if (dis < 0.15*Max)
		{
			//int queryIdx –>是测试图像的特征点描述符(descriptor)的下标,同时也是描述符对应特征点(keypoint)的下标。
			goodkeypointA.push_back(keypointA[matches[i].queryIdx].pt);
			//int trainIdx –> 是样本图像的特征点描述符的下标,同样也是相应的特征点的下标。
			goodkeypointB.push_back(keypointB[matches[i].trainIdx].pt);
			goodmatches.push_back(matches[i]);
		}
	}

	if (draw)
	{
		Mat result;
		drawMatches(imageA, keypointA, imageB, keypointB, goodmatches, result);
		imshow("特征匹配", result);

		Mat temp_A = imageA.clone();
		for (int i = 0; i < goodkeypointA.size(); i++)
		{
			circle(temp_A, goodkeypointA[i], 3, Scalar(0, 255, 0), -1);
		}
		imshow("goodkeypointA", temp_A);

		Mat temp_B = imageB.clone();
		for (int i = 0; i < goodkeypointB.size(); i++)
		{
			circle(temp_B, goodkeypointB[i], 3, Scalar(0, 255, 0), -1);
		}
		imshow("goodkeypointB", temp_B);
	}

	//findHomography计算单应性矩阵至少需要4个点
	/*
	计算多个二维点对之间的最优单映射变换矩阵H(3x3),使用MSE或RANSAC方法,找到两平面之间的变换矩阵
	*/
	if (goodkeypointA.size() < 4 || goodkeypointB.size() < 4) return false;

	//获取图像A到图像B的投影映射矩阵,尺寸为3*3
	Mat H = findHomography(goodkeypointA, goodkeypointB, RANSAC);
	Mat M = (Mat_<double>(3, 3) << 1.0, 0, imageA.cols, 0, 1.0, 0, 0, 0, 1.0);
	Mat Homo = M * H;

	//进行透视变换
	Mat DstImg;
	warpPerspective(imageA, DstImg, Homo, Size(imageB.cols + imageA.cols, imageB.rows));
	imshow("透视变换", DstImg);

	imageB.copyTo(DstImg(Rect(imageA.cols, 0, imageB.cols, imageB.rows)));
	imshow("图像全景拼接", DstImg);

	return true;
}

6.效果

最终拼接效果如图所示。

三、源码

#include<iostream>
#include<opencv2/opencv.hpp>
#include<opencv2/xfeatures2d.hpp>
#include<opencv2/stitching.hpp>
using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;

//1、使用特征检测算法找到两张图像中相似的点,计算变换矩阵
//2、将A透视变换后得到的图片与B拼接

bool Image_Stitching(Mat imageA, Mat imageB, bool draw)
{
	//创建SURF特征检测器
	int Hessian = 800;
	Ptr<SURF>detector = SURF::create(Hessian);

	//进行图像特征检测、特征描述
	vector<KeyPoint>keypointA, keypointB;
	Mat descriptorA, descriptorB;
	detector->detectAndCompute(imageA, Mat(), keypointA, descriptorA);
	detector->detectAndCompute(imageB, Mat(), keypointB, descriptorB);

	//使用FLANN算法进行特征描述子的匹配
	FlannBasedMatcher matcher;
	vector<DMatch>matches;
	matcher.match(descriptorA, descriptorB, matches);

	double Max = 0.0;
	for (int i = 0; i < matches.size(); i++)
	{
		//float distance –>代表这一对匹配的特征点描述符(本质是向量)的欧氏距离,数值越小也就说明两个特征点越相像。
		double dis = matches[i].distance;
		if (dis > Max)
		{
			Max = dis;
		}
	}

	//筛选出匹配程度高的关键点
	vector<DMatch>goodmatches;
	vector<Point2f>goodkeypointA, goodkeypointB;
	for (int i = 0; i < matches.size(); i++)
	{
		double dis = matches[i].distance;
		if (dis < 0.15*Max)
		{
			//int queryIdx –>是测试图像的特征点描述符(descriptor)的下标,同时也是描述符对应特征点(keypoint)的下标。
			goodkeypointA.push_back(keypointA[matches[i].queryIdx].pt);
			//int trainIdx –> 是样本图像的特征点描述符的下标,同样也是相应的特征点的下标。
			goodkeypointB.push_back(keypointB[matches[i].trainIdx].pt);
			goodmatches.push_back(matches[i]);
		}
	}

	if (draw)
	{
		Mat result;
		drawMatches(imageA, keypointA, imageB, keypointB, goodmatches, result);
		imshow("特征匹配", result);

		Mat temp_A = imageA.clone();
		for (int i = 0; i < goodkeypointA.size(); i++)
		{
			circle(temp_A, goodkeypointA[i], 3, Scalar(0, 255, 0), -1);
		}
		imshow("goodkeypointA", temp_A);

		Mat temp_B = imageB.clone();
		for (int i = 0; i < goodkeypointB.size(); i++)
		{
			circle(temp_B, goodkeypointB[i], 3, Scalar(0, 255, 0), -1);
		}
		imshow("goodkeypointB", temp_B);
	}

	//findHomography计算单应性矩阵至少需要4个点
	/*
	计算多个二维点对之间的最优单映射变换矩阵H(3x3),使用MSE或RANSAC方法,找到两平面之间的变换矩阵
	*/
	if (goodkeypointA.size() < 4 || goodkeypointB.size() < 4) return false;

	//获取图像A到图像B的投影映射矩阵,尺寸为3*3
	Mat H = findHomography(goodkeypointA, goodkeypointB, RANSAC);
	Mat M = (Mat_<double>(3, 3) << 1.0, 0, imageA.cols, 0, 1.0, 0, 0, 0, 1.0);
	Mat Homo = M * H;

	//进行透视变换
	Mat DstImg;
	warpPerspective(imageA, DstImg, Homo, Size(imageB.cols + imageA.cols, imageB.rows));
	imshow("透视变换", DstImg);

	imageB.copyTo(DstImg(Rect(imageA.cols, 0, imageB.cols, imageB.rows)));
	imshow("图像全景拼接", DstImg);

	return true;
}

bool OpenCV_Stitching(Mat imageA, Mat imageB)
{
	vector<Mat>images;
	images.push_back(imageA);
	images.push_back(imageB);

	Ptr<Stitcher>stitcher = Stitcher::create();

	Mat result;
	Stitcher::Status status = stitcher->stitch(images, result);// 使用stitch函数进行拼接

	if (status != Stitcher::OK) return false;

	imshow("OpenCV图像全景拼接", result);

	return true;
}

int main()
{

	Mat imageA = imread("image1.jpg");
	Mat imageB = imread("image2.jpg");
	if (imageA.empty() || imageB.empty())
	{
		cout << "No Image!" << endl;
		system("pause");
		return -1;
	}

	if (!Image_Stitching(imageA, imageB, true))
	{
		cout << "can not stitching the image!" << endl;
	}

	if (!OpenCV_Stitching(imageA, imageB))
	{
		cout << "can not stitching the image!" << endl;
	}

	waitKey(0);
	system("pause");
	return 0;
}

总结

本文使用OpenCV C++进行图像全景拼接,关键步骤有以下几点。

1、使用特征检测算子提取两幅图像的关键点,然后进行特征描述子匹配。

2、筛选出匹配程度高的关键点计算两幅图的单应性矩阵。

3、利用计算出来的单应性矩阵对其中一张图片进行透视变换。

4、将透视变换的图片与另一张图片进行拼接。

以上就是C++ OpenCV实战之图像全景拼接的详细内容,更多关于 OpenCV图像全景拼接的资料请关注我们其它相关文章!

(0)

相关推荐

  • python实现图像全景拼接

    图像的全景拼接包括三大部分:特征点提取与匹配.图像配准.图像融合. 1.基于SIFT的特征点的提取与匹配 利用Sift提取图像的局部特征,在尺度空间寻找极值点,并提取出其位置.尺度.方向信息. 具体步骤: 1). 生成高斯差分金字塔(DOG金字塔),尺度空间构建 2). 空间极值点检测(关键点的初步查探) 3). 稳定关键点的精确定位 4). 稳定关键点方向信息分配 5). 关键点描述 6). 特征点匹配 2.图像配准 图像配准是一种确定待拼接图像间的重叠区域以及重叠位置的技术,它是整个图像拼接

  • Opencv使用Stitcher类图像拼接生成全景图像

    Opencv中自带的Stitcher类可以实现全景图像,效果不错.下边的例子是Opencv Samples中的stitching.cpp的简化,源文件可以在这个路径里找到: \opencv\sources\samples\cpp\stitching.cpp #include <fstream> #include "opencv2/highgui/highgui.hpp" #include "opencv2/stitching/stitcher.hpp"

  • OpenCV全景图像拼接的实现示例

    本文主要介绍了OpenCV全景图像拼接的实现示例,分享给大家,具体如下: left_01.jpg right_01.jpg Stitcher.py import numpy as np import cv2 class Stitcher: #拼接函数 def stitch(self, images, ratio=0.75, reprojThresh=4.0,showMatches=False): #获取输入图片 (imageB, imageA) = images #检测A.B图片的SIFT关键特

  • 基于Python和openCV实现图像的全景拼接详细步骤

    基本介绍 图像的全景拼接,即"缝合"两张具有重叠区域的图来创建一张全景图.其中用到了计算机视觉和图像处理技术有:关键点检测.局部不变特征.关键点匹配.RANSAC(Random Sample Consensus,随机采样一致性)和透视变形. 具体步骤 (1)检测左右两张图像的SIFT关键特征点,并提取局部不变特征 : (2)使用knnMatch检测来自右图(左图)的SIFT特征,与左图(右图)进行匹配 : (3)计算视角变换矩阵H,用变换矩阵H对右图进行扭曲变换: (4)将左图(右图)

  • OpenCV 图像拼接和图像融合的实现

    目录 基于SURF的图像拼接 1.特征点提取和匹配 2.图像配准 3. 图像拷贝 4.图像融合(去裂缝处理) 基于ORB的图像拼接 opencv自带的拼接算法stitch 1.opencv stitch选择的特征检测方式 2.opencv stitch获取匹配点的方式 图像拼接在实际的应用场景很广,比如无人机航拍,遥感图像等等,图像拼接是进一步做图像理解基础步骤,拼接效果的好坏直接影响接下来的工作,所以一个好的图像拼接算法非常重要. 再举一个身边的例子吧,你用你的手机对某一场景拍照,但是你没有办

  • 使用c++实现OpenCV图像横向&纵向拼接

    功能函数 // 图像拼接 cv::Mat ImageSplicing(vector<cv::Mat> images,int type) { if (type != 0 && type != 1) type = 0; int num = images.size(); int newrow = 0; int newcol = 0; cv::Mat result; // 横向拼接 if (type == 0) { int minrow = 10000; for (int i = 0;

  • Python+OpenCV实现图像的全景拼接

    本文实例为大家分享了Python+OpenCV实现图像的全景拼接的具体代码,供大家参考,具体内容如下 环境:python3.5.2 + openCV3.4 1.算法目的 将两张相同场景的场景图片进行全景拼接. 2.算法步骤 本算法基本步骤有以下几步: 步骤1:将图形先进行桶形矫正 没有进行桶形变换的图片效果可能会像以下这样: 图片越多拼接可能就会越夸张. 本算法是将图片进行桶形矫正.目的就是来缩减透视变换(Homography)之后图片产生的变形,从而使拼接图片变得畸形. 步骤2:特征点匹配 本

  • C++ OpenCV实战之图像全景拼接

    目录 前言 一.OpenCV Stitcher 1.功能源码 2.效果 二.图像全景拼接 1.特征检测 2.计算单应性矩阵 3.透视变换 4.图像拼接 5.功能源码 6.效果 三.源码 总结 前言 本文将使用OpenCV C++ 进行图像全景拼接.目前使用OpenCV对两幅图像进行拼接大致可以分为两类. 一.使用OpenCV内置API Stitcher 进行拼接. 二.使用特征检测算法匹配两幅图中相似的点.计算变换矩阵.最后对其进行透视变换就可以了. 一.OpenCV Stitcher imag

  • C++ OpenCV实战之图像透视矫正

    目录 前言 一.图像预处理 二.轮廓提取 1.提取最外轮廓 2.提取矩形四个角点 3.将矩形角点排序 三.透视矫正 四.源码 前言 本文将使用OpenCV C++ 进行图像透视矫正. 一.图像预处理 原图如图所示.首先进行图像预处理.将图像进行灰度.滤波.二值化.形态学等操作,目的是为了下面的轮廓提取.在这里我还使用了形态学开.闭操作,目的是使整个二值图像连在一起.大家在做图像预处理时,可以根据图像特征自行处理. Mat gray; cvtColor(src, gray, COLOR_BGR2G

  • C++ OpenCV实战之制作九宫格图像

    目录 前言 一.九宫格图像 二.源码 三.效果显示 总结 前言 本文将使用OpenCV C++ 制作九宫格图像.其实原理很简单,就是将一张图像均等分成九份.然后将这九个小块按一定间隔(九宫格效果)拷贝到新画布上就可以啦. 一.九宫格图像 原图如图所示.本案例的需求是希望将图像均等分成九份,制作九宫格图像.首先得将原图均等分成九份. 如图所示.将原图均等分成九份,然后将这每一个小方块按照一定的间隔(九宫格效果)拷贝到新图像就可以了.具体算法思想请看源码注释. 二.源码 #include<iostr

  • Python+Opencv实战之人脸追踪详解

    目录 前言 人脸追踪技术简介 使用基于 dlib DCF 的跟踪器进行人脸跟踪 使用基于 dlib DCF 的跟踪器进行对象跟踪 小结 前言 人脸处理是人工智能中的一个热门话题,人脸处理可以使用计算机视觉算法从人脸中自动提取大量信息,例如身份.意图和情感:而目标跟踪试图估计目标在整个视频序列中的轨迹,其中只有目标的初始位置是已知的,将这两者进行结合将产生许多有趣的应用.由于外观变化.遮挡.快速运动.运动模糊和比例变化等多种因素,人脸追踪非常具有挑战性. 人脸追踪技术简介 基于判别相关滤波器 (d

  • Python OpenCV实战之与机器学习的碰撞

    目录 0. 前言 1. 机器学习简介 1.1 监督学习 1.2 无监督学习 1.3 半监督学习 2. K均值 (K-Means) 聚类 2.1 K-Means 聚类示例 3. K最近邻 3.1 K最近邻示例 4. 支持向量机 4.1 支持向量机示例 小结 0. 前言 机器学习是人工智能的子集,它为计算机以及其它具有计算能力的系统提供自动预测或决策的能力,诸如虚拟助理.车牌识别系统.智能推荐系统等机器学习应用程序给我们的日常生活带来了便捷的体验.机器学习的蓬勃发展,得益于以下三个关键因素:1) 海

  • Python+OpenCV实战之利用 K-Means 聚类进行色彩量化

    目录 前言 利用 K-Means 聚类进行色彩量化 完整代码 显示色彩量化后的色彩分布 前言 K-Means 聚类算法的目标是将 n 个样本划分(聚类)为 K 个簇,在博文<OpenCV与机器学习的碰撞>中,我们已经学习利用 OpenCV 提供了 cv2.kmeans() 函数实现 K-Means 聚类算法,该算法通过找到簇的中心并将输入样本分组到簇周围,同时通过简单的示例了解了 K-Means 算法的用法.在本文中,我们将学习如何利用 K-Means 聚类进行色彩量化,以减少图像中颜色数量.

  • Python OpenCV学习之图像滤波详解

    目录 背景 一.卷积相关概念 二.卷积实战 三.均值滤波 四.高斯滤波 五.中值滤波 六.双边滤波 七.Sobel算子 八.Scharr算子 九.拉普拉斯算子 十.Canny算法 背景 图像滤波的作用简单来说就是将一副图像通过滤波器得到另一幅图像:明确一个概念,滤波器又被称为卷积核,滤波的过程又被称为卷积:实际上深度学习就是训练许多适应任务的滤波器,本质上就是得到最佳的参数:当然在深度学习之前,也有一些常见的滤波器,本篇主要介绍这些常见的滤波器: 一.卷积相关概念 卷积核大小一般为奇数的原因:

  • OpenCV实战之基于Hu矩实现轮廓匹配

    目录 前言 一.查找轮廓 二.计算Hu矩 三.显示效果 四.源码 总结 前言 本文将使用OpenCV C++ 基于Hu矩进行轮廓匹配. 一.查找轮廓 原图 测试图 vector<vector<Point>>findContour(Mat Image) {     Mat gray;     cvtColor(Image, gray, COLOR_BGR2GRAY);     Mat thresh;     threshold(gray, thresh, 0, 255, THRESH

  • OpenCV实战之图像拼接的示例代码

    目录 背景 实现步骤 一.读取文件 二.单应性矩阵计算 三.图像拼接 总结 背景 图像拼接可以应用到手机中的全景拍摄,也就是将多张图片根据关联信息拼成一张图片: 实现步骤 1.读文件并缩放图片大小: 2.根据特征点和计算描述子,得到单应性矩阵: 3.根据单应性矩阵对图像进行变换,然后平移: 4.图像拼接并输出拼接后结果图: 一.读取文件 第一步实现读取两张图片并缩放到相同尺寸: 代码如下: img1 = cv2.imread('map1.png') img2 = cv2.imread('map2

  • C++ OpenCV实战之车道检测

    目录 前言 一.获取车道ROI区域 二.车道检测 1.灰度.阈值 2.获取非零像素点 3.绘制车道线 总结 前言 本文将使用OpenCV C++ 进行车道检测. 一.获取车道ROI区域 原图如图所示. 使用下面代码段获取ROI区域.该ROI区域点集根据图像特征自己设定.通过fillPoly填充ROI区域,最终通过copyTo在原图中扣出ROI. void GetROI(Mat src, Mat &image) {     Mat mask = Mat::zeros(src.size(), src

随机推荐