opencv-24 图像几何变换03-仿射-cv2.warpAffine()

news/2024/11/19 19:36:04/

什么是仿射?

仿射变换是指图像可以通过一系列的几何变换来实现平移、旋转等多种操作。该变换能够
保持图像的平直性和平行性。平直性是指图像经过仿射变换后,直线仍然是直线;平行性是指 图像在完成仿射变换后,平行线仍然是平行线。

OpenCV 中的仿射函数为 cv2.warpAffine(),其通过一个变换矩阵(映射矩阵)M 实现变换,
具体为:
dst(𝑥, 𝑦) = src(𝑀11𝑥 + 𝑀12𝑦 + 𝑀13, 𝑀21𝑥 + 𝑀22𝑦 + 𝑀23)
如图 5-2 所示,可以通过一个变换矩阵 M,将原始图像 O 变换为仿射图像 R

在这里插入图片描述
因此,可以采用仿射函数 cv2.warpAffine()实现对图像的旋转,该函数的语法格式如下:

dst = cv2.warpAffine( src, M, dsize[, flags[, borderMode[, borderValue]]] )

式中:
dst 代表仿射后的输出图像,该图像的类型和原始图像的类型相同。

dsize 决定输出图像的实际大小。

src 代表要仿射的原始图像。

M 代表一个 2×3 的变换矩阵。使用不同的变换矩阵,就可以实现不同的仿射变换。

dsize 代表输出图像的尺寸大小。

flags 代表插值方法,默认为 INTER_LINEAR。当该值为 WARP_INVERSE_MAP 时,
意味着 M 是逆变换类型,实现从目标图像 dst 到原始图像 src 的逆变换。
 borderMode 代表边类型, 默认为 BORDER_CONSTANT 。 当 该值为 BORDER_TRANSPARENT 时,意味着目标图像内的值不做改变,这些值对应原始图像内的异常
值。
 borderValue 代表边界值,默认是 0。
通过以上分析可知,在 OpenCV 中使用函数 cv2.warpAffine()实现仿射变换,忽略其可选参数后的语法格式为:

dst = cv2.warpAffine( src , M , dsize )

其通过转换矩阵 M 将原始图像 src 转换为目标图像 dst:

dst(𝑥, 𝑦) = src(𝑀11𝑥 + 𝑀12𝑦 + 𝑀13, 𝑀21𝑥 + 𝑀22𝑦 + 𝑀23)

因此,进行何种形式的仿射变换完全取决于转换矩阵 M。下面分别介绍通过不同的转换矩阵 M 实现的不同的仿射变换。

平移

通过转换矩阵 M 实现将原始图像 src 转换为目标图像 dst:
dst(𝑥, 𝑦) = src(𝑀11𝑥 + 𝑀12𝑦 + 𝑀13, 𝑀21𝑥 + 𝑀22𝑦 + 𝑀23)
将原始图像 src 向右侧移动 100 个像素、向下方移动 200 个像素,则其对应关系为:
dst (x, y) = src (x + 100, y + 200)
将上述表达式补充完整,即:
dst (x, y) = src (1·x + 0·y + 100, 0·x + 1·y + 200)
根据上述表达式,可以确定对应的转换矩阵 M 中各个元素的值为:
 M11=1
 M12=0
 M13=100
 M21=0
 M22=1
 M23=200
将上述值代入转换矩阵 M,得到:

在这里插入图片描述
在已知转换矩阵 M 的情况下,可以直接利用转换矩阵 M 调用函数 cv2.warpAffine() 完成图像的平移。

实验:利用自定义转换矩阵完成图像平移。

import cv2
import numpy as np
img=cv2.imread("lena.png")
height,width=img.shape[:2]
x=100
y=200
M = np.float32([[1, 0, x], [0, 1, y]])
move=cv2.warpAffine(img,M,(width,height))
cv2.imshow("original",img)
cv2.imshow("move",move)
cv2.waitKey()
cv2.destroyAllWindows()

运行结果:
在这里插入图片描述
其中左图是原始图像,右图是移动结果图像

旋转

在使用函数 cv2.warpAffine()对图像进行旋转时,可以通过函数 cv2.getRotationMatrix2D()
获取转换矩阵。该函数的语法格式为:

retval=cv2.getRotationMatrix2D(center, angle, scale)

式中:
 center 为旋转的中心点。
 angle 为旋转角度,正数表示逆时针旋转,负数表示顺时针旋转。
 scale 为变换尺度(缩放大小)。

利用函数 cv2.getRotationMatrix2D()可以直接生成要使用的转换矩阵 M。

例如,想要以图像中心为圆点,逆时针旋转 45°,并将目标图像缩小为原始图像的 0.6 倍,则在调用函数
cv2.getRotationMatrix2D()生成转换矩阵 M 时所使用的语句为:

M=cv2.getRotationMatrix2D((height/2,width/2),45,0.6)

实验2:完成图像旋转

代码:

import cv2
import numpy as np
img=cv2.imread("lena.png")
height,width=img.shape[:2]
M=cv2.getRotationMatrix2D((width/2,height/2),45,0.6)
rotate=cv2.warpAffine(img,M,(width,height))
cv2.imshow("original",img)
cv2.imshow("rotation",rotate)
cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述
其中左图是原始图像,右图是旋转结果图像

更复杂的仿射变换

对于更复杂仿射变换,OpenCV 提供了
函数 cv2.getAffineTransform()来生成仿射函数 cv2.warpAffine()所使用的转换矩阵 M。该函数的语法格式为:

retval=cv2.getAffineTransform(src, dst)

式中:
 src 代表输入图像的三个点坐标。
 dst 代表输出图像的三个点坐标。
在该函数中,其参数值 src 和 dst 是包含三个二维数组(x, y)点的数组。上述参数通过函数
cv2.getAffineTransform()定义了两个平行四边形。src 和 dst 中的三个点分别对应平行四边形的
左上角、右上角、左下角三个点。函数 cv2.warpAffine()以函数 cv2.getAffineTransform()获取的
转换矩阵 M 为参数,将 src 中的点仿射到 dst 中。函数 cv2.getAffineTransform()
对所指定的点完成映射后,将所有其他点的映射关系按照指定点的关系计算确定。

实验3:完成图像仿射

import cv2
import numpy as np
img=cv2.imread('lena.png')
rows,cols,ch=img.shape
#定义三个点
p1=np.float32([[0,0],[cols-1,0],[0,rows-1]])print(p1)
#定义三个点的变换位置
p2=np.float32([[0,rows*0.33],[cols*0.85,rows*0.25],[cols*0.15,rows*0.7]])
print(p2)
#生成变换矩阵
M=cv2.getAffineTransform(p1,p2)
#进行仿射变换
dst=cv2.warpAffine(img,M,(cols,rows))
cv2.imshow("origianl",img)
cv2.imshow("result",dst)
cv2.waitKey()
cv2.destroyAllWindows()

首先构造了两个三分量的点集合 p1 和 p2,分别用来指代原始图像和目标图像内平行四边形的三个顶点(左上角、右上角、左下角)。
然后使用
M=cv2.getAffineTransform(p1,p2)
获取转换矩阵 M。接下来,
dst=cv2.warpAffine(img,M,(cols,rows))
完成了从原始图像到目标图像的仿射。

运行结果:

其中左图是原始图像,右图是仿射结果图像


http://www.ppmy.cn/news/981582.html

相关文章

239. 滑动窗口最大值

力扣题目链接 (opens new window) 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回滑动窗口中的最大值。 进阶: 你能在线性时间复杂度内解…

SpringBoot登陆+6套前端主页-【JSB项目实战】

SpringBoot系列文章目录 SpringBoot知识范围-学习步骤【JSB系列之000】 文章目录 SpringBoot系列文章目录本系列校训 SpringBoot技术很多很多环境及工具:上效果图主页登陆 配置文件设置导数据库项目目录如图:代码部分:控制器过滤器详细的解…

fasync driver demo

Linux系统中的fasync用于设备驱动程序与用户空间之间的异步通信。它允许设备驱动程序通知用户空间的进程,当设备状态发生变化时,通过发送SIGIO信号来告知进程。 具体来说,设备驱动程序可以调用fasync_helper()函数来注册一个或多个进程&…

自然语言处理从入门到应用——LangChain:模型(Models)-[文本嵌入模型Ⅱ]

分类目录:《自然语言处理从入门到应用》总目录 本文将介绍如何在LangChain中使用Embedding类。Embedding类是一种与嵌入交互的类。有很多嵌入提供商,如:OpenAI、Cohere、Hugging Face等,这个类旨在为所有这些提供一个标准接口。 …

JAVA面试总结-Redis篇章(五)——持久化

Java面试总结-Redis篇章(五)——持久化 1.RDBRDB全称Redis Database Backup file (Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件&#x…

英语学习相关资源

学习建议 学习英语和口语需要付出一定的努力和时间。以下是一些学习英语和口语的建议: 学习基础词汇和语法:学习英语的第一步是掌握基础词汇和语法。可以通过阅读英语书籍、听英语音频和观看英语视频来学习词汇和语法。 练习听力和口语:英语…

代码随想录day28

46. 全排列 思路:这道题首先是一个排列问题,排列问题是讲究顺序的,例如[1,2]和[2,1]是两个不一样的排列,这里的1我们会有重复使用到,但是,在每一个排列中,每一个元素只能使用一次。所以需要一个…

练习时长两年半的双机热备

1.双机热备技术产生的背景 传统的组网方式如下左图所示,内部用户和外部用户的交互报文全部通过防火墙A。如果防火墙A出现故障,内部网络中所有以防火墙A作为默认网关的主机与外部网络之间的通讯将中断,通讯可靠性无法保证。防火墙作为安全设备…