美文网首页算法Pythonpython开发
python之详细图像仿射变换讲解(图像平移、旋转、缩放、翻转)

python之详细图像仿射变换讲解(图像平移、旋转、缩放、翻转)

作者: 奈何缘浅wyj | 来源:发表于2020-09-22 08:31 被阅读0次

    作者:奈何缘浅wyj
    博客主页:https://blog.csdn.net/ywsydwsbn
    字数:17198
    简介:先点赞在看,养成习惯!!!

    图像仿射变换

    仿射变换简介

    什么是仿射变换

    图像上的仿射变换, 其实就是图片中的一个像素点,通过某种变换,移动到另外一个地方

    从数学上来讲, 就是一个向量空间进行一次线形变换并加上平移向量, 从而变换到另外一个向量空间的过程

    向量空间m : m=(x,y)

    向量空间n : n=(x′,y′)

    向量空间从m到n的变换 n=A∗m+b

    整理得到:


    image.png

    将A跟b 组合在一起就组成了仿射矩阵 M。 它的维度是2∗3


    image.png

    使用不同的矩阵M,就获得了不同的2D仿射变换效果

    在opencv中,实现2D仿射变换, 需要借助warpAffine 函数。

    cv2.warpAffine(image, M, (image.shape[1], image.shape[0])
    

    接下来,带你结合具体的2D仿射变换,分析其变换矩阵。

    图像平移

    公式推导

    平移可以说是最简单的一种空间变换。其表达式为:


    image.png

    其中(b0,b1) 是偏移量。

    例程

    如果是向右平移10个像素, 向下平移30个像素的话, 那么变换矩阵M可以为:


    image.png

    演示代码

    向右平移10个像素, 向下平移30个像素:

    import cv2
    import numpy as np
    
    img = cv2.imread('lena1.jpg')
    height,width,channel = img.shape
    
    # 声明变换矩阵 向右平移10个像素, 向下平移30个像素
    M = np.float32([[1, 0, 10], [0, 1, 30]])
    # 进行2D 仿射变换
    shifted = cv2.warpAffine(img, M, (width, height))
    
    cv2.imwrite('shift_right_10_down_30.jpg', shifted)
    
    

    原始图像:


    image.png

    向右平移10个像素, 向下平移30个像素图像:


    image.png

    向左平移10个像素, 向上平移30个像素:

    # 声明变换矩阵 向左平移10个像素, 向上平移30个像素
    M = np.float32([[1, 0, -10], [0, 1, -30]])
    # 进行2D 仿射变换
    shifted = cv2.warpAffine(img, M, (width, height))
    
    cv2.imwrite('shift_right_-10_down_-30.jpg', shifted)
    
    

    仿射变换图像:


    image.png

    图像平移v2

    我们可以用translate这个函数把这个操作封装一下:

    def translate(image, x, y):
    
        M = np.float32([[1, 0, x], [0, 1, y]])
        shifted = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
        return shifted
    
    

    完成一些的代码:

    import cv2
    import numpy as np
    
    img = cv2.imread('cat.png')
    
    def translate(image, x, y):
    
        M = np.float32([[1, 0, x], [0, 1, y]])
        shifted = cv2.warpAffine(image, M, (image.shape[1], image.shape[0]))
        return shifted
    
    shifted = translate(img, 10, 30)
    cv2.imwrite('shift_right_10_down_30.png', shifted)
    
    

    处理结果同上。。。

    图像旋转

    利用getRotationMatrix2D实现旋转

    opencv中getRotationMatrix2D函数可以直接帮我们生成M 而不需要我们在程序里计算三角函数:

    getRotationMatrix2D(center, angle, scale)
    
    

    参数解析

    • center 旋转中心点 (cx, cy) 你可以随意指定
    • angle 旋转的角度 单位是角度 逆时针方向为正方向 , 角度为正值代表逆时针
    • scale 缩放倍数. 值等于1.0代表尺寸不变

    该函数返回的就是仿射变换矩阵M

    示例代码

    import cv2
    import numpy as np
    
    # 获取旋转矩阵
    rotateMatrix = cv2.getRotationMatrix2D((100, 200), 90, 1.0)
    
    #设置numpy矩阵的打印格式
    np.set_printoptions(precision=2,suppress=True)
    print(rotateMatrix)
    
    OUTPUT
    [[   0.    1. -100.]
     [  -1.    0.  300.]]
    

    为了使用方便, 你也可以封装一下旋转过程

    def rotate(image, angle, center = None, scale = 1.0):
    
        (h, w) = image.shape[:2]
    
        if center is None:
            center = (w / 2, h / 2)
    
        M = cv2.getRotationMatrix2D(center, angle, scale)
        rotated = cv2.warpAffine(image, M, (w, h))
    
        return rotated
    
    

    演示代码

    # -*- coding: utf-8 -*- 
    '''
    围绕原点处旋转 (图片左上角) 正方向为逆时针
    利用getRotationMatrix2D函数生成仿射矩阵
    '''
    import numpy as np
    import cv2
    from math import cos,sin,radians
    from matplotlib import pyplot as plt
    
    img = cv2.imread('lena1.jpg')
    
    height, width, channel = img.shape
    
    # 求得图片中心点, 作为旋转的轴心
    cx = int(width / 2)
    cy = int(height / 2)
    # 旋转的中心
    center = (cx, cy)
    
    new_dim = (width, height)
    
    # 进行2D 仿射变换
    # 围绕原点 逆时针旋转30度
    M = cv2.getRotationMatrix2D(center=center,angle=30, scale=1.0)
    rotated_30 = cv2.warpAffine(img, M, new_dim)
    
    # 围绕原点 逆时针旋转30度
    M = cv2.getRotationMatrix2D(center=center,angle=45, scale=1.0)
    rotated_45 = cv2.warpAffine(img, M, new_dim)
    
    # 围绕原点  逆时针旋转30度
    M = cv2.getRotationMatrix2D(center=center,angle=60, scale=1.0)
    rotated_60 = cv2.warpAffine(img, M, new_dim)
    
    plt.subplot(221)
    plt.title("Src Image")
    plt.imshow(img[:,:,::-1])
    
    plt.subplot(222)
    plt.title("Rotated 30 Degree")
    plt.imshow(rotated_30[:,:,::-1])
    
    plt.subplot(223)
    plt.title("Rotated 45 Degree")
    plt.imshow(rotated_45[:,:,::-1])
    
    plt.subplot(224)
    plt.title("Rotated 60 Degree")
    plt.imshow(rotated_60[:,:,::-1])
    
    plt.show()
    
    

    原始图形:


    image.png

    图像旋转图像(逆时针30度、45度、60度):


    在这里插入图片描述

    利用wrapAffine实现缩放

    数学原理推导

    围绕原点进行旋转


    image.png 在这里插入图片描述

    由此我们得出


    image.png

    所以对应的变换矩阵为


    image.png

    注意,这里我们进行公式推导的时候,参照的原点是在左下角, 而在OpenCV中图像的原点在图像的左上角, 所以我们在代码里面对theta取反。

    我们可以利用math包中的三角函数。但是有一点需要注意 :三角函数输入的角度是弧度制而不是角度制

    我们需要使用radians(x) 函数, 将角度转变为弧度。

    import math
    math.radians(180)
    
    3.141592653589793
    

    代码演示

    # -*- coding: utf-8 -*- 
    '''
    围绕原点处旋转 (图片左上角) 正方向为逆时针
    '''
    import numpy as np
    import cv2
    import math
    from matplotlib import pyplot as plt
    
    img = cv2.imread('lena1.jpg')
    
    height, width, channel = img.shape
    
    def getRotationMatrix2D(theta):
        # 角度值转换为弧度值
        # 因为图像的左上角是原点 需要×-1
        theta = math.radians(-1*theta)
    
        M = np.float32([
            [math.cos(theta), -math.sin(theta), 0],
            [math.sin(theta), math.cos(theta), 0]])
        return M
    
    # 进行2D 仿射变换
    # 围绕原点 顺时针旋转30度
    M = getRotationMatrix2D(30)
    rotated_30 = cv2.warpAffine(img, M, (width, height))
    
    # 围绕原点 顺时针旋转45度
    M = getRotationMatrix2D(45)
    rotated_45 = cv2.warpAffine(img, M, (width, height))
    
    # 围绕原点 顺时针旋转60度
    M = getRotationMatrix2D(60)
    rotated_60 = cv2.warpAffine(img, M, (width, height))
    
    plt.subplot(221)
    plt.title("Src Image")
    plt.imshow(img[:,:,::-1])
    
    plt.subplot(222)
    plt.title("Rotated 30 Degree")
    plt.imshow(rotated_30[:,:,::-1])
    
    plt.subplot(223)
    plt.title("Rotated 45 Degree")
    plt.imshow(rotated_45[:,:,::-1])
    
    plt.subplot(224)
    plt.title("Rotated 60 Degree")
    plt.imshow(rotated_60[:,:,::-1])
    
    plt.show()
    
    

    原始图像:


    原始图像

    旋转之后演示图:


    旋转之后演示图

    围绕任意点进行旋转

    数学原理推导

    那么如何围绕任意点进行旋转呢?

    可以先把当前的旋转中心点平移到原点处, 在原点处旋转后再平移回去

    假定旋转中心为 (cx,cy)

    image.png

    其中


    在这里插入图片描述

    所以


    在这里插入图片描述

    代码演示

    # -*- coding: utf-8 -*- 
    '''
    围绕画面中的任意一点旋转
    '''
    import numpy as np
    import cv2
    from math import cos,sin,radians
    from matplotlib import pyplot as plt
    
    img = cv2.imread('lena1.jpg')
    
    height, width, channel = img.shape
    
    theta = 45
    
    def getRotationMatrix2D(theta, cx=0, cy=0):
        # 角度值转换为弧度值
        # 因为图像的左上角是原点 需要×-1
        theta = radians(-1 * theta)
    
        M = np.float32([
            [cos(theta), -sin(theta), (1-cos(theta))*cx + sin(theta)*cy],
            [sin(theta), cos(theta), -sin(theta)*cx + (1-cos(theta))*cy]])
        return M
    
    # 求得图片中心点, 作为旋转的轴心
    cx = int(width / 2)
    cy = int(height / 2)
    
    # 进行2D 仿射变换
    # 围绕原点 逆时针旋转30度
    M = getRotationMatrix2D(30, cx=cx, cy=cy)
    rotated_30 = cv2.warpAffine(img, M, (width, height))
    
    # 围绕原点 逆时针旋转45度
    M = getRotationMatrix2D(45, cx=cx, cy=cy)
    rotated_45 = cv2.warpAffine(img, M, (width, height))
    
    # 围绕原点 逆时针旋转60度
    M = getRotationMatrix2D(60, cx=cx, cy=cy)
    rotated_60 = cv2.warpAffine(img, M, (width, height))
    
    plt.subplot(221)
    plt.title("Src Image")
    plt.imshow(img[:,:,::-1])
    
    plt.subplot(222)
    plt.title("Rotated 30 Degree")
    plt.imshow(rotated_30[:,:,::-1])
    
    plt.subplot(223)
    plt.title("Rotated 45 Degree")
    plt.imshow(rotated_45[:,:,::-1])
    
    plt.subplot(224)
    plt.title("Rotated 60 Degree")
    plt.imshow(rotated_60[:,:,::-1])
    
    plt.show()
    
    

    旋转效果:

    围绕图片中心点旋转30度至60度


    在这里插入图片描述

    图像缩放

    利用resize函数实现缩放

    opencv其实有专门进行图像缩放的函数resize

    resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) -> dst
    
    

    参数解析

    • src 输入图片
    • dsize 输出图片的尺寸
    • dst 输出图片
    • fx x轴的缩放因子
    • fy y轴的缩放因子
    • interpolation 插值方式
    • INTER_NEAREST - 最近邻插值
    • INTER_LINEAR - 线性插值(默认)
    • INTER_AREA - 区域插值
    • INTER_CUBIC - 三次样条插值
    • INTER_LANCZOS4 - Lanczos插值

    在使用的时候, 我们可以传入指定的图片的尺寸dsize

    '''
    使用resize函数对图像进行缩放
    '''
    import cv2
    import numpy as np
    
    img = cv2.imread('lena1.jpg')
    height,width,channel = img.shape
    
    # 声明新的维度
    new_dimension = (400, 400)
    # 指定新图片的维度与插值算法(interpolation)
    resized = cv2.resize(img, new_dimension)
    
    cv2.imwrite('lena_resize_400_400.png', resized)
    
    

    原始图像:


    原始图像

    缩放后的图像:


    缩放后的图像

    或者指定缩放因子fx,fy

    dsize 设置为 None, 然后指定fx fy

    import cv2
    import numpy as np
    
    img = cv2.imread('lena1.jpg')
    height,width,channel = img.shape
    
    # 指定新图片的维度与插值算法(interpolation)
    resized = cv2.resize(img, None, fx=1.5, fy=2)
    
    cv2.imwrite('lena_resize_fx_fy.jpg', resized)
    
    

    运行结果如下:


    在这里插入图片描述

    或者指定输出图片,并传入输出图片的size:

    '''
    根据fx跟fy进行图像缩放
    '''
    import cv2
    import numpy as np
    
    img = cv2.imread('lena1.jpg')
    height,width,channel = img.shape
    
    # 指定输出图片
    dst = np.zeros((100, 100, 3), dtype='uint8')
    
    # 指定新图片的维度与插值算法(interpolation)
    cv2.resize(img, dst=dst, dsize=(dst.shape[1], dst.shape[0]), fx=1.5, fy=2)
    
    cv2.imwrite('lena_resize_from_dst.jpg', dst)
    
    

    运行结果如下:


    image.png

    更详细的使用说明见opencv-resize 文档

    为了方便使用, 我们也可以将其封装成函数

    def resize(image, width = None, height = None, inter = cv2.INTER_AREA):
        dim = None
        (h, w) = image.shape[:2]
    
        if width is None and height is None:
            return image
    
        if width is None:
            r = height / float(h)
            dim = (int(w * r), height)
    
        if height is None:
            r = width / float(w)
            dim = (width, int(h * r))
    
        if width and height:
            dim = (width, height)
    
        resized = cv2.resize(image, dim, interpolation = inter)
        return resized
    
    

    分辨率 从 5 * 5 放大到 1000 * 1000, 选择不同的插值算法,对应的演示效果

    '''
    差值算法对比
    '''
    import cv2
    import numpy as np
    from matplotlib import pyplot as plt
    
    img = np.uint8(np.random.randint(0,255,size=(5,5)))
    height,width= img.shape
    
    # 声明新的维度
    new_dimension = (1000, 1000)
    
    plt.subplot(231)
    plt.title("SRC Image")
    plt.imshow(img,cmap='seismic')
    
    plt.subplot(232)
    resized = cv2.resize(img, new_dimension, interpolation = cv2.INTER_NEAREST)
    plt.title("INTER_NEAREST")
    plt.imshow(resized,cmap='seismic')
    
    plt.subplot(233)
    resized = cv2.resize(img, new_dimension, interpolation = cv2.INTER_LINEAR)
    plt.title("INTER_LINEAR")
    plt.imshow(resized,cmap='seismic')
    
    plt.subplot(234)
    resized = cv2.resize(img, new_dimension, interpolation = cv2.INTER_AREA)
    plt.title("INTER_AREA")
    plt.imshow(resized,cmap='seismic')
    
    plt.subplot(235)
    resized = cv2.resize(img, new_dimension, interpolation = cv2.INTER_CUBIC)
    plt.title("INTER_CUBIC")
    plt.imshow(resized,cmap='seismic')
    
    plt.subplot(236)
    resized = cv2.resize(img, new_dimension, interpolation = cv2.INTER_LANCZOS4)
    plt.title("INTER_LANCZOS4")
    plt.imshow(resized,cmap='seismic')
    
    plt.show()
    
    
    image.png 在这里插入图片描述

    利用wrapAffine实现缩放

    数学原理

    对图像的伸缩变换的变换矩阵M为


    image.png

    其中,

    fx:代表x轴的焦距(缩放因子)

    fy:代表y轴的焦距(缩放因子)

    则可以得出以下式子:
    [图片上传失败...(image-5f04e3-1600733898607)]

    具体代码演示

    源代码:

    '''
    使用仿射矩阵实现
    '''
    import numpy as np
    import cv2
    
    img = cv2.imread('lena1.jpg')
    
    height,width,channel = img.shape
    
    # x轴焦距 1.5倍
    fx = 1.5
    # y轴焦距 2倍
    fy = 2
    
    # 声明变换矩阵 向右平移10个像素, 向下平移30个像素
    M = np.float32([[fx, 0, 0], [0, fy, 0]])
    
    # 进行2D 仿射变换
    resized = cv2.warpAffine(img, M, (int(width*fx), int(height*fy)))
    cv2.imwrite('resize_raw.jpg', resized)
    
    

    运行效果:

    原始图像:


    在这里插入图片描述 在这里插入图片描述

    我们利用random 模块生成一个5×5的随机矩阵

    # 生成一个随机噪点
    img = np.uint8(np.random.randint(0,255,size=(5,5)))
    
    

    源代码:

    '''
    仿射矩阵实现缩放 fx,fy
    '''
    import numpy as np
    import cv2
    from matplotlib import pyplot as plt
    # 生成一个随机噪点
    img = np.uint8(np.random.randint(0,255,size=(5,5)))
    
    height,width = img.shape
    
    # x轴焦距 1.5倍
    fx = 1.5
    # y轴焦距 2倍
    fy = 2
    
    # 声明变换矩阵 向右平移10个像素, 向下平移30个像素
    M = np.float32([[fx, 0, 0], [0, fy, 0]])
    
    # 进行2D 仿射变换
    resized = cv2.warpAffine(img, M, (int(width*fx), int(height*fy)))
    
    print(img)
    print(resized)
    
    # 数据可视化
    plt.subplot(121)
    plt.imshow(img, cmap="gray")
    plt.subplot(122)
    plt.imshow(resized,cmap="gray")
    plt.show()
    
    

    原图:

    [[224  25  25 165  16]
     [ 37 170 114  16 101]
     [181   5   7  94  41]
     [206 167  23 133 115]
     [217 115 154  97  65]]
    
    

    缩放后:

    [[224  93  25  25 117 114  16]
     [131 109  88  70  83  80  59]
     [ 37 124 151 114  50  45 101]
     [109  95  78  61  57  61  71]
     [181  66   6   7  64  76  41]
     [194 123  62  15  80 101  78]
     [206 180 118  23  95 127 115]
     [212 165 123  89 106 106  90]
     [217 150 128 154 117  86  65]
     [109  75  64  77  58  43  33]]
    
    

    为了更加直观的感受, 我们可以进行数据可视化。

    我们使用matplotlib进行绘制 resize前与resize之后的图片。

    在这里插入图片描述

    图像翻转

    使用flip函数实现翻转

    flip 函数原型

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

    参数解析

    • src 输入图片
    • flipCode 翻转代码
    • 1 水平翻转 Horizontally (图片第二维度是column)
    • 0 垂直翻转 *Vertically * (图片第一维是row)
    • -1 同时水平翻转与垂直反转 Horizontally & Vertically

    为了方便使用, 你也可以封装成下面的函数

    def flip(image, direction):
        if direction == "h":
            flipped = cv2.flip(image, 1)
        elif direction == "v":
            flipped = cv2.flip(image, 0)
        else:
            # both horizontally and vertically
            flipped = cv2.flip(image, -1)
    
    

    具体源码及效果展示

    '''
    反转Demo
    '''
    import numpy as np
    import cv2
    from matplotlib import pyplot as plt
    
    img = cv2.imread('lena1.jpg')
    
    def bgr2rbg(img):
        '''
            将颜色空间从BGR转换为RBG
        '''
        return img[:,:,::-1]
    
    # 水平翻转
    flip_h = cv2.flip(img, 1)
    # 垂直翻转
    flip_v = cv2.flip(img, 0)
    # 同时水平翻转与垂直翻转
    flip_hv = cv2.flip(img, -1)
    
    plt.subplot(221)
    plt.title('SRC')
    plt.imshow(bgr2rbg(img))
    
    plt.subplot(222)
    plt.title('Horizontally')
    plt.imshow(bgr2rbg(flip_h))
    
    plt.subplot(223)
    plt.title('Vertically')
    plt.imshow(bgr2rbg(flip_v))
    
    plt.subplot(224)
    plt.title('Horizontally & Vertically')
    plt.imshow(bgr2rbg(flip_hv))
    
    plt.show()
    
    
    在这里插入图片描述

    利用numpy的索引实现翻转

    利用numpyndarray的索引, 我们可以非常方便地实现图像翻转。

    # 水平翻转
    flip_h =  img[:,::-1]
    # 垂直翻转
    flip_v =  img[::-1]
    # 水平垂直同时翻转
    flip_hv =  img[::-1, ::-1]
    
    

    具体源码及效果展示

    '''
    使用numpy的索引进行图像反转
    '''
    import cv2
    import numpy as np
    from matplotlib import pyplot as plt
    
    img = cv2.imread('lena1.jpg')
    height,width,channel = img.shape
    
    # 水平翻转
    flip_h =  img[:,::-1]
    
    # 垂直翻转
    flip_v =  img[::-1]
    
    # 水平垂直同时翻转
    flip_hv =  img[::-1, ::-1]
    
    def bgr2rbg(img):
        '''
            将颜色空间从BGR转换为RBG
        '''
        return img[:,:,::-1]
    
    plt.subplot(221)
    plt.title('SRC')
    plt.imshow(bgr2rbg(img))
    
    plt.subplot(222)
    plt.title('Horizontally')
    plt.imshow(bgr2rbg(flip_h))
    
    plt.subplot(223)
    plt.title('Vertically')
    plt.imshow(bgr2rbg(flip_v))
    
    plt.subplot(224)
    plt.title('Horizontally & Vertically')
    plt.imshow(bgr2rbg(flip_hv))
    
    plt.show()
    
    
    在这里插入图片描述

    利用wrapAffine实现翻转

    图像翻转的数学原理

    注: width 代表图像的宽度; height代表图像的高度

    水平翻转的变换矩阵

    image.png

    垂直翻转的变换矩阵

    image.png

    同时进行水平翻转与垂直翻转

    image.png

    具体源码及效果展示

    '''
    使用仿射矩阵实现反转
    '''
    import cv2
    import numpy as np
    from matplotlib import pyplot as plt
    
    img = cv2.imread('lena1.jpg')
    height,width,channel = img.shape
    
    # 水平翻转
    M1 = np.float32([[-1, 0, width], [0, 1, 0]])
    flip_h =  cv2.warpAffine(img, M1, (width, height))
    
    # 垂直翻转
    M2 = np.float32([[1, 0, 0], [0, -1, height]])
    flip_v =  cv2.warpAffine(img, M2, (width, height))
    
    # 水平垂直同时翻转
    M3 = np.float32([[-1, 0, width], [0, -1, height]])
    flip_hv =  cv2.warpAffine(img, M3, (width, height))
    
    def bgr2rbg(img):
        '''
            将颜色空间从BGR转换为RBG
        '''
        return img[:,:,::-1]
    
    plt.subplot(221)
    plt.title('SRC')
    plt.imshow(bgr2rbg(img))
    
    plt.subplot(222)
    plt.title('Horizontally')
    plt.imshow(bgr2rbg(flip_h))
    
    plt.subplot(223)
    plt.title('Vertically')
    plt.imshow(bgr2rbg(flip_v))
    
    plt.subplot(224)
    plt.title('Horizontally & Vertically')
    plt.imshow(bgr2rbg(flip_hv))
    
    plt.show()
    
    
    在这里插入图片描述

    资源传送门

    1. 关注【做一个柔情的程序猿】公众号
    2. 在【做一个柔情的程序猿】公众号后台回复 【python资料】【2020秋招】 即可获取相应的惊喜哦!

    「❤️ 感谢大家」

    1. 点赞支持下吧,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-
    2. 欢迎在留言区与我分享你的想法,也欢迎你在留言区记录你的思考过程。

    相关文章

      网友评论

        本文标题:python之详细图像仿射变换讲解(图像平移、旋转、缩放、翻转)

        本文链接:https://www.haomeiwen.com/subject/zomiyktx.html