相機標定
一、相機標定的基本原理
1.1從世界坐標系到相機坐標系
1.2從相機坐標系到理想圖像坐標系(不考慮畸變)
1.3從理想圖像坐標系到實際圖像坐標系(考慮畸變)
1.4從實際圖像坐標系到像素坐標系
二、相機標定的基本實現步驟
三、圖像集
四、實驗代碼及結果截圖
五、總結
一、相機標定的基本原理
1.1從世界坐標系到相機坐標系
剛體從世界坐標系轉換到相機坐標系的過程,可以通過旋轉和平移來得到,我們將其變換矩陣由一個旋轉矩陣和平移向量組合成的齊次坐標矩陣(為什么要引入齊次坐標可見后續文章)來表示:
其中,R為旋轉矩陣,t為平移向量,因為假定在世界坐標系中物點所在平面過世界坐標系原點且與Zw軸垂直(也即棋盤平面與Xw-Yw平面重合,目的在於方便后續計算),所以zw=0,可直接轉換成式1的形式。其中變換矩陣
即為前文提到的外參矩陣,之所稱之為外參矩陣可以理解為只與相機外部參數有關,且外參矩陣隨剛體位置的變化而變化。
下圖表示了用R,t將上述世界坐標系轉換到相機坐標系的過程。
1.2從相機坐標系到理想圖像坐標系(不考慮畸變)
這一過程進行了從三維坐標到二維坐標的轉換,也即投影透視過程(用中心投影法將物體投射到投影面上,從而獲得的一種較為接近視覺效果的單面投影圖,也就是使我們人眼看到景物近大遠小的一種成像方式)。我們還是拿
但是為了在數學上更方便描述,我們將相機坐標系和圖像坐標系位置對調,變成圖三所示的布置方式(沒有實際的物理意義,只是方便計算):
此時,假設相機坐標系中有一點M,則在理想圖像坐標系下(無畸變)的成像點P的坐標為(可由相似三角形原則得出):
將上式化為齊次坐標表示形式為:
1.3從理想圖像坐標系到實際圖像坐標系(考慮畸變))
透鏡的畸變主要分為徑向畸變和切向畸變,還有薄透鏡畸變等等,但都沒有徑向和切向畸變影響顯著,所以我們在這里只考慮徑向和切向畸變。
徑向畸變是由於透鏡形狀的制造工藝導致。且越向透鏡邊緣移動徑向畸變越嚴重。下圖所示是徑向畸變的兩種類型:桶形畸變和枕形畸變。
實際情況中我們常用r=0處的泰勒級數展開的前幾項來近似描述徑向畸變。矯正徑向畸變前后的坐標關系為:
由此可知對於徑向畸變,我們有3個畸變參數需要求解。
切向畸變是由於透鏡和CMOS或者CCD的安裝位置誤差導致。因此,如果存在切向畸變,一個矩形被投影到成像平面上時,很可能會變成一個梯形。切向畸變需要兩個額外的畸變參數來描述,矯正前后的坐標關系為:
由此可知對於切向畸變,我們有2個畸變參數需要求解。
綜上,我們一共需要5個畸變參數(k1、k2、k3、p1和p2 )來描述透鏡畸變。
1.4從實際圖像坐標系到像素坐標系
由於定義的像素坐標系原點與圖像坐標系原點不重合,假設像素坐標系原點在圖像坐標系下的坐標為(u0,v0),每個像素點在圖像坐標系x軸、y軸方向的尺寸為:dx、dy,且像點在實際圖像坐標系下的坐標為(xc,yc),於是可得到像點在像素坐標系下的坐標為:
化為齊次坐標表示形式可得:
公式2中(xp, yp)與公式5中(xc, yc)相同,都是圖像坐標系下的坐標。
若暫不考慮透鏡畸變,則將式2與式5的轉換矩陣相乘即為內參矩陣M:
之所以稱之為內參矩陣可以理解為矩陣內各值只與相機內部參數有關,且不隨物體位置變化而變化。
最后用一幅圖來總結從世界坐標系到像素坐標系(不考慮畸變)的轉換關系:
二、相機標定的基本實現步驟
1.打印一張棋盤格A4紙張(黑白間距已知),並貼在一個平板.上
2.針對棋盤格拍攝若干張圖片(一般10-20張)
3.在圖片中檢測特征點(Harris角點)
4.根據角點位置信息及圖像中的坐標,求解Homographic矩陣
5.利用解析解估算方法計算出5個內部參數,以及6個外部參數
6.根據極大似然估計策略,設計優化目標並實現參數的refinement
三、圖像集
圖像集一共有二十張棋盤格,我是用wps文檔插入表格,改變格子的屬性顏色就可以變成棋盤格,棋盤格的寬和高為18cm*18cm,然后用手機從不同的角度拍攝二十張圖片,把每張圖片的像素改為500*500,我手機的型號為華為P30
圖一
四、實驗代碼及結果截圖及小結
4.1實驗代碼:
1 # coding=utf-8 2 import cv2 3 import numpy as np 4 import glob 5 6 # 設置尋找亞像素角點的參數,采用的停止准則是最大循環次數30和最大誤差容限0.001 7 criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001) 8 9 # 獲取標定板角點的位置 10 objp = np.zeros((7 * 7, 3), np.float32) 11 objp[:, :2] = np.mgrid[0:7, 0:7].T.reshape(-1, 2) # 將世界坐標系建在標定板上,所有點的Z坐標全部為0,所以只需要賦值x和y 12 13 obj_points = [] # 存儲3D點 14 img_points = [] # 存儲2D點 15 16 images = glob.glob("qipan/*.jpg") 17 i=0 18 for fname in images: 19 img = cv2.imread(fname) 20 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 21 size = gray.shape[::-1] 22 ret, corners = cv2.findChessboardCorners(gray, (7, 7), None) 23 #print(corners) 24 25 if ret: 26 27 obj_points.append(objp) 28 29 corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria) # 在原角點的基礎上尋找亞像素角點 30 #print(corners2) 31 if [corners2]: 32 img_points.append(corners2) 33 else: 34 img_points.append(corners) 35 36 cv2.drawChessboardCorners(img, (7, 7), corners, ret) # 記住,OpenCV的繪制函數一般無返回值 37 i+=1 38 cv2.imwrite('qipan'+str(i)+'.jpg', img) 39 cv2.waitKey(1500) 40 41 print(len(img_points)) 42 cv2.destroyAllWindows() 43 44 # 標定 45 ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, size, None, None) 46 47 print("ret:", ret) 48 print("mtx:", mtx) # 內參數矩陣 49 print("dist:", dist) # 畸變系數 distortion cofficients = (k_1,k_2,p_1,p_2,k_3) 50 print("rvecs:", rvecs) # 旋轉向量 # 外參數 51 print("tvecs:", tvecs ) # 平移向量 # 外參數 52 53 print("-----------------------------------------------------") 54 55 img = cv2.imread(images[2]) 56 h, w = img.shape[:2] 57 newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))#顯示更大范圍的圖片(正常重映射之后會刪掉一部分圖像) 58 print (newcameramtx) 59 print("------------------使用undistort函數-------------------") 60 dst = cv2.undistort(img,mtx,dist,None,newcameramtx) 61 x,y,w,h = roi 62 dst1 = dst[y:y+h,x:x+w] 63 cv2.imwrite('calibresult3.jpg', dst1) 64 print ("方法一:dst的大小為:", dst1.shape)
4.2實驗結果:
角點檢測結果:
重投影誤差:
內參矩陣:
畸變系數:
旋轉向量:
位移向量:
矯正前
矯正后
五、總結
1、我從不同的角度拍攝的圖像的重投影會產生一定的誤差,但是誤差不是很大。改變圖像的數量,重投影的誤差也會改變。
2、從實驗中,可以得到多個結果,可以獲得華為p30的內部參數和外部參數,還可以查看重投影的誤差大小等有效信息。
3、就算是統一型號的手機在同一個工廠生產,手機相機的參數都會產生一定的誤差。
4、圖像發生畸變是很明顯的,一眼就可以看出,矯正后的棋盤就很工整。矯正棋盤是實驗的目的。
5、圖像的像素不要過大也不要過小,過大會延長運行時間,過小會影響運行結果,我設置的是500*500。