投影變換##
在放射變換中,物體是在二維空間中變換的。如果物體在三維空間中發生了旋轉,那么這種變換就成為投影變換,在投影變換中就會出現陰影或者遮擋,我們可以運用二維投影對三維投影變換進行模塊化,來處理陰影或者遮擋。在OpenCV中有類似於getAffineTransform函數:getPerspectiveTransform(src,dst)函數 用來處理計算投影變換矩陣。與getAffineTransform函數不同的是傳入的參數是三維空間坐標系的空間坐標,也就是4*2的二維ndarray,其中每一行代表一個坐標並且傳入的數據類型必須為float32.示例:
import cv2
import numpy as np
src=np.array([[0,0],[100,0],[0,100],[100,100]],np.float32)
dst=np.array([[100,10],[100,10],[50,70],[200,150]],np.float32)
P=cv2.getPerspectiveTransform(src,dst)
print(P)
運行結果:
[[-7.77156117e-16 -1.00000000e+00 1.00000000e+02]
[-2.77555756e-15 -1.00000000e-01 1.00000000e+01]
[-2.66713734e-17 -1.00000000e-02 1.00000000e+00]]
由結果可以看出當前輸出的類型是float64.對於仿射變換在OpenCV中提供了如下的函數
cv2.warpPerspective(src,M,dsize[,dst[,flags[,borderMode[,borderValue]]]])
輸入的矩陣類型是3行3列的投影變換矩陣。示例:
import cv2
import numpy as np
import matplotlib
def Perspect(path):
img=cv2.imread(path,cv2.IMREAD_GRAYSCALE)
if not isinstance(img, np.ndarray):
print('PASS')
pass
else:
h,w=img.shape
#設置變換坐標變化
src=np.array([[0,0],[w-1,0],[0,h-1],[w-1,h-1]],np.float32)
dst=np.array([[100,100],[w/3,100],[100,h-1],[w-1,h-1]],np.float32)
#計算投影變換矩陣
P=cv2.getPerspectiveTransform(src,dst)
#利用變化矩陣進行投影變換
r=cv2.warpPerspective(img,P,(w,h),borderValue=126)
#顯示圖像
cv2.imshow('A',img )
cv2.imshow('B',r)
cv2.waitKey(0)
cv2.destroyAllWindows()
print(P)
Perspect('img/aa.jpg')
極坐標變換##
極坐標變換主要處理校正圖像中的圓形物體或者在圓形中物體
1.將笛卡爾坐標轉換為極坐標####
\(r=\sqrt{(x-(\overline{x})^2)+(y-(\overline{y})^2)}\)
以變換中心為圓心的同一個圓心上的點,在極坐標系\(\theta\)or中顯示為一條直線。其中\(\theta\)的取值范圍為[0,2\(\pi\)],函數arctan2返回的角度和笛卡爾積坐標點所在的象限有關系。舉例,(9,15)以(2,10)為中心進行極坐標的變換,示例代碼:
#半徑轉換
r=math.sqrt(math.pow(9-2,2)+math.pow(15-10,2));
#角度轉換
theta=math.atan2(15-10,9-2)/math.pi*180
該示例可以這樣理解,先把坐標原點移動到(2,10)處,則(9,15)平移后的坐標變為(7,5),然后再以(0,0)為中心進行轉換。在OpenCV中的函數cartToPolar(x,y[,magnitude[, angle[,angleIndegress ]]])實現的就是將原點移動到變換中心后的笛卡爾積坐標向極坐標的變換,返回值magnitude,angle是與參數x,y具有相同尺寸和數據類型的ndarray。angleInDegrees的值為True時,返回值為角度,反之返回值為弧度。
例如:計算(0,0)、(1,0)、(2,0)、(0,1)、(1,1)、(2,1)、(0,2)、(1,2)、(2,2)這九個點以(1,1)為中心進行的坐標轉換。首先將坐標原點移動到(1,1)處,按照平移放射矩陣計算出這九個點平移后的新坐標值,然后利用函數cartToPolar進行極坐標的轉換。代碼表示為:
import cv2
import numpy as np
x=np.array([[0,1,2],[0,1,2],[0,1,2]],np.float64)-1
y=np.array([[0,0,0],[1,1,1],[2,2,2]],np.float64)-1
r,theta = cv2.cartToPolar(x,y,angleInDegrees=True)
print("r: %s"%r)
print("theta: %s"%theta)
運行結果:
可以看到執行后的極坐標(\(\theta\),r)
以上九個點的圖像表示就是以(1,1)為圓心的九個點,距離變換中心相等的點轉換為極坐標后在極坐標系中位於同一條直線上。這樣就直觀的給出了極坐標變換是如何校正圖像中的圓形物體或者圓環中的物體的。
2.將極坐標轉換為笛卡兒坐標####
極坐標的變換是可逆的,在已知極坐標和笛卡兒坐標的條件下,計算哪個笛卡兒坐標以(\(\overline{x}\),\(\overline{y}\))為中心的極坐標變換時(\(\theta\),r),計算公式為:
在OpenCV中的實現函數是 cv2.polarToCart(magnitude,angle[,x[, y[, angleInDegrees ]]] )來實現將極坐標轉化為笛卡爾坐標。其參數與cartToPolar參數類似。代碼舉例 實現 根據極坐標系的(30,20),(31,21),(30,10),(30,10)四個點計算迪卡兒坐標系的哪四個點以(-6,6)為中心變換得到的。
import cv2
import numpy as np
# 將迪卡兒坐標轉換為極坐標
angle = np.array([[30, 31], [30, 30]], np.float32)
r = np.array([[20, 21], [10, 10]], np.float32)
x, y = cv2.polarToCart(r, angle, angleInDegrees=True)
print("變換中心為(0,0) 得到的四個迪卡兒坐標")
print("x: %s" % x)
print("y: %s" % y)
x+=-6
y+=6
print("變換中心為(-6,6) 得到的四個迪卡兒坐標")
print("x: %s" % x)
print("y: %s" % y)
運行結果:
3.利用極坐標變換實現圖像修正####
我們利用極坐標和迪卡兒坐標的意義對應關系得到O的每一個像素值:
此公式中的圖像輸出是以1為步長進行離散化的,但是這樣的化輸出的圖像矩陣會出現失真的情況,丟失跟多的圖像信息,。我們的解決方法是將與(\(\overline{x}\),\(\overline{y}\))的距離范圍為[\(r_{min}\),\(r_{max}\)]並且角度范圍在 \([\theta_{min},\theta_{max}]\)內的點進行極坐標向笛卡爾坐標的變換,並且進行離散化。\(\theta\)的變換步長\(\theta_{step}\)一般取\(\tfrac{360}{180*N}\),N\(\ge\) 2,此時輸出的寬為w \(\approx\) \(\tfrac{r_{max}-r_{min}}{r_{step}}\) 圖像矩陣的第i行第j列可通過以下的公式進行計算:
下面我們通過一個具體的實例來實現極坐標的變換:將一個圓環圖像變成矩形圖像
import cv2
import numpy as np
import sys
#實現圖像的極坐標的轉換 center代表及坐標變換中心‘;r是一個二元元組,代表最大與最小的距離;theta代表角度范圍
#rstep代表步長; thetastap代表角度的變化步長
def polar(image,center,r,theta=(0,360),rstep=0.5,thetastep=360.0/(180*4)):
#得到距離的最小值、最大值
minr,maxr=r
#角度的最小范圍
mintheta,maxtheta=theta
#輸出圖像的高、寬 O:指定形狀類型的數組float64
H=int((maxr-minr)/rstep)+1
W=int((maxtheta-mintheta)/thetastep)+1
O=125*np.ones((H,W),image.dtype)
#極坐標轉換 利用tile函數實現W*1鋪成的r個矩陣 並對生成的矩陣進行轉置
r=np.linspace(minr,maxr,H)
r=np.tile(r,(W,1))
r=np.transpose(r)
theta=np.linspace(mintheta,maxtheta,W)
theta=np.tile(theta,(H,1))
x,y=cv2.polarToCart(r,theta,angleInDegrees=True)
#最近插值法
for i in range(H):
for j in range(W):
px=int(round(x[i][j])+cx)
py=int(round(y[i][j])+cy)
if((px>=0 and px<=w-1) and (py>=0 and py<=h-1)):
O[i][j]=image[py][px]
return O
if __name__=="__main__":
img = cv2.imread("img/yu.jpg", cv2.IMREAD_GRAYSCALE)
# 傳入的圖像寬:600 高:400
h, w = img.shape[:2]
print("h:%s w:%s"%(h,w))
# 極坐標的變換中心(300,200)
cx, cy = 300, 200
# 圓的半徑為10 顏色:灰 最小位數3
cv2.circle(img, (int(cx), int(cy)), 10, (255, 0, 0, 0), 3)
L = polar(img, (cx, cy), (100, 350))
# 旋轉
L = cv2.flip(L, 0)
# 顯示與輸出
cv2.imshow('img', img)
cv2.imshow('O', L)
cv2.waitKey(0)
cv2.destroyAllWindows()