天天看點

python張正友相機标定法的實作

背景

我們拍攝的物體都處于三維世界坐标系中,而相機拍攝時鏡頭看到的是三維相機坐标系,成像時三維相機坐标系向二維圖像坐标系轉換。不同的鏡頭成像時的轉換矩陣不同,同時可能引入失真,标定的作用是近似地估算出轉換矩陣和失真系數。為了估算,需要知道若幹點的三維世界坐标系中的坐标和二維圖像坐标系中的坐标,也就是拍攝棋盤的意義。對于張正友棋盤标定法的詳解可以參考:python-OpenCV Tutorial。

相機内參數

設P=(X,Y,Z)為場景中的一點,在針孔相機模型中,其要經過以下幾個變換,最終變為二維圖像上的像點p=(μ,ν):

(1)将P從世界坐标系通過剛體變換(旋轉和平移)變換到相機坐标系,這個變換過程使用的是相機間的相對位姿,也就是相機的外參數。

(2)從相機坐标系,通過透視投影變換到相機的成像平面上的像點p=(x,y)。

(3)将像點p從成像坐标系,通過縮放和平移變換到像素坐标系上點p=(μ,ν)。

相機将場景中的三維點變換為圖像中的二維點,也就是各個坐标系變換的組合,可将上面的變換過程整理為矩陣相乘的形式,将矩陣K稱為相機的内參數,

python張正友相機标定法的實作

張氏标定原理

1.計算外參

2.計算内參

3.最大似然估計

4.徑向畸變估計

python張正友相機标定法的實作

代碼

标定demo

import cv2
import glob
import numpy as np
'''
cbraw和cbcol是我自己加的。tutorial用的棋盤足夠大包含了7×6以上
個角點,我自己用的隻有6×4。這裡如果角點維數超出的話,标定的時候會報錯。
'''
cbraw = 6
cbcol = 4
# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((cbraw*cbcol,3), np.float32) 
'''
設定世界坐标下點的坐标值,因為用的是棋盤可以直接按網格取;
假定棋盤正好在x-y平面上,這樣z值直接取0,簡化初始化步驟。
mgrid把列向量[0:cbraw]複制了cbcol列,把行向量[0:cbcol]複制了cbraw行。
轉置reshape後,每行都是4×6網格中的某個點的坐标。
'''
objp[:,:2] = np.mgrid[0:cbraw,0:cbcol].T.reshape(-1,2)

objpoints = [] # 3d point in real world space
imgpoints = [] # 2d points in image plane.
#glob是個檔案名管理工具
images = glob.glob("*.JPG")
for fname in images:
#對每張圖檔,識别出角點,記錄世界物體坐标和圖像坐标
    img = cv2.imread(fname) #source image
    #我用的圖檔太大,縮小了一半
    img = cv2.resize(img,None,fx=0.5, fy=0.5, interpolation = cv2.INTER_CUBIC)
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #轉灰階
    #cv2.imshow('img',gray)
    #cv2.waitKey(1000)
    #尋找角點,存入corners,ret是找到角點的flag
    ret, corners = cv2.findChessboardCorners(gray,(6,4),None)
    #criteria:角點精準化疊代過程的終止條件
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
    #執行亞像素級角點檢測
    corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)

    objpoints.append(objp)
    imgpoints.append(corners2)
    #在棋盤上繪制角點,隻是可視化工具
    img = cv2.drawChessboardCorners(gray,(6,4),corners2,ret)
    cv2.imshow('img',img)
    #cv2.waitKey(1000)
'''
傳入所有圖檔各自角點的三維、二維坐标,相機标定。
每張圖檔都有自己的旋轉和平移矩陣,但是相機内參和畸變系數隻有一組。
mtx,相機内參;dist,畸變系數;revcs,旋轉矩陣;tvecs,平移矩陣。
'''
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)
img = cv2.imread('1.JPG')
#注意這裡跟循環開頭讀取圖檔一樣,如果圖檔太大要同比例縮放,不然後面優化相機内參肯定是錯的。
img = cv2.resize(img,None,fx=0.5, fy=0.5, interpolation = cv2.INTER_CUBIC)
h,w = img.shape[:2]
'''
優化相機内參(camera matrix),這一步可選。
參數1表示保留所有像素點,同時可能引入黑色像素,
設為0表示盡可能裁剪不想要的像素,這是個scale,0-1都可以取。
'''
newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))
#糾正畸變
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)

#這步隻是輸出糾正畸變以後的圖檔
x,y,w,h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibresult.png',dst)
#列印我們要求的兩個矩陣參數
print("newcameramtx:\n",newcameramtx)
print("dist:\n",dist)
#計算誤差
tot_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2)
    tot_error += error

print("total error: ", tot_error/len(objpoints))
           

運作結果

用iphone7相機标定出來的結果,總體的錯誤率0.68還算可以。

python張正友相機标定法的實作

繼續閱讀