相機校準至少需要10個測試圖案,所需的重要輸入數(shù)據(jù)是3D現(xiàn)實世界點集以及圖像中這些點的相應(yīng)2D坐標。3D點稱為對象點,而2D圖像點稱為圖像點。
準備工作
除了棋盤,我們還可以使用圓形網(wǎng)格。 在這種情況下,我們必須使用函數(shù)cv.findCirclesGrid()來找到模式。 較少的圖像足以使用圓形網(wǎng)格執(zhí)行相機校準。
一旦找到拐角,就可以使用cv.cornerSubPix()來提高其精度。我們還可以使用cv.drawChessboardCorners()繪制圖案。
import numpy as np
import cv2 as cv
import glob
# 終止條件
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 準備對象點, 如 (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)
# 用于存儲所有圖像的對象點和圖像點的數(shù)組。
objpoints = [] # 真實世界中的3d點
imgpoints = [] # 圖像中的2d點
images = glob.glob('*.jpg')
for fname in images:
img = cv.imread(fname)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 找到棋盤角落
ret, corners = cv.findChessboardCorners(gray, (7,6), None)
# 如果找到,添加對象點,圖像點(細化之后)
if ret == True:
objpoints.append(objp)
corners2 = cv.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria)
imgpoints.append(corners)
# 繪制并顯示拐角
cv.drawChessboardCorners(img, (7,6), corners2, ret)
cv.imshow('img', img)
cv.waitKey(500)
cv.destroyAllWindows()
校準
現(xiàn)在我們有了目標點和圖像點,現(xiàn)在可以進行校準了。我們可以使用函數(shù)cv.calibrateCamera()返回相機矩陣,失真系數(shù),旋轉(zhuǎn)和平移矢量等。
ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints,
gray.shape[::-1], None, None)
不失真
現(xiàn)在,我們可以拍攝圖像并對其進行扭曲。OpenCV提供了兩種方法來執(zhí)行此操作。但是,首先,我們可以使用cv.getOptimalNewCameraMatrix()基于自由縮放參數(shù)來優(yōu)化相機矩陣。如果縮放參數(shù)alpha = 0,則返回具有最少不需要像素的未失真圖像。因此,它甚至可能會刪除圖像角落的一些像素。如果alpha = 1,則所有像素都保留有一些額外的黑色圖像。此函數(shù)還返回可用于裁剪結(jié)果的圖像ROI。
使用cv.undistort()
這是最簡單的方法。只需調(diào)用該函數(shù)并使用上面獲得的ROI裁剪結(jié)果即可。
使用remapping
該方式有點困難。首先,找到從扭曲圖像到未扭曲圖像的映射函數(shù)。然后使用重映射功能。
img = cv.imread('left12.jpg')
h, w = img.shape[:2]
newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
# 1
dst = cv.undistort(img, mtx, dist, None, newcameramtx)
# 剪裁圖像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite('calibresult.png', dst)
# 2
mapx, mapy = cv.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w,h), 5)
dst = cv.remap(img, mapx, mapy, cv.INTER_LINEAR)
# 裁剪圖像
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv.imwrite('calibresult.png', dst)
參數(shù):重投影誤差
重投影誤差可以很好地估計找到的參數(shù)的精確程度。重投影誤差越接近零,我們發(fā)現(xiàn)的參數(shù)越準確。給定固有,失真,旋轉(zhuǎn)和平移矩陣,我們必須首先使用cv.projectPoints()將對象點轉(zhuǎn)換為圖像點。然后,我們可以計算出通過變換得到的絕對值和拐角發(fā)現(xiàn)算法之間的絕對值范數(shù)。為了找到平均誤差,我們計算為所有校準圖像計算的誤差的算術(shù)平均值。
mean_error = 0
for i in xrange(len(objpoints)):
imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2)/len(imgpoints2)
mean_error += error
print( "total error: {}".format(mean_error/len(objpoints)) )
姿態(tài)估計
先優(yōu)化。然后使用函數(shù)cv.solvePnPRansac()計算旋轉(zhuǎn)和平移。一旦有了這些變換矩陣,就可以使用它們將軸點投影到圖像平面上。
for fname in glob.glob('left*.jpg'):
img = cv.imread(fname)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret, corners = cv.findChessboardCorners(gray, (7,6),None)
if ret == True:
corners2 = cv.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
# 找到旋轉(zhuǎn)和平移矢量。
ret,rvecs, tvecs = cv.solvePnP(objp, corners2, mtx, dist)
# 將3D點投影到圖像平面
imgpts, jac = cv.projectPoints(axis, rvecs, tvecs, mtx, dist)
img = draw(img,corners2,imgpts)
cv.imshow('img',img)
k = cv.waitKey(0) & 0xFF
if k == ord('s'):
cv.imwrite(fname[:6]+'.png', img)
cv.destroyAllWindows()
對極幾何
首先我們需要在兩個圖像之間找到盡可能多的匹配項,以找到基本矩陣。為此,我們將SIFT描述符與基于FLANN的匹配器和比率測試結(jié)合使用。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img1 = cv.imread('myleft.jpg',0) #索引圖像 # left image
img2 = cv.imread('myright.jpg',0) #訓(xùn)練圖像 # right image
sift = cv.SIFT()
# 使用SIFT查找關(guān)鍵點和描述符
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
# FLANN 參數(shù)
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)
flann = cv.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)
good = []
pts1 = []
pts2 = []
# 根據(jù)Lowe的論文進行比率測試
for i,(m,n) in enumerate(matches):
if m.distance < 0.8*n.distance:
good.append(m)
pts2.append(kp2[m.trainIdx].pt)
pts1.append(kp1[m.queryIdx].pt)
現(xiàn)在,我們獲得了兩張圖片的最佳匹配列表。 讓我們找到基本面矩陣。
pts1 = np.int32(pts1)
pts2 = np.int32(pts2)
F, mask = cv.findFundamentalMat(pts1,pts2,cv.FM_LMEDS)
# 我們只選擇內(nèi)點
pts1 = pts1[mask.ravel()==1]
pts2 = pts2[mask.ravel()==1]
接下來,我們找到Epilines。在第二張圖像上繪制與第一張圖像中的點相對應(yīng)的Epilines。因此,在這里提到正確的圖像很重要。我們得到了一行線。因此,我們定義了一個新功能來在圖像上繪制這些線條。
def drawlines(img1,img2,lines,pts1,pts2):
''' img1 - 我們在img2相應(yīng)位置繪制極點生成的圖像
lines - 對應(yīng)的極點 '''
r,c = img1.shape
img1 = cv.cvtColor(img1,cv.COLOR_GRAY2BGR)
img2 = cv.cvtColor(img2,cv.COLOR_GRAY2BGR)
for r,pt1,pt2 in zip(lines,pts1,pts2):
color = tuple(np.random.randint(0,255,3).tolist())
x0,y0 = map(int, [0, -r[2]/r[1] ])
x1,y1 = map(int, [c, -(r[2]+r[0]*c)/r[1] ])
img1 = cv.line(img1, (x0,y0), (x1,y1), color,1)
img1 = cv.circle(img1,tuple(pt1),5,color,-1)
img2 = cv.circle(img2,tuple(pt2),5,color,-1)
return img1,img2
現(xiàn)在,我們在兩個圖像中都找到了Epiline并將其繪制。文章來源:http://www.zghlxwxcb.cn/news/detail-752512.html
# 在右圖(第二張圖)中找到與點相對應(yīng)的極點,然后在左圖繪制極線
lines1 = cv.computeCorrespondEpilines(pts2.reshape(-1,1,2), 2,F)
lines1 = lines1.reshape(-1,3)
img5,img6 = drawlines(img1,img2,lines1,pts1,pts2)
# 在左圖(第一張圖)中找到與點相對應(yīng)的Epilines,然后在正確的圖像上繪制極線
lines2 = cv.computeCorrespondEpilines(pts1.reshape(-1,1,2), 1,F)
lines2 = lines2.reshape(-1,3)
img3,img4 = drawlines(img2,img1,lines2,pts2,pts1)
plt.subplot(121),plt.imshow(img5)
plt.subplot(122),plt.imshow(img3)
plt.show()
立體圖像的深度圖
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
imgL = cv.imread('tsukuba_l.png',0)
imgR = cv.imread('tsukuba_r.png',0)
stereo = cv.StereoBM_create(numDisparities=16, blockSize=15)
disparity = stereo.compute(imgL,imgR)
plt.imshow(disparity,'gray')
plt.show()
結(jié)果受到高度噪聲的污染。通過調(diào)整numDisparities和blockSize的值,可以獲得更好的結(jié)果。
參數(shù):文章來源地址http://www.zghlxwxcb.cn/news/detail-752512.html
- texture_threshold:過濾出紋理不足以進行可靠匹配
- 區(qū)域斑點范圍和大?。夯趬K的匹配器通常會在對象邊界附近產(chǎn)生“斑點”,其中匹配窗口捕獲一側(cè)的前景和背景
- 在另一場景中,匹配器似乎還在桌子上投影的紋理中找到小的虛假匹配項。為了消除這些偽像,我們使用由speckle_size和speckle_range參數(shù)控制的散斑濾鏡對視差圖像進行后處理。speckle_size是將視差斑點排除為“斑點”的像素數(shù)。speckle_range控制必須將值差異視為同一對象的一部分的程度
- 視差數(shù)量:滑動窗口的像素數(shù)。它越大,可見深度的范圍就越大,但是需要更多的計算
- min_disparity:從開始搜索的左像素的x位置開始的偏移量
- uniqueness_ratio:另一個后過濾步驟。如果最佳匹配視差不足夠好于搜索范圍中的所有其他視差,則將像素濾出。如果texture_threshold和斑點過濾仍在
- 通過虛假匹配,則可以嘗試進行調(diào)整
- prefilter_size和prefilter_cap:預(yù)過濾階段,可標準化圖像亮度并增強紋理,以準備塊匹配
- 通常,你不需要調(diào)整這些
到了這里,關(guān)于OpenCV+相機校準和3D重建的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!