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次合成,可能不一样。
结束