計算機視覺課后作業5——相機標定


                      相機標定

1.實驗要求:

2.實驗環境:

pyCharm

3.實驗簡介:

相機標定——顧名思義,是針對照相機的相關實驗。那么何為標定?我們需要了解到,相機由於生產過程中光學儀器的不精確性等原因,其參數並不像廠商所標注的那么精確,對於成像的准確度會產生誤差。相機標定要做的就是建立圖像像素位置與場景點位置之間的關系,根據相機成像模型,由特征點在圖像中坐標與世界坐標的對應關系,求解相機模型的內部參數和外部參數。

4.實驗原理:

相機的成像過程涉及到四個坐標系——世界坐標系、相機坐標系、圖像坐標系、像素坐標系及其轉換。

世界坐標系:用戶定義的三維世界的坐標系,描述目標物在真實世界里的位置。

相機坐標系:從相機的角度描述物體位置,作為溝通世界坐標系和圖像/像素坐標系的中間一環。

圖像坐標系:描述成像過程中物體從相機坐標系到圖像坐標系的投影透射關系,方便進一步得到像素坐標系下的坐標。

像素坐標系:描述物體成像后的像點在數字圖像上(相片)的坐標,是我們真正從相機內讀取到的信息所在的坐標系。

針孔照相機模型如圖所示:

 

小孔成像基本模型:

在此模型下,物體的世界坐標和圖像坐標之間是線性的關系,因而對相機參數的求解就歸結到求解線性方程組上。四個坐標系的關系圖如下圖所示,其中 M 為三維空間點,m 為 M 在圖像平面投影成的像點。

首先,根據相機針孔原理,可以推導出空間3D點M的坐標和它的像素坐標之間的關系如下所示

                                                    s\tilde{m}=A[R\vert t] \tilde{M}                                                        (1)

式中:\tilde{m} =[u,v,1]^T ,表示像素坐標系下的坐標;\tilde{M} =[X,Y,Z,1]^T ,表示世界坐標系下的坐標;s為尺度因子;R為世界坐標系與圖像坐標系的旋轉矩陣,t為世界坐標系與圖像坐標系的平移向量,R和t中的參數均被稱為相機的外部參數,簡稱外參;矩陣A被稱為內參矩陣,如下所示:

 
 

矩陣A中,(u_{0} ,v_{0})為光軸與像平面焦點在像素坐標系下的坐標,將它成為主點;α和β是圖像u軸和v軸上的尺度因子,γ為像素兩軸非正交項。

其次,是之前學過的單應性變換矩陣,目的是將一張圖中的點映射到另一張圖中對應的點,進行特征點匹配,求解Homography 矩陣。

世界坐標系(Z=0)平面下的點M及其在像平面中的像坐標關系可以用如下的單應性矩陣H聯系起來:

                                              s\tilde{m}=H\tilde{M}    其中,H=A[r_{1}, r_{2},t]                                (2)

最后對相關相機矩陣求解內部參數和外部參數,涉及到的方法屬於線性微分方程部分的內容。

上圖所示模型是理想模型,正如前文所述,實際上由於生產過程中光學儀器的不精確性等原因,造成相機圖像平面上實際所成的像與理想成像之間會存在畸變現象。我們這邊主要討論徑向畸變,徑向畸變分為兩種,分別是枕形畸變和桶型畸變。枕形畸變是在長焦距下發生的,而桶形畸變是在中短焦距下發生的。如下圖所示,前陣是枕形畸變,后者為桶形畸變:

從圖像可以看出,徑向畸變以某一個中心往外延伸,且越往外,畸變越大;顯然畸變與距離成一種非線性的變換關系,可以用多項式來近似。

                  

x,y是歸一化的圖像坐標,即坐標原點已經移動到主點,並且像素坐標除以焦距。K1,k2,k3是徑向畸變系數,r2=x2+y2(2表示2次方)。

如前文所述,我們求解相機參數是在物體的世界坐標和圖像坐標之間求解,那我們就必須使用現實世界的三維圖像。實驗要求中提到的棋盤格則是出自張正友教授提出的棋盤格標定法——通過棋盤格的多視角拍攝多張圖片,用二維圖像代替三維圖像進行相機標定,大大減少了標定的計算量。並且,棋盤格角點突出,有助於我們找到有效的特征點,提高標定准確度。

棋盤格是打印下來的一張由黑白方塊間隔組成的A4紙,並把它夾在板上固定,作為相機標定實驗的標定物。如前文所示,二維平面比三維立體圖形更容易標定,但與此同時,二維平面相對於三維立體圖形會缺少一部分信息,於是我們通過拍攝不同角度的棋盤格,來獲得更豐富的坐標信息。

5.實驗數據——棋盤圖(11張):

手機型號:紅米note9Pro

6.實驗代碼:

import cv2
import numpy as np
import glob

# 找棋盤格角點
# 閾值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
#棋盤格模板規格
w = 6   #內角點個數,內角點是和其他格子連着的點
h = 4

# 世界坐標系中的棋盤格點,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐標,記為二維矩陣
objp = np.zeros((w*h,3), np.float32)
objp[:,:2] = np.mgrid[0:w,0:h].T.reshape(-1,2)
# 儲存棋盤格角點的世界坐標和圖像坐標對
objpoints = [] # 在世界坐標系中的三維點
imgpoints = [] # 在圖像平面的二維點

images = glob.glob('C:/Users/oulia/Pictures/camera/*.jpg')
for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    # 找到棋盤格角點
    # 棋盤圖像(8位灰度或彩色圖像)  棋盤尺寸  存放角點的位置
    ret, corners = cv2.findChessboardCorners(gray, (w,h),None)
    # 如果找到足夠點對,將其存儲起來
    if ret == True:
        # 角點精確檢測
        # 輸入圖像 角點初始坐標 搜索窗口為2*winsize+1 死區 求角點的迭代終止條件
        cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
        objpoints.append(objp)
        imgpoints.append(corners)
        # 將角點在圖像上顯示
        cv2.drawChessboardCorners(img, (w,h), corners, ret)
        cv2.imshow('findCorners',img)
        cv2.waitKey(1000)
cv2.destroyAllWindows()
#標定、去畸變
# 輸入:世界坐標系里的位置 像素坐標 圖像的像素尺寸大小 3*3矩陣,相機內參數矩陣 畸變矩陣
# 輸出:標定結果 相機的內參數矩陣 畸變系數 旋轉矩陣 平移向量
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# mtx:內參數矩陣
# dist:畸變系數
# rvecs:旋轉向量 (外參數)
# tvecs :平移向量 (外參數)
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)    # 平移向量  # 外參數
# 去畸變
img2 = cv2.imread('C:/Users/oulia/Pictures/camera/1.jpg')
h,w = img2.shape[:2]
# 我們已經得到了相機內參和畸變系數,在將圖像去畸變之前,
# 我們還可以使用cv.getOptimalNewCameraMatrix()優化內參數和畸變系數,
# 通過設定自由自由比例因子alpha。當alpha設為0的時候,
# 將會返回一個剪裁過的將去畸變后不想要的像素去掉的內參數和畸變系數;
# 當alpha設為1的時候,將會返回一個包含額外黑色像素點的內參數和畸變系數,並返回一個ROI用於將其剪裁掉
newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),0,(w,h)) # 自由比例參數

dst = cv2.undistort(img2, mtx, dist, None, newcameramtx)
# 根據前面ROI區域裁剪圖片
x,y,w,h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibresult.jpg',dst)

# 反投影誤差
# 通過反投影誤差,我們可以來評估結果的好壞。越接近0,說明結果越理想。
# 通過之前計算的內參數矩陣、畸變系數、旋轉矩陣和平移向量,使用cv2.projectPoints()計算三維點到二維圖像的投影,
# 然后計算反投影得到的點與圖像上檢測到的點的誤差,最后計算一個對於所有標定圖像的平均誤差,這個值就是反投影誤差。
total_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)
    total_error += error
print (("total error: "), total_error/len(objpoints))

7.實驗結果截圖:

特征點匹配過程:

內參數矩陣:

畸變參數:

旋轉向量(第一張圖):

平移向量(第一張圖):

8.實驗小結:

使用cv.getOptimalNewCameraMatrix()優化內參數和畸變系數,修改自由自由比例因子alpha=1后得到的實驗結果和=0也是一樣的,應該說只是多進行了一步ROI的裁剪。在結果部分無法直接體現出優化的結果。但是是提供了一種可以對內部參數合畸變參數進行不同取值,不同操作優化的方法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM