【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列和通过上面的例子,可以快速高效的实现平移、缩放和旋转等功能。
相关文章:

【C++】【Opencv】cv::warpAffine()仿射变换函数详解,实现平移、缩放和旋转等功能
仿射变换是一种二维变换,它可以将一个二维图形映射到另一个二维图形上,保持了图形的“形状”和“大小”不变,但可能会改变图形的方向和位置。仿射变换可以用一个线性变换矩阵来表示,该矩阵包含了六个参数,可以进行平移…...
WPF实现右键菜单
在WPF中,创建上下文菜单(通常称为“右键菜单”)是通过使用ContextMenu控件来实现的。你可以在XAML中声明上下文菜单,并将其关联到任何FrameworkElement。以下是如何在WPF中实现上下文菜单的基本步骤: 1. 在XAML中定义…...

Java智慧工地SaaS管理平台源码:AI/云计算/物联网
智慧工地是指运用信息化手段,围绕施工过程管理,建立互联协同、智能生产、科学管理的施工项目信息化生态圈,并将此数据在虚拟现实环境下与物联网采集到的工程信息进行数据挖掘分析,提供过程趋势预测及专家预案,实现工程…...

【漏洞复现】通达oa 前台sql注入
漏洞描述 通达OA(Office Automation)是一款企业级协同办公软件,旨在为企业提供高效、便捷、安全、可控的办公环境。它涵盖了企业日常办公所需的各项功能,包括人事管理、财务管理、采购管理、销售管理、库存管理、生产管理、办公自动化等。通达OA支持PC端和移动端使用,可以…...

机器学习笔记 - Ocr识别中的文本检测EAST网络概述
一、文本检测 文本检测简单来说就是找到图像中可以出现文本的区域。例如,请参见下图,其中在检测到的文本周围绘制了绿色边框。 在进行文本检测时,你可能会遇到两种情况 具有结构化文本的图像:这是指具有干净/均匀背景和常规字体的图像。文本大多密集,行结构正确,…...

【SQL server】数据库、数据表的创建
创建数据库 --如果存在就删除 --所有的数据库都存在sys.databases当中 if exists(select * from sys.databases where name DBTEST)drop database DBTEST--创建数据库 else create database DBTEST on --数据文件 (nameDBTEST,--逻辑名称 字符串用单引号filenameD:\DATA\DBT…...
vue的生命周期分别是什么?
Vue的生命周期分为8个阶段,分别是: beforeCreate:实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。 created:实例已经创建完成后被调用,这时候实例已完成以下的配置&#…...

Java拼图游戏
运行出的游戏界面如下: 按住A不松开,显示完整图片;松开A显示随机打乱的图片。 User类 package domain;/*** ClassName: User* Author: Kox* Data: 2023/2/2* Sketch:*/ public class User {private String username;private String password…...
Vue框架的element组件table文字居中
1.直接上代码 <el-table max-height"500px" :data"datas.roles" style"width: 100%" border :header-cell-style"{textAlign: center}" :cell-style"{ textAlign: center }"><el-table-column prop"id" …...

科技创新 共铸典范 | 江西卫健办邓敏、飞图影像董事长洪诗诗一行到访拓世科技集团,提振公共卫生事业发展
2023年11月15日,拓世科技集团总部迎来了江西省卫健项目办项目负责人邓敏、江西飞图影像科技有限公司董事长洪诗诗一行的考察参观,集团董事长李火亮、集团高级副总裁方高强进行热情接待。此次多方交流,旨在共同探讨携手合作,激发科…...

Linux安装OpenCV并配置VSCode环境
Linux安装OpenCV并配置VSCode环境 安装OpenCV环境安装必需工具下载并解压OpenCV库(Opencv Core Modules和opencv_contrib)创建构建目录,进行构建验证构建结果安装验证安装结果 配置VSCode环境创建项目文件修改配置信息执行程序 安装环境 Ubun…...
Django(ORM事务操作|ORM常见字段类型|ORM常见字段参数|关系字段|Meta元信息)
文章目录 ORM事务操作什么是事务?事务的产生事务的四大特征ORM中如何使用事务 ORM字段类型常用字段与不常用字段类型ORM还支持用户自定义字段类型 ORM字段参数关系字段ForeignKey外键on_delete参数设置的值 OneToOneField与ForeignKey的区别多对多关系建立的方式ORM…...

【mujoco】Ubuntu20.04配置mujoco210
【mujoco】Ubuntu20.04配置mujoco210 文章目录 【mujoco】Ubuntu20.04配置mujoco2101. 安装mujoco2102. 安装mujoco-py3.使用render时报错Reference 本文简要介绍一下如何在ubuntu20.04系统中配置mujoco210,用于强化学习。 1. 安装mujoco210 在官方资源里找到http…...
【洛谷 P3853】[TJOI2007] 路标设置 题解(二分答案+循环)
[TJOI2007] 路标设置 题目背景 B 市和 T 市之间有一条长长的高速公路,这条公路的某些地方设有路标,但是大家都感觉路标设得太少了,相邻两个路标之间往往隔着相当长的一段距离。为了便于研究这个问题,我们把公路上相邻路标的最大…...

蓝桥杯 vector
vector的定义和特性 注意:vector需要开C11标准 vector的常用函数 push_back():将元素添加到vector末尾 pop_back():删除vector末尾的元素 begin()和end():返回指向vector第一个元素和最后一个元素之后一个位置的迭代器。 示例 vector<int> vec{10,20,30};f…...
ai绘画部署教程
在部署AI绘画Web环境的过程中,你提供了一些关键步骤。以下是一些详细说明: 1. 克隆webui 首先,通过以下命令从GitHub上克隆webui的代码: git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui 这将下载webui的源…...
策略模式的应用——应对频繁的需求变更
秋招结束后,间接性堕落了一段时间,学习几乎停止下来了。内心甚是焦灼,感觉生活很无趣!为了在参加工作后能够快速上手和成为一名优秀的中级开发者,从这篇文章开始将不断学习优秀的编码经验,学习是永无止境的…...

qt-C++笔记之treeWidget初次使用
qt-C笔记之treeWidget初次使用 code review! 文章目录 qt-C笔记之treeWidget初次使用1.运行2.文件结构3.main.cpp4.widget.h5.widget.cpp6.widget.ui7.main.qrc8.qt_widget_test.pro9.options.png 1.运行 2.文件结构 3.main.cpp 代码 #include "widget.h"#include…...

SQL零基础入门教程,贼拉详细!贼拉简单! 速通数据库期末考!(八)
FULL OUTER JOIN 除了前面讲到的 INNER JOIN(内连接)、LEFT JOIN(左连接)、RIGHT JOIN(右连接),还有另外一种关联方式,即 FULL OUTER JOIN(全外连接) FULL O…...
C语言编程陷阱(八)
陷阱36:不要使用指针作为函数的返回值 有时候,我们可能想要用一个函数来返回一个指针,比如返回一个动态分配的内存,或者返回一个数组的某个元素的地址。但是,如果我们不小心,我们可能会犯一个很常见的错误,就是返回一个局部变量的地址。例如,看看下面的代码: #inclu…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...