畸變主要有兩種:徑向畸變和切想畸變危尿。如下圖所示寿弱,用紅色直線將棋盤的兩
個(gè)邊標(biāo)注出來,但是你會(huì)發(fā)現(xiàn)棋盤的邊界并不和紅線重合嚷硫。所有我們認(rèn)為應(yīng)該
是直線的也都凸出來了检访。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? xcorrected = x (1 + k1r2 + k2r4 + k3r6)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ycorrected = y (1 + k1r2 + k2r4 + k3r6)
于此相似,另外一個(gè)畸變是切向畸變仔掸,這是由于透鏡與成像平面不可能絕
對平行造成的烛谊。這種畸變會(huì)造成圖像中的某些點(diǎn)看上去的位置會(huì)比我們認(rèn)為
的位置要近一些。它可以通過下列方程組進(jìn)行校正:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? xcorrected = x + [2p1xy + p2 (r2 + 2x2)]
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ycorrected = y + [2p1xy + p2 (r2 + 2x2)]
簡單來說嘉汰,如果我們想對畸變的圖像進(jìn)行校正就必須找到五個(gè)造成畸變的
系數(shù):
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Distortion cofficients = (k1; k2; p1; p2; k3)
除此之外丹禀,我們還需要再找到一些信息,比如攝像機(jī)的內(nèi)部和外部參數(shù)鞋怀。
內(nèi)部參數(shù)是攝像機(jī)特異的双泪。它包括的信息有焦距(fx; fy),光學(xué)中心(cx; cy)
等密似。這也被稱為攝像機(jī)矩陣焙矛。它完全取決于攝像機(jī)自身,只需要計(jì)算一次残腌,以
后就可以已知使用了村斟∑兜迹可以用下面的3x3 的矩陣表示:
? ? ? ? ? ? ? ? ? ? ? ? ? fx? 0? cx
camera matrix =0? ? fy? cy?
? ? ? ? ? ? ? ? ? ? ? ? ? 0? ? 0? ? 1
外部參數(shù)與旋轉(zhuǎn)和變換向量相對應(yīng),它可以將 3D 點(diǎn)的坐標(biāo)轉(zhuǎn)換到坐標(biāo)系統(tǒng)中蟆盹。
在3D 相關(guān)應(yīng)用中孩灯,必須要先校正這些畸變。為了找到這些參數(shù)逾滥,我們必
須要提供一些包含明顯圖案模式的樣本圖片(比如說棋盤)峰档。我們可以在上面找
到一些特殊點(diǎn)(如棋盤的四個(gè)角點(diǎn))。我們起到這些特殊點(diǎn)在圖片中的位置以及
它們的真是位置寨昙。有了這些信息讥巡,我們就可以使用數(shù)學(xué)方法求解畸變系數(shù)。這
就是整個(gè)故事的摘要了舔哪。為了得到更好的結(jié)果欢顷,我們至少需要10 個(gè)這樣的圖
案模式。
代碼如下:
import numpy as np
import cv2
import glob
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
objpoints = [] # 3d point in real world space
imgpoints = [] # 2d points in image plane.
images = glob.glob('*.jpg')
for fname in images:
? ? ? ? ? ? img= cv2.imread(fname)
? ? ? ? ? ? gray= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
? ? ? ? ? ? ret, corners = cv2.findChessboardCorners(gray, (7,6),None)
? ? ? ? ? ? if ret == True:
? ? ? ? ? ? ? ? ? ? ? ? objpoints.append(objp)
? ? ? ? ? ? ? ? ? ? ? ? corners2= cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
? ? ? ? ? ? ? ? ? ? ? ? imgpoints.append(corners2)
? ? ? ? ? ? ? ? ? ? ? ? img = cv2.drawChessboardCorners(img, (7,6), corners2,ret)
? ? ? ? ? ? ? ? ? ? ? ? cv2.imshow('img',img)
? ? ? ? ? ? ? ? ? ? ? ? cv2.waitKey(500)
cv2.destroyAllWindows()
標(biāo)定
在得到了這些對象點(diǎn)和圖像點(diǎn)之后捉蚤,我們已經(jīng)準(zhǔn)備好來做攝像機(jī)標(biāo)定了吱涉。
我們要使用的函數(shù)是cv2.calibrateCamera()。它會(huì)返回?cái)z像機(jī)矩陣外里,畸
變系數(shù),旋轉(zhuǎn)和變換向量等特石。
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints,imgpoints,gray.shape[::-1],None,None)
畸變校正
現(xiàn)在我們找到我們想要的東西了盅蝗,我們可以找到一幅圖像來對他進(jìn)行校正
了。OpenCV 提供了兩種方法姆蘸,我們都學(xué)習(xí)一下墩莫。不過在那之前我們可以使用
從函數(shù)cv2.getOptimalNewCameraMatrix() 得到的自由縮放系數(shù)對攝
像機(jī)矩陣進(jìn)行優(yōu)化。如果縮放系數(shù)alpha = 0逞敷,返回的非畸變圖像會(huì)帶有最少量
的不想要的像素狂秦。它甚至有可能在圖像角點(diǎn)去除一些像素。如果alpha = 1推捐,所
有的像素都會(huì)被返回裂问,還有一些黑圖像。它還會(huì)返回一個(gè)ROI 圖像牛柒,我們可以
用來對結(jié)果進(jìn)行裁剪堪簿。我們讀取一個(gè)新的圖像(new.ipg)
img = cv2.imread('new.jpg')
h, w= img.shape[:2]
newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))
最后對得到的ROI 對結(jié)果進(jìn)行裁剪。
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
x,y,w,h = roi
dst= dst[y:y+h, x:x+w]
cv2.imwrite('cap.png',dst)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 你會(huì)發(fā)現(xiàn)結(jié)果圖像中所有的邊界都變直了皮壁。