影象的仿射變換
轉自:https://zhuanlan.zhihu.com/p/80852438
https://blog.csdn.net/hty1053240123/article/details/51992398
一,概述
影象的幾何變換主要包括:平移、縮放、旋轉、仿射、透視等等。影象變換是建立在矩陣運算基礎上的,通過矩陣運算可以很快的找到不同影象的對應關係。理解變換的原理需要理解變換的構造方法以及矩陣的運算方法。
影象的幾何變換主要分為三類:剛性變換、仿射變換和透視變換,如下圖:
仿射變換是從一個二維座標系變換到另一個二維座標系,屬於線性變換。通過已知3對座標點可以求得變換矩陣。
透視變換是從一個二維座標系變換到一個三維座標系,屬於非線性變換。通過已知4對座標點可以求得變換矩陣。
二,影象基本變換
影象的幾何變換包含很多變換,其中有一些基本變換,而仿射變換和透視變換就是對這些基本變換進行組合實現的。
基本變換具體包括:平移(Translation)、縮放(Scale)、旋轉(Rotation)、翻轉(Flip)和錯切(Shear)。
a. 平移
b. 縮放
c. 旋轉
d. 翻轉
e. 錯切:錯切亦稱為剪下或錯位變換,包含水平錯切和垂直錯切,常用於產生彈性物體的變形處理。
下面這張圖可能更形象:
三,仿射變換
3.1 原理
對於二維座標系的一個座標點(x, y),可以使用一個2*2矩陣來調整x, y的值,而通過調整x, y可以實現二維形狀的線性變換(旋轉,縮放),所以整個轉換過程的就是對(x, y)調整的過程。
仿射變換(Affine Transformation)是指在向量空間中進行一次線性變換(乘以一個矩陣)和一次平移(加上一個向量),變換到另一個向量空間的過程。
仿射變換代表的是兩幅圖之間的對映關係,仿射變換矩陣為2*3的軍陣,如下圖中的矩陣M, 其中的B起著平移的作用,而A中的對角線決定縮放,反對角線決定旋轉或錯切。所以仿射變換可以由一個矩陣A和一個向量B給出:
原畫素點座標(x, y),經過仿射變換後的點的座標是T,則矩陣仿射變換基本演算法原理:
所以仿射變換是一種二維座標(x, y)到二維座標(u, v)的線性變換,其數學表示式如下:
其實到這裡還沒完,我們知道縮放和旋轉通過矩陣乘法實現,而平移是通過矩陣加法來實現的,為了將這幾個操作都通過一個矩陣來實現,所以構造出了上面那個2*3的矩陣。
但是這個會改變影象的尺寸,比如一個2*2的影象,乘以2*3的矩陣,會得到2*3的影象,所以為了解決這個問題,我們就增加一個維度,也就是構造齊次座標矩陣:
【齊次座標矩陣:】
首先給出簡短的定義:仿射變換是線性變換(旋轉和縮放)加平移變換,齊次座標就是用高一維的空間座標表示低一維空間的座標。線性變換可以用矩陣表示,旋轉和縮放都是線性變換,用矩陣表示如下:
仿射變換也就是上面三個變換的疊加,在上面三個變換中平移變換時沒辦法使用矩陣相乘的方式來獲取的。但是如果將座標在高一維空間進行表示的時候,也就是採用齊次座標的時候,平移變換可以用矩陣乘法來進行表示。假設p=(x, y, 1)是齊次座標下二維點p(x,y)的座標表示,具體表示如下:
這樣就以上三個進行統一,便得到了仿射變換的矩陣表示,其定義也更容易表達,仿射變換也就是下面的變換:
【\齊次座標矩陣】
最終得到齊次座標矩陣表達形式為
仿射變換保持了二維影象的“平直性”和“平行性”:
平直性:
- 直線經昂設變換後還是直線
- 圓弧經仿射變換後還是圓弧
平行性:
- 直線之間的相對位置關係保持變
- 平行線經仿射變換後依然為平行線
- 直線上點的位置順序不會發生變換
- 向量間夾角可能會發生變化
3.2 python實現
通過放射變換將圖片中的每個畫素點按照一定的規律對映到新的位置,仿射變化需要一個轉換矩陣,但是由於仿射變換比較複雜,一般很難直接找到這個矩陣,opencv提供了根據原影象和目標影象上三個對應的點來自動建立變換矩陣,矩陣維度為2*3。
兩個影象中非共線的三對對應點確定唯一的一個仿射變換。經仿射變換後,影象中的三個關鍵點依然構成三角形,但三角形形狀已經發生變化。這個函式是cv2.getAffineTransform(pos1, pos2),其中兩個位置就是變換前後的對應位置關係。輸出的就是仿射矩陣M,最後這個矩陣會被傳給函式cv2.warpAffine()來實現仿射變換。
原圖為: 仿射變換後的圖:
import cv2 import numpy as np img = cv2.imread('image0.jpg', 1) height, width = img.shape[:2] # 405x413 # 在原影象和目標影象上各選擇三個點 matSrc = np.float32([[0, 0],[0, height-1],[width-1, 0]]) matDst = np.float32([[0, 0],[30, height-30],[width-30, 30]]) # 得到變換矩陣 matAffine = cv2.getAffineTransform(matSrc, matDst) # 進行仿射變換 dst = cv2.warpAffine(img, matAffine, (width,height))
變換矩陣的資料型別是np.float32, 函式cv2.waroAffine()的第三個引數是輸出影象的尺寸(寬,高)。
因為平移和縮放的矩陣比較簡單,我們可以直接手動指定:
# 影象平移 # 移位矩陣,水平方向移動100個畫素,豎直方向移動200個畫素 matShift = np.float32([[1,0,100],[0,1,200]]) # 2行3列 dst = cv2.warpAffine(img, matShift, (width,height)) # 影象縮放 # 縮放矩陣,長寬各縮放一半 matScale = np.float32([[0.5,0,0],[0,0.5,0]]) dst = cv2.warpAffine(img, matScale, (int(width/2),int(height/2)))
向右下平移: 縮小50%
要實現影象旋轉,需要通過cv2.getRotationMatrix2D來得到二維旋轉變換矩陣(2行3列)。cv2.getRotationMatrix2D三個引數分別為:1,旋轉中心;2,旋轉角度;3,縮放比例。角度為證,則影象逆時針旋轉,旋轉後圖像可能會超出邊界。
matRotate = cv2.getRotationMatrix2D((width*0.5, height*0.5), 45, 1.0) dst = cv2.warpAffine(img, matRotate, (width,height))