OpenCV-Python系列之相機校準實踐
上個教程已經談到,為了校準攝像頭,我們至少需要10種測試模式。現在我們使用示例圖片:
用同一相機從不同的位置,不同的角度,拍攝標定板的多張照片(10-20張最佳),將照片放到資料夾中:
設想一張棋盤的影象,需要用於校準攝像頭最重要的輸入資料是3D真實世界點的集合以及影象中這些點的相應2D座標。2D影象點我們得能夠輕易從影象中找出來。(這些影象點是棋盤中兩個黑色方塊相互接觸的位置)
那現實世界空間的3D點怎麼來呢?這些影象是從一個靜止的攝像頭上取下來的,並且棋盤被放置成了不同的位置和方向。所以我們得知道(X,Y,Z)值。但是為了簡單,我們可以說棋盤在XY平面保持靜止(所以 Z總是等於0),並且攝像頭也相應的移動了。這樣的考慮方式幫助我們可以只算出XY值。現在對於X,Y值,我們可以簡單的傳入點,比如(0,0), (1,0), (2,0), ... 用於表示點的位置。在這種情況下,我們得到的結果將是棋盤方格的放縮後的大小。但如果我們知道方格尺寸(比方說 30 mm),我們可以傳入這樣的值(0,0), (30,0), (60,0), ... . 於是我們得到的結果就是mm為單位的。(當前情況下,我們不知道方格的尺寸,因為我們沒有取那些影象,所以我們按照方格尺寸放縮的模式傳參)。
3D 點被稱為是object points 物件點,而2D 影象上的點被稱為 image points 圖象點。
實現步驟:
1、為了找到棋盤格模板,可使用OpenCV中的函式cv2.findChessboardCorners()。需要告訴程式標明模板是何規格,在這裡我們使用的是13x11的棋盤格,含有12x10的內部角點。這個函式如果檢測到模板,會返回對應的角點,並返回true。當然不一定所有的影象都能找到需要的模板,所以我們可以使用多幅影象進行定標。
2、找到角點後,我們可以使用cv2.cornerSubPix()可以得到更為準確的角點畫素座標。我們也可以使用cv2.drawChessboardCorners()將角點繪製到影象上顯示。
3、通過前面的步驟,得到了用於標定的三維點和與其對應的影象上的二維點對。我們使用cv2.calibrateCamera()進行標定,這個函式會返回標定結果、相機的內參數矩陣、畸變係數、旋轉矩陣和平移向量。
4、通過反投影誤差,我們可以來評估結果的好壞。越接近0,說明結果越理想。通過之前計算的內參數矩陣、畸變係數、旋轉矩陣和平移向量,使用cv2.projectPoints()計算三維點到二維影象的投影,然後計算反投影得到的點與影象上檢測到的點的誤差,最後計算一個對於所有標定影象的平均誤差,這個值就是反投影誤差。
5、使用cv2.undistort()方法去除畸變
相關函式的引數解釋在這裡不再詳細說明。
來看程式碼:
import cv2 import numpy as np import glob # 設定尋找亞畫素角點的引數,採用的停止準則是最大迴圈次數30和最大誤差容限0.001 criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001) # 獲取標定板角點的位置 objp = np.zeros((6 * 4, 3), np.float32) objp[:, :2] = np.mgrid[0:6, 0:4].T.reshape(-1, 2) # 將世界座標系建在標定板上,所有點的Z座標全部為0,所以只需要賦值x和y obj_points = [] # 儲存3D點 img_points = [] # 儲存2D點 images = glob.glob("D:/Python/ComputerView/test1/*.jpg") for fname in images: img = cv2.imread(fname) cv2.imshow('img',img) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) size = gray.shape[::-1] ret, corners = cv2.findChessboardCorners(gray, (6, 4), None) print(ret) if ret: obj_points.append(objp) corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria) # 在原角點的基礎上尋找亞畫素角點 #print(corners2) if [corners2]: img_points.append(corners2) else: img_points.append(corners) cv2.drawChessboardCorners(img, (8, 6), corners, ret) # 記住,OpenCV的繪製函式一般無返回值 cv2.imshow('img', img) cv2.waitKey(10) print(len(img_points)) cv2.destroyAllWindows() # 標定 ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, size, None, None) print("ret:", ret) print("mtx:\n", mtx) # 內參數矩陣 print("dist:\n", dist) # 畸變係數 distortion cofficients = (k_1,k_2,p_1,p_2,k_3) print("rvecs:\n", rvecs) # 旋轉向量 # 外引數 print("tvecs:\n", tvecs ) # 平移向量 # 外引數 print("-----------------------------------------------------") img = cv2.imread(images[2]) h, w = img.shape[:2] newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))#顯示更大範圍的圖片(正常重對映之後會刪掉一部分影象) print (newcameramtx) dst = cv2.undistort(img,mtx,dist,None,newcameramtx) x,y,w,h = roi dst1 = dst[y:y+h,x:x+w] cv2.imwrite('D:/Python/ComputerView/test1/calibresult3.jpg', dst1) print ("dst的大小為:", dst1.shape)天道酬勤 循序漸進 技壓群雄