OpenCV實現圖像變換(python)-仿射變換原理


一般對圖像的變化操作有放大、縮小、旋轉等,統稱為幾何變換,對一個圖像的圖像變換主要有兩大步驟,一是實現空間坐標的轉換,就是使圖像從初始位置到終止位置的移動。二是使用一個插值的算法完成輸出圖像的每個像素的灰度值。其中主要的圖像變換有:仿射變換、投影變換、極坐標變換。

仿射變換##

二維空間坐標的仿射變換公式:

\[\left( \begin{matrix} \overline{x} \\ \overline{y} \end{matrix} \right) =\left( \begin{matrix} a_{11} &&a_{12} \\ a_{21}&&a_{22} \end{matrix} \right) \left( \begin{matrix} x \\ y \end{matrix} \right) + \left( \begin{matrix} a_{13} \\ a_{23} \end{matrix} \right) \]

在以下矩陣中:

\[\left( \begin{matrix} \overline{x} \\ \overline{y} \\ 1 \end{matrix} \right) =A \left( \begin{matrix} x \\ y \\ 1 \end{matrix} \right) \]

矩陣A就是仿射矩陣,因為它最后一行為(0,0,1)

\[A= \left( \begin{matrix} a_{11}&a_{12}&a_{13} \\ a_{21}&a_{22}&a_{23} \\ 0&0&1 1 \end{matrix} \right) \]

平移
平移是最簡單的仿射變換如將空間坐標(x,y)沿着x軸移動100,沿着y軸移動200。平移后的坐標為(x+100,y+200)。將這個過程一般化后,假設任意的空間坐標(x,y)先沿着x軸平移Px再沿着y軸平移Py。得到的坐標為(x+Px,y+Py)。用矩陣表示這個平移過程為:

\[\left( \begin{matrix} \overline{x} \\ \overline{y} \\ 1 \end{matrix} \right) =\left( \begin{matrix} 1 &0&Px \\ 0&1&Py \\ 0&0&1 \end{matrix} \right) \left( \begin{matrix} x \\ y \\ 1 \end{matrix} \right) \]

對於Px和Py若大於0則表示沿着軸正向移動,若小於0則表示沿着軸負向移動。
放大縮小
在坐標軸中以原點為中心的放大與縮小S倍是指對其x軸方向的橫坐標放縮成原坐標的橫坐標距離中心點(0,0)的距離的S倍並對其y軸方向的橫坐標放縮成原坐標的縱坐標距離原點的距離的S倍。其中若S大於1則表示增大,若小於1則表示縮小。放縮在矩陣中的表示為:

\[\left( \begin{matrix} \overline{x} \\ \overline{y} \\ 1 \end{matrix} \right) =\left( \begin{matrix} s_x&0&0 \\ 0&s_y&0 \\ 0&0&1 \end{matrix} \right) \left( \begin{matrix} x \\ y \\ 1 \end{matrix} \right) \]

坐標(x,y)在坐標軸中以任意一點的坐標(x0,y0)為中心在水平和垂直方向上放縮S倍,放縮后的坐標為$$ ( (x_0+S_x(x-x_0),y_0+S_y(y-y_0) )$$用矩陣可以表示為:

\[\left( \begin{matrix} \overline{x} \\ \overline{y} \\ 1 \end{matrix} \right) =\left( \begin{matrix} 1 &0&X_0 \\ 0&1&Y_0 \\ 0&0&1 \end{matrix} \right) \left( \begin{matrix} s_x&0&0 \\ 0&s_y&0 \\ 0&0&1 \end{matrix} \right) \left( \begin{matrix} 1 &0&-X_0 \\ 0&1&-Y_0 \\ 0&0&1 \end{matrix} \right) \left( \begin{matrix} x \\ y \\ 1 \end{matrix} \right) \]

坐標(x,y)繞原點順時針旋轉α(α>0),cosΘ=x/p sinΘ=y/p.其中p代表(x,y)到中心點(0,0)的距離。則
cos(Θ+α)=cosΘcosα-sinΘsinα=(x/p)cosα -(y/p)sinα=Ex/p
sin(Θ+α)=sinΘcosα+cosΘsinα=(y/p)cosα -(y/p)sinα=Ey/p
化解以上公式,使用矩陣表示為:

\[\left( \begin{matrix} \overline{x} \\ \overline{y} \\ 1 \end{matrix} \right) =\left( \begin{matrix} cosα&-sinα&0 \\ sinα&cosα&0 \\ 0&0&1 \end{matrix} \right) \left( \begin{matrix} x \\ y \\ 1 \end{matrix} \right) \]

放射矩陣的計算
如果已知坐標以及其放射變換后的矩陣,從而計算出變換后的坐標,就需要放射矩陣的計算,主要的實現方法有:方程法,矩陣法,插值算法。在OpenCV中有對應的實現函數,如使用方程法:cv2.getAffineTransform(src,dst) 該方法就是通過計算參數src到dst的對應仿射變換的矩陣,其中參數src和dst分別代表原坐標和變換后的坐標,並且均為3行2列的二維ndarray,數據必須為浮點型。實現代碼:

import numpy as np

src=np.array([[0,0],[200,0],[0,200]],np.float32)
dst=np.array([[0,0],[100,0],[0,100]],np.float32)
A=cv2.getAffineTransform(src,dst)

print(A)

運行結果:

在矩陣法中,需要預先知道具體的變化步驟,比如先放大再平移還是先移動再放大

\[\left( \begin{matrix} \overline{x} \\ \overline{y} \\ 1 \end{matrix} \right) =\left( \begin{matrix} 1 &0&X_0 \\ 0&1&Y_0 \\ 0&0&1 \end{matrix} \right) \left( \begin{matrix} s_x&0&0 \\ 0&s_y&0 \\ 0&0&1 \end{matrix} \right) \left( \begin{matrix} x \\ y \\ 1 \end{matrix} \right) \]

以上的矩陣變換就是平移仿射矩陣乘以縮放仿射矩陣得到的而不是縮放仿射矩陣乘以平移仿射矩陣得到的,由於等式是由右向左運行,所以必須要知道變化順序。矩陣的乘法並不是矩陣的點乘,再Numpy中乘法是通過dot函數實現的,關於Numpy語法可以參考我之前寫的博文。我們通過一個實例來了解矩陣的乘法計算。
假設先對一矩陣等比例放大二倍,然后水平與垂直方向上分別平移100,計算該矩陣的算法如下:

import numpy as np

#先對矩陣進行放大
s=np.array([[2,0,0],[0,2,0],[0,0,1]])
#再對矩陣進行平移
t=np.array([[1,0,100],[0,1,100],[0,0,1]])
#矩陣相乘
A=np.dot(t,s)
print(A)

運行結果:

一定要注意傳入dot參數的順序。
下面介紹插值算法,我們可以將圖像理解為一個二維的函數,行數為H,列數為W的圖像矩陣I:Z=F(x,y), 0<=x<W,0<=y<H,x⊆N,y⊆N
矩陣的列號對應x坐標,垂直方向為y軸,行號對應y坐標,稱該函數為圖像函數。
利用已知的整數坐標處的函數值估算非整數坐標處的函數值的方法主要有:最近鄰插值(就是從四個相鄰整數坐標中找到一個最近的)輸出的圖像經過放大后會有鋸齒狀的外觀。雙線性插值法有兩個變量的插值函數的線性插值擴展,其核心是在兩個方向分別進行一次線性插值。待插點像素值取原圖像中與其相鄰的4個點像素值的水平、垂直兩個方向上的線性內插,即根據待采樣點與周圍4個鄰點的距離確定相應的權重,從而計算出待采樣點的像素值。有的時候需要更高階的插值函數,如三次樣條插值、Lengendre中心函數和sin(axs)函數,高階插值常用二維離散卷積運算來實現。后續博客會對二維離散卷積運算做詳細的描述。
對於雙線性插值法在這表一次形象化的描述:


如圖:先估計f1在(x,[y])處的函數值,再估計f1在(x,[y]+1)處的函數值,最后估計f1在(x,y)處的函數值

對於空間坐標變換和插值方法在已知的仿射變換矩陣上OpenV提供了warpAffine(src,M,dsize[,flags[,borderMode[,borderValue ]]])函數

參數 釋義
src 圖像矩陣
M 2行3列的仿射變換矩陣
dsize 一個二元元組,輸出圖像的大小
flags 插值法:INTE_NEAREST、INTE_LINEAR(默認)等
borderMode 填充模式,如:BORDER_CONSTANT等
borderValue 當borderMode=BORDER_CONSTANT時的填充值

下面使用python實現圖像的幾何變換:

import numpy as np
import cv2
import sys
import math

img=cv2.imread('yun.jpg',cv2.IMREAD_GRAYSCALE)
cv2.imwrite('yun.jpg',img)
#原圖的寬高
h,w=img.shape[:2]
#仿射變換矩陣 縮小2倍
A1=np.array([[0.5,0,0],[0,0.5,0]],np.float32)
A2=cv2.warpAffine(img,A1,(w,h),borderValue=126)
#縮小后平移
B1=np.array([[0.5,0,w/4],[0,0.5,h/4]],np.float32)
B2=cv2.warpAffine(img,B1,(w,h),borderValue=126)
#使圖像旋轉
C1=cv2.getRotationMatrix2D((w/2.0,h/2.0),30,1)
C2=cv2.warpAffine(img,C1,(w,h),borderValue=126)

cv2.imshow('img',img)
cv2.imshow('A2',A2)
cv2.imshow('B2',B2)
cv2.imshow('C2',C2)
cv2.waitKey(0)
cv2.destroyAllWindows()

運行效果

參考文獻:
[1]ROBERT G.KEYS.Cubic Convolution Interpolation for Digital Image Processing.IEEE TRANSACTIONS ON ACOUSTICS.SPEECH,AND SIGNAL PROCESSING,1981
[1]R. Hartley and A. Zisserman, “Multiple View Geometry in Computer Vision,” 2-nd edition, Cambridge University Press, 2004.

今天就先寫到這吧!投影變換和極坐標變換后續再寫


免責聲明!

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



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