【OpenCV】Chapter3.图像的仿射变换

举报
zstar 发表于 2022/08/18 01:06:51 2022/08/18
【摘要】 最近想对OpenCV进行系统学习,看到网上这份教程写得不错,于是跟着来学习实践一下。 【youcans@qq.com, youcans 的 OpenCV 例程, https://youcans.bl...

最近想对OpenCV进行系统学习,看到网上这份教程写得不错,于是跟着来学习实践一下。
【youcans@qq.com, youcans 的 OpenCV 例程, https://youcans.blog.csdn.net/article/details/125112487
程序仓库:https://github.com/zstar1003/OpenCV-Learning

仿射变换原理

仿射变换其实包含了一系列的操作:平移,缩放,旋转等,不过所有的操作都可以通过这个仿射变换矩阵来实现。

仿射变换矩阵:

[ x y 1 ] = [ a 0 a 1 a 2 a 3 a 4 a 5 0 0 1 ] [ x 0 y 0 1 ] [ x y 1 ] = [ a 0 a 1 a 2 a 3 a 4 a 5 0 0 1 ] [ x 0 y 0 1 ] xy1 = a0a30a1a40a2a51 x0y01

其中 x x x y y y表示输出图像像素的坐标, x 0 x_0 x0, y 0 y_0 y0表示输入图像像素的坐标

变换名称 a 0 a_0 a0 a 1 a_1 a1 a 2 a_2 a2 a 3 a_3 a3 a 4 a_4 a4 a 5 a_5 a5
平移 1 0 △ x \triangle x x 0 1 △ y \triangle y y
均匀缩放 s s s 0 0 0 s s s 0
不均匀缩放 s x s_x sx 0 0 0 s y s_y sy 0
顺时针旋转角度 θ \theta θ c o s θ cos\theta cosθ s i n θ sin\theta sinθ 0 − s i n θ -sin\theta sinθ c o s θ cos\theta cosθ 0
逆时针旋转角度 θ \theta θ c o s θ cos\theta cosθ − s i n θ -sin\theta sinθ 0 s i n θ sin\theta sinθ c o s θ cos\theta cosθ 0
垂直偏移变换 1 0 0 h 1 0
水平偏移变换 1 h 0 0 1 0

表格来源:https://github.com/datawhalechina/magic-cv

在OpenCV中,需要定义的核心就是2行3列的仿射变换矩阵。

主要函数:

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

参数说明:

  • scr:变换操作的输入图像
  • M:仿射变换矩阵,2行3列
  • dsize: 输出图像的大小,二元元组 (width, height)
  • dst:变换操作的输出图像,可选项
  • flags:插值方法,整型(int),可选项
    • cv2.INTER_LINEAR:线性插值,默认选项
    • cv2.INTER_NEAREST:最近邻插值
    • cv2.INTER_AREA:区域插值
    • cv2.INTER_CUBIC:三次样条插值
    • cv2.INTER_LANCZOS4:Lanczos 插值
  • borderMode:边界像素方法,整型(int),可选项,默认值为 cv2.BORDER_REFLECT
  • borderValue:边界填充值,可选项,默认值为 0(黑色填充)
  • 返回值:dst,变换操作的输出图像,ndarray 多维数组

平移

参照上面的表格,实现平移操作就需要保证 a 0 , a 4 = 1 a_0,a_4=1 a0,a4=1,同时 a 2 , a 5 a_2,a_5 a2,a5分别为x和y的平移距离。
在这里插入图片描述
示例程序:

"""
图像平移
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)
rows, cols, ch = img.shape

dx, dy = 100, 50  # dx=100 向右偏移量, dy=50 向下偏移量
MAT = np.float32([[1, 0, dx], [0, 1, dy]])  # 构造平移变换矩阵
# dst = cv2.warpAffine(img, MAT, (cols, rows))  # 默认为黑色填充
dst = cv2.warpAffine(img, MAT, (cols, rows), borderValue=(255, 255, 255))  # 设置白色填充

plt.figure(figsize=(9, 6))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title("Original")
plt.subplot(122), plt.imshow(cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)), plt.title("Translational")
plt.show()

  

在这里插入图片描述

旋转

以原点为中心

仿射变换矩阵:
在这里插入图片描述
示例程序:

"""
图像旋转(以原点为中心)
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)
rows, cols, ch = img.shape

theta = np.pi / 8.0  # 顺时针旋转角度
cosTheta = np.cos(theta)
sinTheta = np.sin(theta)
MAT = np.float32([[cosTheta, -sinTheta, 0], [sinTheta, cosTheta, 0]])  # 构造旋转变换矩阵
# dst = cv2.warpAffine(img, MAT, (cols, rows))  # 默认为黑色填充
dst = cv2.warpAffine(img, MAT, (cols, rows), borderValue=(255, 255, 255))  # 设置白色填充

plt.figure(figsize=(9, 6))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title("Origin")
plt.subplot(122), plt.imshow(cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)), plt.title("Rotation")
plt.show()


  

在这里插入图片描述

以任意点为中心

以任意点为中心旋转似乎乍一看有些难度,不过换一种思路,可以采用先平移后旋转再平移的操作。
仿射变换矩阵:
在这里插入图片描述
为了操作简便,OpenCV提供了cv2.getRotationMatrix2D函数, 根据旋转角度和位移计算旋转变换矩阵 MAR.

cv2.getRotationMatrix2D(center, angle, scale) → M

参数说明:

  • center:旋转中心坐标,二元元组 (x0, y0)
  • angle:旋转角度,单位为角度,逆时针为正数,顺时针为负数
  • scale: 缩放因子
  • 返回值:M, 旋转变换矩阵,2行3列

示例程序:

"""
图像旋转(以任意点为中心)
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)
height, width = img.shape[:2]  # 图片的高度和宽度

theta1, theta2 = 30, 45  # 顺时针旋转角度,单位为角度
x0, y0 = width // 2, height // 2  # 以图像中心作为旋转中心
MAR1 = cv2.getRotationMatrix2D((x0, y0), theta1, 1.0)
MAR2 = cv2.getRotationMatrix2D((x0, y0), theta2, 1.0)
imgR1 = cv2.warpAffine(img, MAR1, (width, height))  # 旋转变换,默认为黑色填充
imgR2 = cv2.warpAffine(img, MAR2, (width, height), borderValue=(255, 255, 255))  # 设置白色填充

plt.figure(figsize=(10, 6))
plt.subplot(131), plt.axis('off'), plt.title(r"$Origin$")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.subplot(132), plt.axis('off'), plt.title(r"$Rotation {}^o$".format(theta1))
plt.imshow(cv2.cvtColor(imgR1, cv2.COLOR_BGR2RGB))
plt.subplot(133), plt.axis('off'), plt.title(r"$Rotation {}^o$".format(theta2))
plt.imshow(cv2.cvtColor(imgR2, cv2.COLOR_BGR2RGB))
plt.show()

  

在这里插入图片描述

直角旋转

直角旋转指的是旋转角度为 90,180,270度,这时无需再去写仿射变换矩阵。
OpenCV提供了cv2.rotate(src, rotateCode) 这个函数可以快速实现该功能。

cv2.rotate( src, rotateCode[, dst] ) → M

参数说明:

  • src:变换操作的输入图像
  • rotateCode:枚举,指定旋转角度。
    • cv2.ROTATE_90_CLOCKWISE:顺时针旋转 90 度
    • cv2.ROTATE_180: 旋转 180 度
    • cv2.ROTATE_90_COUNTERCLOCKWISE:逆时针旋转 90 度
  • 返回值:dst,变换操作的输出图像,ndarray 多维数组

示例程序:

"""
图像旋转(直角旋转)
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)

imgR90 = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
imgR180 = cv2.rotate(img, cv2.ROTATE_180)
imgR270 = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE)

plt.figure(figsize=(9, 7))
plt.subplot(221), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title(r"$Origin$")
plt.subplot(222), plt.imshow(cv2.cvtColor(imgR90, cv2.COLOR_BGR2RGB)), plt.title(r"$Rotation 90^{o}$")
plt.subplot(223), plt.imshow(cv2.cvtColor(imgR180, cv2.COLOR_BGR2RGB)), plt.title(r"$Rotation 180^{o}$")
plt.subplot(224), plt.imshow(cv2.cvtColor(imgR270, cv2.COLOR_BGR2RGB)), plt.title(r"$Rotation 270^{o}$")
plt.show()

  

在这里插入图片描述

翻转

仿射变换矩阵:
在这里插入图片描述
OpenCV提供了cv2.flip函数,可以将图像沿水平方向、垂直方向、或水平/垂直方向同时进行翻转。

cv2.flip(src, flipCode[, dst]) -> dst

参数说明:

  • scr:变换操作的输入图像
  • flipCode:控制参数,整型(int),flipCode>0 水平翻转,flipCode=0 垂直翻转,flipCode<0 水平和垂直翻转
  • dst:变换操作的输出图像,可选项

示例程序:

"""
图像翻转
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)

imgFlip1 = cv2.flip(img, 0)  # 垂直翻转
imgFlip2 = cv2.flip(img, 1)  # 水平翻转
imgFlip3 = cv2.flip(img, -1)  # 水平和垂直翻转

plt.figure(figsize=(9, 6))
plt.subplot(221), plt.axis('off'), plt.title("Original")
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))  # 原始图像
plt.subplot(222), plt.axis('off'), plt.title("Flipped Horizontally")
plt.imshow(cv2.cvtColor(imgFlip2, cv2.COLOR_BGR2RGB))  # 水平翻转
plt.subplot(223), plt.axis('off'), plt.title("Flipped Vertically")
plt.imshow(cv2.cvtColor(imgFlip1, cv2.COLOR_BGR2RGB))  # 垂直翻转
plt.subplot(224), plt.axis('off'), plt.title("Flipped Horizontally & Vertically")
plt.imshow(cv2.cvtColor(imgFlip3, cv2.COLOR_BGR2RGB))  # 水平垂直翻转
plt.show()

  

在这里插入图片描述

缩放

OpenCV提供了cv2.resize函数,实现图像的缩放。

cv2.resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) → dst

参数说明:

  • scr:变换操作的输入图像
  • dsize: 输出图像的大小,二元元组 (width, height)
  • dst:变换操作的输出图像,可选项
  • fx, fy:x 轴、y 轴上的缩放比例,实型,可选项
  • interpolation:插值方法,整型,可选项
    • cv2.INTER_LINEAR:双线性插值(默认方法)
    • cv2.INTER_AREA:使用像素区域关系重采样,缩小图像时可以避免波纹出现
    • cv2.INTER_NEAREST:最近邻插值
    • cv2.INTER_CUBIC:4x4 像素邻域的双三次插值
    • cv2.INTER_LANCZOS4:8x8 像素邻域的Lanczos插值
  • 返回值:dst,变换操作的输出图像,ndarray 多维数组

示例程序:

"""
图像缩放
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)

height, width = img.shape[:2]  # 图片的高度和宽度
imgZoom1 = cv2.resize(img, (int(0.75 * width), int(height)))
imgZoom2 = cv2.resize(img, None, fx=0.75, fy=1.0, interpolation=cv2.INTER_AREA)

plt.figure(figsize=(8, 6))
plt.subplot(121), plt.axis('off'), plt.title("Zoom: 0.75*W,1.0*H")
plt.imshow(cv2.cvtColor(imgZoom1, cv2.COLOR_BGR2RGB))
plt.subplot(122), plt.axis('off'), plt.title("Zoom: fx=0.75,fy=1.0")
plt.imshow(cv2.cvtColor(imgZoom2, cv2.COLOR_BGR2RGB))
plt.show()

  

在这里插入图片描述

错切

错切指平面景物在投影平面上的非垂直投影,使图像中的图形在水平方向或垂直方向产生扭变。
以水平错切为例,仿射矩阵为
在这里插入图片描述
示例程序:

"""
图像错切
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)
height, width = img.shape[:2]  # 图片的高度和宽度

MAS = np.float32([[1, 0.2, 0], [0, 1, 0]])  # 构造错切变换矩阵
imgShear = cv2.warpAffine(img, MAS, (width, height))

plt.figure(figsize=(9, 6))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title("imgOrigin")
plt.subplot(122), plt.imshow(cv2.cvtColor(imgShear, cv2.COLOR_BGR2RGB)), plt.title("imgShear")
plt.show()

  

在这里插入图片描述

投影变换

投影变换也称透视变换,指的是建立两平面场之间的对应关系,即将图片投影到一个新的视平面。
OpenCV提供了cv2.warpPerspective函数实现投影变换的操作。

cv2.getPerspectiveTransform(src, dst[,solveMethod]) → MP
cv2.warpPerspective(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) → dst

参数说明:

  • src:变换前图像四边形顶点坐标
  • dst:变换后图像四边形顶点坐标
  • solveMethod:矩阵分解方法,传递给 cv2.solve 求解变换矩阵 MP
    • cv2.DECOMP_LU:选择最优轴的高斯消去法,默认方法
    • cv2.DECOMP_SVD:奇异值分解(SVD)方法
    • cv2.DECOMP_EIG:特征值分解方法,src 必须对称
    • cv2.DECOMP_QR:QR(正交三角)分解
    • cv2.DECOMP_CHOLESKY:Cholesky LLT 分解
  • MP:透视变换矩阵,3行3列
  • dsize: 输出图像的大小,二元元组 (width, height)
  • dst:变换操作的输出图像,可选项
  • flags:插值方法,整型(int),可选项
    • cv2.INTER_LINEAR:线性插值,默认选项
    • cv2.INTER_NEAREST:最近邻插值
    • cv2.INTER_AREA:区域插值
    • cv2.INTER_CUBIC:三次样条插值
    • cv2.INTER_LANCZOS4:Lanczos 插值
  • borderMode:边界像素方法,整型(int),可选项,默认值为 cv2.BORDER_REFLECT
  • borderValue:边界填充模式,可选项,默认值为 0(黑色填充)
  • 返回值:dst,透视变换操作的输出图像,ndarray 多维数组

示例程序:

"""
投影变换
"""
import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread("../img/img.jpg")  # 读取彩色图像(BGR)
h, w = img.shape[:2]  # 图片的高度和宽度

pointSrc = np.float32([[0, 0], [w - 1, 0], [0, h - 100], [w - 1, h - 100]])  # 原始图像中 4点坐标
pointDst = np.float32([[180, 50], [w - 180, 50], [0, h - 100], [w - 1, h - 100]])  # 变换图像中 4点坐标
MP = cv2.getPerspectiveTransform(pointSrc, pointDst)  # 计算投影变换矩阵 M
imgP = cv2.warpPerspective(img, MP, (512, 512))  # 用变换矩阵 M 进行投影变换

plt.figure(figsize=(9, 6))
plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title("Original")
plt.subplot(122), plt.imshow(cv2.cvtColor(imgP, cv2.COLOR_BGR2RGB)), plt.title("Projective")
plt.show()

  

在这里插入图片描述

文章来源: zstar.blog.csdn.net,作者:zstar-_,版权归原作者所有,如需转载,请联系作者。

原文链接:zstar.blog.csdn.net/article/details/126348592

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。