溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C++?OpenCV如何進行圖像全景拼接

發(fā)布時間:2022-01-05 14:18:12 來源:億速云 閱讀:623 作者:柒染 欄目:開發(fā)技術(shù)

C++ OpenCV如何進行圖像全景拼接,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

    前言

    下面將使用OpenCV C++ 進行圖像全景拼接。目前使用OpenCV對兩幅圖像進行拼接大致可以分為兩類。

    一、使用OpenCV內(nèi)置API Stitcher 進行拼接。

    二、使用特征檢測算法匹配兩幅圖中相似的點、計算變換矩陣、最后對其進行透視變換就可以了。

    一、OpenCV Stitcher

    imageA

    C++?OpenCV如何進行圖像全景拼接

    imageB

    C++?OpenCV如何進行圖像全景拼接

    原圖如圖所示。本案例的需求是將上述兩幅圖片拼接成一幅圖像。首先使用OpenCV提供的Stitcher進行拼接。關(guān)于Stitcher的具體原理請大家自行查找相關(guān)資料。

    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函數(shù)進行拼接
    
    	if (status != Stitcher::OK) return false;
    
    	imshow("OpenCV圖像全景拼接", result);
    
    	return true;
    }

    2.效果

    C++?OpenCV如何進行圖像全景拼接

    這就是使用OpenCV 內(nèi)置Stitcher拼接出來的效果。

    二、圖像全景拼接

    1.特征檢測

    使用方法二進行圖像全景拼接。目前網(wǎng)上教程大致流程歸為:

    1、使用特征檢測算子提取兩幅圖像的關(guān)鍵點,然后進行特征描述子匹配。我這里使用的是SURF算子。當(dāng)然SIFT等其他特征檢測算子也可以。

    	//創(chuàng)建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);

    C++?OpenCV如何進行圖像全景拼接

     如圖為使用FLANN算法進行特征描述子匹配的結(jié)果。我們需要把那些匹配程度高的關(guān)鍵點篩選出來用以下面計算兩幅圖像的單應(yīng)性矩陣。

    2、篩選出匹配程度高的關(guān)鍵點

    	double Max = 0.0;
    	for (int i = 0; i < matches.size(); i++)
    	{
    		//float distance –>代表這一對匹配的特征點描述符(本質(zhì)是向量)的歐氏距離,數(shù)值越小也就說明兩個特征點越相像。
    		double dis = matches[i].distance;
    		if (dis > Max)
    		{
    			Max = dis;
    		}
    	}
    
    	//篩選出匹配程度高的關(guān)鍵點
    	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)的下標(biāo),同時也是描述符對應(yīng)特征點(keypoint)的下標(biāo)。
    			goodkeypointA.push_back(keypointA[matches[i].queryIdx].pt);
    			//int trainIdx –> 是樣本圖像的特征點描述符的下標(biāo),同樣也是相應(yīng)的特征點的下標(biāo)。
    			goodkeypointB.push_back(keypointB[matches[i].trainIdx].pt);
    			goodmatches.push_back(matches[i]);
    		}
    	}

    如圖為imageA篩選出來的關(guān)鍵點。

    C++?OpenCV如何進行圖像全景拼接

    如圖為imageB篩選出來的關(guān)鍵點。

    C++?OpenCV如何進行圖像全景拼接

    從上圖可以看出,我們已經(jīng)篩選出imageA,imageB共有的關(guān)鍵點部分。接下來,我們需要使用這兩個點集計算兩幅圖的單應(yīng)性矩陣。

    2.計算單應(yīng)性矩陣

    計算單應(yīng)性變換矩陣

        //獲取圖像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.透視變換

    根據(jù)計算出來的單應(yīng)性矩陣對imageA進行透視變換

        //進行透視變換
        Mat DstImg;
        warpPerspective(imageA, DstImg, Homo, Size(imageB.cols + imageA.cols, imageB.rows));
        imshow("透視變換", DstImg);

    C++?OpenCV如何進行圖像全景拼接

    如圖所示為imageA進行透視變換得到的結(jié)果。

    4.圖像拼接

    根據(jù)上述操作,我們已經(jīng)得到了經(jīng)透視變換的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)
    {
    	//創(chuàng)建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 –>代表這一對匹配的特征點描述符(本質(zhì)是向量)的歐氏距離,數(shù)值越小也就說明兩個特征點越相像。
    		double dis = matches[i].distance;
    		if (dis > Max)
    		{
    			Max = dis;
    		}
    	}
    
    	//篩選出匹配程度高的關(guān)鍵點
    	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)的下標(biāo),同時也是描述符對應(yīng)特征點(keypoint)的下標(biāo)。
    			goodkeypointA.push_back(keypointA[matches[i].queryIdx].pt);
    			//int trainIdx –> 是樣本圖像的特征點描述符的下標(biāo),同樣也是相應(yīng)的特征點的下標(biāo)。
    			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計算單應(yīng)性矩陣至少需要4個點
    	/*
    	計算多個二維點對之間的最優(yōu)單映射變換矩陣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.效果

    C++?OpenCV如何進行圖像全景拼接

    最終拼接效果如圖所示。

    三、源碼

    #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)
    {
    	//創(chuàng)建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 –>代表這一對匹配的特征點描述符(本質(zhì)是向量)的歐氏距離,數(shù)值越小也就說明兩個特征點越相像。
    		double dis = matches[i].distance;
    		if (dis > Max)
    		{
    			Max = dis;
    		}
    	}
    
    	//篩選出匹配程度高的關(guān)鍵點
    	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)的下標(biāo),同時也是描述符對應(yīng)特征點(keypoint)的下標(biāo)。
    			goodkeypointA.push_back(keypointA[matches[i].queryIdx].pt);
    			//int trainIdx –> 是樣本圖像的特征點描述符的下標(biāo),同樣也是相應(yīng)的特征點的下標(biāo)。
    			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計算單應(yīng)性矩陣至少需要4個點
    	/*
    	計算多個二維點對之間的最優(yōu)單映射變換矩陣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函數(shù)進行拼接
    
    	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++進行圖像全景拼接,關(guān)鍵步驟有以下幾點。

    1、使用特征檢測算子提取兩幅圖像的關(guān)鍵點,然后進行特征描述子匹配。

    2、篩選出匹配程度高的關(guān)鍵點計算兩幅圖的單應(yīng)性矩陣。

    3、利用計算出來的單應(yīng)性矩陣對其中一張圖片進行透視變換。

    4、將透視變換的圖片與另一張圖片進行拼接。

    看完上述內(nèi)容,你們掌握C++ OpenCV如何進行圖像全景拼接的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

    向AI問一下細節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI