6.2.3 圖像的仿射變換
圖像的仿射變換涉及到圖像的形狀位置角度的變化,是深度學(xué)習(xí)預(yù)處理中常到的功能,在此簡(jiǎn)單回顧一下。仿射變換具體到圖像中的應(yīng)用,主要是對(duì)圖像的縮放,旋轉(zhuǎn),剪切,翻轉(zhuǎn)和平移的組合。在OpenCV中,仿射變換的矩陣是一個(gè)2×3的矩陣,其中左邊的2×2子矩陣是線性變換矩陣,右邊的2×1的兩項(xiàng)是平移項(xiàng):
對(duì)于圖像上的任一位置(x,y),仿射變換執(zhí)行的是如下的操作:
需要注意的是,對(duì)于圖像而言,寬度方向是x,高度方向是y,坐標(biāo)的順序和圖像像素對(duì)應(yīng)下標(biāo)一致。所以原點(diǎn)的位置不是左下角而是右上角,y的方向也不是向上,而是向下。在OpenCV中實(shí)現(xiàn)仿射變換是通過(guò)仿射變換矩陣和cv2.warpAffine()這個(gè)函數(shù),還是通過(guò)代碼來(lái)理解一下,例子中圖片的分辨率為600×400:
import cv2
import numpy as np
# 讀取一張斯里蘭卡拍攝的大象照片
img = cv2.imread('lanka_safari.jpg')
# 沿著橫縱軸放大1.6倍,然后平移(-150,-240),最后沿原圖大小截取,等效于裁剪并放大
M_crop_elephant = np.array([
[1.6, 0, -150],
[0, 1.6, -240]
], dtype=np.float32)
img_elephant = cv2.warpAffine(img, M_crop_elephant, (400, 600))
cv2.imwrite('lanka_elephant.jpg', img_elephant)
# x軸的剪切變換,角度15°
theta = 15 * np.pi / 180
M_shear = np.array([
[1, np.tan(theta), 0],
[0, 1, 0]
], dtype=np.float32)
img_sheared = cv2.warpAffine(img, M_shear, (400, 600))
cv2.imwrite('lanka_safari_sheared.jpg', img_sheared)
# 順時(shí)針旋轉(zhuǎn),角度15°
M_rotate = np.array([
[np.cos(theta), -np.sin(theta), 0],
[np.sin(theta), np.cos(theta), 0]
], dtype=np.float32)
img_rotated = cv2.warpAffine(img, M_rotate, (400, 600))
cv2.imwrite('lanka_safari_rotated.jpg', img_rotated)
# 某種變換,具體旋轉(zhuǎn)+縮放+旋轉(zhuǎn)組合可以通過(guò)SVD分解理解
M = np.array([
[1, 1.5, -400],
[0.5, 2, -100]
], dtype=np.float32)
img_transformed = cv2.warpAffine(img, M, (400, 600))
cv2.imwrite('lanka_safari_transformed.jpg', img_transformed)
代碼實(shí)現(xiàn)的操作示意在下圖中:
6.2.4 基本繪圖
OpenCV提供了各種繪圖的函數(shù),可以在畫(huà)面上繪制線段,圓,矩形和多邊形等,還可以在圖像上指定位置打印文字,比如下面例子:
import numpy as np
import cv2
# 定義一塊寬600,高400的畫(huà)布,初始化為白色
canvas = np.zeros((400, 600, 3), dtype=np.uint8) + 255
# 畫(huà)一條縱向的正中央的黑色分界線
cv2.line(canvas, (300, 0), (300, 399), (0, 0, 0), 2)
# 畫(huà)一條右半部份畫(huà)面以150為界的橫向分界線
cv2.line(canvas, (300, 149), (599, 149), (0, 0, 0), 2)
# 左半部分的右下角畫(huà)個(gè)紅色的圓
cv2.circle(canvas, (200, 300), 75, (0, 0, 255), 5)
# 左半部分的左下角畫(huà)個(gè)藍(lán)色的矩形
cv2.rectangle(canvas, (20, 240), (100, 360), (255, 0, 0), thickness=3)
# 定義兩個(gè)三角形,并執(zhí)行內(nèi)部綠色填充
triangles = np.array([
[(200, 240), (145, 333), (255, 333)],
[(60, 180), (20, 237), (100, 237)]])
cv2.fillPoly(canvas, triangles, (0, 255, 0))
# 畫(huà)一個(gè)黃色五角星
# 第一步通過(guò)旋轉(zhuǎn)角度的辦法求出五個(gè)頂點(diǎn)
phi = 4 * np.pi / 5
rotations = [[[np.cos(i * phi), -np.sin(i * phi)], [i * np.sin(phi), np.cos(i * phi)]] for i in range(1, 5)]
pentagram = np.array([[[[0, -1]] + [np.dot(m, (0, -1)) for m in rotations]]], dtype=np.float)
# 定義縮放倍數(shù)和平移向量把五角星畫(huà)在左半部分畫(huà)面的上方
pentagram = np.round(pentagram * 80 + np.array([160, 120])).astype(np.int)
# 將5個(gè)頂點(diǎn)作為多邊形頂點(diǎn)連線,得到五角星
cv2.polylines(canvas, pentagram, True, (0, 255, 255), 9)
# 按像素為間隔從左至右在畫(huà)面右半部份的上方畫(huà)出HSV空間的色調(diào)連續(xù)變化
for x in range(302, 600):
color_pixel = np.array([[[round(180*float(x-302)/298), 255, 255]]], dtype=np.uint8)
line_color = [int(c) for c in cv2.cvtColor(color_pixel, cv2.COLOR_HSV2BGR)[0][0]]
cv2.line(canvas, (x, 0), (x, 147), line_color)
# 如果定義圓的線寬大于半斤,則等效于畫(huà)圓點(diǎn),隨機(jī)在畫(huà)面右下角的框內(nèi)生成坐標(biāo)
np.random.seed(42)
n_pts = 30
pts_x = np.random.randint(310, 590, n_pts)
pts_y = np.random.randint(160, 390, n_pts)
pts = zip(pts_x, pts_y)
# 畫(huà)出每個(gè)點(diǎn),顏色隨機(jī)
for pt in pts:
pt_color = [int(c) for c in np.random.randint(0, 255, 3)]
cv2.circle(canvas, pt, 3, pt_color, 5)
# 在左半部分最上方打印文字
cv2.putText(canvas,
'Python-OpenCV Drawing Example',
(5, 15),
cv2.FONT_HERSHEY_SIMPLEX,
0.5,
(0, 0, 0),
1)
cv2.imshow('Example of basic drawing functions', canvas)
cv2.waitKey()
執(zhí)行這段代碼得到如下的圖像:
評(píng)論
查看更多