OpenCV-Python 對極幾何

目標

在本節中

  • 我們將學習多視圖幾何的基礎知識
  • 我們將瞭解什麼是極點,極線,極線約束等。

基礎概念

當我們使用針孔相機拍攝圖像時,我們失去了重要信息,即圖像深度。 或者圖像中的每個點距相機多遠,因為它是3D到2D轉換。 因此,是否能夠使用這些攝像機找到深度信息是一個重要的問題。 答案是使用不止一臺攝像機。 在使用兩臺攝像機(兩隻眼睛)的情況下,我們的眼睛工作方式相似,這稱為立體視覺。 因此,讓我們看看OpenCV在此字段中提供了什麼。

(通過Gary Bradsky學習OpenCV在該領域有很多信息。)

在深入圖像之前,讓我們首先了解多視圖幾何中的一些基本概念。在本節中,我們將討論對極幾何。請參見下圖,該圖顯示了使用兩臺攝像機拍攝同一場景的圖像的基本設置。


如果僅使用左攝像機,則無法找到與圖像中的點相對應的3D點,因為線上的每個點都投影到圖像平面上的同一點。但也要考慮正確的形象。現在,直線$OX$上的不同點投射到右側平面上的不同點($x'$)。因此,使用這兩個圖像,我們可以對正確的3D點進行三角剖分。這就是整個想法。

不同點的投影在右平面$OX$上形成一條線(line$l'$)。我們稱其為對應於該點的Epiline。這意味著,要在正確的圖像上找到該點,請沿著該輪廓線搜索。它應該在這條線上的某處(以這種方式考慮,可以在其他圖像中找到匹配點,而無需搜索整個圖像,只需沿著Epiline搜索即可。因此,它可以提供更好的性能和準確性)。這稱為對極約束。類似地,所有點在另一幅圖像中將具有其對應的Epiline。該平面稱為對極面

$O$和$O'$是相機中心。從上面給出的設置中,您可以看到在點處的左側圖像上可以看到右側攝像機$O'$的投影。它稱為極點。極點是穿過相機中心和圖像平面的線的交點。左攝像機的極點也同理。在某些情況下,您將無法在圖像中找到極點,它們可能位於圖像外部(這意味著一個攝像機看不到另一個攝像機)。

所有的極線都通過其極點。因此,要找到中心線的位置,我們可以找到許多中心線並找到它們的交點。

因此,在節中,我們將重點放在尋找對極線和極線。但是要找到它們,我們需要另外兩種成分,即

基礎矩陣(F)基本矩陣(E),基礎矩陣包含有關平移和旋轉的信息,這些信息在全局座標中描述了第二個攝像頭相對於第一個攝像頭的位置。參見下圖(圖像由Gary Bradsky提供:Learning OpenCV):

OpenCV-Python 對極幾何 | 五十一

但是我們會更喜歡在像素座標中進行測量,對吧? 基本矩陣除包含有關兩個攝像頭的內在信息之外,還包含與基本矩陣相同的信息,因此我們可以將兩個攝像頭的像素座標關聯起來。(如果我們使用的是校正後的圖像,並用焦距除以標準化該點,$F=E$)。簡而言之,基本矩陣F將一個圖像中的點映射到另一圖像中的線(上)。這是從兩個圖像的匹配點計算得出的。 至少需要8個這樣的點才能找到基本矩陣(使用8點算法時)。 選擇更多點並使用RANSAC將獲得更可靠的結果。

代碼

因此,首先我們需要在兩個圖像之間找到儘可能多的匹配項,以找到基本矩陣。為此,我們將SIFT描述符與基於FLANN的匹配器和比率測試結合使用。

<code>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) #訓練圖像 # right image
sift = cv.SIFT()
# 使用SIFT查找關鍵點和描述符
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)
# FLANN 參數
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 = []
# 根據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)/<code>

現在,我們獲得了兩張圖片的最佳匹配列表。 讓我們找到基本面矩陣。

<code>pts1 = np.int32(pts1)
pts2 = np.int32(pts2)
F, mask = cv.findFundamentalMat(pts1,pts2,cv.FM_LMEDS)
# 我們只選擇內點
pts1 = pts1[mask.ravel()==1]
pts2 = pts2[mask.ravel()==1]/<code>

接下來,我們找到Epilines。在第二張圖像上繪製與第一張圖像中的點相對應的Epilines。因此,在這裡提到正確的圖像很重要。我們得到了一行線。因此,我們定義了一個新功能來在圖像上繪製這些線條。

<code>def drawlines(img1,img2,lines,pts1,pts2):
''' img1 - 我們在img2相應位置繪製極點生成的圖像
lines - 對應的極點 '''
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/<code>

現在,我們在兩個圖像中都找到了Epiline並將其繪製。

<code># 在右圖(第二張圖)中找到與點相對應的極點,然後在左圖繪製極線
lines1 = cv.computeCorrespondEpilines(pts2.reshape(-1,1,2), 2,F)
lines1 = lines1.reshape(-1,3)
img5,img6 = drawlines(img1,img2,lines1,pts1,pts2)
# 在左圖(第一張圖)中找到與點相對應的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()/<code>

以下是我們得到的結果:

OpenCV-Python 對極幾何 | 五十一

您可以在左側圖像中看到所有極點都收斂在右側圖像的外部。那個匯合點就是極點。 為了獲得更好的結果,應使用具有良好分辨率和許多非平面點的圖像。

練習

  1. 一個重要的主題是相機的前移。然後,將在兩個位置的相同位置看到極點,並且從固定點出現極點。 請參閱此討論。
  2. 基本矩陣估計對匹配,離群值等的質量敏感。如果所有選定的匹配都位於同一平面上,則情況會變得更糟。檢查此討論。


分享到:


相關文章: