python詳細圖像仿射變換講解


仿射變換簡介

什么是放射變換

圖像上的仿射變換, 其實就是圖片中的一個像素點,通過某種變換,移動到另外一個地方

從數學上來講, 就是一個向量空間進行一次線形變換並加上平移向量, 從而變換到另外一個向量空間的過程

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

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

向量空間從m到n的變換 n=A∗m+b

整理得到:

在這里插入圖片描述

 

將A跟b 組合在一起就組成了仿射矩陣 M。 它的維度是2∗3

在這里插入圖片描述

 

使用不同的矩陣M,就獲得了不同的2D仿射變換效果

在opencv中,實現2D仿射變換, 需要借助warpAffine 函數。

cv2.warpAffine(image, M, (image.shape[1], image.shape[0])
復制代碼

接下來,帶你結合具體的2D仿射變換,分析其變換矩陣。

圖像平移

公式推導

平移可以說是最簡單的一種空間變換。其表達式為:

在這里插入圖片描述

 

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

例程

如果是向右平移10個像素, 向下平移30個像素的話, 那么變換矩陣M可以為:

在這里插入圖片描述

 

演示代碼

向右平移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)
復制代碼

原始圖像:

在這里插入圖片描述
向右平移10個像素, 向下平移30個像素圖像:
在這里插入圖片描述

 

向左平移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)
復制代碼

仿射變換圖像:

在這里插入圖片描述

 

圖像平移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()
復制代碼

原始圖形:

在這里插入圖片描述
圖像旋轉圖像(逆時針30度、45度、60度):
在這里插入圖片描述

 

利用wrapAffine實現縮放

數學原理推導

圍繞原點進行旋轉

在這里插入圖片描述
在這里插入圖片描述
由此我們得出
在這里插入圖片描述
所以對應的變換矩陣為
在這里插入圖片描述

 

注意,這里我們進行公式推導的時候,參照的原點是在左下角, 而在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)

在這里插入圖片描述
其中
在這里插入圖片描述
所以
在這里插入圖片描述

 

代碼演示

# -*- 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)
復制代碼

運行結果如下:

在這里插入圖片描述

 

更詳細的使用說明見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()
復制代碼

 

在這里插入圖片描述
在這里插入圖片描述

 

利用wrapAffine實現縮放

數學原理

對圖像的伸縮變換的變換矩陣M為

在這里插入圖片描述
其中,

 

fx:代表x軸的焦距(縮放因子)

fy:代表y軸的焦距(縮放因子)

則可以得出以下式子:

在這里插入圖片描述

 

具體代碼演示

源代碼:

'''
使用仿射矩陣實現
'''
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()

12345678910111213141516171819202122232425262728293031323334353637383940414243
復制代碼

 

在這里插入圖片描述

 

利用wrapAffine實現翻轉

圖像翻轉的數學原理

注: width 代表圖像的寬度; height代表圖像的高度

水平翻轉的變換矩陣

在這里插入圖片描述
垂直翻轉的變換矩陣
在這里插入圖片描述
同時進行水平翻轉與垂直翻轉
在這里插入圖片描述

 

具體源碼及效果展示

'''
使用仿射矩陣實現反轉
'''
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()
復制代碼

 

在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM