当前位置: 首页 > news >正文

利用OpenCV实现图像拼接

一、介绍

     图像拼接.

二、分步实现

     要实现图像拼接,简单来说有以下几步:

  1. 对每幅图进行特征点提取
  2. 对对特征点进行匹配
  3. 进行图像配准
  4. 把图像拷贝到另一幅图像的特定位置
  5. 对重叠边界进行特殊处理

     PS:需要使用低版本的opencv,否则无法使用特征角点提取算子。

#include "highgui/highgui.hpp"    
#include "opencv2/nonfree/nonfree.hpp"    
#include "opencv2/legacy/legacy.hpp"   
#include <iostream>  using namespace cv;
using namespace std;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)
{// 左上角(0, 0, 1)double v2[3] = { 0, 0, 1 };double v1[3] = { 0 };Mat V2 = Mat(3, 1, CV_64FC1, v2);Mat V1 = Mat(3, 1, CV_64FC1, v1);V1 = H * V2;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];
}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);}}
}int main(int argc, char* argv[])
{Mat image01 = imread("image2.png", 1); //右图Mat image02 = imread("image1.png", 1); //左图imshow("p2", image01);imshow("p1", image02);// 灰度图转换  Mat image1, image2;cvtColor(image01, image1, CV_RGB2GRAY);cvtColor(image02, image2, CV_RGB2GRAY);// 提取特征点SurfFeatureDetector Detector(2000);vector<KeyPoint> keyPoint1, keyPoint2;Detector.detect(image1, keyPoint1);Detector.detect(image2, keyPoint2);// 特征点描述SurfDescriptorExtractor Descriptor;Mat imageDesc1, imageDesc2;Descriptor.compute(image1, keyPoint1, imageDesc1);Descriptor.compute(image2, keyPoint2, imageDesc2);FlannBasedMatcher matcher;vector<vector<DMatch> > matchePoints;vector<Mat> train_desc(1, imageDesc1);matcher.add(train_desc);matcher.train();matcher.knnMatch(imageDesc2, matchePoints, 2);cout << "total match points: " << matchePoints.size() << endl;// Lowe's algorithm,获取优秀匹配点vector<DMatch> GoodMatchePoints;for (int i = 0; i < matchePoints.size(); i++){if (matchePoints[i][0].distance < 0.4 * matchePoints[i][1].distance){GoodMatchePoints.push_back(matchePoints[i][0]);}}// draw matchMat first_match;drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);imshow("first_match ", first_match);vector<Point2f> imagePoints1, imagePoints2;for (int i = 0; i < GoodMatchePoints.size(); i++){imagePoints2.push_back(keyPoint2[GoodMatchePoints[i].queryIdx].pt);imagePoints1.push_back(keyPoint1[GoodMatchePoints[i].trainIdx].pt);}// 获取图像1到图像2的投影映射矩阵 尺寸为3*3  Mat homo = findHomography(imagePoints1, imagePoints2, CV_RANSAC);cout << "变换矩阵为:\n" << homo << endl << endl; // 输出映射矩阵      // 计算配准图的四个顶点坐标CalcCorners(homo, image01);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(image01, imageTransform1, homo, Size(MAX(corners.right_top.x, corners.right_bottom.x), image02.rows));// warpPerspective(image01, imageTransform2, adjustMat*homo, Size(image02.cols*1.3, image02.rows*1.8));imshow("直接经过透视矩阵变换", imageTransform1);// 创建拼接后的图,需提前计算图的大小int dst_width = imageTransform1.cols;  // 取最右点的长度为拼接图的长度int dst_height = image02.rows;Mat dst(dst_height, dst_width, CV_8UC3);dst.setTo(0);imageTransform1.copyTo(dst(Rect(0, 0, imageTransform1.cols, imageTransform1.rows)));image02.copyTo(dst(Rect(0, 0, image02.cols, image02.rows)));imshow("b_dst", dst);// 优化拼接处OptimizeSeam(image02, imageTransform1, dst);imshow("dst", dst);waitKey();return 0;
}

  

 

三、利用stitch实现

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/stitching.hpp"
#include <iostream>using namespace std;
using namespace cv;int main(int argc, char* argv[])
{Mat img1 = imread("image1.png", cv::IMREAD_COLOR);Mat img2 = imread("image2.png", cv::IMREAD_COLOR);vector<Mat> imgs;imgs.push_back(img1);imgs.push_back(img2);Mat pano;Ptr<Stitcher> stitcher = Stitcher::create(Stitcher::PANORAMA);Stitcher::Status status = stitcher->stitch(imgs, pano);if (status != Stitcher::OK){cout << "Can't stitch images, error code = " << int(status) << endl;return EXIT_FAILURE;}string result_name = "result1.jpg";imwrite(result_name, pano);cout << "stitching completed successfully\n" << result_name << " saved!";return EXIT_SUCCESS;
}

相关文章:

利用OpenCV实现图像拼接

一、介绍 图像拼接. 二、分步实现 要实现图像拼接&#xff0c;简单来说有以下几步&#xff1a; 对每幅图进行特征点提取对对特征点进行匹配进行图像配准把图像拷贝到另一幅图像的特定位置对重叠边界进行特殊处理 PS&#xff1a;需要使用低版本的opencv&#xff0c;否则无法使…...

【java安全】无Commons-Collections的Shiro550反序列化利用

文章目录 【java安全】无Commons-Collections的Shiro550反序列化利用Shiro550利用的难点CommonsBeanutils1是否可以Shiro中&#xff1f;什么是serialVersionUID&#xff1f;W 无依赖的Shiro反序列化利用链POC 【java安全】无Commons-Collections的Shiro550反序列化利用 Shiro5…...

CSS 滚动条

一、滚动条样式属性 ::-webkit-scrollbar {width: 6px; /* 竖向滚动条宽度 */height: 6px; /* 横向滚动条高度 */ }::-webkit-scrollbar-thumb {border-radius: 10px; /* 滚动条样式 */-webkit-box-shadow: inset 0 0 3px red; /* 内阴影 */background-color: blue; /* 滚动条…...

Linux: security: openssh: sshd 出现defunct的一种情况

最近遇到了一个问题,就出现了一对遗留进程对,类似于下面这两个 root 77399 19100 77399 0 1 01:46 ? 00:00:00 sshd: \mzhan017 [priv] sshd 77400 77399 77400 0 1 01:46 ? 00:00:00 sshd: [defunct] 人生中的第一次遇到这种情况。一定要记录一下! 关于[priv]这个解释,…...

Self-regulating Prompts: Foundational Model Adaptation without Forgetting

本文也是大模型系列的文章&#xff0c;主要是与Prompt Learning有关。针对《Self-regulating Prompts: Foundational Model Adaptation without Forgetting》的翻译。 自我调节的提示&#xff1a;不遗忘的基础模型适应 摘要1 引言2 相关工作3 提出的方法3.1 前言3.2 提示学习的…...

平时工资不够用?推荐4种适合工作之余做的兼职副业!

你是否也曾经在为每个月的工资发愁&#xff1f;你是否想过做点副业来增加收入&#xff1f;现在很多上班族的工资&#xff0c;已经难以满足他们的生活需求了&#xff0c;很多人开始尝试通过副业来增加收入。那么上班族要如何寻找适合自己的副业呢&#xff1f;下面就给大家分享几…...

21.Netty源码之编码器

highlight: arduino-light Netty如何实现自定义通信协议 在学习完如何设计协议之后&#xff0c;我们又该如何在 Netty 中实现自定义的通信协议呢&#xff1f;其实 Netty 作为一个非常优秀的网络通信框架&#xff0c;已经为我们提供了非常丰富的编解码抽象基类&#xff0c;帮助我…...

Linux 快速创建桌面图标

在安装 tar.gz 这类型压缩文件时&#xff0c;通常启动文件是.sh文件。文章主要记录快速添加到桌面图标。 1、解压 tar -zxvf XXX.tar.gz 2、创建桌面图标文件 touch XXX.desktop 3、文件中配置 [Desktop Entry] NameXXX CommentZZZ Exec/软件可执行文件所在目录/可执行文…...

数据结构—哈夫曼树及其应用

5.6哈夫曼树及其应用 5.6.1哈夫曼树的基本概念 路径&#xff1a;从树中一个结点到另一个结点之间的分支构成这两个结点间的路径。 结点的路径长度&#xff1a;两结点间路径上的分支数。 树的路径长度&#xff1a;从树根到每一个结点的路径长度之和。记作 TL 结点数目相同的…...

NeRF-SLAM: Real-Time Dense Monocular SLAM with Neural Radiance Fields 论文阅读

论文信息 题目&#xff1a;NeRF-SLAM: Real-Time Dense Monocular SLAM with Neural Radiance Fields 作者&#xff1a;Antoni Rosinol, John J. Leonard&#xff0c; Luca Carlone 代码&#xff1a;https://github.com/ToniRV/NeRF-SLAM 来源&#xff1a;arxiv 时间&#xff…...

机器学习之弹性网络(Elastic Net)

弹性网络 代码原文 下面代码参考scikit-learn中文社区&#xff0c;链接在上面。 但是由于scikit-learn中文社区上的代码有些地方跑不通&#xff0c;故对此代码做了修改&#xff0c;输出结果与社区中显示的结果相同。 对弹性网络进行简单的介绍&#xff1a; ElasticNet是一个训…...

嵌入式入门教学——C51

一、前期准备 1、硬件设备 2、软件设备 二、预备知识 1、什么是单片机&#xff1f; 在一片集成电路芯片上集成微处理器、存储器、IO接口电路&#xff0c;从而构成了单芯片微型计算机&#xff0c;及单片机。STC89C52单片机&#xff1a; STC&#xff1a;公司89&#xff1a;所属…...

2023-08-03力扣每日一题

链接&#xff1a; 722. 删除注释 题意&#xff1a; 如题&#xff0c;特殊规则见链接 解&#xff1a; 字符串处理&#xff0c;嗯写就完事了,主要是判断指针位置和特殊规则 实际代码&#xff1a; #include<bits/stdc.h> using namespace std; vector<string> …...

【蓝桥杯备考资料】如何进入国赛?

目录 写在前面注意事项数组、字符串处理BigInteger日期问题DFS 2013年真题Java B组世纪末的星期马虎的算式振兴中华黄金连分数有理数类&#xff08;填空题&#xff09;三部排序&#xff08;填空题&#xff09;错误票据幸运数字带分数连号区间数 2014年真题蓝桥杯Java B组03猜字…...

QtWebApp开发https服务器,完成客户端与服务器基于ssl的双向认证

引言&#xff1a;所谓http协议&#xff0c;本质上也是基于TCP/IP上服务器与客户端请求和应答的标准&#xff0c;web开发中常用的http server有apache和nginx。Qt程序作为http client可以使用QNetworkAccessManager很方便的进行http相关的操作。Qt本身并没有http server相关的库…...

动态IP代理的优势展现与应用场景

在当今数字化时代&#xff0c;网络安全和隐私保护变得愈发重要。作为一家动态IP代理产品供应商&#xff0c;我们深知在保护个人隐私和提高网络安全性方面的重要性。本文将会分享动态IP代理的优势及其在不同应用场景下的实际应用案例&#xff0c;帮助更好地了解和应用动态IP代理…...

ad+硬件每日学习十个知识点(22)23.8.2(LDO datasheet手册解读)

文章目录 1.LDO的概述、features2.LDO的绝对参数&#xff08;功率升温和结温&#xff09;3.LDO的引脚功能4.LDO的电气特性5.LDO的典型电路&#xff08;电容不能真用1uF&#xff0c;虽然按比例取输出值&#xff0c;但是R2的取值要考虑释放电流&#xff09;6.LDO的开关速度和线性…...

这可是全网最全的网络工程师零基础实战视频整理,最新版分享

互联网中每一项傍身的技能都是需要从如何入门开始的&#xff0c;网络技术也是如此&#xff01; 网络技术区别其他互联网技能的一点是学习需要从设备开始&#xff0c;只有认识了解了路由器、交换机、防火墙这些网络设备&#xff0c;才开始从网络通信原理开始&#xff0c;这使得网…...

笔记本WIFI连接无网络【实测有效解决方案,不用重启电脑】

笔记本Wifi连接无网络实测有效解决方案 问题描述&#xff1a; 笔记本买来一段时间后&#xff0c;WIFI网络连接开机一段时间还正常连接&#xff0c;但是过一段时间显示网络连接不上解决方案&#xff1a; 1.编写网络重启bat脚本&#xff0c;将以下内容写到文本文件&#xff0c;把…...

js 正则表达式配合replace进行过滤html字符串遇到的性能问题

问题场景复现&#xff1a; 博主要实现一个邮箱列表&#xff0c;其中列表中的每一封邮件都有一个摘要&#xff0c;但是摘要是要自己从后端提供的content内容区自己过滤掉所有&#xff0c;只留下纯文本内容的前面几行作为摘要。 性能问题 当我测试到一个邮箱&#xff0c;其中的…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

算法—栈系列

一&#xff1a;删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...