仿射变换和透视变换
前言
在前面做換臉的博客中提到了使用仿射變換和透視變換將兩張不同的人臉基于關鍵點進行對齊,保證一張人臉貼到另一張人臉時,大小完全一致;所以有必要理解一下這兩個概念的區別,由于以實用性為目的,所以所有的圖像算法都會以opencv為例,去探索其用法。
國際慣例,參考博客:
opencv中的warpAffine
opencv中的warpPerspective
【opencv實踐】仿射變換和透視變換
仿射變換與透視變換區別
圖像處理的仿射變換與透視變換
Affine and Projective Transformations
OpenCV Transformationmatrix: affine vs. perspective warping
點乘即投影向量
【TensorFlow-windows】擴展層之STN
理論
在做數據增強的時候,圖像里面有很多幾何變換,比如旋轉、平移、縮放、拉伸等,但是他們的本質還是通過某個矩陣,將圖像每個像素點的坐標變換到另一個新的位置。這種通過某個矩陣將圖像進行變換的方法通常稱為線性變換,也就是說利用了向量加法和標量乘法。
【注】本文的變換僅僅針對二維矩陣,非針對三維矩陣的變換。
仿射變換其實是透視變換的一種特殊形式,他倆都可以用下面這個矩陣表示
M=[a1a2b1a3a4b2c1c21]M=\begin{bmatrix} a_1&a_2&b_1\\ a_3&a_4&b_2\\ c_1&c_2&1 \end{bmatrix} M=???a1?a3?c1??a2?a4?c2??b1?b2?1????
用此矩陣將(x,y)(x,y)(x,y)變換到新的坐標點就是
[x′y′1]=[a1a2b1a3a4b2c1c21][xy1]\begin{bmatrix} x'\\ y'\\ 1 \end{bmatrix} =\begin{bmatrix} a_1&a_2&b_1\\ a_3&a_4&b_2\\ c_1&c_2&1 \end{bmatrix}\begin{bmatrix} x\\ y\\ 1 \end{bmatrix} ???x′y′1????=???a1?a3?c1??a2?a4?c2??b1?b2?1???????xy1????
學過線性代數就知道,這里面
- [a1a2a3a4]\begin{bmatrix} a_1&a_2\\ a_3&a_4 \end{bmatrix}[a1?a3??a2?a4??]用來處理旋轉和縮放,比如有個點是[2,3][2,3][2,3],經過[2002]\begin{bmatrix} 2&0\\ 0&2 \end{bmatrix}[20?02?]矩陣變換后就成了[4,6][4,6][4,6]即被放大了四倍,而經過[0110]\begin{bmatrix} 0&1\\ 1&0 \end{bmatrix}[01?10?]就變成了[3,2][3,2][3,2]即從原來的[2,3][2,3][2,3]坐標點旋轉到了[3,2][3,2][3,2]坐標點。
- [b1b2]\begin{bmatrix} b_1\\ b_2 \end{bmatrix}[b1?b2??]這個就顯而易見是平移
- [c1,c2][c_1,c_2][c1?,c2?]是投影向量,因為點乘就是c1x+c2yc_1x+c_2yc1?x+c2?y,剛好代表一個向量在另一個向量的投影
投影變換(projective transformation)展示的是當觀察者視角變化以后,觀察體的變化情況,通常用于產生透視畸變(perspective distortion),有時候稱為透視變換(perspective transformation)
仿射變換(affine transformation)用于縮放(scaling)、拉伸(skew)、旋轉(rotation)
注意的點:
- 兩個變換都是將直線投影到直線
- 兩條平行直線通過仿射變換后依舊是兩條平行的直線
- 兩條平行直線通過透視變換后可以是兩條相交的直線
從數學上來講,它倆的區別在變換矩陣的最后一行[c1,c2][c_1,c_2][c1?,c2?]的值上,仿射變換是0值,而透視變換通常不是。所以這一點也能說明仿射變換是透視變換的子集。
但是有一個要求,變換矩陣一定不能是奇異矩陣,因為奇異矩陣會導致AX=bAX=bAX=b有無窮解或者無解,也就是說會出現多個點變換到同一個點的情況。
變換公式
根據OpenCV中所述:
仿射變換的變換公式為:
dst(x,y)=src(M11x+M12y+M13,M21x+M22y+M23)\texttt{dst} (x,y) = \texttt{src} ( \texttt{M} _{11} x + \texttt{M} _{12} y + \texttt{M} _{13}, \texttt{M} _{21} x + \texttt{M} _{22} y + \texttt{M} _{23}) dst(x,y)=src(M11?x+M12?y+M13?,M21?x+M22?y+M23?)
透視變換變換公式為:
dst(x,y)=src(M11x+M12y+M13M31x+M32y+M33,M21x+M22y+M23M31x+M32y+M33)\texttt{dst} (x,y) = \texttt{src} \left ( \frac{M_{11} x + M_{12} y + M_{13}}{M_{31} x + M_{32} y + M_{33}} , \frac{M_{21} x + M_{22} y + M_{23}}{M_{31} x + M_{32} y + M_{33}} \right )dst(x,y)=src(M31?x+M32?y+M33?M11?x+M12?y+M13??,M31?x+M32?y+M33?M21?x+M22?y+M23??)
代碼實踐
使用opencv測試效果
仿射變換
使用warpAffine函數,將圖片旋轉45度,同時向右平移300像素,向下平移100像素
#仿射變換 degree=np.deg2rad(45) M1=np.array([[np.cos(degree),-np.sin(degree),300],[np.sin(degree),np.cos(degree),100] ]) dst1 = cv2.warpAffine(img,M1,(img.shape[1]*2,img.shape[0]*2))plt.figure(figsize=(8,8)) plt.subplot(121) plt.imshow(img) plt.subplot(122) plt.imshow(dst1)透視變換
使用warpPerspective函數
如果將透視變換使用上面的仿射變換矩陣,補齊第三行,可以得到和仿射變換一樣的結果
#透視變換 degree=np.deg2rad(45) M2=np.array([[np.cos(degree),-np.sin(degree),300],[np.sin(degree),np.cos(degree),100],[0,0,1] ]) dst2 = cv2.warpPerspective(img,M2,(img.shape[1]*2,img.shape[0]*2))plt.figure(figsize=(8,8)) plt.subplot(121) plt.imshow(img) plt.subplot(122) plt.imshow(dst2)一旦稍微改變投影向量,也就是第三行的值,就會發生很大的變化
#透視變換 degree=np.deg2rad(45) M2=np.array([[np.cos(degree),-np.sin(degree),300],[np.sin(degree),np.cos(degree),100],[0,-0.0015,1] ]) dst2 = cv2.warpPerspective(img,M2,(img.shape[1]*2,img.shape[0]*2))plt.figure(figsize=(8,8)) plt.subplot(121) plt.imshow(img) plt.subplot(122) plt.imshow(dst2)所以我們通常能夠通過仿射變換矩陣思考出變換后的樣子,但是透視變換卻很難預測出變換后的樣子。
理論擴展
上面說過仿射變換是特殊的透視變換,后者變換矩陣的第3行c1,c2c_1,c_2c1?,c2?為0的時候就變成了前者。
為了讓變換可控,我們可以預先構建某些點來規定變換矩陣的映射是什么樣的,依據變換矩陣能看出參數量:透視變換的矩陣為8個參數,仿射變換矩陣為6個參數。
根據線性代數,如果需要
- 求解仿射變換矩陣:6個未知數需要6個方程,即需要3組對應點
- 求解透視變換矩陣:8個未知數需要8個方程,即需要4組對應點
所以比如想把原圖變成平行四邊形時,可以平行四邊形上的三個點求解仿射變換:
#獲取仿射變換矩陣 src_pts = np.float32([[0,0],[0,1],[1,1]]) dst_pts = np.float32([[0,0],[1,1],[2,1]]) M = cv2.getAffineTransform(src_pts,dst_pts) dst1 = cv2.warpAffine(img,M,(img.shape[1]*2,img.shape[0]*2))plt.figure(figsize=(8,8)) plt.subplot(121) plt.imshow(img) plt.subplot(122) plt.imshow(dst1)想把原圖變成直角梯形時,可以使用直角梯形上的四個點求解透視變換
#獲取透視變換矩陣 src_pts = np.float32([[0,0],[0,300],[400,300],[400,0]]) dst_pts = np.float32([[0,0],[0,300],[200,300],[400,0]]) M = cv2.getPerspectiveTransform(src_pts,dst_pts) dst2 = cv2.warpPerspective(img,M,(img.shape[1]*2,img.shape[0]*2))plt.figure(figsize=(8,8)) plt.subplot(121) plt.imshow(img) plt.subplot(122) plt.imshow(dst2)總結
其實就是對圖像處理的一些基本知識補充,在之前寫過的換臉博客1和博客2中有用到相關理論。
博客和公眾號致力于圖像、機器學習、運動捕捉方向的理論和代碼實踐,注重基礎和實踐,有興趣可關注一波,代碼通常公布在公眾號中的github網址
總結
- 上一篇: Ogre共享骨骼与两种骨骼驱动方法
- 下一篇: 安装串口转USB CH340 驱动后,可