看了《图像拼接(七):OpenCV单应变换模型拼接多幅图像》
《图像拼接(十三):OpenCV拼接多幅图像(以中间图像为参考)》两个博文,感觉思路和代码都非常好,
但是哪个结果是不能用的,所以我把它改一下用在这里。
流程:
1。在List.txt文件中读出图像文件名(便于前面的视频选图接力)(其中的文件名请按从左到右顺序排好,程序中不作检测,否则出错)
2。检测每个图的特征点
3。两两匹配
4。并记录两两的单应矩阵
5。从右到左变换
请准备好 2 至 8 个右边有部分重合的图。
main函数:
int main () { /* 特征点的提取与匹配 */ vector<string> image_names; // image_names[i]表示第i个图像的名称 LoadImageNamesFromFile("list.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); // 特征点匹配 image_descriptor.swap(vector<Mat>());//匹配完清除内存 Mat img0 = imread(image_names[0]);//读出一个图 //gms_match_features(image_keypoints,img0.size(),image_matches); //显示匹配 //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<Mat> im_Homography; // im_Homography[i]表示第i+1-->i的单应矩阵 for (unsigned int i=0;i<image_matches.size ();i++) { //单应矩阵 Mat h12 = myfindHomography(image_matches[i], image_keypoints[i], image_keypoints[i+1] ); Mat h21; invert(h12, h21, DECOMP_LU); im_Homography.push_back(h21); } vector<vector<KeyPoint>>().swap(image_keypoints);//已经用不到了,清除容器并最小化它的容量 //拼接 Mat canvas; int canvasSize=image_names.size()*1.5; unsigned int j=image_names.size(); j--; Mat img2 = imread(image_names[j]);//读出最后的哪个图 for (unsigned int i=0;i<image_matches.size ();i++) { //从后到前 Mat img1; Mat h21; j--; if(j==image_matches.size ()-1){//最右图 h21=im_Homography[j]; //使用透视变换 warpPerspective(img2, canvas, h21, Size(img0.cols*canvasSize, img0.rows)); img1 = imread(image_names[j]);//读出最后的哪个图 //拼接 img1.copyTo(canvas(Range::all(), Range(0, img0.cols))); } else{//其它 h21=im_Homography[j]; Mat temp2=canvas.clone(); //保存拷贝 warpPerspective(temp2, canvas, h21, Size(img0.cols*canvasSize, img0.rows));//一起透视变换 img1 = imread(image_names[j]);//读出当前的哪个图 img1.copyTo(canvas(Range::all(), Range(0, img0.cols)));//加当前(拼接) } imshow("拼接图",canvas); char wname[255]; sprintf(wname,"can%d.jpg",i); imwrite(String(wname),canvas); waitKey(); } return 0; }
效果图:
这里是4张图
这里是到第5张图至,后面的图都已经没法用了。
list.txt内容分别为:
trees_000.jpg trees_001.jpg trees_002.jpg trees_003.jpg
C:/Users/ASUS/Videos/38.jpg C:/Users/ASUS/Videos/107.jpg C:/Users/ASUS/Videos/173.jpg C:/Users/ASUS/Videos/209.jpg C:/Users/ASUS/Videos/237.jpg C:/Users/ASUS/Videos/283.jpg C:/Users/ASUS/Videos/323.jpg C:/Users/ASUS/Videos/363.jpg C:/Users/ASUS/Videos/543.jpg C:/Users/ASUS/Videos/575.jpg用到的函数:
//读入一行, 并且去掉结尾的换行符(如果有的话) char *myfgets(char *s, FILE *fp1) { char tmp[255]; fgets(tmp,255, fp1);//读入一行 char *ret=strrchr(tmp, 10); if(ret!=NULL){//去掉结尾的换行符(如果有的话) *ret='\0';//memset(ret, 0,1); memcpy(s, tmp, strlen(tmp)+1);//包括\0 } else return "no";//文件最后一行必须有一个换行符(空行) return "ok"; } //从list.txt文件装载图像文件名 void LoadImageNamesFromFile(char* name,vector<string>& image_names) { FILE *f = fopen(name, "r"); if (f == NULL) { printf("Error opening file List.txt for reading\n"); exit(1); } char s[255]; while(1) { if (myfgets(s,f)=="ok") image_names.push_back(string(s)); else break; } } /******************************************************************************************************** 参数: image_names[i] 第i个图像的名称; image_keypoints[i] 第i个图像的特征点; image_descriptor[i] 第i个图像的特征向量(描述子); image_colors[i] 第i个图像特征点的颜色。 功能: 从一系列图像(image_names)中提取出它们的特征点保存在image_keypoints中,特征向量保存在image_descriptor中, 特征点的颜色保存在image_colors中。 *********************************************************************************************************/ bool extract_features ( vector<string> image_names, vector<vector<KeyPoint>>& image_keypoints, vector<Mat>& image_descriptor//, //vector<vector<Vec3b>>& image_colors ) { //Ptr<Feature2D> sift = xfeatures2d::SIFT::create (); // SIFT特征提取器 //sift->detectAndCompute (image, noArray (), keypoints, descriptor); //Ptr<AKAZE> akaze = AKAZE::create(); //akaze->detect(image, keypoints, descriptor); Ptr<ORB> orb = ORB::create(2100); orb->setFastThreshold(0); //int size; for (unsigned int k=0;k<image_names.size();k++) { string name=image_names[k]; Mat img1 = imread(name);//data/nn_left.jpg if (img1.empty()) { printf("出现一个错误,没有找到图像:%s\n",name); return false;} // 提取特征并计算特征向量 vector<KeyPoint> kp1; Mat d1; cout << "正在检测特征点: " << name << endl; orb->detectAndCompute(img1, Mat(), kp1, d1); image_keypoints.push_back(kp1); image_descriptor.push_back (d1); //cout << "保存角点颜色: " << endl; //vector<Vec3b> colors; //for (unsigned int i = 0; i < kp1.size(); i++) //{ // Point2f p = kp1[i].pt; // colors.push_back(img1.at<Vec3b>((int)p.y, (int)p.x)); //} //image_colors.push_back(colors); } return true; } /******************************************************************************************************** 参数: image_descriptor[i] 第i个图像的特征向量; image_matches[i] 第i个特征向量和第i + 1个特征向量匹配的结果。 功能: 对一组特征向量(image_descriptor)两两匹配,将结果保存在image_matches中。 *********************************************************************************************************/ void match_features2 (vector<Mat> image_descriptor, vector<vector<DMatch>>& image_matches) { for (unsigned int i = 0; i < image_descriptor.size () - 1; i++) { cout << "正在匹配 " << i << " - " << i + 1 << endl; vector<DMatch> matches; //match_features1 (image_descriptor[i], image_descriptor[i + 1], matches); BFMatcher matcher(NORM_HAMMING); matcher.match(image_descriptor[i], image_descriptor[i + 1], matches); cout << "有 " << matches.size() << " 个匹配点" << endl; image_matches.push_back (matches); } } //利用findHomography函数利用匹配的关键点找出相应的变换: Mat myfindHomography(std::vector< DMatch > & good_matches, std::vector<KeyPoint>& keypoints_1,std::vector<KeyPoint> & keypoints_2 ) { //-- Localize the object from img_1 in img_2 //在img_2中定位来自img_1的对象 std::vector<Point2f> obj; std::vector<Point2f> scene; for(unsigned int i = 0; i < good_matches.size(); i++ ) { //-- Get the keypoints from the good matches //从好的匹配中获取关键点 obj.push_back( keypoints_1[ good_matches[i].queryIdx ].pt ); scene.push_back( keypoints_2[ good_matches[i].trainIdx ].pt ); } //两个平面上相匹配的特征点求出变换公式 Mat H = findHomography( obj, scene, CV_RANSAC ); return H; }
结束。