OpenCV图像旋转原理及示例
OpenCV计算机视觉开发实践:基于Qt C++ - 商品搜索 - 京东
图像旋转是数字图像处理的一个非常重要的环节,是图像的几何变换手法之一。图像旋转算法是图像处理的基础算法。在数字图像处理过程中,经常要用到旋转,例如在进行图像扫描时,需要运用旋转实现图像的倾斜校正;在进行多幅图像的比较、模式识别及对图像进行剪裁和拼接前,都需要进行图像的旋转处理。
图像旋转是指图像以某一点为中心旋转一定的角度,形成一幅新的图像的过程。图像旋转通常可以分为两种情况,一种是以坐标原点为中心进行旋转;另外一种是以任意图形中的某点为坐标原点进行旋转。图像的旋转变换是图像的位置变换,旋转后图像的大小一般会改变。在图像旋转变换中,既可以把转出显示区域的图像截去,也可以扩大图像范围以显示所有的图像。
1. 以坐标原点为中心进行旋转的原理
点P0绕坐标原点逆时针旋转θ角度得到点P1,如图7-2所示。
图7-2
2. 以任意图形中的某点为坐标原点进行旋转的原理
以任意图形中的某点为坐标原点进行旋转的原理如图7-3所示。
图7-3
由图7-3可以看出,以任意图形中心点为坐标原点进行旋转需要如下3步:
将坐标系Ⅰ变成坐标系Ⅱ。
由Figure1得到Figure2可知,变换矩阵为:
在坐标系Ⅱ中旋转θ角度。参考以坐标原点为中心进行旋转的原理。
将坐标系Ⅱ变成坐标系Ⅰ。
由图7-3中的Figure3得到Figure4可知,变换矩阵为(其实就是步骤01中变换矩阵的逆变换):
在OpenCV中,图像旋转首先根据旋转角度和旋转中心获取旋转矩阵,然后根据旋转矩阵进行变换,即可实现任意角度和任意中心的旋转效果。
下面我们实现图像旋转算法,有两种方式:一种是根据算法手动实现,另一种是根据OpenCV提供的现成函数自动实现。现在先来介绍根据算法手动实现的方式。
【例7.2】手动实现图像旋转
打开Qt Creator,新建一个控制台工程,工程名是test。
在工程中打开main.cpp,输入如下代码:
#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;
Mat imgRotate(Mat matSrc, float angle, bool direction)
{float theta = angle * CV_PI / 180.0;const int nRowsSrc = matSrc.rows;const 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, (float)nRowsSrc}, {(float)nColsSrc, (float)nRowsSrc}, {(float)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("cat.png");if (matSrc.empty())return 1;float angle = 30;Mat matRet = imgRotate(matSrc, angle, true);imshow("src", matSrc);imshow("rotate", matRet);// 保存图像imwrite("rotate_panda.jpg", matRet);waitKey();return 0;}
以上代码完全是根据前面的算法原理公式来实现的。图像旋转是指图像按照某个位置转动一定角度的过程,旋转中图像仍保持原始尺寸。图像旋转后,图像的水平对称轴、垂直对称轴及中心坐标原点都可能发生变换,因此需要对图像旋转中的坐标进行相应转换。
保存工程并运行,结果如图7-4所示。
如果不想通过算法公式手动实现图像旋转,也可以利用OpenCV提供的库函数getRotationMatrix2D来实现图像旋转。该函数用来计算旋转矩阵,其声明如下:
Mat getRotationMatrix2D(Point2f center, double angle, double scale);
其中参数center表示旋转的中心点;angle表示旋转的角度;scale表示图像缩放因子。该函数的返回值为一个2×3的矩阵,其中矩阵前两列代表旋转,最后一列代表平移。
图7-4
计算出旋转矩阵后,还需要把旋转应用到仿射变换的输出,仿射变换函数是warpAffine,该函数在下一节介绍。
【例7.3】使用函数实现图像的旋转
打开Qt Creator,新建一个控制台工程,工程名是test。
在工程中打开main.cpp,输入如下代码:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;// 图像旋转,angle表示要旋转的角度
void Rotate(const Mat &srcImage, Mat &destImage, double 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("lena.png");if (!srcImage.data){puts("Fail to open file.");return -1;}imshow("srcImage", srcImage); // 原图像也绘制出来以作参照Mat destImage;double angle = 9.9;// 角度Rotate(srcImage, destImage, angle);// 最后把仿射变换和旋转的结果绘制在窗体中imshow("dst", destImage);waitKey(0);return 0;
}
保存工程并运行,结果如图7-5所示。
图7-5
相关文章:

OpenCV图像旋转原理及示例
OpenCV计算机视觉开发实践:基于Qt C - 商品搜索 - 京东 图像旋转是数字图像处理的一个非常重要的环节,是图像的几何变换手法之一。图像旋转算法是图像处理的基础算法。在数字图像处理过程中,经常要用到旋转,例如在进行图像扫描时…...
LLM Text2SQL NL2SQL 实战总结
目录 尽量全面的描述表的功能 尽量全面的描述字段的功能 适当放弃意义等价的字段 放弃业务上无用的字段 对于LLM来说,由于它没有什么行业经验,所以我们需要尽可能的给予它恰当的“背景信息”,才能使它更好的工作。所谓恰当,不是越多越好,因为太多的信息会消耗掉LLM的可…...
k8s 中使用 Service 访问时NetworkPolicy不生效问题排查
背景 针对一个服务如下NetworkPolicy, 表示只有n9e命名空间的POD才能访问 k8s-man 服务 kind: NetworkPolicy apiVersion: networking.k8s.io/v1 metadata:name: k8s-mannamespace: n9elabels:app: k8s-manversion: v1 spec:podSelector:matchLabels:app: k8s-manversion: v1…...

【实战篇】数字化打印——打印部署管理接口开发
前言 前面的章节已经介绍了打印管理模块的主要界面设计,本篇介绍用myBuilder开发界面接口,实现最终的功能。 1. 配置打印应用菜单 首先配置挂载好模块菜单 让菜单点击能访问到对应的页面 2. 打印部署管理数据表详细设计 以下是打印部署管理的数据表字…...

MacOS Python3安装
python一般在Mac上会自带,但是大多都是python2。 python2和python3并不存在上下版本兼容的情况,所以python2和python3可以同时安装在一台设备上,并且python3的一些语法和python2并不互通。 所以在Mac电脑上即使有自带python,想要使…...
磁盘I/O瓶颈排查:面试通关“三部曲”心法
想象一下,你就是线上系统的“交通调度总指挥”,服务器的磁盘是所有数据进出的“核心枢纽港口”。当这个“港口”突然拥堵不堪,卡车(数据请求)排起长龙,进不去也出不来,整个系统的“物流”&#…...

idea启动报错:java: 警告: 源发行版 11 需要目标发行版 11(亲测解决)
引起原因 idea的jdk没有替换干净 1.配置project file–Project Structrue–Project 2.配置Modules-Sources file–Project Structrue–Modules-Sources 改为jdk11 3.配置Modules-Dependencies file–Project Structrue–Modules-Dependencies...
树莓派4 yolo 11l.pt性能优化后的版本
树莓派4 使用 Picamera2 拍摄图像,然后通过 YOLO11l.pt 进行目标检测,并在实时视频流中显示结果。但当前的代码在运行时可能会比较卡顿,主要原因包括: picam2.capture_array() 是一个较慢的操作;YOLO 推理可能耗时较长…...
鸿蒙OSUniApp开发支持多语言的国际化组件#三方框架 #Uniapp
使用UniApp开发支持多语言的国际化组件 在全球化的今天,一个优秀的应用往往需要支持多种语言以满足不同地区用户的需求。本文将详细讲解如何在UniApp框架中实现一套完整的国际化解决方案,从而轻松实现多语言切换功能。 前言 去年接手了一个面向国际市场…...
国产数据库工具突围:SQLynx如何解决Navicat的三大痛点?深度体验报告
引言:Navicat的"中国困境" 当开发者面对达梦数据库的存储过程调试,或是在人大金仓中处理复杂查询时,Navicat突然变得力不从心——这不是个例。 真实痛点:某政务系统迁移至OceanBase后,开发团队发现Navicat无…...

《Adversarial Sticker: A Stealthy Attack Method in the Physical World》论文分享(侵删)
原文链接:Adversarial Sticker: A Stealthy Attack Method in the Physical World | IEEE Journals & Magazine | IEEE Xplore author{Xingxing Wei and Ying Guo and Jie Yu} 摘要 为了评估深度学习在物理世界中的脆弱性,最近的工作引入了对抗补丁…...
Python生成器:高效处理大数据的秘密武器
生成器概述 生成器是 Python 中的一种特殊迭代器,通过普通函数的语法实现,但使用 yield 语句返回数据。生成器自动实现了 __iter__() 和 __next__() 方法,因此可以直接用于迭代。生成器的核心特点是延迟计算(lazy evaluation&…...
React Native/Flutter 原生模块开发
以下是关于 React Native 和 Flutter 原生模块开发的基本知识点总结: 一、核心概念对比 维度React NativeFlutter架构基础JavaScriptCore/Hermes + Bridge/TurboModulesDart VM + Skia引擎原生交互方式Native Modules + Native UI ComponentsPlatform Channels + Platform Vie…...

嵌入式STM32学习——继电器
继电器模块引脚说明 VCC(): 供电正极。连接此引脚到电源(通常是直流电源),以提供继电器线圈所需的电流。 GND(-): 地。连接此引脚到电源的负极或地。 IN(或…...

从基础到实习项目:C++后端开发学习指南
在当今技术快速迭代的背景下,后端开发作为软件工程的核心支柱持续发挥着关键作用。C凭借其卓越的性能表现和系统级控制能力,依然是构建高性能后端服务的首选语言之一。本文将系统性地解析现代C后端开发的核心技术体系,包括从语言特性精要到架…...
AI软件汇总与功能解析:赋能未来的智能工具库
人工智能(AI)技术的快速发展催生了大量功能强大的软件工具,覆盖自然语言处理、计算机视觉、数据分析、自动化决策等多个领域。本文将汇总当前主流的AI软件,并解析其核心功能与应用场景,为企业和开发者提供参考指南。 一…...

Xinference推理框架
概述 GitHub,官方文档。 核心优势 性能优化:通过vLLM、SGLang等引擎实现低延迟推理,吞吐量提升2-3倍;企业级支持:支持分布式部署、国产硬件适配及模型全生命周期管理;生态兼容:无缝对接LangC…...

前端ECS简介
ECS概念 ECS是一种软件架构模式,常见于游戏业务场景,其主要对象分类为 • Entity 实体,ECS架构中所有的业务对象都必须拥有一个唯一的Entity实体 • Component 组件,存储着数据结构,对应着某一种业务属性,一个Entity上可以动态挂载多个Component • …...
ET ProcessOuterSender类(实体) 分析
ProcessOuterSender 夸进程发送Actor消息,只在NetInner(Scene)使用。 字段 TIMEOUT_TIME RPC超时时间RpcId rpcIdrequestCallback 存储RPC的回调事件AService 进程之间的网络服务InnerProtocol 内部网络协议类型 目前固定KCP 方法 OnRead 方法,收包…...
redis中key的过期和淘汰
一、过期(redis主动删除) 设置了ttl过期时间的key,在ttl时间到的时候redis会删除过期的key。但是redis是惰性过期。惰性过期:redis并不会立即删除过期的key,而是会在获取key的时候判断key是否过期,如果发现…...

Dify与n8n全面对比指南:AI应用开发与工作流自动化平台选择【2025最新】
Dify与n8n全面对比指南:AI应用开发与工作流自动化平台选择【2025最新】 随着AI技术与自动化工具的迅速发展,开发者和企业面临着多种平台选择。Dify和n8n作为两个备受关注的自动化平台,分别专注于不同领域:Dify主要面向AI应用开发&…...

【深度学习之四】知识蒸馏综述提炼
知识蒸馏综述提炼 目录 知识蒸馏综述提炼 前言 参考文献 一、什么是知识蒸馏? 二、为什么要知识蒸馏? 三、一点点理论 四、知识蒸馏代码 总结 前言 知识蒸馏作为一种新兴的、通用的模型压缩和迁移学习架构,在最近几年展现出蓬勃的活力…...

redis解决常见的秒杀问题
title: redis解决常见的秒杀问题 date: 2025-03-07 14:24:13 tags: redis categories: redis的应用 秒杀问题 每个店铺都可以发布优惠券,保存到 tb_voucher 表中;当用户抢购时,生成订单并保存到 tb_voucher_order 表中。 订单表如果使用数据…...

TypeScript中文文档
最近一直想学习TypeScript,一直找不到一个全面的完整的TypeScript 中文文档。在网直上找了了久,终于找到一个全面的中文的typescript中文学习站,有学习ts的朋友可以年。 文档地址:https://typescript.uihtm.com 该TypeScript 官…...

Function Calling
在介绍Function Calling之前我们先了解一个概念,接口。 接口 两种常见接口: 人机交互接口,User Interface,简称 UI应用程序编程接口,Application Programming Interface,简称 API接口能「通」的关键,是两边都要遵守约定。 人要按照 UI 的设计来操作。UI 的设计要符合人…...
【搭建Node-RED + MQTT Broker实现AI大模型交互】
搭建Node-RED MQTT Broker实现AI大模型交互 搭建Node-RED MQTT Broker实现AI大模型交互一、系统架构二、环境准备与安装1. 安装Node.js2. 安装Mosquitto MQTT Broker3. 配置Mosquitto4. 安装Node-RED5. 配置Node-RED监听所有网络接口6. 启动Node-RED 三、Node-RED流程配置1. …...
高可靠低纹波国产4644电源芯片在工业设备的应用
摘要 随着工业自动化和智能化的飞速发展,工业设备对于电源芯片的性能和可靠性提出了前所未有的严格要求。电源芯片作为工业设备的核心供电组件,其性能直接影响到整个设备的运行效率和稳定性。本文以国科安芯的ASP4644四通道降压稳压器为例,通…...

面试--HTML
1.src和href的区别 总结来说: <font style"color:rgb(238, 39, 70);background-color:rgb(249, 241, 219);">src</font>用于替换当前元素,指向的资源会嵌入到文档中,例如脚本、图像、框架等。<font style"co…...

SparkSQL操作Mysql-准备mysql环境
我们计划在hadoop001这台设备上安装mysql服务器,(当然也可以重新使用一台全新的虚拟机)。 以下是具体步骤: 使用finalshell连接hadoop001.查看是否已安装MySQL。命令是: rpm -qa|grep mariadb若已安装,需要先做卸载MyS…...
Linux常用方法
1、查看日志后100行 tail -f -n 100 catalina.out 2、ps命令 ps命令用来列出系统中当前运行的那些进程。ps命令列出的是当前那些进程的快照 ps -ef 显示所有进程信息,连同命令行,ps 与grep 常用组合用法,查找特定进程 ps aux列出目前所有的…...