原图


#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv.hpp>using namespace cv;
using namespace std;void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst);typedef struct
{Point2f left_top;Point2f left_bottom;Point2f right_top;Point2f right_bottom;
}four_corners_t;four_corners_t corners;void CalcCorners(const Mat& H, const Mat& src)
{double v2[] = { 0, 0, 1 };//左上角double v1[3];//变换后的坐标值Mat V2 = Mat(3, 1, CV_64FC1, v2);  //列向量Mat V1 = Mat(3, 1, CV_64FC1, v1);  //列向量V1 = H * V2;//左上角(0,0,1)cout << "V2: " << V2 << endl;cout << "V1: " << V1 << endl;corners.left_top.x = v1[0] / v1[2];corners.left_top.y = v1[1] / v1[2];//左下角(0,src.rows,1)v2[0] = 0;v2[1] = src.rows;v2[2] = 1;V2 = Mat(3, 1, CV_64FC1, v2);  //列向量V1 = Mat(3, 1, CV_64FC1, v1);  //列向量V1 = H * V2;corners.left_bottom.x = v1[0] / v1[2];corners.left_bottom.y = v1[1] / v1[2];//右上角(src.cols,0,1)v2[0] = src.cols;v2[1] = 0;v2[2] = 1;V2 = Mat(3, 1, CV_64FC1, v2);  //列向量V1 = Mat(3, 1, CV_64FC1, v1);  //列向量V1 = H * V2;corners.right_top.x = v1[0] / v1[2];corners.right_top.y = v1[1] / v1[2];//右下角(src.cols,src.rows,1)v2[0] = src.cols;v2[1] = src.rows;v2[2] = 1;V2 = Mat(3, 1, CV_64FC1, v2);  //列向量V1 = Mat(3, 1, CV_64FC1, v1);  //列向量V1 = H * V2;corners.right_bottom.x = v1[0] / v1[2];corners.right_bottom.y = v1[1] / v1[2];}int main(int argc, char* argv[])
{//Mat imageRight = imread("images/imgR.jpg", 1);    //右图//Mat imageLeft = imread("images/imgL.jpg", 1);    //左图//Mat imageRight = imread("images/SR.jpg", 1);    //右图//Mat imageLeft = imread("images/SL.jpg", 1);    //左图Mat imageRight = imread("images/flowerR.jpg", 1);    //右图Mat imageLeft = imread("images/flowerL.jpg", 1);    //左图//灰度图转换  Mat image_r, image_l;cvtColor(imageRight, image_r, COLOR_BGR2GRAY);cvtColor(imageLeft, image_l, COLOR_BGR2GRAY);//直接从可能重复的区域提取特征点匹配 当前是左右图在拼接处大概有1/3是重复的Rect rect_right = Rect(0, 0, imageRight.cols / 3, imageRight.rows);Rect rect_left = Rect(2*imageLeft.cols/3, 0, (imageLeft.cols/3) -1 , imageLeft.rows);Mat image_r_rect = imageRight(Rect(rect_right));Mat image_l_rect = imageLeft(Rect(rect_left));double start = getTickCount();//提取特征点    Ptr<FeatureDetector> ORBDetector = ORB::create(10000);vector<KeyPoint> keyPoints_r, keyPoints_l;ORBDetector->detect(image_r_rect, keyPoints_r);ORBDetector->detect(image_l_rect, keyPoints_l);//特征点描述,为下边的特征点匹配做准备    Ptr<DescriptorExtractor> ORBDescriptor = ORB::create(10000);Mat imageDesc_r, imageDesc_l;ORBDescriptor->compute(image_r_rect, keyPoints_r, imageDesc_r);ORBDescriptor->compute(image_l_rect, keyPoints_l, imageDesc_l);flann::Index flannIndex(imageDesc_r, flann::LshIndexParams(12, 20, 2), cvflann::FLANN_DIST_HAMMING);vector<DMatch> GoodMatchePoints;Mat macthIndex(imageDesc_l.rows, 2, CV_32SC1), matchDistance(imageDesc_l.rows, 2, CV_32FC1);flannIndex.knnSearch(imageDesc_l, macthIndex, matchDistance, 2, flann::SearchParams());// Lowe's algorithm,获取优秀匹配点for (int i = 0; i < matchDistance.rows; i++){if (matchDistance.at<float>(i, 0) < 0.4 * matchDistance.at<float>(i, 1)){DMatch dmatches(i, macthIndex.at<int>(i, 0), matchDistance.at<float>(i, 0));GoodMatchePoints.push_back(dmatches);}}Mat first_match;//drawMatches(imageLeft, keyPoints_l, imageRight, keyPoints_r, GoodMatchePoints, first_match);drawMatches(image_l_rect, keyPoints_l, image_r_rect, keyPoints_r, GoodMatchePoints, first_match);//namedWindow("first_match ", 2);//imshow("first_match ", first_match);//waitKey();vector<Point2f> imagePoints1, imagePoints2;for (int i = 0; i < GoodMatchePoints.size(); i++){imagePoints2.push_back(keyPoints_l[GoodMatchePoints[i].queryIdx].pt);imagePoints1.push_back(keyPoints_r[GoodMatchePoints[i].trainIdx].pt);}if (imagePoints1.size() <= 10 || imagePoints2.size() <= 10){printf("There is little keypoints\n");getchar();}//将左图的坐标转化到原图的位置,否则其变换矩阵在x方向的平稳不对for (auto iter = imagePoints2.begin(); iter != imagePoints2.end(); iter++){(*iter).x  += 2 * imageLeft.cols / 3;}//获取图像1到图像2的投影映射矩阵 尺寸为3*3  Mat homo = findHomography(imagePoints1, imagePoints2, RANSAC);// 也可以使用getPerspectiveTransform方法获得透视变换矩阵,不过要求只能有4个点,效果稍差//Mat   homo=getPerspectiveTransform(imagePoints1,imagePoints2);  cout << "变换矩阵为:\n" << homo << endl << endl; //输出映射矩阵      //计算配准图的四个顶点坐标CalcCorners(homo, imageRight);//cout << "left_top:" << corners.left_top << endl;//cout << "left_bottom:" << corners.left_bottom << endl;//cout << "right_top:" << corners.right_top << endl;//cout << "right_bottom:" << corners.right_bottom << endl;//图像配准  Mat imageTransform1, imageTransform2;warpPerspective(imageRight, imageTransform1, homo, Size(MAX(corners.right_top.x, corners.right_bottom.x), imageLeft.rows));rectangle(imageRight, Rect(imageRight.cols - MAX(corners.right_top.x, corners.right_bottom.x), 0, MAX(corners.right_top.x, corners.right_bottom.x), 500), (0, 0, 255));imshow("rectangle", imageRight);//imshow("透视矩阵变换Right", imageTransform1);//imwrite("trans1.jpg", imageTransform1);//waitKey();//创建拼接后的图,需提前计算图的大小int move_x = 900;int dst_width = imageTransform1.cols;  //取最右点的长度为拼接图的长度int dst_height = imageLeft.rows;Mat dst(dst_height, dst_width, CV_8UC3);dst.setTo(0);imageTransform1.copyTo(dst(Rect(0, 0, imageTransform1.cols, imageTransform1.rows)));imshow("transform1", dst);imageLeft.copyTo(dst(Rect(0, 0, imageLeft.cols, imageLeft.rows)));imshow("b_dst", dst);waitKey();OptimizeSeam(imageLeft, imageTransform1, dst);double end = getTickCount();double useTime = (end - start) / getTickFrequency();cout << "use-time : " << useTime << "s" << endl;imshow("dstOptimize", dst);//imwrite("dst.jpg", dst);waitKey();return 0;
}//优化两图的连接处,使得拼接自然
void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst)
{int start = MIN(corners.left_top.x, corners.left_bottom.x);//开始位置,即重叠区域的左边界  double processWidth = img1.cols - start;//重叠区域的宽度  int rows = dst.rows;int cols = img1.cols; //注意,是列数*通道数double alpha = 1;//img1中像素的权重  for (int i = 0; i < rows; i++){uchar* p = img1.ptr<uchar>(i);  //获取第i行的首地址uchar* t = trans.ptr<uchar>(i);uchar* d = dst.ptr<uchar>(i);for (int j = start; j < cols; j++){//如果遇到图像trans中无像素的黑点,则完全拷贝img1中的数据if (t[j * 3] == 0 && t[j * 3 + 1] == 0 && t[j * 3 + 2] == 0){alpha = 1;}else{//img1中像素的权重,与当前处理点距重叠区域左边界的距离成正比,实验证明,这种方法确实好  alpha = (processWidth - (j - start)) / processWidth;}d[j * 3] = p[j * 3] * alpha + t[j * 3] * (1 - alpha);d[j * 3 + 1] = p[j * 3 + 1] * alpha + t[j * 3 + 1] * (1 - alpha);d[j * 3 + 2] = p[j * 3 + 2] * alpha + t[j * 3 + 2] * (1 - alpha);}}}

特征点匹配

拼接结果

opencv 基于ORB特征点图像拼接相关推荐

  1. 基于SIFT特征的图像拼接融合(matlab+vlfeat实现)

    基于SIFT特征的图像拼接融合(matlab+vlfeat实现) piccolo,之前做的东西,简单整理下,不是做图像方向的,写的不好轻喷 主要原理参看SIFT算法详解和SIFT特征匹配算法介绍--寻 ...

  2. opencv 画orb特征点

    ORB关键点 画特征点 drawKeypoints 函数 绘制匹配结果 drawMatches函数 画特征点 drawKeypoints 函数 前期准备,读取图像,检测ORB特征点 //读取图像 cv ...

  3. 基于OpenCV&ORB和特征匹配的双视频图像拼接(源码&部署教程)

    1.双视频拼接效果展示 2.视频演示 [项目分享]Python基于OpenCV&ORB和特征匹配的双视频图像拼接(源码&部署教程)_哔哩哔哩_bilibili 3.背景 随着汽车电子和 ...

  4. Python基于OpenCV&ORB和特征匹配的双视频图像拼接(源码&部署教程)

    1.双视频拼接效果展示 2.视频演示 [项目分享]Python基于OpenCV&ORB和特征匹配的双视频图像拼接(源码&部署教程)_哔哩哔哩_bilibili 3.背景 随着汽车电子和 ...

  5. ORB特征描述原理、python实现及基于opencv实现

    写在前面: 黄宁然,看你看过的算法系列*,不如,就到此吧. 参考文献镇楼: [1]汪洋,扫地机器人定位算法设计与嵌入式系统实现 [2]王雯涛,ORB图像特征提取算法的FPGA设计与实现 [3]房亮,基 ...

  6. java opencv orb_OpenCV提取ORB特征并匹配

    一.什么是特征? 图像的特征(Feature),是图像上最具代表性的一些点.所谓最具代表性,就是说这些点包含了图像表述的大部分信息.即使旋转.缩放,甚至调整图像的亮度,这些点仍然稳定地存在,不会丢失. ...

  7. opencv与ORB SLAM提取orb特征点比较

    在ORB SLAM中,通过四叉树的方式存储关键点,使得图像上特征点分布均匀,便于追踪.此次实验分别用opencv接口和ORB SLAM实现特征点的提取,效果图如下: 实现程序: #include &l ...

  8. 视觉SLAM——ORB特征

    一.OpenCV 的ORB 特征--slambook2/ch7/orb_ cv.cpp #include <iostream> #include <opencv2/core/core ...

  9. akaze特征匹配怎么去掉不合适的点_单应性矩阵应用基于特征的图像拼接

    点击上方蓝字关注我们 微信公众号:OpenCV学堂 关注获取更多计算机视觉与深度学习知识 前言 前面写了一篇关于单应性矩阵的相关文章,结尾说到基于特征的图像拼接跟对象检测中单应性矩阵应用场景.得到很多 ...

最新文章

  1. PostgreSQL_case when
  2. cgroup代码浅析(2)
  3. MongoDB数据导入hbase + 代码
  4. Flink的Table API 与SQL介绍及调用
  5. ant design 预览图片_AntD框架的upload组件上传图片时遇到的一些坑
  6. linux进程运行队列,Linux进程调度中队列的使用
  7. es6 Promise 的含义
  8. mysqldump全量恢复_mysql全量备份和快速恢复的方法整理
  9. Flex与.NET互操作(十六):FluorineFx + Flex视频聊天室案例开发
  10. [shell] IT运维之Linux服务器监控方案
  11. PyQt之按钮传递鼠标按下事件点击失效
  12. 图像数据增强扩充数据库_分析数据扩充以进行图像分类
  13. 计算机图形学——Bresenham画线算法
  14. matlab解耦合方程,如何在Matlab中求解耦合随机微分方程
  15. 一些lightbox插件(弹出层)
  16. 华为Mate系列平板手机安装谷歌框架
  17. sis最新地址获取方法_微信表情悄悄更新,获取最新表情方法!
  18. 第六十一章 方法关键字 - Deprecated
  19. 一位16年老员工的反思:什么才是真正的执行力?
  20. vscode运行命令是报错:标记“”不是此版本中的有效语句分隔符。

热门文章

  1. mac 重置mysql root密码_MAC 重置MySQL root 密码
  2. 充电口 米兔积木机器人_米兔积木机器人怎么充电
  3. 小小一方士 C# Async\Await
  4. JAVA-日期类(Date、SimpleDateFormat)
  5. office启动出现oxc0000142的问题的解决方案
  6. SSM基于java的线上阅读平台的设计与实现 毕业设计-附源码291023
  7. #荣耀双十一# 就是耀免单,年度旗舰任性送
  8. check if DVE variable is valid
  9. 当java碰到防火墙
  10. 关于python语言数值操作符_下列哪种物质是体内硫酸基的提供者