1. 程式人生 > >opencv 基於sift的多張圖片全景圖拼接

opencv 基於sift的多張圖片全景圖拼接

這裡是基於sift來尋找特徵點經行影象的匹配的原理來進行影象拼接的,具體步驟如下:

1、利用sift特徵探測器來檢測出兩幅圖片的sift特徵點



2根據上一步提取到的特徵點來提取特徵向量,使用SiftDescriptorExtractor對完成特徵向量提取的工作,通過他對關鍵點周圍鄰域內的畫素分塊進行梯度運算,得到128維的特徵向量



3、進行特徵向量臨近匹配,找到兩幅圖之間相互匹配的特徵點


匹配之後的結果如下圖:(2張圖片的)


4、在第一次匹配的基礎上再進行篩選,算出匹配的特徵向量之間的距離,然後只去向量距離小於2倍的最小距離的特徵點


這樣得到的結果為:


5、通過隨機抽樣一致
RANSAC演算法找出特徵點之間的對映關係,求出單應矩陣H


6、判斷兩幅圖在結果圖中的左邊還是右邊,這裡是根據特徵點在影象的位置來判斷的,求出影象上特徵點的x座標大於影象寬的一半的特徵點數量與總的特徵點數量的比值,比值大的就是在結果圖的右邊


7、把第6步算得的右圖經過透視轉換轉換到結果圖中,這裡要使用單應矩陣H再把計算得到的左圖放到結果圖中:


部分拼接結果:





原始碼為:(為了方便測試我把影象都命名為1.jpg 2.jpg .......)

#include <opencv2/opencv.hpp>
#include<opencv2/nonfree/nonfree.hpp>
#include<opencv2/legacy/legacy.hpp>
#include<vector>
#include<iostream>
#include<sstream>
#include<string>
#include<time.h> 
using namespace std;
using namespace cv;
Mat Stitched(Mat img1, Mat img2) {
	Mat g1(img1, Rect(0, 0, img1.cols, img1.rows));
	Mat g2(img2, Rect(0, 0, img2.cols, img2.rows));
	cvtColor(g1, g1, CV_BGR2GRAY);
	cvtColor(g2, g2, CV_BGR2GRAY);
	SiftFeatureDetector siftdet;
	vector<KeyPoint>kp1, kp2;
	//SIFT sift;
	SiftDescriptorExtractor extractor;
	Mat descriptor1, descriptor2;
	FlannBasedMatcher matcher;
	vector<DMatch> matches, goodmatches;
	/*進行特徵點提取*/
	siftdet.detect(g1, kp1);
	siftdet.detect(g2, kp2);
	/* 進行特徵向量提取 */
	extractor.compute(g1, kp1, descriptor1);
	extractor.compute(g2, kp2, descriptor2);
	/* 進行特徵向量臨近匹配 */
	matcher.match(descriptor1, descriptor2, matches);
	Mat  firstmatches;
	/*畫出第一次匹配的結果*/
	drawMatches(img1, kp1, img2, kp2,
		matches, firstmatches, Scalar::all(-1), Scalar::all(-1),
		vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
	imshow("first_matches", firstmatches);
	/* 下面計算向量距離的最大值與最小值 */
	double max_dist = 0; double min_dist = 1000;
	for (int i = 0; i < descriptor1.rows; i++) {
		if (matches[i].distance > max_dist) {
			max_dist = matches[i].distance;
		}
		if (matches[i].distance < min_dist) {
			min_dist = matches[i].distance;
		}
	}
	cout << "The max distance is: " << max_dist << endl;
	cout << "The min distance is: " << min_dist << endl;
	for (int i = 0; i < descriptor1.rows; i++) {
		if (matches[i].distance < 2 * min_dist) {
			goodmatches.push_back(matches[i]);
		}
	}
	Mat img_matches;
	/*第二次篩選後的結果*/
	drawMatches(img1, kp1, img2, kp2,
		goodmatches, img_matches, Scalar::all(-1), Scalar::all(-1),
		vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
	imshow("good_matches", img_matches);
	vector<Point2f> keypoints1, keypoints2;
	for (int i = 0; i < goodmatches.size(); i++) {
		keypoints1.push_back(kp1[goodmatches[i].queryIdx].pt);
		keypoints2.push_back(kp2[goodmatches[i].trainIdx].pt);
	}
	/*計算單應矩陣*/
	Mat H = findHomography(keypoints1, keypoints2, CV_RANSAC);
	Mat stitchedImage;
	int mRows = img2.rows;
	if (img1.rows> img2.rows)
	{
		mRows = img1.rows;
	}
	/*判斷影象在左邊還是在右邊*/
	int propimg1 = 0, propimg2 = 0;
	for (int i = 0; i < goodmatches.size(); i++) {
		if (kp1[goodmatches[i].queryIdx].pt.x > img1.cols / 2) {
			propimg1++;
		}
		if (kp2[goodmatches[i].trainIdx].pt.x > img2.cols / 2) {
			propimg2++;
		}
	}
	bool flag = false;
	Mat imgright;
	Mat imgleft;
	if ((propimg1 / (goodmatches.size() + 0.0)) > (propimg2 / (goodmatches.size() + 0.0))) {
		imgleft = img1.clone();
		flag = true;
	}
	else {
		imgleft = img2.clone();
		flag = false;
	}
	if (flag) {
		imgright = img2.clone();
		flag = false;
	}
	else {
		imgright = img1.clone();
	}
	/*把上邊求得的右邊的影象經過矩陣H轉換到stitchedImage中對應的位置*/
	warpPerspective(imgright, stitchedImage, H, Size(img2.cols + img1.cols, mRows));
	/*把左邊的影象放進來*/
	Mat half(stitchedImage, Rect(0, 0, imgleft.cols, imgleft.rows));
	imgleft.copyTo(half);
	return stitchedImage;
}
int main() {
	Mat img1 = imread("1.jpg");
	Mat  stitchedImage;
	int n;
	cout << "Dataset2" << endl;
	cout << "請輸入想拼接的圖片數量(大於1小於18)" << endl;
	cin >> n;
	cout << "輸入成功,開始計時" << endl;
	clock_t start,finish;  
    double totaltime;
    start=clock();  
	resize(img1, img1, Size(img1.cols / 4, img1.rows / 4));
	for (int k = 2; k <= n; k++) {
		stringstream stream;
		string str;
		stream << k;
		stream >> str;
		string filename = str + ".jpg";
		cout << "正在拼接......." << filename << endl;
		Mat img = imread(filename);
		resize(img, img, Size(img.cols / 4, img.rows / 4));
		stitchedImage = Stitched(img1, img);
		img1 = stitchedImage;
	}
	finish = clock();
	totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
	cout << "拼接成功" << endl;
	cout << "拼接花費總時間為:" << totaltime << "秒!" << endl;
	imshow("ResultImage", stitchedImage);
	imwrite("ResultImage.jpg", stitchedImage);
	waitKey(0);
	return 0;
}