前面的视频是同一个方向转圈,如果到了一圈相反转,再相反转会怎样呢?
重新拍了一个视频,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"
本篇的所有完整代码都在上面了。