背景
我們拍攝的物體都處於三維世界座標系中,而相機拍攝時鏡頭看到的是三維相機座標系,成像時三維相機座標係向二維影象座標系轉換。不同的鏡頭成像時的轉換矩陣不同,同時可能引入失真,標定的作用是近似地估算出轉換矩陣和失真係數。為了估算,需要知道若干點的三維世界座標系中的座標和二維影象座標系中的座標,也就是拍攝棋盤的意義。對於張正友棋盤標定法的詳解可以參考:python-opencv tutorial。
相機內引數
設p=(x,y,z)為場景中的一點,在針孔相機模型中,其要經過以下幾個變換,最終變為二維影象上的像點p=(μ,ν):
(1)將p從世界座標系通過剛體變換(旋轉和平移)變換到相機座標系,這個變換過程使用的是相機間的相對位姿,也就是相機的外引數。
(2)從相機座標系,通過透視投影變換到相機的成像平面上的像點p=(x,y)。
(3)將像點p從成像座標系,通過縮放和平移變換到畫素座標系上點p=(μ,ν)。
相機將場景中的三維點變換為影象中的二維點,也就是各個座標系變換的組合,可將上面的變換過程整理為矩陣相乘的形式,將矩陣k稱為相機的內引數,
張氏標定原理
1.計算外參
2.計算內參
3.最大似然估計
4.徑向畸變估計
** 標定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是個檔名管理工具
#對每張,識別出角點,記錄世界物體座標和影象座標
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)
#在棋盤上繪製角點,只是視覺化工具
img = cv2.drawchessboardcorners(gray,(6,4),corners2,ret)
cv2.imshow('img',img)
#cv2.waitkey(1000)
'''傳入所有各自角點的三維、二維座標,相機標定。
每張都有自己的旋轉和平移矩陣,但是相機內參和畸變係數只有一組。
mtx,相機內參;dist,畸變係數;revcs,旋轉矩陣;tvecs,平移矩陣。
#注意這裡跟迴圈開頭讀取一樣,如果太大要同比例縮放,不然後面優化相機內參肯定是錯的。
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還算可以。
張正友相機標定法
1.為什麼需要相機標定 因為每個鏡頭在生產和組裝過程中可能會出現不同程度的畸變 畸變是一種光學錯位現象,通俗的講就是本來是直線的物體,實際拍出的效果是扭曲的 而通過相機標定可以矯正這種畸變,避免拍出的影象出現畸變。另外,可以根據相機標定得到的相機引數建立相機成像幾何模型,將世界座標系中的3d影象對映...
關於張正友標定法
關於演算法的實現最好參照一下 opencv2計算機視覺程式設計手冊 第191頁,講的非常好,事實上我們只需要有3d點和2d點的對應我們就可以計算出對應的相機矩陣了,但是一直讓我迷惑的是3d點如何得到。那麼張正友標定法事實上是建立了乙個棋盤模型,對這個棋盤模型進行了不同角度的拍照,這樣這些拍照imag...
張正友相機標定
1.關於資料說明 張正友標定2000 lm演算法1978 the levenberg marquardt algorithm,implementation and theory opencv 參考 其中關於calibratecamera函式可見 此函式會輸出乙個3x3內參矩陣,乙個5維畸變係數,n幅...