商會(huì) 網(wǎng)站模板/推廣技巧
《數(shù)字圖像處理-OpenCV/Python》連載(44)圖像的投影變換
本書京東優(yōu)惠購(gòu)書鏈接:https://item.jd.com/14098452.html
本書CSDN獨(dú)家連載專欄:https://blog.csdn.net/youcans/category_12418787.html
第 6 章 圖像的幾何變換
幾何變換分為等距變換、相似變換、仿射變換和投影變換,是指對(duì)圖像的位置、大小、形狀和投影進(jìn)行變換,將圖像從原始平面投影到新的視平面。OpenCV圖像的幾何變換,本質(zhì)上是將一個(gè)多維數(shù)組通過映射關(guān)系轉(zhuǎn)換為另一個(gè)多維數(shù)組。
本章內(nèi)容概要
- 介紹仿射變換,學(xué)習(xí)使用仿射變換矩陣實(shí)現(xiàn)圖像的仿射變換。
- 學(xué)習(xí)使用函數(shù)實(shí)現(xiàn)圖像的平移、縮放、旋轉(zhuǎn)、翻轉(zhuǎn)和斜切。
- 介紹投影變換,學(xué)習(xí)使用投影變換矩陣實(shí)現(xiàn)圖像的投影變換。
- 介紹圖像的重映射,學(xué)習(xí)使用映射函數(shù)實(shí)現(xiàn)圖像的自定義變換和動(dòng)態(tài)變換。
6.6 圖像的投影變換
透視變換(Perspective Transformation)是OpenCV中常用的投影變換,是指將圖像投影到一個(gè)新的視平面。投影變換的特點(diǎn)是原始圖像中的平行關(guān)系和比例關(guān)系都可以改變,但圖像中的直線在投影變換后仍然能保持直線。
投影變換可以通過對(duì)三維空間中的物體旋轉(zhuǎn)進(jìn)行校正,主要用于圖像拼接和校正透視投影導(dǎo)致的圖像失真。
投影變換的方法是在原始圖像上確定不共線的4個(gè)點(diǎn),給定這4個(gè)點(diǎn)在變換圖像中的位置,就確定了一個(gè)投影變換,其變換關(guān)系可以由如下的3×3矩陣來描述。
[ x ~ y ~ z ~ ] = M P [ x y z ] , M P = [ M 11 M 12 M 13 M 21 M 22 M 23 M 31 M 32 M 33 ] \begin{bmatrix} \tilde{x}\\ \tilde{y}\\ \tilde{z} \end{bmatrix} = M_P \begin{bmatrix} x\\ y\\ z \end{bmatrix} ,\hspace{1em} M_P= \begin{bmatrix} M_{11} &M_{12} &M_{13}\\ M_{21} &M_{22} &M_{23}\\ M_{31} &M_{32} &M_{33} \end{bmatrix} ?x~y~?z~? ?=MP? ?xyz? ?,MP?= ?M11?M21?M31??M12?M22?M32??M13?M23?M33?? ?
仿射變換是在二維平面進(jìn)行變換的,而投影變換是在三維坐標(biāo)系進(jìn)行變換的。仿射變換是3點(diǎn)變換,投影變換是4點(diǎn)變換。比較仿射變換與投影變換的描述公式,仿射變換可以被視為z軸不變的透視變換。
在OpenCV中,先由函數(shù)cv.getPerspectiveTransform計(jì)算投影變換矩陣 M P M_P MP?,再由函數(shù)cv.warpPerspective根據(jù)投影變換矩陣 M P M_P MP? 計(jì)算得到投影變換圖像。
函數(shù)cv.getPerspectiveTransform能根據(jù)圖像中不共線的4個(gè)點(diǎn)在變換前后的對(duì)應(yīng)位置坐標(biāo),求解得到投影變換矩陣 M P M_P MP?。
函數(shù)原型
cv.getPerspectiveTransform(src, dst[,solveMethod]) → MP
參數(shù)說明
- src:原始圖像中不共線4個(gè)點(diǎn)的坐標(biāo),是形狀為(4,2)的Numpy數(shù)組。
- dst:投影變換圖像中對(duì)應(yīng)的不共線4個(gè)點(diǎn)的坐標(biāo),是形狀為(4,2)的Numpy數(shù)組。
- solveMethod:矩陣分解方法。
- DECOMP_LU:選擇最佳軸的高斯消元法,默認(rèn)方法。
- DECOMP_SVD:奇異值分解(SVD)方法。
- DECOMP_EIG:特征值分解方法,必須與src對(duì)稱。
- DECOMP_CHOLESKY:Cholesky LLT分解方法。
- DECOMP_QR:正交三角(QR)分解方法。
- DECOMP_NORMAL:使用正則方程,與前述方法聯(lián)合使用。
- MP:投影變換矩陣,是形狀為(3,3)、類型為np.float32的Numpy數(shù)組。
注意問題
- (1)雖然參數(shù)src、dst通常表示輸入、輸出圖像,但在函數(shù)cv.getPerspectiveTransform中是指原始圖像與變換圖像中不共線的4個(gè)點(diǎn),也被稱為四邊形的頂點(diǎn)。
- (2) 參數(shù)src、dst是形狀為(4,2)的Numpy數(shù)組,數(shù)值是圖像中4個(gè)頂點(diǎn)的坐標(biāo)(x,y)。
函數(shù)cv.warpPerspective可通過投影變換矩陣計(jì)算投影變換圖像。
函數(shù)原型
cv.warpPerspective (src, M, dsize[, dst, flags, borderMode, borderValue]) → dst
由投影變換矩陣M計(jì)算投影變換圖像的公式為
參數(shù)說明
- src:原始圖像,是Numpy數(shù)組。
- dst:投影變換輸出圖像,類型與src相同,圖像尺寸由參數(shù)dsize確定。
- M:投影變換矩陣,是形狀為(3,3)、類型為np.float32的Numpy數(shù)組。
- dsize:輸出圖像大小,格式為元組(w,h)。
- flags:插值方法與逆變換標(biāo)志,可選項(xiàng)。
- INTER_LINEAR:雙線性插值,默認(rèn)方法。
- INTER_NEAREST:最近鄰插值。
- WARP_INVERSE_MAP:逆變換標(biāo)志。
- borderMode:邊界擴(kuò)充方法,可選項(xiàng),默認(rèn)為cv.BORDER_CONSTANT。
- borderValue:邊界填充值,可選項(xiàng),默認(rèn)值為0,表示黑色填充。
注意問題
- (1) 輸出圖像大小dsize的格式為(w,h),與OpenCV中圖像形狀(h,w)的順序相反。
- (2) 通過函數(shù)cv.warpPerspective計(jì)算投影變換,投影變換矩陣M的形狀為(3,3),數(shù)據(jù)類型必須是np.float32。
- (3) 當(dāng)flags設(shè)為WARP_INVERSE_MAP時(shí),先由投影變換矩陣計(jì)算逆投影變換矩陣,再計(jì)算輸入圖像的逆投影變換圖像。
【例程0606】基于投影變換實(shí)現(xiàn)圖像校正
手機(jī)或相機(jī)拍攝的照片,通常都存在投影變形。本例程通過投影變換實(shí)現(xiàn)圖像校正。
先用鼠標(biāo)在圖像中依次選取矩形的4個(gè)頂點(diǎn),獲取4個(gè)頂點(diǎn)的坐標(biāo),再根據(jù)長(zhǎng)寬比計(jì)算4個(gè)頂點(diǎn)在投影變換后的坐標(biāo),進(jìn)行投影變換,就可以實(shí)現(xiàn)圖像校正。
# 【0606】基于投影變換實(shí)現(xiàn)圖像校正
import cv2 as cv
import numpy as np
from matplotlib import pyplot as pltdef onMouseAction(event, x, y, flags, param): # 鼠標(biāo)交互 (單擊選點(diǎn),右擊完成)setpoint = (x, y)if event == cv.EVENT_LBUTTONDOWN: # 單擊pts.append(setpoint) # 選中一個(gè)多邊形頂點(diǎn)print("選擇頂點(diǎn) {}:{}".format(len(pts), setpoint))if __name__ == '__main__':img = cv.imread("../images/Fig0602.png") # 讀取彩色圖像(BGR)imgCopy = img.copy()height, width = img.shape[:2]# 鼠標(biāo)交互從輸入圖像選擇 4 個(gè)頂點(diǎn)print("單擊左鍵選擇 4 個(gè)頂點(diǎn) (左上-左下-右下-右上):")pts = [] # 初始化 ROI 頂點(diǎn)坐標(biāo)集合status = True # 進(jìn)入繪圖狀態(tài)cv.namedWindow('origin') # 創(chuàng)建圖像顯示窗口cv.setMouseCallback('origin', onMouseAction, status) # 綁定回調(diào)函數(shù)while True:if len(pts) > 0:cv.circle(imgCopy, pts[-1], 5, (0,0,255), -1) # 繪制最近的一個(gè)頂點(diǎn)if len(pts) > 1:cv.line(imgCopy, pts[-1], pts[-2], (255, 0, 0), 2) # 繪制最近的一段線段if len(pts) == 4: # 已有 4個(gè)頂點(diǎn),結(jié)束繪制cv.line(imgCopy, pts[0], pts[-1], (255,0,0), 2) # 繪制最后的一段線段cv.imshow('origin', imgCopy)cv.waitKey(1000)breakcv.imshow('origin', imgCopy)cv.waitKey(100)cv.destroyAllWindows() # 釋放圖像窗口ptsSrc = np.array(pts) # 列表轉(zhuǎn)換為 (4,2),Numpy 數(shù)組print(ptsSrc)# 計(jì)算投影變換矩陣 MPptsSrc = np.float32(pts) # 列表轉(zhuǎn)換為Numpy數(shù)組,圖像4個(gè)頂點(diǎn)坐標(biāo)為 (x,y)x1, y1, x2, y2 = int(0.1*width), int(0.1*height), int(0.9*width), int(0.9*height)ptsDst = np.float32([[x1,y1], [x1,y2], [x2,y2], [x2,y1]]) # 投影變換后的 4 個(gè)頂點(diǎn)坐標(biāo)MP = cv.getPerspectiveTransform(ptsSrc, ptsDst)# 投影變換dsize = (450, 400) # 輸出圖像尺寸為 (w, h)perspect = cv.warpPerspective(img, MP, dsize, borderValue=(255,255,255)) # 投影變換print(img.shape, ptsSrc.shape, ptsDst.shape)plt.figure(figsize=(9, 3.4))plt.subplot(131), plt.axis('off'), plt.title("1.Original")plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))plt.subplot(132), plt.axis('off'), plt.title("2.Selected vertex")plt.imshow(cv.cvtColor(imgCopy, cv.COLOR_BGR2RGB))plt.subplot(133), plt.axis('off'), plt.title("3.Perspective correction")plt.imshow(cv.cvtColor(perspect, cv.COLOR_BGR2RGB))plt.tight_layout()plt.show()
程序說明:
(1) 本例程設(shè)置了回調(diào)函數(shù),通過鼠標(biāo)交互從輸入圖像選擇了4個(gè)頂點(diǎn)。鼠標(biāo)交互操作的使用方法詳見4.9節(jié)。
(2) 投影變換后4個(gè)頂點(diǎn)的坐標(biāo)是用戶設(shè)定的,可以根據(jù)需要修改。
(3) 基于投影變換實(shí)現(xiàn)圖像校正的運(yùn)行結(jié)果如圖6-6所示,圖6-6(1)所示為原始圖像,圖6-6(2)所示為用鼠標(biāo)在原始圖像上選定棋盤的4個(gè)頂點(diǎn),圖6-6(3)所示為投影變換后的圖像。可以看出,原始圖像中透視拍照的傾斜棋盤被校正為矩形。
*圖6-6 圖像的投影變換
版權(quán)聲明:
youcans@xupt 原創(chuàng)作品,轉(zhuǎn)載必須標(biāo)注原文鏈接:(https://blog.csdn.net/youcans/article/details/134487182)
Copyright 2023 youcans, XUPT
Crated:2023-11-20
歡迎關(guān)注本書CSDN獨(dú)家連載專欄
《數(shù)字圖像處理-OpenCV/Python》連載: https://blog.csdn.net/youcans/category_12418787.html