7.2图像旋转
实验原理
在OpenCV中,图像旋转也是一种常见的几何变换,它可以用来调整图像的方向。图像旋转通常涉及绕着图像中心点旋转一定角度的操作。与图像平移类似,旋转也可以通过仿射变换来实现,但是旋转需要使用到旋转矩阵来定义旋转的角度和旋转中心。
图像旋转的原理
图像旋转通常需要两步完成:
1. 构建旋转矩阵:
根据旋转中心点和旋转角度构建一个2x3的仿射变换矩阵。
为了旋转图像,你需要创建一个旋转矩阵,该矩阵描述了旋转的角度和旋转中心。在OpenCV中,旋转矩阵是一个2x3的矩阵,可以使用 cv::getRotationMatrix2D 函数来创建。该函数接收三个参数:旋转中心、旋转角度和缩放因子。
2. 应用旋转变换:
使用cv::warpAffine函数将这个矩阵应用于图像。这与平移操作非常相似,只是使用的变换矩阵不同。
函数原型
cv::getRotationMatrix2D 是 OpenCV 库中的一个函数,用于构建一个 2x3 的仿射变换矩阵,该矩阵可以用于绕给定点(通常是图像的中心点)旋转图像。此函数非常适合于实现图像的旋转操作。
cv::Mat getRotationMatrix2D(Point2f center, // 旋转中心点double angle, // 旋转角度,顺时针为负,逆时针为正double scale // 缩放因子,默认为1(无缩放)
);参数说明
center: cv::Point2f 类型,指定了旋转的中心点。通常设置为图像的中心点 (width / 2.0, height / 2.0)。
angle: double 类型,指定了旋转的角度,单位是度。正值表示逆时针旋转,负值表示顺时针旋转。
scale: double 类型,指定了旋转后的图像相对于原图像的缩放比例。默认值为 1.0,表示不进行缩放。如果设置为小于 1 的值,则图像会缩小;如果设置为大于 1 的值,则图像会放大。
如何使用
使用 cv::getRotationMatrix2D 获取到旋转矩阵之后,你可以通过 cv::warpAffine 函数将该矩阵应用于图像,从而实现图像的旋转。
示例代码1
下面是一个详细的示例代码,展示了如何使用 cv::getRotationMatrix2D 和 cv::warpAffine 来旋转图像:
#include "pch.h"
#include <opencv2/opencv.hpp>
#include <iostream>int main()
{// 加载图像cv::Mat img = cv::imread("D1.png");if (img.empty()){std::cout << "Error: Image not found." << std::endl;return -1;}// 获取图像的尺寸int width = img.cols;int height = img.rows;// 定义旋转中心点(图像中心)cv::Point2f center(width / 2.0, height / 2.0);// 定义旋转角度和缩放因子double angle = 60; // 旋转60度double scale = 1.0; // 不缩放// 创建旋转矩阵cv::Mat rotationMatrix = cv::getRotationMatrix2D(center, angle, scale);// 计算旋转后的图像大小(可选,如果需要保持原图大小可以省略)cv::Size dsize = cv::Size(img.cols, img.rows);// 应用旋转变换cv::Mat rotatedImg;cv::warpAffine(img, rotatedImg, rotationMatrix, dsize);// 显示原图和旋转后的图像cv::namedWindow("Original Image", cv::WINDOW_NORMAL);cv::imshow("Original Image", img);cv::namedWindow("Rotated Image", cv::WINDOW_NORMAL);cv::imshow("Rotated Image", rotatedImg);cv::waitKey(0);return 0;
}在这个示例中,我们首先加载了一张图像,并计算了图像的中心点。接着,我们定义了旋转的角度(60度)和缩放因子(1.0)。使用 cv::getRotationMatrix2D 构建了旋转矩阵,并将其应用于图像,最终得到了旋转后的图像。请注意,旋转可能会导致图像的一部分超出边界,此时可以调整 dsize 或者使用不同的 borderMode 来处理边界情况。
运行结果1
总结
图像旋转是通过仿射变换来实现的,它涉及到将图像绕着一个点旋转一定角度。在OpenCV中,可以通过cv::getRotationMatrix2D 构建一个2x3的仿射变换矩阵,并使用cv::warpAffine函数来实现图像的旋转。通过调整旋转角度和旋转中心点,可以控制图像的旋转效果。此外,还可以通过指定不同的边界处理方式来处理旋转后超出原图像范围的情况。
示例代码2
示例代码下面是一个使用OpenCV和C++实现图像旋转的示例代码:
#include "pch.h"
#include <opencv2/opencv.hpp>
#include <iostream>int main(int argc, char** argv)
{// 读取图像cv::Mat src = cv::imread("D3.png", cv::IMREAD_COLOR);if (src.empty()){std::cout << "Error opening image" << std::endl;return -1;}// 创建输出图像cv::Mat dst;// 定义旋转参数double angleInDegrees = 45; // 旋转角度(度)cv::Point center(src.cols / 2.0, src.rows / 2.0); // 旋转中心点// 计算旋转矩阵double angleInRadians = angleInDegrees * (CV_PI / 180.0); // 将角度转换为弧度double cosTheta = std::cos(angleInRadians);double sinTheta = std::sin(angleInRadians);cv::Mat rotationMatrix = (cv::Mat_<double>(2, 3) <<cosTheta, -sinTheta, 0,sinTheta, cosTheta, 0);// 更新平移部分rotationMatrix.at<double>(0, 2) = center.x - (center.x * cosTheta - center.y * sinTheta);rotationMatrix.at<double>(1, 2) = center.y - (center.x * sinTheta + center.y * cosTheta);// 应用仿射变换cv::warpAffine(src, dst, rotationMatrix, src.size());// 显示结果cv::namedWindow("Original Image", cv::WINDOW_NORMAL);cv::imshow("Original Image", src);cv::namedWindow("Rotated Image", cv::WINDOW_NORMAL);cv::imshow("Rotated Image", dst);cv::waitKey(0);return 0;
}代码解释
1. 读取图像:使用cv::imread读取输入图像,并确保它是彩色图像。
2. 创建输出图像:创建一个新的cv::Mat对象来存储旋转后的结果。
3. 定义旋转参数:定义旋转的角度和旋转中心点。
4. 计算旋转矩阵:根据旋转角度和旋转中心点计算旋转矩阵。首先将角度转换为弧度,然后根据旋转矩阵的定义构建矩阵。
5. 更新平移部分:为了使图像绕着旋转中心点旋转,需要更新旋转矩阵中的平移部分。
6. 应用仿射变换:使用cv::warpAffine函数对图像进行旋转变换。
7. 显示结果:使用cv::imshow函数显示原始图像和旋转后的图像,并等待用户按键退出。
运行结果2
示例代码3
7.3函数实现图像旋转
// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include "pch.h"
#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
//#pragma comment(lib, "opencv_world450d.lib") //引用引入库 // 图像旋转
void Rotate(const Mat &srcImage, Mat &destImage, double angle)//angle表示要旋转的角度
{Point2f center(srcImage.cols / 2, srcImage.rows / 2);//中心Mat M = getRotationMatrix2D(center, angle, 1);//计算旋转的仿射变换矩阵 warpAffine(srcImage, destImage, M, Size(srcImage.cols, srcImage.rows));//仿射变换 circle(destImage, center, 2, Scalar(255, 0, 0));
}int main()
{//读入图像,并判断图像是否读入正确cv::Mat srcImage = imread("02.jpeg");if (!srcImage.data){puts("打开图像文件失败");return -1;}namedWindow("原图", WINDOW_NORMAL);imshow("原图", srcImage);//将图片按比例缩放至宽为250像素的大小Mat destImage;double angle = 9.9;//角度Rotate(srcImage, destImage, angle);namedWindow("旋转图", WINDOW_NORMAL);imshow("旋转图", destImage);waitKey(0);return 0;
}
运行结果3
示例代码4
7.2手工实现旋转
// test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include "pch.h"
#include <iostream>#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <string>
#include <cmath>using namespace cv;//#pragma comment(lib, "opencv_world450d.lib") //引用引入库 Mat imgRotate(Mat matSrc, float angle, bool direction)
{float theta = angle * CV_PI / 180.0;int nRowsSrc = matSrc.rows;int nColsSrc = matSrc.cols;// 如果是顺时针旋转if (!direction)theta = 2 * CV_PI - theta;// 全部以逆时针旋转来计算// 逆时针旋转矩阵float matRotate[3][3]{{std::cos(theta), -std::sin(theta), 0},{std::sin(theta), std::cos(theta), 0 },{0, 0, 1}};float pt[3][2]{{ 0, nRowsSrc },{nColsSrc, nRowsSrc},{nColsSrc, 0}};for (int i = 0; i < 3; i++){float x = pt[i][0] * matRotate[0][0] + pt[i][1] * matRotate[1][0];float y = pt[i][0] * matRotate[0][1] + pt[i][1] * matRotate[1][1];pt[i][0] = x;pt[i][1] = y;}// 计算出旋转后图像的极值点和尺寸float fMin_x = min(min(min(pt[0][0], pt[1][0]), pt[2][0]), (float)0.0);float fMin_y = min(min(min(pt[0][1], pt[1][1]), pt[2][1]), (float)0.0);float fMax_x = max(max(max(pt[0][0], pt[1][0]), pt[2][0]), (float)0.0);float fMax_y = max(max(max(pt[0][1], pt[1][1]), pt[2][1]), (float)0.0);int nRows = cvRound(fMax_y - fMin_y + 0.5) + 1;int nCols = cvRound(fMax_x - fMin_x + 0.5) + 1;int nMin_x = cvRound(fMin_x + 0.5);int nMin_y = cvRound(fMin_y + 0.5);// 拷贝输出图像Mat matRet(nRows, nCols, matSrc.type(), Scalar(0));for (int j = 0; j < nRows; j++){for (int i = 0; i < nCols; i++){// 计算出输出图像在原图像中的对应点的坐标,然后复制该坐标的灰度值// 因为是逆时针转换,所以这里映射到原图像的时候可以看成是,输出图像// 到顺时针旋转到原图像的,而顺时针旋转矩阵刚好是逆时针旋转矩阵的转置// 同时还要考虑到要把旋转后的图像的左上角移动到坐标原点。int x = (i + nMin_x) * matRotate[0][0] + (j + nMin_y) * matRotate[0][1];int y = (i + nMin_x) * matRotate[1][0] + (j + nMin_y) * matRotate[1][1];if (x >= 0 && x < nColsSrc && y >= 0 && y < nRowsSrc){matRet.at<Vec3b>(j, i) = matSrc.at<Vec3b>(y, x);}}}return matRet;
}int main(){Mat matSrc = imread("2.jpeg");if (matSrc.empty())return 1;float angle = 30;Mat matRet = imgRotate(matSrc, angle, true);namedWindow("原图", WINDOW_NORMAL);imshow("原图", matSrc);namedWindow("旋转图", WINDOW_NORMAL);imshow("旋转图", matRet);// 保存图像imwrite("rotate.jpg", matRet);waitKey();return 0;}
手工实现旋转
相关文章:

7.2图像旋转
实验原理 在OpenCV中,图像旋转也是一种常见的几何变换,它可以用来调整图像的方向。图像旋转通常涉及绕着图像中心点旋转一定角度的操作。与图像平移类似,旋转也可以通过仿射变换来实现,但是旋转需要使用到旋转矩阵来定义旋转的角…...

学学vue-2
1.7 指令修饰符 keyup.enter:监听键盘回车事件,回车触发事件keyup.enter代码 v-model修饰符: v-model.trim:去首尾空格v-model.number:变数字(如果是数字的话,转变为数字) 事件名.…...

什么是 Grafana?
什么是 Grafana? Grafana 是一个功能强大的开源平台,用于创建、查看、查询和分析来自多个来源的数据。通过可视化仪表盘(Dashboard),它能够帮助用户监控实时数据、生成历史报告,甚至进行预测分析。Grafana…...

【Prompt Engineering:思维树 (ToT)、检索增强生成 (RAG)、自动推理并使用工具 (ART)】
思维树 (ToT) 对于需要探索或预判战略的复杂任务来说,传统或简单的提示技巧是不够的。最近,Yao et el. (2023)(opens in a new tab) 提出了思维树(Tree of Thoughts,ToT)框架,该框架基于思维链提示进行了总…...
【习题】应用/元服务上架
判断题 1. 一个完整的发布软件包必须包含一个Profile文件。 A、正确(True) B、错误(False) 2. 编译打包的软件包存放在项目目录build > outputs > default下。 A、正确(True) B、错误(False) 单选题 1. 创建应用时,应用包名需要和在DevEco …...

性能测试的复习3-jmeter的断言、参数化、提取器
一、断言、参数化、提取器 需求: 提取查天气获取城市名请求的响应结果:城市对查天气获取城市名的响应结果进行响应断言和json断言对查天气获取城市名添加用户参数 1、步骤 查看天气获取城市名 json提取器(对响应结果提取、另一个接口请求…...

ORB-SLAM2关键点总结
1.ORB-SLAM2的总体框架是怎样的 ORB-SLAM2一共有三个线程,分别是Tracking、Local Mapping、Loop Closing线程,,其中Tracking负责完成关键点提取,并进行帧间匹配,同时初步选取关键帧;Local Mapping线程主要…...

拱式桥安全结构健康监测解决方案
拱式桥作为一种常见的桥梁结构,其拱形设计不仅美观,还具有较高的承载能力。然而,随着使用年限的增加和环境因素的影响,拱式桥的结构健康和稳定需要持续监测和评估。自动化监测技术的应用,可以提升拱式桥的监测效率和准…...

windows和linux安装mysql5.7.31保姆级教程
一,资源如下,里面有windows和linux版的安装软件,内含Visual C2013中文版windows系统插件 windows资源地址:https://download.csdn.net/download/l1o3v1e4ding/89725150 linux(centos)资源地址:…...
如何使用 PowerShell 脚本来自动化 Windows 开发流程的教程(包括理论介绍和实践示例)
PowerShell 是一种强大的任务自动化和配置管理框架,它为系统管理员和开发人员提供了管理 Windows 操作系统和应用程序的能力。下面是一个关于如何使用 PowerShell 脚本来自动化 Windows 开发流程的教程,包括理论介绍和实践示例。 第一部分:理…...

CTFHub技能树-信息泄露-HG泄漏
目录 漏洞产生原因 解题过程 当开发人员使用 Mercurial 进行版本控制,对站点自动部署。如果配置不当,可能会将.hg 文件夹直接部署到线上环境。这就引起了 hg 泄露漏洞。 漏洞产生原因 Mercurial(hg)是一种分布式版本控制系统,它与Git类似也可以用于管…...
OpenCV结构分析与形状描述符(18)比较两个轮廓相似度的函数matchShapes()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 比较两个形状。 该函数用于比较两个形状。所有三个实现的方法都使用了 Hu 不变矩(参见 HuMoments) 函数原型 double c…...

CCS811二氧化碳传感器详解(STM32)
目录 一、介绍 二、传感器原理 1.原理图 2.引脚描述 3.工作原理介绍 三、程序设计 main.c文件 ccs811.h文件 ccs811.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 CCS811模块是一种气体传感器,可以测量环境中TVOC(总挥发性有机物质)浓度和eCO2…...

Navicat 17 新特性 | 聚焦 MongoDB
随着 Navicat 17 的盛大发布,其一系列创新特性赢得了广大用户的热烈反响。它不仅在模型设计上实现了突破性优化,提升了查询与配置的效率,还大幅优化了用户界面的交互体验,原生支持国产平台与操作系统,同时增强 BI 能力…...

openssl的使用
1、编译 Github下载:https://github.com/openssl/openssl 官网下载:https://openssl-library.org/source/index.html 官网历史版本:https://www.openssl.org/source/old/ 1.1 Windows下编译 我的文章:OPC UA使用 Openssl库编译…...

ICETEK-DM6437-AICOM—— DMA直接存储器访问设计
#一、设计目的: 1 进一步了解 ICETEK-DM6437-AF 的内部存储器空间的分配及指令寻址方式: 内部存储器空间分配:研究 ICETEK-DM6437-AF 的存储器架构,包括但不限于片内 SRAM、片外 DRAM 和其他存储器模块。了解这些存储器的大小、起…...
【AcWing】快速排序的Go实现
快速排序的Go实现 这一部分参考了AcWing当中使用Go语言实现快速排序的题解:https://www.acwing.com/activity/content/code/content/296206/。 其中有很多部分非常值得参考,故写一个博客进行记录。 Code package mainimport "fmt"func qui…...
使用C++11的`std::future`和`std::promise`实现异步网络通信
使用C11的std::future和std::promise实现异步网络通信 在现代C编程中,异步编程是一个重要的主题。C11引入了std::future和std::promise,为异步编程提供了强大的工具。本文将详细介绍如何使用std::future和std::promise实现异步网络通信,并提…...

【C++登堂入室】类与对象(上)
目录 一、面向过程和面向对象初步认识 二、类的引入 三、类的定义 四、类的访问限定符及封装 4.1 访问限定符 4.2 封装 五、类的作用域 六、类的实例化 七、类对象模型 7.1如何计算类对象的大小 7.2 类对象的存储方式猜测 7.3 结构体内存对齐规则 八、this指针 …...

【西电电装实习】5. 无人机模块及作用、上位机的操作
文章目录 前言一、硬件结构电源、电源电压测试电路晶振外围陀螺仪信号放大电路及天线空心杯(电极)驱动电路 软件设置整机装配PID 参数设置公式 参考文献 前言 西电电装实习,无人机原理图、上位机的调节方法 一、硬件结构 电源、电源电压测试…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...