OpenCV圖像識別之相機校準Camera?Calibration學習
目標
在本節(jié)中,將學習
- 由相機引起的失真類型
- 如何找到相機的固有和非固有特性
- 如何基于這些特性使圖像不失真
基礎
一些針孔相機會給圖像帶來明顯的失真。兩種主要的變形:
- 徑向變形( radial distortion)
- 切向變形( tangential distortion)
徑向變形
徑向變形會導致直線出現(xiàn)彎曲。距圖像中心越遠,徑向畸變越大。例如,下面顯示一個圖像,其中棋盤的兩個邊緣用紅線標記。但是,會看到棋盤的邊框不是直線,并且與紅線不匹配。所有預期的直線都凸出。
徑向變形的變形量可以用下面的式子表示:
除此之外,還需要其他一些信息,相機的內(nèi)在和外在參數(shù).
- 外在參數(shù)對應于旋轉(zhuǎn)和平移矢量,其將3D點的坐標平移為坐標系。
對于3D應用,首先需要糾正這些失真,就要找到這些參數(shù)。以國際象棋棋盤為示例圖像,找到一些已經(jīng)知道其相對位置的特定點(例如棋盤上的四角)。假設知道了現(xiàn)實世界空間中這些點的坐標,也知道圖像中的坐標,就可以求解失真系數(shù)了。
代碼
如上所述,相機校準至少需要10個測試圖案。OpenCV項目中附帶了一些國際象棋棋盤的圖像,因此將利用這些圖像進行講解。假設給定一張棋盤圖像,相機校準所需的重要輸入數(shù)據(jù)是3D真實點集以及圖像中這些點的相應2D坐標。2D圖像點可以從圖像中輕松找到點(這些圖像點是國際象棋棋盤中兩個黑色正方形相互接觸的位置)。
真實3D點如何處理?這些圖像是從靜態(tài)相機拍攝的,而國際象棋棋盤放置在不同的位置和方向。因此,需要知道(X,Y,Z)(X, Y, Z)(X,Y,Z)值。但是為簡單起見,可以說棋盤在XYXYXY平面上保持靜止(因此Z始終為0),并且照相機也相應地移動了。這種考慮有助于僅找到X,Y值?,F(xiàn)在對于X,Y值,可以簡單地將點傳遞為(0,0),(1,0),(2,0),…,這表示點的位置。在這種情況下,得到的結(jié)果將是棋盤正方形的大小比例。但是,當我們知道正方形大小(例如30毫米),則可以將值傳遞為(0,0),(30,0),(60,0),…。因此,得到的結(jié)果以毫米為單位。(在這種情況下,我們不知道正方形的大小,因為我們沒有拍攝那些圖像,因此我們以正方形的大小進行傳遞)。
3D點稱為對象點,而2D圖像點稱為圖像點。
開始
為了在國際象棋棋盤圖像中查找圖案,可以使用函數(shù)cv2.findChessboardCorners()
。該函數(shù)需要傳遞所需的圖案,例如8x8網(wǎng)格,5x5網(wǎng)格等。在此示例中使用7x6網(wǎng)格。(通常,棋盤有8x8的正方形和7x7的內(nèi)部角)。它返回角點和返回值,如果獲得圖案,則返回值為True。這些角將按順序放置(從左到右,從上到下)
此函數(shù)可能無法在所有圖像中找到所需的圖案。因此一個不錯的選擇是編寫代碼,使它啟動相機并檢查每幀所需的圖案。獲得圖案后,找到角并將其存儲在列表中。另外,在讀下一幀之前請設置一些時間間隔,以便可以在不同方向上調(diào)整棋盤。繼續(xù)此過程,直到獲得所需數(shù)量的良好圖案為止。即使在此處提供的示例中,也不確定給出的14張圖像中有多少張是好的。因此,必須閱讀所有圖像并僅保存好的團。 除了棋盤之外,還可以使用圓形網(wǎng)格。 在這種情況下,必須使用函數(shù)cv2.findCirclesGrid()
來找到模式。 較少的圖像足以使用圓形網(wǎng)格執(zhí)行相機校準。
一旦找到拐角,就可以使用cv2.cornerSubPix()
來提高其精度。還可以使用cv2.drawChessboardCorners()
繪制圖案。所有這些步驟都包含在以下代碼中:
# termination criteria criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) # prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0) objp = np.zeros((6*7,3), np.float32) objp[:, :2] = np.mgrid[0:7,0:6].T.reshape(-1,2) # # Arrays to store object points and image points from all the images. objpoints = [] # 3d point in real world space imgpoints = [] # 2d points in image plane. images = glob.glob(r'right*.jpg') for fname in images: img = cv2.imread(fname) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # find the chess board corners ret, corners = cv2.findChessboardCorners(gray, (7,6), None) if ret: objpoints.append(objp) corners2 = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria) imgpoints.append(corners) # draw and display the corners cv2.drawChessboardCorners(img, (7, 6), corners2, ret) cv2.imshow('img', img) cv2.waitKey(0) cv2.destroyAllWindows()
校準
有了目標點和圖像點,就可以進行校準??梢允褂煤瘮?shù)cv2.calibrateCamera()
返回相機矩陣,失真系數(shù),旋轉(zhuǎn)和平移矢量等。
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
不畸變
現(xiàn)在,可以對拍攝圖像進行不畸變處理。OpenCV提供了兩種方法來執(zhí)行此操作。但首先,可以使用cv2.getOptimalNewCameraMatrix()
基于自由縮放參數(shù)來優(yōu)化相機矩陣。如果縮放參數(shù)alpha = 0
,則返回具有最少不需要像素的未失真圖像。因此,它甚至可能會刪除圖像角落的一些像素。如果alpha = 1,則所有像素都保留有一些額外的黑色圖像。此函數(shù)還返回可用于裁剪結(jié)果的圖像ROI。
實例:
img = cv2.imread('right12.jpg') h, w, _ = img.shape newcameratx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
1使用cv2.undistort
只需調(diào)用該函數(shù)并使用上面獲得的ROI裁剪結(jié)果即可。
# undistort dst = cv2.undistort(img, mtx, dist, None, newcameratx) # copy the image x, y, w, h = roi dst = dst[y:y+h, x:x+w] cv2.imshow('calibresult.png', dst) cv2.waitKey(0) cv2.destroyAllWindows()
2. 使用remapping
該方式有點困難。首先,找到從扭曲圖像到未扭曲圖像的映射函數(shù)。然后使用重映射函數(shù)。
盡管如此,兩種方法都給出相同的結(jié)果??吹较旅娴慕Y(jié)果:
# undistort mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, newcameratx, (w, h), 5) dst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR) # crop the image x, y, w, h = roi dst = dst[y:y+h, x:x+w] cv2.imshow('calibresult.png', dst) cv2.waitKey(0) cv2.destroyAllWindows()
可以看到所有邊緣都是筆直的。 另外,可以使用NumPy中的寫入功能(np.savez,np.savetxt
等)存儲相機矩陣和失真系數(shù),以備將來使用。
重投影誤差|Re-projection Error
重投影誤差可以很好地估計找到的參數(shù)的精確程度。重投影誤差越接近零,表明發(fā)現(xiàn)的參數(shù)越準
確。給定固有,失真,旋轉(zhuǎn)和平移矩陣,必須首先使用cv2.projectPoints()
將對象點轉(zhuǎn)換為
圖像點。然后,可以計算出通過變換得到的絕對值和拐角發(fā)現(xiàn)算法之間的絕對值范數(shù)。為了
找到平均誤差,計算為所有校準圖像計算的誤差的算術(shù)平均值。
# re projection error mean_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) mean_error += error print( "total error: {}".format(mean_error/len(objpoints)) )
total error: 0.025365426097625716
保存相機矩陣和失真系數(shù)與加載 可以以json或者pickle等方式保存相機矩陣和失真系數(shù)
import pickle # save data cam_calib = {"cam_matrix": mtx, "dist_coeffs": dist} with open("cam_calib.p", "wb") as f: pickle.dump(cam_calib, f) # load previously save data with open(r"cam_calib.p", "rb") as f: data = pickle.load(f) mtx = data['cam_matrix'] dist = data['dist_coeffs']
附加資源
以上就是OpenCV圖像識別之相機校準Camera Calibration學習的詳細內(nèi)容,更多關于OpenCV圖像識別相機校準的資料請關注腳本之家其它相關文章!
相關文章
使用Python操作Redis所有數(shù)據(jù)類型的方法
當今互聯(lián)網(wǎng)時代,數(shù)據(jù)處理已經(jīng)成為了一個非常重要的任務,而Redis作為一款高性能的NoSQL數(shù)據(jù)庫,越來越受到了廣大開發(fā)者的喜愛,本篇博客將介紹如何使用Python操作Redis的所有類型,以及一些高級用法,需要的朋友可以參考下2023-11-11python實現(xiàn)網(wǎng)頁鏈接提取的方法分享
這篇文章主要介紹了python實現(xiàn)的網(wǎng)頁鏈接提取的方法,需要的朋友可以參考下2014-02-02關于Torch?torchvision?Python版本對應關系說明
這篇文章主要介紹了關于Torch?torchvision?Python版本對應關系說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05Python unittest discover批量執(zhí)行代碼實例
這篇文章主要介紹了Python unittest discover批量執(zhí)行代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-09-09