【C++】【Opencv】cv::warpAffine()仿射变换函数详解,实现平移、缩放和旋转等功能

仿射变换是一种二维变换,它可以将一个二维图形映射到另一个二维图形上,保持了图形的“形状”和“大小”不变,但可能会改变图形的方向和位置。仿射变换可以用一个线性变换矩阵来表示,该矩阵包含了六个参数,可以进行平移、缩放、旋转等操作。通过原理、函数和示例进行解析,帮助大家理解和使用。

下面我们将依次实现平移、旋转、缩放和仿射变换等功能,使用C++语言和OpenCV库。

目录

  • 原理和函数
    • 原理
    • warpAffine()函数详解
  • 示例
    • 平移
      • 原理
      • 运行示例
    • 缩放
      • 原理
      • 缩小示例
      • 放大示例
    • 旋转
      • 原理
      • 顺时针示例
      • 逆时针示例
  • 总结

原理和函数

原理

在这里插入图片描述在这里插入图片描述
由于矩阵A的最后一行为(0,0,1),所以认为A是仿射变换矩阵,变换类型主要包括平移、缩放和旋转。

warpAffine()函数详解

warpAffine() 是 OpenCV 库中的一个函数,用于进行二维仿射变换。该函数将输入图像映射到输出图像,应用仿射变换。

函数原型如下:

void cv::warpAffine(InputArray src, OutputArray dst, InputArray mat, Size dsize = Size(), int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, Scalar borderValue = Scalar());参数详解:src:输入图像,必须是单通道或三通道的8位或32位浮点型图像。
dst:输出图像,其大小和类型与输入图像相同。
mat:2x3的变换矩阵。
dsize:输出图像的大小,如果这个参数为 Size() ,则输出图像的大小将与输入图像相同。
flags:用于指定插值的方法,默认为线性插值。可用的选项有 INTER_NEAREST, INTER_LINEAR, INTER_CUBIC 等。
borderMode:用于指定如何处理输出图像边缘的像素,默认为常量填充模式。可用的选项有 BORDER_CONSTANT, BORDER_REPLICATE, BORDER_WRAP 等。
borderValue:用于指定填充的边界值,默认为0。

这个函数使用仿射变换来将输入图像映射到输出图像。仿射变换包括旋转、缩放、平移等操作,但不包括扭曲和剪切。这个函数非常有用,特别是在需要将图像映射到另一个大小或以特定方式旋转或倾斜图像时。

示例

平移

原理

平移变换可以用一个3x3的变换矩阵来表示,其中第一行和第二行表示原始图像的行向量和列向量,第三行表示变换后的行向量和列向量,和原理部分类似,但有些版本只需要设置一个2x3的变换矩阵即可,可以省略第三行。在本例中,即定义的是2x3的变换矩阵,我们将原始图像向右移动100个像素,向下移动300个像素。其中,数值为正,则向正方向移,数值为负,则向反向相移。
在这里插入图片描述
如上述矩阵转换矩阵,只需要设置第一行的第三个元素tx和第二行第三列的ty即可,体现在2行3列的矩阵中如下面运行示例中的100和300所示。

运行示例

#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;int main() {Mat src = imread("ceshi.jpg");if (src.empty()) {cout << "Could not read the source image" << endl;return -1;}Mat trans_mat = (Mat_<double>(2, 3) << 1, 0, 100, 0, 1, 300);Mat dst;warpAffine(src, dst, trans_mat, src.size());imshow("Source Image", src);imshow("Affine Transformed Image", dst);imwrite("pingyi.jpg", dst);waitKey(0);return 0;
}

在代码中,Mat trans_mat = (Mat_<double>(2, 3) << 1, 0, 100, 0, 1, 300);是定义的一个2行3列的转换矩阵,第一行是右移100,第二行是下移300。最后,我们使用warpAffine()函数进行仿射变换,将原始图像映射到输出图像中,并显示原始图像和变换后的图像。运行可以看到相应的效果,如下图所示。
在这里插入图片描述
上面为原图,下面为平移后的图像。
在这里插入图片描述

缩放

原理

仿射变换中的缩放指的是对图像进行等比例的放大或缩小。在仿射变换矩阵中,可以通过设置第一行和第二行的元素来控制缩放。
在这里插入图片描述
由上面的平移可知,第一行第三列和第二行第三列的数值是控制平移的,在此处可知,第一行第一列和第二行第二列是用于控制x轴和y轴的缩放比例的,在下面的运行示例中也可以看出缩放因子在的位置与此相对应。

缩小示例

#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;int main() {Mat src = imread("ceshi.jpg");if (src.empty()) {cout << "Could not read the source image" << endl;return -1;}// 设置缩放比例double scale = 0.5;// 计算缩放矩阵Mat scale_mat = (Mat_<double>(2, 3) << scale, 0, 0, 0, scale, 0);// 进行仿射变换Mat dst;warpAffine(src, dst, scale_mat, src.size());// 显示原始图像和变换后的图像imshow("Source Image", src);imshow("Affine Transformed Image", dst);imwrite("suoxiao.jpg", dst);waitKey(0);return 0;
}

在上面的代码中,我们首先读取一张名为“ceshi.jpg”的图像,然后设置缩放比例为0.5。接着,我们计算缩放矩阵,其中第一行和第二行的前两个元素分别表示水平方向和垂直方向的缩放比例,第三行和第四行的前两个元素为0,表示不进行平移操作。最后,我们使用warpAffine()函数进行仿射变换,将原始图像映射到输出图像中,并显示原始图像和变换后的图像。变换后的效果如下图所示。
在这里插入图片描述

放大示例

此处,我们把缩放因子设置为1.5,即放大,

#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;int main() {Mat src = imread("ceshi.jpg");if (src.empty()) {cout << "Could not read the source image" << endl;return -1;}// 设置缩放比例double scale = 1.5;// 计算缩放矩阵Mat scale_mat = (Mat_<double>(2, 3) << scale, 0, 0, 0, scale, 0);// 进行仿射变换Mat dst;warpAffine(src, dst, scale_mat, src.size());// 显示原始图像和变换后的图像imshow("Source Image", src);imshow("Affine Transformed Image", dst);imwrite("fangda.jpg", dst);waitKey(0);return 0;
}

放大的效果如下图所示。
在这里插入图片描述
相当于把图像一部分放大,但显示的内容比原图就少了很多,因为图像的总体大小没有改变。

旋转

原理

在这里插入图片描述
在这里插入图片描述

此处,以原点(0,0)坐标为中心进行,α 是旋转角度。
旋转矩阵的元素是由旋转角度和旋转中心计算得出的。假设我们有一个二维坐标系,其中原点是旋转中心,x轴正方向是右方,y轴正方向是上方。现在我们要将点 (x, y) 绕原点逆时针旋转θ角度,那么旋转矩阵可以表示为:

[ cosα, -sinα, 0 ]
[ sinα,  cosα, 0 ]
其中,cosθ 和 sinθ 是使用弧度值计算得出的。

也可以是如下公式:
在这里插入图片描述

若为上述A变换矩阵,则当α 为正时,旋转方向为顺时针;当 α 为负时,旋转方向为逆时针。
同时,另外一种情况与此相反,如变换矩阵为:

[ cosα, sinα, 0 ]
[ -sinα,  cosα, 0 ]

在这里插入图片描述
则当α 为正时,旋转方向为逆时针;当 α 为负时,旋转方向为顺时针。
此外,也可以控制α 的值,如α 为45和为-45逆顺情况是相反的。

下面进行两个示例,分别进行顺时针和逆时针旋转。

顺时针示例

#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;int main() {Mat src = imread("ceshi.jpg");if (src.empty()) {cout << "Could not read the source image" << endl;return -1;}// 设置旋转角度double angle = 45;// 计算旋转矩阵Mat rot_mat = (Mat_<double>(2, 3) << cos(angle), -sin(angle), sin(angle), cos(angle), 1, 0);cout<< cos(angle) <<endl;cout << -sin(angle) << endl;cout << sin(angle) << endl;//rot_mat = rot_mat * 100; // 设置旋转中心点,这里将图像中心设置为旋转中心// 进行仿射变换Mat dst;warpAffine(src, dst, rot_mat, src.size());// 显示原始图像和变换后的图像imshow("Source Image", src);imshow("Affine Transformed Image", dst);imwrite("shunshizhen.jpg", dst);waitKey(0);return 0;
}

上述代码中设置的double angle = 45;为旋转角度,45为正数,所以是逆时针旋转,同时,(Mat_<double>(2, 3) << cos(angle), -sin(angle), sin(angle), cos(angle), 1, 0);转换矩阵表示当α 为正时,旋转方向为顺时针;当 α 为负时,旋转方向为逆时针。
旋转结果为:
在这里插入图片描述

逆时针示例

(1)通过改变 α的值,如将上面顺时针的45变为-45.
(2)改变变换矩阵的顺序,如使用Mat rot_mat = (Mat_<double>(2, 3) << cos(angle), sin(angle), -sin(angle), cos(angle), 1, 0);替换上面代码中的转换矩阵。
以原点(0,0)为中心,逆时针旋转45度效果图如下:
在这里插入图片描述

总结

我们可以看出,在使用warpAffine()函数是比较方便的,通过使用定义2行3列和通过上面的例子,可以快速高效的实现平移、缩放和旋转等功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/147467.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV技术应用(3)— 把.png图像保存为.jpg图像

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。本节课就手把手教你如何把.png图像保存为.jpg图像&#xff0c;希望大家学习之后能够有所收获~&#xff01;&#x1f308; 目录 &#x1f680;1.技术介绍 &#x1f680;2.实现代码 &#x1f680;1.技术介绍 如果在电脑某…

【SQL server】 表结构的约束和维护

表结构的约束和维护 修改表结构 (1)添加列 (2)删除列 (3)修改列alter table 表名 add 新列名 数据类型给员工表添加一列邮箱 alter table People add PeopleMail varchar(200)删除列 alter table People drop column PeopleMain修改列 alter table 表名 alter column 列名 数据…

【vue实战项目】通用管理系统:首页

前言 本文为博主的vue实战小项目系列中的第三篇&#xff0c;很适合后端或者才入门的小伙伴看&#xff0c;一个前端项目从0到1的保姆级教学。前面的内容&#xff1a; 【vue实战项目】通用管理系统&#xff1a;登录页-CSDN博客 【vue实战项目】通用管理系统&#xff1a;封装to…

学习css过渡动画-transition

文章目录 前言transition属性语法宽度改变效果透明度改变效果位置改变效果如有启发&#xff0c;可点赞收藏哟~ 前言 通常&#xff0c;当一个元素的样式属性值发生变化时&#xff0c;会立即看到页面发生变化。 css属性transition能让页面元素不是立即的、而是慢慢的从一种状态变…

Flink1.17 DataStream API

目录 一.执行环境&#xff08;Execution Environment&#xff09; 1.1 创建执行环境 1.2 执行模式 1.3 触发程序执行 二.源算子&#xff08;Source&#xff09; 2.1 从集合中读取数据 2.2 从文件读取数据 2.3 从 RabbitMQ 中读取数据 2.4 从数据生成器读取数据 2.5 …

计算机毕业设计选题推荐-二手交易跳蚤市场微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

ubuntu下载conda

系统&#xff1a;Ubuntu18.04 &#xff08;1&#xff09;下载安装包 wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-2021.11-Linux-x86_64.sh 报错错误 403&#xff1a;Forbidden 解决方法 wget -U NoSuchBrowser/1.0 https://mirrors.tuna.tsingh…

Smart Tomcat的使用

文章目录 Smart Tomcat的作用Smart Tomcat的安装Smart Tomcat的配置Smart Tomcat的启动 Smart Tomcat的作用 我们知道使用Servlet来完成一个项目一共需要七个步骤&#xff0c;即创建maven项目、添加依赖、创建目录结构、编写代码、打包程序、部署程序、验证程序。这样的确是完…

让资产权利归于建设者:Kiosk使过程变得更简单

区块链凭借着其将人的权利地位置于平台之上的能力&#xff0c;可以重塑互联网&#xff0c;而自托管为个人提供了控制和管理其资产和数据的能力。链上交易支持建设者和客户之间的点对点交易。这些特质联合起来&#xff0c;可以将数字世界从基于价值提取的模式转变为基于价值创造…

应用场景丨迭代市政综合管廊监测系统建设

市政综合管廊是指在城市地下建造的隧道空间&#xff0c;将市政、电力、通讯、燃气、给排水等各种管线集于一体&#xff0c;实施统一规划、设计、建设和管理。综合管廊有利于解决反复开挖路面、架空线网密集、管线事故频发等问题&#xff0c;是保障城市运行的重要基础设施和“生…

V10 桌面版、服务器版系统加固

V10 桌面版、服务器版系统加固 一、 文档说明 本文档中涉及的加固方法主要包括&#xff1a;密码策略配置、防火墙规 则配置、禁用高风险服务等。 二、 V10 桌面版系统加固 2.1 密码策略配置 密码策略包括密码老化控制策略和密码复杂度策略。密码老化 控制策略需要配置/etc…

【C++入门到精通】右值引用 | 完美转发 C++11 [ C++入门 ]

阅读导航 引言一、左值引用和右值引用1. 什么是左值&#xff1f;什么是左值引用&#xff1f;2. 什么是右值&#xff1f;什么是右值引用&#xff1f;3. move( )函数 二、左值引用与右值引用比较三、右值引用使用场景和意义四、完美转发std::forward 函数完美转发实际中的使用场景…

【LearnOpenGL基础入门——3】绘制纯色三角形

目录 一.写在前面 二.顶点输入 三.顶点着色器 四.编译着色器 五.片段着色器 六.着色器程序 七.链接顶点属性 彩蛋 一.写在前面 我们先认识一下OpenGL常用的几个名词&#xff1a; 顶点数组对象&#xff1a;Vertex Array Object&#xff0c;VAO顶点缓冲对象&#xff1a;…

2023全新付费进群系统源码 带定位完整版 附教程

这源码是我付费花钱买的分享给大家&#xff0c;功能完整。 搭建教程 Nginx1.2 PHP5.6-7.2均可 最好是7.2 第一步上传文件程序到网站根目录解压 第二步导入数据库&#xff08;58soho.cn.sql&#xff09; 第三步修改/config/database.php里面的数据库地址 第四步修改/conf…

linux配置固定ip(两种方法)

首先刚下载的vm&#xff0c;刚创建的虚拟机&#xff0c;肯定是需要配置ip的 其次以前我的每次都是设置自动ip&#xff0c;这样每次登录都会自动获取ip地址&#xff0c;并且每次的ip都不相同。 ~方法&#xff1a; 开机登陆后 1)Cd /etc/sysconfig/network-scripts 2)Vi ifcf…

Elasticsearch备份与还原:使用elasticdump

在数据管理的世界里&#xff0c;备份和还原数据是重中之重的日常工作&#xff0c;特别是对于Elasticsearch这样的强大而复杂的搜索引擎。备份不仅可以用于灾难恢复&#xff0c;还可以在数据迁移、测试或者升级等场景中发挥重要作用。 在本博客中&#xff0c;我们将会重点介绍如…

轻量级 Java 日志组件

日志记录功能在开发中很常用&#xff0c;不仅可以记录程序运行的细节&#xff0c;方便调试&#xff0c;也可以记录用户的行为&#xff0c;是框架中不可或缺的组件。为最大程度复用现有的组件&#xff0c;我们就地取材使用了 JDK 自带的 JUL&#xff08;java.util.logging&#…

聚观早报 |联想集团Q2财季业绩;小鹏汽车Q3营收

【聚观365】11月17日消息 联想集团Q2财季业绩 小鹏汽车Q3营收 微软发布两款自研AI芯片 FAA批准SpaceX再次发射星际飞船 2023 OPPO开发者大会 联想集团Q2财季业绩 全球数字经济领导企业联想集团公布截至2023年9月30日的2023/24财年第二财季业绩&#xff1a;整体营收达到10…

微信小程序H5 uniapp

最近微信小程序对有视频播放的审核严&#xff0c;需要提供“文娱类资质”。而申请这个资质比较繁琐。所以我们在小程序上用web-view做跳转到H5&#xff0c;H5使用uniapp编写。这是小程序关于web-view文档说明。https://developers.weixin.qq.com/miniprogram/dev/component/web…

硬件工程师基础能力课

第一课时--基本定理、电阻、电容等 首先了解下面几个概念&#xff0c;基尔霍夫定理&#xff1a;KCL & KVL&#xff0c;叠加定理&#xff0c;戴维南定理&#xff08;电压源等效&#xff09;和诺顿定理&#xff08;电流源等效&#xff09;、奈奎斯特采样定理。 上面说的这些东…