1. 程式人生 > 其它 >OpenCV-Python系列之相機校準實踐

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)
天道酬勤 循序漸進 技壓群雄