1、相機標定法原理
在圖像測量過程以及機器視覺應用中,為確定空間物體表面某點的三維幾何關系位置與其在圖像中對應點之間的相互關系,必須建立相機成像的幾何模型,這些模型參數就是相機參數。在大多數條件下這些參數必須通過實驗與計算才能得到,這個求解參數(內參,外參,畸變參數)的過程就稱之為相機標定。
一般來說,標定的過程分為兩個部分:
第一步:從世界坐標系轉換為相機坐標系,這一步是三維點到三維點的轉換,包括 R,t (相機外參)等參數;
第二步:從相機坐標系轉為圖像坐標系,這一步是三維點到二維點的轉換,包括 K(相機內參)等參數;
針孔相機成像原理其實就是利用投影將真實的三維世界坐標轉換到二維的相機坐標上去,其模型示意圖如下圖所示:
從示意圖中可以看出,在世界坐標中的一條直線上的點在相機上只呈現出了一個點,其中發生了非常大的變化,同時也損失很多重要的信息,這也成了我們3D重建、目標檢測與識別領域的重點和難點。
2、相機標定的意義
- 在機器視覺領域,相機的標定是一個關鍵的環節,它決定了機器視覺系統能否有效的定位,能否有效的計算目標物。
- 相機的標定基本上可以分為兩種:
第一種是相機的自標定;是相機拍攝周圍物體,通過數字圖像處理的方法和相關的幾何計算得到相機參數,但是這種方法標定的結果誤差較大,不適合於高精度應用場合。
第二種是依賴於標定參照物的標定方法。是通過標定參照物,由相機成像,並通過數字圖像處理的方法,以及后期的空間算術運算計算相機的內參和外參。這種方法標定的精度高,適用於對精度要求高的應用場合。
3、實驗要求
1.制作棋盤格(每個格子的大小可測量);
2.根據棋盤格,采集10-20張圖片,提取角點;
3.解算出內外參數,內參截圖放在博客中,外部參數最好能可視化。
4、實驗內容
4.1 數據集
- 該數據集是通過將8*8棋盤格投影到電腦屏幕上,然后用VIVO手機拍攝的20張圖片 ,格子大小為1.8cm*1.8cm。
4.2 代碼實現
# coding=utf-8
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((7 * 7, 3), np.float32)
objp[:, :2] = np.mgrid[0:7, 0:7].T.reshape(-1, 2) # 將世界坐標系建在標定板上,所有點的Z坐標全部為0,所以只需要賦值x和y
obj_points = [] # 存儲3D點
img_points = [] # 存儲2D點
images = glob.glob("C:/Users/Administrator/Desktop/棋盤格/IMG_20200407_163457/*.jpg")
i=0
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
size = gray.shape[::-1]
ret, corners = cv2.findChessboardCorners(gray, (7, 7), None)
#print(corners)
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, (7, 7), corners, ret) # 記住,OpenCV的繪制函數一般無返回值
i+=1
cv2.imwrite('C:/Users/Administrator/Desktop/棋盤格/IMG_20200407_163457/'+str(i)+'.jpg', img)
cv2.waitKey(1500)
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:", mtx) # 內參數矩陣
print("dist:", dist) # 畸變系數 distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print("rvecs:", rvecs) # 旋轉向量 # 外參數
print("tvecs:", 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)
print("------------------使用undistort函數-------------------")
dst = cv2.undistort(img,mtx,dist,None,newcameramtx)
x,y,w,h = roi
dst1 = dst[y:y+h,x:x+w]
cv2.imwrite('calibresult3.jpg', dst1)
print ("方法一:dst的大小為:", dst1.shape)
4、實驗結果
4.1 內參矩陣mtx
4.2 畸變系數dist
4.3 旋轉向量rvecs
4.4 平移向量tvecs
4.5 相機內置參數矩陣
5、分析與總結
- 由於拍攝角度的不同,所以每一張圖片的外參也不相同。
- 反投影誤差可以反映結果的好壞,反投影誤差越接近0,表明結果越好。
- 內參矩陣只關心相機坐標和圖像坐標之間的關系,與相機的絕對尺寸無關。投影面的位置與其尺寸相關。