OpenCV 图像旋转和平移 数学和代码原理详解

文章目录

    • 数学原理
      • 旋转矩阵
      • 平移和旋转
      • 合成变换矩阵
      • 应用在OpenCV中的实现
    • 代码关键点解读
    • 完整代码
      • C++代码:
      • Python代码:

在OpenCV中进行图像旋转涉及到一些基本的几何变换和图像处理操作。

数学原理

在图像旋转中,背后的数学原理主要涉及二维欧几里得空间中的几何变换。具体来说,图像旋转可以通过二维旋转矩阵来实现。

旋转矩阵

对于一个二维平面上的点 (x, y),绕原点逆时针旋转角度 θ 后的新坐标 (x', y') 可以通过以下旋转矩阵计算得到:

在这里插入图片描述

平移和旋转

在实际应用中,图像通常不会绕原点旋转,而是绕图像的某个中心点 (cx, cy) 进行旋转。要实现绕任意点旋转,我们需要先将该点平移到原点,进行旋转,然后再平移回原来的位置。

具体步骤如下:

  1. 平移中心点到原点:将中心点 (cx, cy) 平移到原点 (0, 0)
  2. 旋转:在原点进行旋转。
  3. 平移回原位置:将旋转后的点再平移回 (cx, cy)

合成变换矩阵

综合上述步骤,绕任意点 (cx, cy) 逆时针旋转角度 θ 的变换矩阵可以表示为:

M = T ⋅ R ⋅ T − 1 M = T \cdot R \cdot T^{-1} M=TRT1
其中:

  • ( T ) 是平移矩阵,用于将中心点平移到原点。
  • ( R ) 是旋转矩阵,用于在原点进行旋转。
  • ( T^{-1} ) 是逆平移矩阵,用于将旋转后的点平移回原位置。

具体形式为:

T = [ 1 0 − c x 0 1 − c y 0 0 1 ] T = \begin{bmatrix} 1 & 0 & -cx \\ 0 & 1 & -cy \\ 0 & 0 & 1 \end{bmatrix} T= 100010cxcy1

R = [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] R = \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} R= cosθsinθ0sinθcosθ0001

T − 1 = [ 1 0 c x 0 1 c y 0 0 1 ] T^{-1} = \begin{bmatrix} 1 & 0 & cx \\ 0 & 1 & cy \\ 0 & 0 & 1 \end{bmatrix} T1= 100010cxcy1
所以综合后的旋转矩阵 ( M ) 为:

M = [ cos ⁡ θ − sin ⁡ θ c x ( 1 − cos ⁡ θ ) + c y sin ⁡ θ sin ⁡ θ cos ⁡ θ c y ( 1 − cos ⁡ θ ) − c x sin ⁡ θ 0 0 1 ] M = \begin{bmatrix} \cos\theta & -\sin\theta & cx(1-\cos\theta) + cy\sin\theta \\ \sin\theta & \cos\theta & cy(1-\cos\theta) - cx\sin\theta \\ 0 & 0 & 1 \end{bmatrix} M= cosθsinθ0sinθcosθ0cx(1cosθ)+cysinθcy(1cosθ)cxsinθ1
由于图像坐标是二维的,我们只需要前三列中的前两行:

M = [ cos ⁡ θ − sin ⁡ θ c x ( 1 − cos ⁡ θ ) + c y sin ⁡ θ sin ⁡ θ cos ⁡ θ c y ( 1 − cos ⁡ θ ) − c x sin ⁡ θ ] M = \begin{bmatrix} \cos\theta & -\sin\theta & cx(1-\cos\theta) + cy\sin\theta \\ \sin\theta & \cos\theta & cy(1-\cos\theta) - cx\sin\theta \end{bmatrix} M=[cosθsinθsinθcosθcx(1cosθ)+cysinθcy(1cosθ)cxsinθ]

应用在OpenCV中的实现

在OpenCV中,函数 cv::getRotationMatrix2D 就是用来计算这个旋转矩阵的:

cv::Mat cv::getRotationMatrix2D(cv::Point2f center, double angle, double scale);

center 参数表示旋转中心 (cx, cy)angle 表示旋转角度 θ,scale 表示缩放比例。

然后通过 cv::warpAffine 函数应用这个旋转矩阵来实现图像的旋转:

cv::warpAffine(src, dst, M, cv::Size(width, height));

其中 M 就是通过 getRotationMatrix2D 计算得到的旋转矩阵。

总结一下,图像旋转的数学原理是通过平移和旋转组合的方式,利用二维旋转矩阵实现绕任意点的旋转。OpenCV中提供的函数封装了这些数学计算,使得图像旋转操作变得简单直观。

代码关键点解读

以胖虎为例

在这里插入图片描述

C++代码:

void rotate_demo(Mat &image){int width = image.cols;int height = image.rows;//计算旋转中心坐标Point2f center(width/2.0,height/2.0);double angle =180;Mat rotation_matrix = getRotationMatrix2D(center,angle,1.0);Mat rotate_image ;warpAffine(image,rotate_image,rotation_matrix,Size(width,height));imshow("Rotate Image",rotate_image);
}

调用上述代码就会发现一个问题: 图像是旋转了,但是旋转后的图像尺寸不对了, 向右旋转之后,图像宽度不正常。宽度应该是原来的高度,原来的高度应该是宽度才对

在这里插入图片描述

warpAffine(image,rotate_image,rotation_matrix,Size(width,height));中尝试调换一下宽度和高度试试。

warpAffine(image,rotate_image,rotation_matrix,Size(height,width));

尝试运行之后,发现虽然窗口尺寸对了但是图像右上角都是黑边,原来的图像现了缺失

在这里插入图片描述

出现这个原因是我们一开始是按照中心点进行旋转,并不是从左上角开始旋转的,因此会出现图片缺失问题,因此我们需要把图像再平移回去。平移回去的第一大问题就是旋转之后的图像宽度和高度发生了变化,我们需要重新计算旋转后的图像尺寸

OpenCV提供了计算旋转后的的图像边界尺寸的工具,bbox就是以某个角度旋转之后的矩阵,为什么需要这个函数?举个栗子,旋转九十度,图像的宽度是原来图像的高度,图像的高度是原来的宽度,但如果是旋转45度?旋转后的图像并不是原来图像的高度,需要重新计算。

 Rect bbox = RotatedRect(Point2f(), image.size(), angle).boundingRect();

旋转矩阵 rotation_matrix 是一个 2x3 的矩阵,其形式为:
[ a b t x c d t y ] \begin{bmatrix} a & b & tx \\ c & d & ty \end{bmatrix} [acbdtxty]
其中 txty 是平移部分。

因此我们需要调整tx和ty的值

  • 旋转后的图像尺寸 bbox.size() 的中心点坐标为 (bbox.width / 2.0, bbox.height / 2.0)
  • 原始旋转中心点为 center,坐标为 (center.x, center.y)
  • 调整 txty 的目的是将旋转中心点平移到新图像中心,使图像内容在旋转后居中。

调整后的 txty 计算如下:

  • tx:将图像沿 x 轴平移 bbox.width / 2.0 - center.x
  • ty:将图像沿 y 轴平移 bbox.height / 2.0 - center.y

对应的代码为:

rotation_matrix.at<double>(0, 2) += bbox.width / 2.0 - center.x;
rotation_matrix.at<double>(1, 2) += bbox.height / 2.0 - center.y;

at<double>(0, 2) 访问矩阵中的 tx 值,at<double>(1, 2) 访问矩阵中的 ty 值。

再来运行一下代码,可以看到此时图像就正常了。

在这里插入图片描述

试试45度旋转,修改angle的值为45,效果如下,注意图像的尺寸已经发生了改变,重新计算尺寸后不会造成像素丢失的问题

在这里插入图片描述

完整代码

C++代码:

void rotate_demo(Mat &image){int width = image.cols;int height = image.rows;//计算旋转中心坐标Point2f center(width/2.0,height/2.0);double angle =45;Mat rotation_matrix = getRotationMatrix2D(center,angle,1.0);//因为涉及到旋转,图像的高度和宽度其实发生了变化// 计算旋转后的图像边界尺寸Rect bbox = RotatedRect(Point2f(), image.size(), angle).boundingRect();rotation_matrix.at<double>(0, 2) += bbox.width / 2.0 - center.x;rotation_matrix.at<double>(1, 2) += bbox.height / 2.0 - center.y;Mat rotate_image ;warpAffine(image,rotate_image,rotation_matrix,bbox.size());imshow("Rotate Image",rotate_image);
}

Python代码:

import cv2
import numpy as npdef rotate_demo(image_path, angle=45):# 读取图像image = cv2.imread(image_path)if image is None:print("Could not read the image.")returnheight, width = image.shape[:2]# 计算旋转中心坐标center = (width / 2.0, height / 2.0)# 计算旋转矩阵rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)# 计算旋转后的图像边界尺寸corners = np.array([[0, 0],[width, 0],[width, height],[0, height]])corners = np.hstack((corners, np.ones((4, 1))))rotated_corners = rotation_matrix.dot(corners.T).Tx_coords = rotated_corners[:, 0]y_coords = rotated_corners[:, 1]bbox_width = int(np.ceil(x_coords.max() - x_coords.min()))bbox_height = int(np.ceil(y_coords.max() - y_coords.min()))# 调整旋转矩阵的平移部分rotation_matrix[0, 2] += bbox_width / 2.0 - center[0]rotation_matrix[1, 2] += bbox_height / 2.0 - center[1]# 执行旋转操作rotate_image = cv2.warpAffine(image, rotation_matrix, (bbox_width, bbox_height))# 显示旋转后的图像cv2.imshow("Rotate Image", rotate_image)cv2.waitKey(0)cv2.destroyAllWindows()# 调用旋转函数
rotate_demo("path_to_your_image.jpg", 45)

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

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

相关文章

阿里云ubuntu宝塔面板部署uni-app-flask-websocket前后端项目

1.下载宝塔面板 wget -O install.sh https://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh ed8484bec 然后去安全组开放对应的端口 面板账户登录信息 【云服务器】请在安全组放行 29725 端口 进入控制面板后修改默认用户名和密码 2. …

linux、windows、macos,命令终端清屏

文章目录 LinuxWindowsmacOS 在Linux、Windows和macOS的命令终端中&#xff0c;清屏的命令或方法各不相同。以下是针对这三种系统的清屏方法&#xff1a; Linux clear命令&#xff1a;这是最常用的清空终端屏幕的命令之一。在终端中输入clear命令后&#xff0c;屏幕上的所有内容…

Web开发:ASP.NET CORE使用Ajax定时获取后端数据

一、低难度&#xff08;刷新a标签&#xff09; 1、需求 给a标签每15s刷新一次&#xff0c;显示最新的时间&#xff08;时间必须由后端获取&#xff09; 应该如何操作呢 2、代码 后端 using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Mi…

微信小程序获取蓝牙并实现内容打印

通过微信小程序如何实现获取蓝牙打印机并实现打印能力&#xff0c;之前做过一个测试Dome&#xff0c;能够获取附近的蓝牙打印机设备并实现打印,今天开放出来供大家参考。 wxml <!--右下角搜索--> <view class"ly-cass-box"><view class"ly-cas…

Docker核心技术:Docker原理之Namespace

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 Docker核心技术 系列文章&#xff1a;Docker原理之Namespace&#xff0c;其他文章快捷链接如下&#xff1a; 应用架构演进容器技术要解决哪些问题Docker的基本使用Docker是如何实现的 Docker核心技术&#xff1…

设计模式思想

设计模式思想 1. 理论2. 结构型模式——更优雅的声明和构建对象2.1 适配器模式——将一个类的接口适配成用户所期待的类2.2 代理模式——创建一个代理对象劫持对目标对象的调用&#xff0c;为目标访问增加一层控制和拦截&#xff0c;将控制逻辑和主逻辑隔离2.3 装饰器模式——在…

017、Vue动态tag标签

文章目录 1、先看效果2、代码 1、先看效果 2、代码 <template><div class "tags"><el-tag size"medium"closable v-for"item,index in tags":key"item.path":effect"item.title$route.name?dark:plain"cl…

【中项】系统集成项目管理工程师-第4章 信息系统架构-4.3应用架构

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

安卓系统签名的制作与使用(SignApk.jar)踩坑记录

看到这里的你应该能区分apk签名跟系统签名吧,如果无法区分的请看下面链接 android 应用的证书签名跟系统签名 看过上面的文章应该知道系统签名需要的文件清单大概有哪些 前两个是编译安卓系统时在build目录下,详细目录为 /build/target/product/security 每组签名用途不同&am…

Godot学习笔记2——GDScript变量与函数

目录 一、代码编写界面 二、变量 三、函数 四、变量的类型 Godot使用的编程语言是GDS&#xff0c;语法上与python有些类似。 一、代码编写界面 在新建的Godot项目中&#xff0c;点击“创建根节点”中的“其他节点”&#xff0c;选择“Node”。 点击场景界面右上角的绿色…

ISP代理和双ISP代理:区别和优势

随着互联网技术的不断发展和普及&#xff0c;网络代理服务成为众多用户保护隐私、提高网络性能、增强安全性的重要工具。其中&#xff0c;ISP代理和双ISP代理是两种常见的网络代理服务形式。本文将详细探讨ISP代理和双ISP代理的区别和优势&#xff0c;以便用户更好地了解并选择…

真实测评,霍尼韦尔、希喂、352宠物空气净化器性能对比

在快节奏的社会生活中&#xff0c;人们越来越注重精神需要&#xff0c;许多年轻人纷纷选择拥抱宠物&#xff0c;作为生活中的温馨伴侣。宠物们治愈心灵的同时也要付出一定“代价”&#xff0c;日常养护&#xff0c;如清理猫毛、管理气味以及保持宠物环境的清洁&#xff0c;都是…

开源发票识别InvoiceNet项目Windows部署踩坑记(1)

今天安装在github上的InvoiceNet开源项目&#xff0c;准备对它进行测试&#xff0c;安装过程出现了一些问题&#xff0c;做个记录&#xff0c;给遇到兄弟爬坑。 第一个问题&#xff0c;conda的问题&#xff0c; 这是另外一个包管理器&#xff0c;不仅仅可以管理python的虚拟…

UFO:革新Windows操作系统交互的UI聚焦代理

人工智能咨询培训老师叶梓 转载标明出处 人机交互的便捷性和效率直接影响着我们的工作和生活质量。尽管现代操作系统如Windows提供了丰富的图形用户界面&#xff08;GUI&#xff09;&#xff0c;使得用户能够通过视觉和简单的点击操作来控制计算机&#xff0c;但随着应用程序功…

IP第一次综合实验

一、实验拓扑 二、实验要求 1、R6为ISP&#xff0c;接口IP地址均为公有地址&#xff0c;该设备只能配置地址之后不能冉对其进行任何配置 2、R1-R5为局域网&#xff0c;私有Ip地址192.168.1.0/24&#xff0c;请合理分配 3、R1、82、R4&#xff0c;各有两个环回IP地址;R5,R6各…

Linux、Windows和macOS上使用Telnet

文章目录 LinuxWindowsmacOS 在Linux、Windows和macOS上使用Telnet时&#xff0c;不同的系统有不同的工具和设置方法。以下是在这些系统上使用Telnet的简要说明&#xff1a; Linux 在Linux上&#xff0c;Telnet通常是通过telnet命令来使用的。首先&#xff0c;你需要确保你的系…

Ubuntu 24.04 LTS Noble安装 FileZilla Server

FileZilla Server 是一款使用图形用户界面快速创建 FTP 服务器的软件。它有助于测试需要 FTP 服务器功能的各种项目。虽然早期的 FileZilla FTP 服务器仅适用于 Windows 和 macOS&#xff0c;但现在我们也可以在 Linux&#xff08;例如 Ubuntu 24.04&#xff09;上安装 FileZil…

解决Visual studio内报错信息:MSB8036:找不到 Windows SDK 版本问题

问题描述&#xff1a; 找不到WindowsSDK版本&#xff0c;请安装所需版本的Windows SDK&#xff0c;或者在项目属性页中通过右键单击解决方案并选择“重定解决方案目标”来更改SDK版本。 首先&#xff0c;如果你尝试了以下两种方法&#xff1a; &#xff08;1&#xff09;重新…

43 华三AC登录Web页面

一 无线上WEB页面 1 创建vlan 56 [AC-KongZhi]vlan 56 2 退出 [AC-KongZhi-vlan56]quit 3 进入vlan三层口 配置IP地址 [AC-KongZhi]interface Vlan-interface 56 [AC-KongZhi-Vlan-interface56]ip address 192.168.56.55 24 4 在AC控制器与Host主机的接口上能通关vlan 5…

【人工智能】使用Python的dlib库实现人脸识别技术

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、引言二、传统人脸识别技术1. 基于几何特征的方法2. 基于模板匹配的方法3. 基于统计学习的方法 三、深度学习在脸识别中的应用1. 卷积神经网络&#xff08;CNN&#xff09;2. FaceNet和ArcFace 四、使用Python和dlib库实…