看了《图像拼接(七):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;
}
结束。
-----------------------分隔线---------------------------------
应有人要完整的程序,现修改一下:
请把-main- "复制" 保存为 “主main函数.cpp”
再把-用到的函数- "复制" 保存为 "用到的函数.cpp"
最后把下面的头文件- "复制" 保存为 "头包含.cpp"
#include <opencv2\opencv.hpp>
#include <algorithm>
#include <vector>
#include <iostream>
#include<fstream>
#include <ctime>
#include <stdio.h>
#include <stdlib.h>
#include<string>
using namespace std;
using namespace cv;
完整的程序为:
#include "头包含.cpp"
#include "用到的函数.cpp"
#include "主main函数.cpp"