简单快速的多图像拼接---百图拼接---补充

前面的视频是同一个方向转圈,如果到了一圈相反转,再相反转会怎样呢?

重新拍了一个视频,3圈(右--左--右),用前面的选片工具,选了86张图

接片效果图:

上下没有重合,如果前面有一张没有配好,后面的就全部不重合了。

先来看看一圈的效果:

去掉单图黑边

 

Mat myimread(string & n)
{
	Mat im=imread(n);
	Mat roi2;
	im(Range(3, im.rows-6), Range(3, im.cols-6)).copyTo(roi2);//消除黑边
	return roi2;

}

用这个函数代替imread:

 

加上左右融合:

 

void 融合(Mat & im,Mat &dst,Point2f p)//四边融合
{
	int w=100; //融合宽;
	int c=im.cols,r=im.rows;
	int c2=dst.cols,r2=dst.rows;

	float a;//权重
	int x,y;
	for(int i=0;i<r;i++)
	{
		//左边
		for(int j=0;j<w;j++)
		{
			x=p.x+j;
			y=p.y+i;

			if (dst.at<Vec3b>(y,x)[0]!=0 || dst.at<Vec3b>(y,x)[1]!=0 || dst.at<Vec3b>(y,x)[2]!=0)
			{
				a=1.0-(float)j/w;//权重
				//原图和新图又叠加
				dst.at<Vec3b>(y,x)[0]=a*dst.at<Vec3b>(y,x)[0] + (1.0-a)*im.at<Vec3b>(i,j)[0];
				dst.at<Vec3b>(y,x)[1]=a*dst.at<Vec3b>(y,x)[1] + (1.0-a)*im.at<Vec3b>(i,j)[1];
				dst.at<Vec3b>(y,x)[2]=a*dst.at<Vec3b>(y,x)[2] + (1.0-a)*im.at<Vec3b>(i,j)[2];
			}
			else
			{
				//直接复制
				dst.at<Vec3b>(y,x)[0]=im.at<Vec3b>(i,j)[0];
				dst.at<Vec3b>(y,x)[1]=im.at<Vec3b>(i,j)[1];
				dst.at<Vec3b>(y,x)[2]=im.at<Vec3b>(i,j)[2];
			}
		}
		//右边
		for(int j=c-w;j<c;j++)
		{
			x=p.x+j;
			y=p.y+i;

			if (dst.at<Vec3b>(y,x)[0]!=0 || dst.at<Vec3b>(y,x)[1]!=0 || dst.at<Vec3b>(y,x)[2]!=0)
			{
				a=(float)(j-(c-w))/w;//右边权重
				dst.at<Vec3b>(y,x)[0]=a*dst.at<Vec3b>(y,x)[0] + (1.0-a)*im.at<Vec3b>(i,j)[0];
				dst.at<Vec3b>(y,x)[1]=a*dst.at<Vec3b>(y,x)[1] + (1.0-a)*im.at<Vec3b>(i,j)[1];
				dst.at<Vec3b>(y,x)[2]=a*dst.at<Vec3b>(y,x)[2] + (1.0-a)*im.at<Vec3b>(i,j)[2];
			}
			else
			{
				dst.at<Vec3b>(y,x)[0]=im.at<Vec3b>(i,j)[0];
				dst.at<Vec3b>(y,x)[1]=im.at<Vec3b>(i,j)[1];
				dst.at<Vec3b>(y,x)[2]=im.at<Vec3b>(i,j)[2];
			}
		}
	}
	Mat roi2(dst, Rect(p.x+w, p.y, im.cols-2*w, im.rows));//复制中间部分
        im(Range(0, im.rows), Range(w, im.cols-w)).copyTo(roi2);

}

用这个函数代替复制部分:

 

 

效果好一点了,再把亮度校正下(匹配点):

	//计算各图匹配点亮度比例
	vector<float> 比_colors;//比_colors[i]表示第i个图像颜色和第一个图的比值
								//亮度比值,第一个为1,其它相对于第一个
	get亮度比率(image_colors,image_matches ,比_colors);

	//再把所有图像放到一个大图中(拼接)
	for (unsigned int i=0;i<position_da.size ();i++)
	{
		Mat im = myimread(image_names[i]);//读出一个图

		//亮度调整
		im/=比_colors[i];//按比值反向修正

		int x=position_da[i].x-xmin;
		int y=position_da[i].y-ymin;
		//Mat roi2(stitch, Rect(x, y, im.cols, im.rows));
  //      im(Range(0, im.rows), Range(0, im.cols)).copyTo(roi2);
		//加融合+++++++++++++++++++++++++++
		融合(im,stitch,Point2f(x,y));//二边融合

	}

最后二张图已经是第二圈的先把它去掉:

这样就好多了,再调整一下左右图高度,把它对齐:

	前后对齐
	{
		//再计算最小,最大边界
		int xmin=0,xmax=0;
		int ixmin=1,ixmax=1;//最左和最右的位置
		for (unsigned int i=1;i<position_da.size ();i++)
		{
			int ti,ta;
			ti=xmin;ta=xmax;
			xmin=(position_da[i].x<xmin)?position_da[i].x:xmin;
			xmax=(position_da[i].x>xmax)?position_da[i].x:xmax;
			if(ti!=xmin)
				ixmin=i;
			if(ta!=xmax)
				ixmax=i;

		}
		vector<DMatch> m;
		
		match_features1 (image_descriptor[ixmin], image_descriptor[ixmax], m);
		refineMatchesWithHomography( image_keypoints[ixmin], image_keypoints[ixmax],1.0, m   );    

		cout<<"前后匹配数:"<<m.size ()<<endl;
		if(m.size ()>0){

			//得到匹配点坐标
			vector<Point2f> points1, points2;
			int src=ixmin;
			int dis=ixmax;

			get_match_points (image_keypoints[src], image_keypoints[dis] ,m, points1, points2);

			Point2f a = getOffset(points1,points2);
			cout << "前后两个相差:"<<a<< endl;

			//在大图的位置
			//新的位置
			position_s.x=position_da[src].x+a.x;
			position_s.y=position_da[src].y+a.y;
			//差值(新 - 老)
			position_s.x-=position_da[dis].x;
			position_s.y-=position_da[dis].y;
			cout << "位置差值:"<<position_s<< endl;

			//计算平均校正值
			int p=dis-src-1;//中间数;
			float 平均x=position_s.x/p;
			float 平均y=position_s.y/p;
			for (unsigned int j=src+1;j<dis;j++)
			{
				//position_da[j].x+=平均x;
				position_da[j].y+=平均y;
			}
	
		}
	}

这是最终效果,虽然也不是很好,比前面已经好多了。

-----------------------分隔线---------------------------------
应有人要完整的程序,现修改一下:

把-“去掉单图黑边- "复制" 保存为 “去掉单图黑边.cpp”
第一去黑边部分完整的程序为:
 

#include "头包含.cpp"

//------------------------------------------------------新加>
#include "去掉单图黑边.cpp" //myimread函数

#define imread myimread   //用myimread函数代 替imread
//------------------------------------------------------新加<

#include "用到的函数.cpp"

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

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

#include "mynarrow函数.cpp"

#include "过滤函数.cpp"

#include "获取匹配点坐标.cpp"

#include "快主main函数.cpp"

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

-----------------------分隔线---------------------------------
把-左右融合函数- "复制" 保存为 "左右融合函数.cpp"
再 把"快主main函数.cpp"中的下面二句修改后 保存为 "加融合的快主main函数.cpp"
 

		//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);
		//加融合
		int x=position_da[i].x-xmin;
		int y=position_da[i].y-ymin;
		融合(img0,stitch,Point2f(x,y));//四边融合

第二融合部分完整的程序为:
 

#include "头包含.cpp"

//------------------------------------------------------新加>
#include "去掉单图黑边.cpp" //myimread函数

#define imread myimread   //用myimread函数代 替imread
//------------------------------------------------------新加<

#include "用到的函数.cpp"

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

//=================分隔线(在上一篇中)=======================

#include "mynarrow函数.cpp"

#include "过滤函数.cpp"

#include "获取匹配点坐标.cpp"

//#include "快主main函数.cpp"

//=================分隔线(在上一篇中)=======================

#include "左右融合函数.cpp"

#include "加融合的快主main函数.cpp"

-----------------------分隔线---------------------------------
后面的两部分就合成一起了
把-下面的这些函数- "复制" 保存为 "有颜色的匹配等函数.cpp"
 

/********************************************************************************************************
参数:
colors1 第一张图片的特征点颜色; colors2 第二张图片的特征点颜色; matches 匹配的结果; (colors3[i], colors4[i]) 第
i个匹配的特征点对的颜色。
功能:
利用两张图片的特征点的颜色colors1、colors2和匹配的结果matches,可以得到两个数组colors3和colors4,
(colors3[i], colors4[i])表示第i个匹配的特征点对的颜色。
*********************************************************************************************************/
void get_match_colors (
	vector<Vec3b> colors1,
	vector<Vec3b> colors2,
	vector<DMatch> matches,
	vector<Vec3b>& colors3,
	vector<Vec3b>& colors4
)
{
	for (unsigned int i = 0; i < matches.size (); i++)
	{
		colors3.push_back (colors1[matches[i].queryIdx]);
		colors4.push_back (colors2[matches[i].trainIdx]);
	}
}

void get亮度比率(vector<vector<Vec3b>> &image_colors, // image_colors[i]表示第i个图像特征点的颜色
	vector<vector<DMatch>> &image_matches ,// image[i]表示第i幅图像和第i+1幅图像特征点匹配的结果
	vector<float> & 比_colors // 比_colors[i]表示第i个图像颜色和第一个图的比值
		)//亮度调整
{

	float c1,c2,c;
	比_colors.push_back (1.0);
	c=1;
	for (unsigned int i=0;i<image_matches.size ();i++)
	{
		if(image_matches[i].size ()==0)break;//如果无匹配点,则后面的就取消了
		c1=0;c2=0;
		vector<Vec3b> colors3,colors4;
		get_match_colors (	image_colors[i],	image_colors[i+1],	image_matches[i],	colors3,	colors4);
		for (unsigned int j=0;j<image_matches[i].size ();j++)
		{

			c1+=0.59*colors3[j][0]+0.11*colors3[j][1]+0.3*colors3[j][2];
			c2+=0.59*colors4[j][0]+0.11*colors4[j][1]+0.3*colors4[j][2];
		}
		c*=c2/c1;//当前比值和前一个比值相乘  得到相对于第一个的比率
		比_colors.push_back (c);
	}
}

/********************************************************************************************************
参数:
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<ORB> orb = ORB::create();
			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;}
			img1= mynarrow(img1);//如果太大缩小一点。
			// 提取特征并计算特征向量
			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;
}

//计算位移
Point2f getOffset(vector<Point2f> &points1,vector<Point2f> &points2)
{
		unsigned int shi=points1.size ();
		shi=(shi>10)?10:shi;//只取前十个
		Point2f a=Point2f(0,0);
		//由当前差值计算其它的匹配点的距离差之和,选择最小的
		float chax[10];//差值
		float chay[10];//差值
		for(unsigned int j=0;j<shi;j++)
		{
			a.x+=chax[j]=points1[j].x-points2[j].x;
			a.y+=chay[j]=points1[j].y-points2[j].y;
		}
		a.x /=shi;a.y /=shi;//取平均值
		if(shi>=3){//求和平均值最小的x,y
			float cha[10]={0};//平方和
			float minc;int p;//最小值和位置
			for(unsigned int i=0;i<shi;i++)
			{

				for(unsigned int j=1;j<shi;j++)
				{
					cha[i]+=chax[j]*chax[j]+chay[j]*chay[j];//平方和
				}
				if(i==0){
					minc=cha[0];//初始值
					p=0;
				}else
					if(cha[i]<minc){minc=cha[i];p=i;}//最小吗?
			}
			a.x=chax[p];a.y=chay[p];//取值
		}
		//cout << "两个相差:"<<a<< endl;
    return a;
}

/********************************************************************************************************
参数:
descriptor1 第一个特征向量; descriptor1 第二个特征向量; matches 匹配的结果。
功能:
对两个特征向量进行匹配,将结果保存在matches中。
*********************************************************************************************************/
void match_features (Mat descriptor1, Mat descriptor2, vector<DMatch>& matches)
{
	vector<vector<DMatch>> knnMatches;
	BFMatcher matcher;
	matcher.knnMatch (descriptor1, descriptor2, knnMatches, 2); // KNN算法寻找与该特征最匹配的两个特征
	double minDistance = 0x3f3f3f3f;
	for (unsigned int i = 0; i < knnMatches.size (); i++)
	{
		if (knnMatches[i][0].distance > 0.6 * knnMatches[i][1].distance) // Ratio test
			continue;
		if (minDistance > knnMatches[i][0].distance)
			minDistance = knnMatches[i][0].distance;
	}
	cout << "knn匹配个数: " << knnMatches.size() << endl;
	for (unsigned int i = 0; i < knnMatches.size (); i++)
	{
		if (knnMatches[i][0].distance > 0.8 * knnMatches[i][1].distance ||
			knnMatches[i][0].distance > 5 * max (minDistance, (double)10.0)) // 排除不满足Ratio test和距离过大的点
			continue;
		matches.push_back (knnMatches[i][0]); // 保存匹配点
	}
	cout << "最终的匹配个数: " << matches.size() << endl;

}

再把-下面的主函数- "复制" 保存为 "有亮度校正的快主main函数.cpp"
 

int main ()
{
	/*	特征点的提取与匹配 	*/

	vector<string> image_names; // image_names[i]表示第i个图像的名称

    LoadImageNamesFromFile("list.txt",image_names);//从list.txt文件装载图像文件名
	cout<<"有 "<<image_names.size ()<<" 个图"<<endl;

	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); // 提取特征点

	//2。前后匹配(串联)
	match_features2 (image_descriptor, 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]    );    
	}	

	Mat img0 = myimread(image_names[0]);//读出一个图


	//3。计算匹配图像的相对位置
	//4。以第一张图左上角点为原点,找到所有图的位置
	vector<cv::Point2f> position_da; // position_da[i]表示第i个图像在大图中的位置(左上角)
	vector<cv::Point2f> position_2; // position_da[i]表示第i个图像i+1的相对位置(前+这=后)
	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);

		Point2f a = getOffset(points1,points2);//取得匹配相对位移

		position_2.push_back (a);//保存相对位置

		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;
		



	}

	//前后对齐
	{
		//再计算最小,最大边界
		int xmin=0,xmax=0;
		int ixmin=1,ixmax=1;//最左和最右的位置
		for (unsigned int i=1;i<position_da.size ();i++)
		{
			int ti,ta;
			ti=xmin;ta=xmax;
			xmin=(position_da[i].x<xmin)?position_da[i].x:xmin;
			xmax=(position_da[i].x>xmax)?position_da[i].x:xmax;
			if(ti!=xmin)
				ixmin=i;
			if(ta!=xmax)
				ixmax=i;

		}
		vector<DMatch> m;
		
		match_features (image_descriptor[ixmin], image_descriptor[ixmax], m);
		refineMatchesWithHomography( image_keypoints[ixmin], image_keypoints[ixmax],1.0, m   );    

		cout<<"前后匹配数:"<<m.size ()<<endl;
		if(m.size ()>0){

			//得到匹配点坐标
			vector<Point2f> points1, points2;
			int src=ixmin;
			int dis=ixmax;

			get_match_points (image_keypoints[src], image_keypoints[dis] ,m, points1, points2);

			Point2f a = getOffset(points1,points2);
			cout << "前后两个相差:"<<a<< endl;

			//在大图的位置
			//新的位置
			position_s.x=position_da[src].x+a.x;
			position_s.y=position_da[src].y+a.y;
			//差值(新 - 老)
			position_s.x-=position_da[dis].x;
			position_s.y-=position_da[dis].y;
			cout << "位置差值:"<<position_s<< endl;

			//这里要加上校正两个匹配 中间图的位置(平均)+++++++++++++++++++++++++++++
			//计算平均校正值
			int p=dis-src-1;//中间数;
			float 平均x=position_s.x/p;
			float 平均y=position_s.y/p;
			for (unsigned int j=src+1;j<dis;j++)
			{
				//position_da[j].x+=平均x;
				position_da[j].y+=平均y;
			}
	
		}
	}



	//-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=结束
	image_descriptor.swap(vector<Mat>());//匹配完清除内存
	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;//拼接图列数(宽度)
	cout<<"大图宽,高:"<<w<<","<<h<<endl;

	Mat stitch = Mat::zeros(h, w, CV_8UC3);

	//计算各图匹配点亮度比例
	vector<float> 比_colors;//比_colors[i]表示第i个图像颜色和第一个图的比值
								//亮度比值,第一个为1,其它相对于第一个
	get亮度比率(image_colors,image_matches ,比_colors);

	//再把所有图像放到一个大图中(拼接)
	for (unsigned int i=0;i<position_da.size ();i++)
	{
		Mat im = myimread(image_names[i]);//读出一个图

		//亮度调整
		im/=比_colors[i];//按比值反向修正

		int x=position_da[i].x-xmin;
		int y=position_da[i].y-ymin;
		//Mat roi2(stitch, Rect(x, y, im.cols, im.rows));
  //      im(Range(0, im.rows), Range(0, im.cols)).copyTo(roi2);
		//加融合+++++++++++++++++++++++++++
		融合(im,stitch,Point2f(x,y));//四边融合

	}

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

最后两部分有亮度校正完整的程序为:

#include "去掉单图黑边.cpp" //myimread函数

#define imread myimread   //用myimread函数代 替imread
//------------------------------------------------------新加<

#include "用到的函数.cpp"

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

//=================分隔线(在上一篇中)=======================

#include "mynarrow函数.cpp"

#include "过滤函数.cpp"

#include "获取匹配点坐标.cpp"

//#include "快主main函数.cpp"

//=================分隔线(在上一篇中)=======================

#include "左右融合函数.cpp"

//#include "加融合的快主main函数.cpp"

#include "有颜色的匹配等函数.cpp"

#include "有亮度校正的快主main函数.cpp"

本篇的所有完整代码都在上面了。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值