SIFT+RANSAC+反距離加權圖像拼接融合(代碼記錄)

單純記錄一下之前的代碼

#include <opencv2/imgproc/imgproc.hpp> 
#include <opencv2/opencv.hpp>  
#include<opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <cv.h>
#include <iostream>  
#include<fstream>
#include <stdio.h>  
//#include<opencv2/legacy/legacy.hpp>
#include <string>
#include <vector>
#include<opencv2/xfeatures2d.hpp>
#include<algorithm>
using namespace cv;
using namespace std;
using namespace cv::xfeatures2d;
using namespace cv::ml;

int main()
{

	int64 begintime = getTickCount();

	ofstream outfile1, outfile2;
	outfile1.open("matrix.txt");
	//outfile2.open("mmright.txt");



	//讀入圖像並進行灰度處理 
	//Mat img1 = imread("D:/SU/Tao13_LF_Depth_Light/DEPTH_FROM_CORR_DEFOC_JPEGONLY/Desk/9168/AllFocus.tif");
	//Mat img2 = imread("D:/SU/Tao13_LF_Depth_Light/DEPTH_FROM_CORR_DEFOC_JPEGONLY/Desk/9169/AllFocus.tif");
	Mat img1 = imread("panoLeft.tif");
	Mat img2 = imread("panoRight.tif");
	
	
	//第一步,用SIFT算子檢測關鍵點;

	Ptr<SiftFeatureDetector>detector=SiftFeatureDetector::create();
	std::vector<KeyPoint> m_LeftKey, m_RightKey;//構造2個專門由點組成的點向量用來存儲特徵點
	detector->detect(img1, m_LeftKey);//將img1圖像中檢測到的特徵點存儲起來放在m_LeftKey中
	detector->detect(img2, m_RightKey);//同理
	cout << "圖像1特徵點的個數:" << m_LeftKey.size() << endl;
	cout << "圖像2特徵點的個數:" << m_RightKey.size() << endl;
	//計算特徵向量

	
	cv::Mat descriptors1, descriptors2;//存放特徵向量的矩陣
	detector->compute(img1, m_LeftKey, descriptors1);
	detector->compute(img2, m_RightKey, descriptors2);
	
	//畫出特徵點  
	Mat img_m_LeftKey, img_m_RightKey;
	drawKeypoints(img1, m_LeftKey, img_m_LeftKey, Scalar::all(-1), 0);  //cvScalar(255,0,0)畫的圈圈是藍色,對應於特徵點的顏色,DrawMatchesFlags::DRAW_RICH_KEYPOINTS表示關鍵點上圓圈的尺寸與特徵的尺度成正比,對應於0,是“標誌位”的意思
	drawKeypoints(img2, m_RightKey, img_m_RightKey, Scalar::all(-1), 0);
	imshow("Keysrc1", img_m_LeftKey);
	imshow("Keysrc2", img_m_RightKey);
	imwrite("圖像2的特徵點.tif", img_m_LeftKey);
	imwrite("圖像3的特徵點.tif", img_m_RightKey);
	
	BFMatcher matcher(NORM_L2, false);
	vector<vector<DMatch>> matches2;
	vector<DMatch>matches;
	matcher.match(descriptors1, descriptors2, matches);
	const float ratio = 0.7;
	matches.clear();
	matcher.knnMatch(descriptors1, descriptors2, matches2, 2);
	for (int n = 0; n < matches2.size(); n++)
	{
		DMatch& bestmatch = matches2[n][0];
		DMatch& bettermatch = matches2[n][1];
		if (bestmatch.distance < ratio*bettermatch.distance)
		{
			matches.push_back(bestmatch);
		}
	}
	cout << "match個數:" << matches.size() << endl;
	//畫出直接匹配結果
	Mat key_matches;
	drawMatches(img1, m_LeftKey, img2, m_RightKey, matches, key_matches, CV_RGB(255, 0, 255), CV_RGB(0, 255, 0), Mat(), 2);
	imshow("ratio_matches", key_matches);
	imwrite("ratio_matches.tif", key_matches);

	//RANSAC匹配過程
	vector<DMatch> m_Matches =matches;

	//cout<<"m_Matches="<<m_Matches.size()<<endl;
	// 分配空間
	int ptCount = (int)m_Matches.size();
	//cout<<"m_Matches="<<ptCount<<endl;
	Mat p1(ptCount, 2, CV_32F);
	Mat p2(ptCount, 2, CV_32F);
	//cout<<"p1="<<p1<<endl;
	// 把Keypoint轉換爲Mat
	Point2f pt;
	for (int i = 0; i<ptCount; i++)
	{
		pt = m_LeftKey[m_Matches[i].queryIdx].pt;
		p1.at<float>(i, 0) = pt.x;
		p1.at<float>(i, 1) = pt.y;
		pt = m_RightKey[m_Matches[i].trainIdx].pt;
		p2.at<float>(i, 0) = pt.x;
		p2.at<float>(i, 1) = pt.y;
	}
	//cout<<"p1="<<p1<<endl;//圖1的匹配點座標
	//cout<<"p2="<<p2<<endl;//圖2的匹配點座標
	// 用RANSAC方法計算F(基礎矩陣)
	Mat m_Fundamental;
	vector<uchar> m_RANSACStatus;       // 這個變量用於存儲RANSAC後每個點的狀態
	findFundamentalMat(p1, p2, m_RANSACStatus, FM_RANSAC);
	// 計算內點個數
	int OutlinerCount = 0;
	for (int i = 0; i<ptCount; i++)
	{
		if (m_RANSACStatus[i] == 0)    // 狀態爲0表示外點
		{
			OutlinerCount++;
		}
	}
	int InlinerCount = ptCount - OutlinerCount;   // 計算內點	
	cout << "內點數爲:" << InlinerCount << endl;
	cout << "外點數爲:" << OutlinerCount << endl;
	// 這三個變量用於保存內點和匹配關係
	vector<Point2f> m_LeftInlier;
	vector<Point2f> m_RightInlier;
	vector<DMatch> m_InlierMatches;
	m_InlierMatches.resize(InlinerCount);
	m_LeftInlier.resize(InlinerCount);
	m_RightInlier.resize(InlinerCount);
	InlinerCount = 0;
	float inlier_minRx = img1.cols;        //用於存儲內點中右圖最小橫座標,以便後續融合
										   //cout<<"inlier="<<inlier_minRx<<endl;
	for (int i = 0; i<ptCount; i++)
	{
		if (m_RANSACStatus[i] != 0)
		{
			m_LeftInlier[InlinerCount].x = p1.at<float>(i, 0);
			m_LeftInlier[InlinerCount].y = p1.at<float>(i, 1);
			m_RightInlier[InlinerCount].x = p2.at<float>(i, 0);
			m_RightInlier[InlinerCount].y = p2.at<float>(i, 1);
			m_InlierMatches[InlinerCount].queryIdx = InlinerCount;
			m_InlierMatches[InlinerCount].trainIdx = InlinerCount;

		



			if (m_RightInlier[InlinerCount].x<inlier_minRx) inlier_minRx = m_RightInlier[InlinerCount].x;   //存儲內點中右圖最小橫座標
			InlinerCount++;
		}
	}
	cout<<"inlier="<<inlier_minRx<<endl;//最小橫座標	

	// 把內點轉換爲drawMatches可以使用的格式
	vector<KeyPoint> key1(InlinerCount);
	vector<KeyPoint> key2(InlinerCount);
	KeyPoint::convert(m_LeftInlier, key1);
	KeyPoint::convert(m_RightInlier, key2);
	
	// 顯示計算F過後的內點匹配
	Mat OutImage;
	drawMatches(img1, key1, img2, key2, m_InlierMatches, OutImage, CV_RGB(255, 0, 255), CV_RGB(0, 255, 0));
	imshow("RANSAC match features", OutImage);
	imwrite("RansacMatchb.tif", OutImage);

	//矩陣H用以存儲RANSAC得到的單應矩陣
	Mat H = findHomography(m_LeftInlier, m_RightInlier, RANSAC);
	outfile1 << H << endl;
	cout << H << endl;
	//存儲左圖四角,及其變換到右圖位置
	std::vector<Point2f> obj_corners(4);
	obj_corners[0] = Point(0, 0); obj_corners[1] = Point(img1.cols, 0);
	obj_corners[2] = Point(img1.cols, img1.rows); obj_corners[3] = Point(0, img1.rows);
	std::vector<Point2f> scene_corners(4);
	perspectiveTransform(obj_corners, scene_corners, H);
/*	//畫出變換後圖像位置
	Point2f offset((float)img1.cols, 0);
	line(OutImage, scene_corners[0] + offset, scene_corners[1] + offset, Scalar(0, 255, 0), 4);
	line(OutImage, scene_corners[1] + offset, scene_corners[2] + offset, Scalar(0, 255, 0), 4);
	line(OutImage, scene_corners[2] + offset, scene_corners[3] + offset, Scalar(0, 255, 0), 4);
	line(OutImage, scene_corners[3] + offset, scene_corners[0] + offset, Scalar(0, 255, 0), 4);
	imshow("Good Matches & Object detection", OutImage);
	imwrite("transform.tif", OutImage);*/
	int drift = scene_corners[1].x;
	//儲存偏移量
	cout << "scene=" << scene_corners << endl;
	cout << "scene0=" << scene_corners[0].x << endl;
	cout << "scene1=" << scene_corners[1].x << endl;
	cout << "scene2=" << scene_corners[2].x << endl;
	cout << "scene3=" << scene_corners[3].x << endl;
	//新建一個矩陣存儲配準後四角的位置
	int width = int(max(abs(scene_corners[1].x), abs(scene_corners[2].x)));//等於左圖右邊兩個頂點中橫座標較大的那個
	int height = img1.rows;                                                                  //或者:int height = int(max(abs(scene_corners[2].y), abs(scene_corners[3].y)));
	float origin_x = 0, origin_y = 0;
	if (scene_corners[0].x<0) {
		if (scene_corners[3].x<0) origin_x += min(scene_corners[0].x, scene_corners[3].x);
		else origin_x += scene_corners[0].x;
	}
	width -= int(origin_x);//圖像的寬度變爲左圖變換之後左邊的定點座標小的數值+右邊的橫座標較大的數值
	if (scene_corners[0].y<0) {
		if (scene_corners[1].y) origin_y += min(scene_corners[0].y, scene_corners[1].y);
		else origin_y += scene_corners[0].y;
	}
	//height-=int(origin_y);

	//imageturn爲變換隻有的左圖的大小
	Mat imageturn = Mat::zeros(width, height, img1.type());
	cout << "PerspectiveWidth: " << width << endl;
	cout << "PerspectiveHeight: " << height << endl;
	cout << "img1.type(): " << img1.type() << endl;
	
	//獲取新的變換矩陣,使圖像完整顯示
	for (int i = 0; i<4; i++) { scene_corners[i].x -= origin_x; }//相當於把整個圖像往右移動至右圖上,移動距離爲初次變換之後最小的橫座標	
	Mat H1 = getPerspectiveTransform(obj_corners, scene_corners);//然後移動之後的與右圖再求一次矩陣
	//進行圖像變換,顯示效果
	warpPerspective(img1, imageturn, H1, Size(width, height));
	imshow("image_Perspective", imageturn);
	imwrite("perpective.tif", imageturn);

	cout << "origin_x=" << origin_x << endl;
	cout << "origin_y=" << origin_y << endl;
	cout << "img1.width=" << img1.cols << endl;
	cout << "inlier_minRx=" << inlier_minRx << endl;
	cout << "scene_corners= " << scene_corners << endl;
	cout << "單應矩陣H=" << H << endl;
	cout << "變換矩陣H1=" << H1 << endl;
	
	//圖像融合
	int width_ol = width - int(inlier_minRx - origin_x);//重疊區域的寬
	int start_x = int(inlier_minRx - origin_x);//imageturn上的重疊區域的起始座標  右側內點的最小點-origin_x
	cout << "start_X:" << start_x << endl;
	/*//imageturn爲左圖圖轉化之後的 重疊區域 開始的橫座標爲右圖上的最小橫座標的內點在imageturn上的座標,右側爲左圖的右邊界
	Mat subValue_image = Mat::zeros(imageturn.rows, width_ol, imageturn.type());//創建一個圖用於保存差值圖像

	Mat sub1 = imageturn(Rect(start_x, 0, width_ol, imageturn.rows));//start_x  重疊區域的寬 高不變
	Mat sub2 = img2(Rect(inlier_minRx, 0, width_ol, img2.rows));
	
	//求兩個圖像插值
	absdiff(sub1, sub2, subValue_image);
	imshow("差值圖", subValue_image);

	//創建一個mask
	Mat mask = Mat::zeros(height, width_ol, img1.type());//重疊區域大小
	Mat imgL = imageturn(Rect(start_x, 0, width_ol, height));
	Mat imgR = img2(Rect(0, 0, width_ol, height));
	mask(Rect(0, 0, width_ol*0.5, height)).setTo(255);
	imshow("mask", mask);

	Point Center(imgL.cols*0.5, imgL.rows*0.5);

	Mat resultImage;
	resultImage.create(imgL.size(), imgL.type());
	seamlessClone(imgL, imgR, mask, Center, resultImage, NORMAL_CLONE);

	imshow("blending", resultImage);*/


	uchar* ptr = imageturn.data;//perspective的圖像數據
	double alpha = 0, beta = 1;//定義權重
	for (int row = 0; row<height; row++) //圖像轉換之後的高度
	{
		//step可以理解爲Mat矩陣中每一行的“步長”,以字節爲基本單位,
		//每一行中所有元素的字節總量,是累計了一行中所有元素、所有通道、所有通道的elemSize1之後的值;	
		ptr = imageturn.data + row*imageturn.step + (start_x)*imageturn.elemSize(); //data   uchar類型的指針,指向Mat數據矩陣的首地址。可以理解爲標示一個房屋的門牌號;
		for (int col = 0; col<width_ol; col++)//圖像轉換之後寬度			
		{
			uchar* ptr_c1 = ptr + imageturn.elemSize1();
			uchar*  ptr_c2 = ptr_c1 + imageturn.elemSize1();
			uchar* ptr2 = img2.data + row*img2.step + (col + int(inlier_minRx))*img2.elemSize();
			uchar* ptr2_c1 = ptr2 + img2.elemSize1();  
			uchar* ptr2_c2 = ptr2_c1 + img2.elemSize1();
			alpha = double(col) / double(width_ol); 
			beta = 1 - alpha;
			if (*ptr == 0 && *ptr_c1 == 0 && *ptr_c2 == 0) {//左圖中轉換了之後的無像素值的黑點,則完全拷貝右側圖的像素值
				*ptr = (*ptr2);//該像素第一通道
				*ptr_c1 = (*ptr2_c1);//該像素第二通道
				*ptr_c2 = (*ptr2_c2);//該像素第三通道
			}
			//如不爲0
			*ptr = (*ptr)*beta + (*ptr2)*alpha;//左圖通道1的像素值乘以權重+右側通道1像素值乘以權重
			*ptr_c1 = (*ptr_c1)*beta + (*ptr2_c1)*alpha;
			*ptr_c2 = (*ptr_c2)*beta + (*ptr2_c2)*alpha;
			ptr += imageturn.elemSize();//指針指向下一個元素
		}
	}
	imshow("image_overlap", imageturn);

	Mat img_result = Mat::zeros(height, width + img2.cols - drift, img1.type());//int drift = scene_corners[1].x;
	cout << "寬:" << width + img2.cols - drift << endl;
	cout << "高:" << height << endl;
	uchar* ptr_r = imageturn.data;
	for (int row = 0; row<height; row++) {
		ptr_r = img_result.data + row*img_result.step;
		for (int col = 0; col<imageturn.cols; col++)
		{
			uchar* ptr_rc1 = ptr_r + imageturn.elemSize1();  uchar*  ptr_rc2 = ptr_rc1 + imageturn.elemSize1();
			uchar* ptr = imageturn.data + row*imageturn.step + col*imageturn.elemSize();
			uchar* ptr_c1 = ptr + imageturn.elemSize1();  uchar*  ptr_c2 = ptr_c1 + imageturn.elemSize1();
			*ptr_r = *ptr;
			*ptr_rc1 = *ptr_c1;
			*ptr_rc2 = *ptr_c2;
			ptr_r += img_result.elemSize();
		}
		ptr_r = img_result.data + row*img_result.step + imageturn.cols*img_result.elemSize();
		for (int col = imageturn.cols; col<img_result.cols; col++)
		{
			uchar* ptr_rc1 = ptr_r + imageturn.elemSize1();  uchar*  ptr_rc2 = ptr_rc1 + imageturn.elemSize1();
			uchar* ptr2 = img2.data + row*img2.step + (col - imageturn.cols + drift)*img2.elemSize();
			uchar* ptr2_c1 = ptr2 + img2.elemSize1();  uchar* ptr2_c2 = ptr2_c1 + img2.elemSize1();
			*ptr_r = *ptr2;
			*ptr_rc1 = *ptr2_c1;
			*ptr_rc2 = *ptr2_c2;
			ptr_r += img_result.elemSize();
		}
	}
	imshow("image_result", img_result);
	imwrite("panoAll.tif", img_result);
	//imwrite("14.jpg",img_result);
	int64 endtime = getTickCount();
	cout << "時間:" << (endtime - begintime) / getTickFrequency() << "s" << endl;
	waitKey(0);
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章