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


    最近想对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 ]

    [xy1]" role="presentation" style="position: relative;">[xy1]
    =
    [a0a1a2a3a4a5001]" role="presentation" style="position: relative;">[a0a1a2a3a4a5001]
    [x0y01]" role="presentation" style="position: relative;">[x0y01]
    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
    平移10 △ x \triangle x x01 △ y \triangle y y
    均匀缩放 s s s000 s s s0
    不均匀缩放 s x s_x sx000 s y s_y sy0
    顺时针旋转角度 θ \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
    垂直偏移变换100h10
    水平偏移变换1h0010

    表格来源: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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    旋转

    以原点为中心

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

    """
    图像旋转(以原点为中心)
    """
    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()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    以任意点为中心

    以任意点为中心旋转似乎乍一看有些难度,不过换一种思路,可以采用先平移后旋转再平移的操作。
    仿射变换矩阵:
    在这里插入图片描述
    为了操作简便,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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述

    直角旋转

    直角旋转指的是旋转角度为 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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    翻转

    仿射变换矩阵:
    在这里插入图片描述
    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    缩放

    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    错切

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

    """
    图像错切
    """
    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    投影变换

    投影变换也称透视变换,指的是建立两平面场之间的对应关系,即将图片投影到一个新的视平面。
    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

  • 相关阅读:
    大数据架构设计模式主要考虑八方面问题
    Python 基于 TCP 传输协议的网络通信实现
    【笔记】神经网络中的注意力机制
    Linux帧缓冲注册OLED驱动
    软件过程与建模学习之:Quality Management
    求1~100000之间所有的“水仙花数”,并输出
    uwsgi配置Django(在树莓派系统linux)
    无限递归一定会栈溢出吗
    【Python】文本文件处理
    CPI教程-异步接口创建及使用
  • 原文地址:https://blog.csdn.net/qq1198768105/article/details/126348592