opencv自带的stitching速度很慢,而且对多图容易出错,好象对竖着拍摄的图(高>宽)不能用。其中一个最大的原因是每一张图都要和其它的图去匹配,如果有10张图,除去自身不用匹配外,要匹配 10X(10-1) = 90 次。所以慢得不能忍受。(其实我认为光束平差法才是最慢的)
我们拍摄全景图的时候都是从左到右,或者从右到左,前后两张图一般有部分重合。如果按顺序读取图像,我们这里只对前后两张图匹配,然后连成一串即可。
1从列表(list.txt)文件装载图像文件名
2前后匹配
3计算匹配图像的相对位置
4以第一张图左上角点为原点,找到所有图的位置(同一坐标系)
5再计算最小,最大边界,并构建一个大图
6再把所有图像放到一个大图中
100张图片才耗时37s;(我这里的图像特征是离线的,当然也可以用orb,surf等等)
拼接效果,对于相机拍照(相机可以上下左右移动,但不能旋转)的情况拼接相当好。相机发生旋转扭动则效果很差。
运行环境OpenCV2.4.9
#include "opencv2/core/core.hpp"
#include "highgui.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "opencv2/legacy/legacy.hpp"
#include <iostream>
#include <fstream>
#include <string>
#include<vector>
#include "opencv2/opencv_modules.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/stitching/detail/autocalib.hpp"
#include "opencv2/stitching/detail/blenders.hpp"
#include "opencv2/stitching/detail/camera.hpp"
#include "opencv2/stitching/detail/exposure_compensate.hpp"
#include "opencv2/stitching/detail/matchers.hpp"
#include "opencv2/stitching/detail/motion_estimators.hpp"
#include "opencv2/stitching/detail/seam_finders.hpp"
#include "opencv2/stitching/detail/util.hpp"
#include "opencv2/stitching/detail/warpers.hpp"
#include "opencv2/stitching/warpers.hpp"
#include "opencv2/calib3d/calib3d.hpp"
using namespace std;
using namespace cv;
using namespace cv::detail;
//如果图像太大缩小一半
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;
}
//利用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;
}
//用单应性过滤匹配
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 = 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;
}
//获得匹配点坐标函数
Point2f 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++)
{
int index1 = matches.at(i).queryIdx;
int index2 = matches.at(i).trainIdx;
points1.push_back(keypoints1.at(index1).pt);
points2.push_back(keypoints2.at(index2).pt);
}
}
int main()
{
/* 特征点的提取与匹配 */
int num_images = 100; //图像数量,可修改
vector<string> image_names; // image_names[i]表示第i个图像的名称
string name;
ifstream f("D:\\list低分辨率.txt");
assert(f.is_open());
for(int i=0;i<num_images;i++)
{
getline(f, name);
name = "D:\\低分辨率截图\\" + name;
cout << name;
image_names.push_back(name);
}
vector<vector<DMatch> > image_matches; // image_matches[i]表示第i幅图像和第i+1幅图像特征点匹配的结果
// 提取特征点
vector<ImageFeatures> features(num_images); //表示图像特征
char temp[100];
double ge[100];//100张图的特征点个数
Point2f point;
KeyPoint kp;
float temp1 = 0, temp2 = 0;
char ptsname[100];
char descname[100];
ifstream g("D:\\特征\\特征点个数.txt");//将100张图的特征点个数导入数组
assert(g.is_open());
for (int i = 1; i <= num_images; i++)
{
g >> ge[i - 1];
}
g.close();
for (int i = 1; i <= num_images; i++)
{
sprintf(ptsname, "D:\\特征\\pts%d.txt", i); //格式化输出文件名
ifstream infile(ptsname);
assert(infile.is_open()); //若失败,则输出错误消息,并终止程序运行
for (int a = 0; !infile.eof(); a++)
{
infile >> temp1 >> temp2;
point.x = temp1;
point.y = temp2;
kp = KeyPoint(point, 1.f);
features[i - 1].keypoints.push_back(kp);
}
infile.close();
//infile.clear();
sprintf(descname, "D:\\特征\\desc%d.txt", i); //格式化输出文件名
ifstream des(descname);
assert(des.is_open()); //若失败,则输出错误消息,并终止程序运行
cout << ge[i - 1];
features[i - 1].descriptors= Mat::zeros(ge[i - 1], 256, CV_32FC1);//同理features[0].descriptors
for (int k = 0; k < ge[i - 1]; k++)
{
for (int j = 0; j < 256; j++)
{
des >> features[i - 1].descriptors.at<float>(k, j);
}
}
des.close();
//des.clear();
}
//match_features2(features.descriptor, image_matches); // 特征点匹配
//gms_match_features(image_keypoints,img0.size(),image_matches);
for (unsigned int i = 0; i < num_images - 1; i++)
{
cout << "正在匹配 " << i << " - " << i + 1 << endl;
vector<DMatch> matches;
//match_features1 (image_descriptor[i], image_descriptor[i + 1], matches);
//使用暴力匹配器进行暴力匹配——BruteForceMatcher类的match()方法
//opencv3.4使用
//Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce");
//matcher->match(features[i].descriptors, features[i + 1].descriptors, matches);
BruteForceMatcher<L2<float> > matcher;//实例化暴力匹配器
matcher.match(features[i].descriptors, features[i+1].descriptors, matches);
cout << "有 " << matches.size() << " 个匹配点" << endl;
image_matches.push_back(matches);
}
cout<<"单应性过滤特征点"<<endl;
//单应性过滤特征点
for (unsigned int i = 0; i < image_matches.size(); i++)
{
refineMatchesWithHomography(features[i].keypoints, features[i+1].keypoints, 1.0, image_matches[i]);
}
//image_descriptor.swap(vector<Mat>());//匹配完清除内存
cout<<"narrow";
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, features[i].keypoints, features[i+1].keypoints, image_matches[i], 1);
imshow("匹配图", show);
char wname[255];
sprintf(wname,"met%d.jpg",i);
imwrite(String(wname),show);
waitKey();
}
*/
cout<<"position"<<endl;
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(features[i].keypoints, features[i + 1].keypoints, 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);//已经用不到了,清除容器并最小化它的容量
cout<<"再计算最小,最大边界"<<endl;
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;
}
cout<<"计算大图宽高"<<endl;
int h = img0.rows + ymax - ymin;//拼接图行数(高度)
int w = img0.cols + xmax - xmin;//拼接图列数(宽度)
Mat stitch = Mat::zeros(h, w, CV_8UC3);
cout<<"再把所有图像放到一个大图中(拼接)"<<endl;
for (unsigned int i = 0; i < position_da.size(); i++)
{
img0 = imread(image_names[i]);//读出一个图//左图像
img0 = mynarrow(img0);//如果太大缩小一点。
cout<<image_names[i];
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;
}