OpenCV-Python系列之對極幾何實踐
OpenCV中的對極幾何
在上個教程中我們已經簡述了對極幾何的一些理論知識,本次我們來使用OpenCV中的一些函式來進行實戰,為了得到基礎矩陣我們應該在兩幅影象中找到儘量多的匹配點。我們可以使用 SIFT 描述符,FLANN 匹配器和比值檢測。
我們使用示例圖片:
先看程式碼:
import numpy as np import cv2 as cv from matplotlib import pyplot as plt img1 = cv.imread('myleft.jpg', 0) # queryimage # left image img2 = cv.imread('myright.jpg', 0) # trainimage # right image sift = cv.xfeatures2d.SIFT_create() # find the keypoints and descriptors with SIFT kp1, des1 = sift.detectAndCompute(img1, None) kp2, des2 = sift.detectAndCompute(img2, None) # FLANN parameters 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 = [] # ratio test as per Lowe's paper 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)
現在得到了一個匹配點列表,我們就可以使用它來計算基礎矩陣了。
retval, mask=cv.findFundamentalMat(points1, points2, method, ransacReprojThreshold, confidence, mask)
· points1:從第一張圖片開始的N個點的陣列。點座標應該是浮點數(單精度或雙精度)。
· points2:與點1大小和格式相同的第二影象點的陣列。
· method:計算基本矩陣的方法。
· cv2.FM_7POINT for a 7-point algorithm. N=7
· cv2.FM_8POINT for an 8-point algorithm. N≥8
· cv2.FM_RANSAC (預設) for the RANSAC algorithm. N≥8
· cv2.FM_LMEDS for the LMedS algorithm. N≥8
· ransacReprojThreshold:僅用於RANSAC方法的引數,預設3。它是一個點到極線的最大距離(以畫素為單位),超過這個點就被認為是一個離群點,不用於計算最終的基本矩陣。根據點定位、影象解析度和影象噪聲的準確性,可以將其設定為1-3左右。
· confidence:僅用於RANSAC和LMedS方法的引數,預設0.99。它指定了一個理想的置信水平(概率),即估計矩陣是正確的。
· mask:輸出
pts1 = np.int32(pts1)
pts2 = np.int32(pts2)
F, mask = cv.findFundamentalMat(pts1, pts2, cv.FM_LMEDS)
# We select only inlier points
pts1 = pts1[mask.ravel() == 1]
pts2 = pts2[mask.ravel() == 1]
下一步我們要找到極線。我們會得到一個包含很多線的陣列。所以我們要 定義一個新的函式將這些線繪製到影象中。
def drawlines(img1, img2, lines, pts1, pts2): ''' img1 - image on which we draw the epilines for the points in img2 lines - corresponding epilines ''' 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
現在我們兩幅影象中計算並繪製極線。
lines = cv.computeCorrespondEpilines(points, whichImage, F, lines)
· points:輸入點。型別為CV_32FC2N×1或1×N矩陣。
· whichImage:包含點的影象(1或2)的索引。
· F:基本矩陣,可使用findFundamentalMat或stereoRectify 進行估計。
· lines:對應於另一幅影象中點的極線的輸出向量(a,b,c)表示直線ax+by+c=0。
# Find epilines corresponding to points in right image (second image) and # drawing its lines on left image lines1 = cv.computeCorrespondEpilines(pts2.reshape(-1, 1, 2), 2, F) lines1 = lines1.reshape(-1, 3) img5, img6 = drawlines(img1, img2, lines1, pts1, pts2) # Find epilines corresponding to points in left image (first image) and # drawing its lines on right image 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 def drawlines(img1, img2, lines, pts1, pts2): ''' img1 - image on which we draw the epilines for the points in img2 lines - corresponding epilines ''' 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 img1 = cv.imread('myleft.jpg', 0) # queryimage # left image img2 = cv.imread('myright.jpg', 0) # trainimage # right image sift = cv.xfeatures2d.SIFT_create() # find the keypoints and descriptors with SIFT kp1, des1 = sift.detectAndCompute(img1, None) kp2, des2 = sift.detectAndCompute(img2, None) # FLANN parameters 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 = [] # ratio test as per Lowe's paper 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) pts1 = np.int32(pts1) pts2 = np.int32(pts2) F, mask = cv.findFundamentalMat(pts1, pts2, cv.FM_LMEDS) # We select only inlier points pts1 = pts1[mask.ravel() == 1] pts2 = pts2[mask.ravel() == 1] # Find epilines corresponding to points in right image (second image) and # drawing its lines on left image lines1 = cv.computeCorrespondEpilines(pts2.reshape(-1, 1, 2), 2, F) lines1 = lines1.reshape(-1, 3) img5, img6 = drawlines(img1, img2, lines1, pts1, pts2) # Find epilines corresponding to points in left image (first image) and # drawing its lines on right image 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()
結果:
我們可以在左側影象中看到所有Epilines都在右側影象的一點處收斂。那個匯合點就是極點。
天道酬勤 循序漸進 技壓群雄