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

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中&#xff0c;图像旋转也是一种常见的几何变换&#xff0c;它可以用来调整图像的方向。图像旋转通常涉及绕着图像中心点旋转一定角度的操作。与图像平移类似&#xff0c;旋转也可以通过仿射变换来实现&#xff0c;但是旋转需要使用到旋转矩阵来定义旋转的角…...

学学vue-2

1.7 指令修饰符 keyup.enter&#xff1a;监听键盘回车事件&#xff0c;回车触发事件keyup.enter代码 v-model修饰符&#xff1a; v-model.trim&#xff1a;去首尾空格v-model.number&#xff1a;变数字&#xff08;如果是数字的话&#xff0c;转变为数字&#xff09; 事件名.…...

什么是 Grafana?

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

【Prompt Engineering:思维树 (ToT)、检索增强生成 (RAG)、自动推理并使用工具 (ART)】

思维树 (ToT) 对于需要探索或预判战略的复杂任务来说&#xff0c;传统或简单的提示技巧是不够的。最近&#xff0c;Yao et el. (2023)(opens in a new tab) 提出了思维树&#xff08;Tree of Thoughts&#xff0c;ToT&#xff09;框架&#xff0c;该框架基于思维链提示进行了总…...

【习题】应用/元服务上架

判断题 1. 一个完整的发布软件包必须包含一个Profile文件。 A、正确(True) B、错误(False) 2. 编译打包的软件包存放在项目目录build > outputs > default下。 A、正确(True) B、错误(False) 单选题 1. 创建应用时&#xff0c;应用包名需要和在DevEco …...

性能测试的复习3-jmeter的断言、参数化、提取器

一、断言、参数化、提取器 需求&#xff1a; 提取查天气获取城市名请求的响应结果&#xff1a;城市对查天气获取城市名的响应结果进行响应断言和json断言对查天气获取城市名添加用户参数 1、步骤 查看天气获取城市名 json提取器&#xff08;对响应结果提取、另一个接口请求…...

ORB-SLAM2关键点总结

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

拱式桥安全结构健康监测解决方案

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

windows和linux安装mysql5.7.31保姆级教程

一&#xff0c;资源如下&#xff0c;里面有windows和linux版的安装软件&#xff0c;内含Visual C2013中文版windows系统插件 windows资源地址&#xff1a;https://download.csdn.net/download/l1o3v1e4ding/89725150 linux&#xff08;centos&#xff09;资源地址&#xff1a;…...

如何使用 PowerShell 脚本来自动化 Windows 开发流程的教程(包括理论介绍和实践示例)

PowerShell 是一种强大的任务自动化和配置管理框架&#xff0c;它为系统管理员和开发人员提供了管理 Windows 操作系统和应用程序的能力。下面是一个关于如何使用 PowerShell 脚本来自动化 Windows 开发流程的教程&#xff0c;包括理论介绍和实践示例。 第一部分&#xff1a;理…...

CTFHub技能树-信息泄露-HG泄漏

目录 漏洞产生原因 解题过程 当开发人员使用 Mercurial 进行版本控制&#xff0c;对站点自动部署。如果配置不当,可能会将.hg 文件夹直接部署到线上环境。这就引起了 hg 泄露漏洞。 漏洞产生原因 Mercurial(hg)是一种分布式版本控制系统&#xff0c;它与Git类似也可以用于管…...

OpenCV结构分析与形状描述符(18)比较两个轮廓相似度的函数matchShapes()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 比较两个形状。 该函数用于比较两个形状。所有三个实现的方法都使用了 Hu 不变矩&#xff08;参见 HuMoments&#xff09; 函数原型 double c…...

CCS811二氧化碳传感器详解(STM32)

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

Navicat 17 新特性 | 聚焦 MongoDB

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

openssl的使用

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

ICETEK-DM6437-AICOM—— DMA直接存储器访问设计

#一、设计目的&#xff1a; 1 进一步了解 ICETEK-DM6437-AF 的内部存储器空间的分配及指令寻址方式&#xff1a; 内部存储器空间分配&#xff1a;研究 ICETEK-DM6437-AF 的存储器架构&#xff0c;包括但不限于片内 SRAM、片外 DRAM 和其他存储器模块。了解这些存储器的大小、起…...

【AcWing】快速排序的Go实现

快速排序的Go实现 这一部分参考了AcWing当中使用Go语言实现快速排序的题解&#xff1a;https://www.acwing.com/activity/content/code/content/296206/。 其中有很多部分非常值得参考&#xff0c;故写一个博客进行记录。 Code package mainimport "fmt"func qui…...

使用C++11的`std::future`和`std::promise`实现异步网络通信

使用C11的std::future和std::promise实现异步网络通信 在现代C编程中&#xff0c;异步编程是一个重要的主题。C11引入了std::future和std::promise&#xff0c;为异步编程提供了强大的工具。本文将详细介绍如何使用std::future和std::promise实现异步网络通信&#xff0c;并提…...

【C++登堂入室】类与对象(上)

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

【西电电装实习】5. 无人机模块及作用、上位机的操作

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

N_m3u8DL-RE:终极跨平台流媒体下载工具,轻松保存加密视频内容

N_m3u8DL-RE&#xff1a;终极跨平台流媒体下载工具&#xff0c;轻松保存加密视频内容 【免费下载链接】N_m3u8DL-RE Cross-Platform, modern and powerful stream downloader for MPD/M3U8/ISM. English/简体中文/繁體中文. 项目地址: https://gitcode.com/GitHub_Trending/…...

别再死记硬背了!用Vivado 2022.1和Vitis搭建ZYNQ工程,这份避坑清单帮你省下3小时

ZYNQ开发实战&#xff1a;从Vivado到Vitis的高效避坑指南 当第一次打开Vivado和Vitis的开发者&#xff0c;往往会被复杂的界面和繁琐的配置流程所困扰。本文将以"按键控制LED"这一经典案例为主线&#xff0c;揭示ZYNQ开发中最容易踩中的12个深坑&#xff0c;并提供经…...

保姆级教程:在Qt 6.5桌面应用中集成WebRTC实现一对一视频通话(附完整源码)

Qt 6.5与WebRTC深度整合实战&#xff1a;构建企业级视频通话解决方案 1. 环境配置与依赖管理 在开始Qt 6.5与WebRTC的集成之旅前&#xff0c;我们需要搭建一个稳定的开发环境。不同于普通的Qt项目&#xff0c;这种集成对工具链和系统配置有特殊要求。 推荐开发环境配置&…...

SAP EWM实战:从产品到处理单位,两种库存转移操作保姆级教程

SAP EWM库存转移实战指南&#xff1a;产品与处理单位的精准操作 在仓库管理的日常工作中&#xff0c;库存转移是最基础却最容易出错的环节之一。特别是对于刚接触SAP EWM系统的管理员来说&#xff0c;面对不同形态的物料——散件产品和带包装的处理单位(HU)&#xff0c;往往会产…...

深入理解强化学习基础:价值函数、策略梯度与PPO算法核心原理

深入理解强化学习基础&#xff1a;价值函数、策略梯度与PPO算法核心原理 【免费下载链接】LLM-RL-Visualized &#x1f31f;100 原创 LLM / RL 原理图&#x1f4da;&#xff0c;《大模型算法》作者巨献&#xff01;&#x1f4a5;&#xff08;100 LLM/RL Algorithm Maps &#x…...

Python股票数据查询工具:适配器模式与缓存策略实战

1. 项目概述&#xff1a;一个股票价格查询工具的核心价值最近在GitHub上看到一个挺有意思的项目&#xff0c;叫tjefferson/stock-price-query。光看名字&#xff0c;你可能会觉得这不就是个简单的数据抓取脚本吗&#xff1f;市面上类似的工具一抓一大把。但作为一个在金融数据和…...

启动我进入数据科学的那一个思维方式转变

原文&#xff1a;towardsdatascience.com/the-one-mindset-change-that-launched-me-into-data-science-3f72bd1df46f?sourcecollection_archive---------2-----------------------#2024-10-19 让它成为现实&#xff1a;微小的改变帮助你进入数据科学或任何梦想职业 https://…...

1951-2025年中国1km月平均气温逐年年内季节波动幅度数据集

中国1000米分辨率月平均气温数据集&#xff08;1951-2025&#xff09;提供了长时间序列、规则网格的气象背景信息&#xff0c;为开展气候变化分析和区域比较研究提供了基础数据支撑。针对原始月尺度序列直接使用不够便捷的问题&#xff0c;需要进一步形成具有明确主题和统一格式…...

3分钟解决游戏操作冲突:Hitboxer SOCD工具让你的键盘操作职业化

3分钟解决游戏操作冲突&#xff1a;Hitboxer SOCD工具让你的键盘操作职业化 【免费下载链接】socd Key remapper for epic gamers 项目地址: https://gitcode.com/gh_mirrors/so/socd 你是否在玩《街头霸王6》时连招总是失败&#xff1f;或者在《Apex英雄》中急停转向时…...

GaussDB GDS 搭建完全指南:从安装到启动,一文搞定数据迁移服务

在进行 GaussDB 跨库数据迁移时&#xff0c;GDS&#xff08;Gauss Data Service&#xff09; 是实现外表迁移的核心组件。本文将手把手带你完成 GDS 的下载、安装、配置与启动&#xff0c;确保数据迁移通道畅通无阻。 &#x1f4ce; 关联阅读&#xff1a;GaussDB GDS 外表迁移实…...