天天看點

簡單快速的多圖像拼接---百圖拼接

opencv自帶的stitching速度很慢,而且對多圖容易出錯,好象對豎着拍攝的圖(高>寬)不能用。

其中一個最大的原因是每一張圖都要和其它的圖去比對,如果有10張圖,除去自身不用比對外,

要比對 10X(10-1) = 90 次。是以慢得不能忍受。(等了很久很久,咚的出錯,這感受真的不好)

我們拍攝全景圖的時候都是從左到右,或者從右到左,前後兩張圖一般有部分重合。

是以我們這裡隻對前後兩張圖比對,然後連成一串。

流程:

1。從清單(list.txt)檔案裝載圖像檔案名
2。前後比對
3。計算比對圖像的相對位置
4。以第一張圖左上角點為原點,找到所有圖的位置(同一坐标系)
5。再計算最小,最大邊界,并建構一個大圖
6。再把所有圖像放到一個大圖中
           

main:

int main ()
{
	/*	特征點的提取與比對 	*/

	vector<string> image_names; // image_names[i]表示第i個圖像的名稱

    LoadImageNamesFromFile("list0.txt",image_names);//從list.txt檔案裝載圖像檔案名

	vector<vector<KeyPoint>> image_keypoints; // image_keypoints[i]表示第i個圖像的特征點
	vector<Mat> image_descriptor; // image_descriptor[i]表示第i個圖像的特征向量描述符
	//vector<vector<Vec3b>> image_colors; // image_colors[i]表示第i個圖像特征點的顔色
	vector<vector<DMatch>> image_matches; // image[i]表示第i幅圖像和第i+1幅圖像特征點比對的結果
	extract_features (image_names, image_keypoints, image_descriptor/*, image_colors*/); // 提取特征點
	match_features2 (image_descriptor, image_matches); // 特征點比對
	//gms_match_features(image_keypoints,img0.size(),image_matches);

	//單應性過濾特征點
	for (unsigned int i=0;i<image_matches.size ();i++)
	{
		refineMatchesWithHomography( image_keypoints[i], image_keypoints[i+1],1.0, image_matches[i]    );    
	}	
	image_descriptor.swap(vector<Mat>());//比對完清除記憶體

	Mat img0 = imread(image_names[0]);//讀出一個圖
	img0= mynarrow(img0);//如果太大縮小一點。(>2400*1200的)


	//顯示比對
	//for (unsigned int i=0;i<image_matches.size ();i++)
	//{
	//	Mat img1 = imread(image_names[i]);
	//	Mat img2 = imread(image_names[i+1]);//讀出一個圖

	//	Mat show = DrawInlier(img1, img2, image_keypoints[i], image_keypoints[i+1], image_matches[i], 1);
	//	imshow("比對圖", show);
	//	char wname[255];
	//	sprintf(wname,"met%d.jpg",i);
	//	imwrite(String(wname),show);


	//	waitKey();
	//}

	vector<cv::Point2f> position_da; // position_da[i]表示第i個圖像在大圖中的位置(左上角)
	Point2f position_s=Point2f(0,0);
	position_da.push_back (position_s); // 第1個圖像為原點


	for (unsigned int i=0;i<image_matches.size ();i++)
	{

		if(image_matches[i].size ()==0)break;//如果無比對點,則後面的就取消了

		//得到比對點坐标
		vector<Point2f> points1, points2;
		get_match_points (image_keypoints[i], image_keypoints[i+1] ,image_matches[i], points1, points2);
		unsigned int shi=image_matches[i].size ();
		shi=(shi>10)?10:shi;//隻取前十個
		Point2f a;
		for(unsigned int j=0;j<shi;j++)
		{
			a.x+=points1[j].x-points2[j].x;
			a.y+=points1[j].y-points2[j].y;
		}
		a.x /=shi;a.y /=shi;//取平均值
		cout << "兩個相差:"<<a<< endl;

		//在大圖的位置
		position_s.x=position_s.x+a.x;
		position_s.y=position_s.y+a.y;
		position_da.push_back (position_s);
		cout << "目前位置:"<<position_s<< endl;
		



	}
	vector<vector<KeyPoint>>().swap(image_keypoints);//已經用不到了,清除容器并最小化它的容量


	//再計算最小,最大邊界
	int xmin=0,xmax=0,ymin=0,ymax=0;
	for (unsigned int i=1;i<position_da.size ();i++)
	{
		xmin=(position_da[i].x<xmin)?position_da[i].x:xmin;
		xmax=(position_da[i].x>xmax)?position_da[i].x:xmax;
		ymin=(position_da[i].y<ymin)?position_da[i].y:ymin;
		ymax=(position_da[i].y>ymax)?position_da[i].y:ymax;

	}
	//計算大圖寬高
	int h = img0.rows + ymax-ymin;//拼接圖行數(高度)
	int w = img0.cols + xmax-xmin;//拼接圖列數(寬度)
	Mat stitch = Mat::zeros(h, w, CV_8UC3);

	//再把所有圖像放到一個大圖中(拼接)
	for (unsigned int i=0;i<position_da.size ();i++)
	{
		img0 = imread(image_names[i]);//讀出一個圖//左圖像
		img0= mynarrow(img0);//如果太大縮小一點。

		Mat roi2(stitch, Rect(position_da[i].x-xmin, position_da[i].y-ymin, img0.cols, img0.rows));
        img0(Range(0, img0.rows), Range(0, img0.cols)).copyTo(roi2);

	}


    imshow("拼接結果", stitch);
    imwrite("stitch.jpg", stitch);
		waitKey();
	return 0;
}           

用到的函數:

//如果圖像太大縮小一半
Mat mynarrow(Mat img)
{
	Mat dst ;//讀出一個圖
	if(img.rows*img.cols>2400*1200)
		resize(img,dst,Size(),0.5,0.5); 
	else
		dst=img.clone();
	return dst;
}
           

在所有讀圖的地方都要用上。

過濾函數:

//用單應性過濾比對
bool refineMatchesWithHomography(const std::vector<cv::KeyPoint>& queryKeypoints,      
    const std::vector<cv::KeyPoint>& trainKeypoints,       
    float reprojectionThreshold,      
    std::vector<cv::DMatch>& matches//,      
    //cv::Mat& homography
	)    
{    cv::Mat homography;
    const int minNumberMatchesAllowed = 4;      
    if (matches.size() < minNumberMatchesAllowed)      
        return false;      
    // 為 cv::findHomography 準備資料    
    std::vector<cv::Point2f> queryPoints(matches.size());      
    std::vector<cv::Point2f> trainPoints(matches.size());      
    for (size_t i = 0; i < matches.size(); i++)      
    {      
        queryPoints[i] = queryKeypoints[matches[i].queryIdx].pt;      
        trainPoints[i] = trainKeypoints[matches[i].trainIdx].pt;      
    }      
    // 查找單應矩陣并擷取内點掩碼    
    std::vector<unsigned char> inliersMask(matches.size());      
    homography = cv::findHomography(queryPoints,       
        trainPoints,       
        CV_FM_RANSAC,       
        reprojectionThreshold,       
        inliersMask);      
    std::vector<cv::DMatch> inliers;      
    for (size_t i=0; i<inliersMask.size(); i++)      
    {      
        if (inliersMask[i])      
            inliers.push_back(matches[i]);      
    }      
    matches.swap(inliers);    
    //Mat homoShow;    
    //drawMatches(src,queryKeypoints,frameImg,trainKeypoints,matches,homoShow,Scalar::all(-1),CV_RGB(255,255,255),Mat(),2);         
    //imshow("homoShow",homoShow);     
    return matches.size() > minNumberMatchesAllowed;     
  
}             

其它的在前一個文章中

效果圖:

簡單快速的多圖像拼接---百圖拼接

38個圖合成

簡單快速的多圖像拼接---百圖拼接

75個圖合成

由于原始圖像太大,上傳是縮小了。雖然效果不是很理想,但速度很快

第一個拍了2圈,第二個拍了3圈,如果分别分成2次和3次合成,可能不一樣。

結束

-----------------------分隔線---------------------------------

應有人要完整的程式,現修改一下:

請把-main- "複制" 儲存為 “快主main函數.cpp”

再把-mynarrow函數- "複制" 儲存為 "mynarrow函數.cpp"

把-過濾函數- "複制" 儲存為 "過濾函數.cpp"

最後把下面的函數- "複制" 儲存為 "擷取比對點坐标.cpp"

//擷取比對點坐标
/********************************************************************************************************
參數:
keypoints1 第一張圖檔的特征點; keypoints2 第二張圖檔的特征點; matches 比對的結果; (points1[i], points2[i]) 第
i個比對的特征點對。
功能:
利用兩張圖檔的特征點keypoints1、keypoints2和比對的結果matches,可以得到兩個數組points1和points2,
(points1[i], points2[i])表示第i個比對的特征點對。
*********************************************************************************************************/
void get_match_points (
	vector<KeyPoint> keypoints1,
	vector<KeyPoint> keypoints2,
	vector<DMatch> matches,
	vector<Point2f>& points1,
	vector<Point2f>& points2
)
{
	for (int i = 0; i < matches.size (); i++)
	{
		points1.push_back (keypoints1[matches[i].queryIdx].pt);
		points2.push_back (keypoints2[matches[i].trainIdx].pt);
	}
}

           

完整的程式為:

#include "頭包含.cpp"

#include "用到的函數.cpp"

//#include "主main函數.cpp"

//=================分隔線(之前在上一篇中)=======================

#include "mynarrow函數.cpp"

#include "過濾函數.cpp"

#include "擷取比對點坐标.cpp"

#include "快主main函數.cpp"