1. 程式人生 > >OpenCV實現影象變換(python)

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.

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