基礎(chǔ)概念
當我們使用針孔攝像機拍攝圖像時,我們會丟失重要信息据途,圖像的深度⌒鸬椋或者圖像每個點離攝像機有多遠颖医,因為這是一個3D到2D的轉(zhuǎn)換。所以我們是否能找到深度信息就變得很重要裆蒸∪巯簦回答是使用多個攝像機。我們的眼睛也是類似的方法,我們使用兩個攝像機(雙眼)佛致,這被叫做立體影像贮缕。所以我們看看OpenCV提供了什么。
在更進一步之前俺榆,我們先明白一些多視幾何的基礎(chǔ)概念感昼。在本節(jié)里,我們會用核面幾何處理罐脊《ㄉぃ看下面的圖像顯示了兩個攝像機對同一個場景拍攝圖像的基本設(shè)置。
如果我們只用左邊的攝像機萍桌,我們沒法找到點x的對應3D點宵溅。因為線OX上的每一個點在圖像平面都投影到同一個點。但是如果加上右邊的攝像機梗夸,現(xiàn)在OX線上的不同點在右邊的平面投影到了不同的點(x')层玲。所以用兩個圖像,我們可以三角測量正確的3D點反症。這就是所有想法辛块。
在右邊平面上不同點投影出來的直線(l')。我們叫做點x對應的極線铅碍。它表示润绵,要找到右邊圖像里的點x,沿著這根直線找就行胞谈。它應該在這條線上的某出(這么想尘盼,要找到在另一張圖上的對應點,你不需要搜索整張圖片烦绳,只需要在這根直線上找就行卿捎。所以它提供了更好的性能和準確性)。這被叫做極限約束径密。類似的所有的點都會在另一張圖上有對應的極線午阵。平面XOO'背景叫做偏斜面。
O和O'是攝像機中心享扔,從上面可以看到右邊攝像機的O'的投影可以在左邊圖片看到底桂,e點。這被叫做極點惧眠。極點是通過兩個攝像機中心的直線與圖片平面的焦點籽懦。類似的,e'是左邊攝像機的極點氛魁。在某些情況下暮顺,你沒法在圖像內(nèi)定位極點厅篓,他們可能在圖像外(這表示攝像機沒法看見彼此)。
所有的極線都通過極點拖云。所以要找到極點的位置贷笛,我們可以找多條極線应又,然后找他們的交點宙项。
我們要找到極線和極點,需要兩個東西株扛,基礎(chǔ)矩陣(F)和本質(zhì)矩陣(E)尤筐。本質(zhì)矩陣包含平移和旋轉(zhuǎn)的信息,描述了第二個攝像機相對于第一個在全局坐標系里的位置洞就∨璺保看下圖:
基礎(chǔ)矩陣包含了和本質(zhì)矩陣一樣的信息,另外還有兩個攝像機內(nèi)聯(lián)信息旬蟋,這樣我們可以把兩個攝像機在像素坐標系內(nèi)關(guān)聯(lián)起來油昂。(如果我們使用修正過的圖像并按焦距把點分開正規(guī)化了,F(xiàn)=E)倾贰。簡單的說冕碟,基礎(chǔ)矩陣F把一個圖像里的點映射到另一個圖里的線(極線)。這是通過兩個圖像里的匹配點計算的匆浙。最少需要8個點來得到基礎(chǔ)矩陣(使用8點算法)安寺,最好能有多個點來使用RANSAC來得到更健壯得到結(jié)果。
編碼:
所以我們需要找到兩張圖像里盡可能多的匹配來找基礎(chǔ)矩陣首尼。為了這個挑庶,我們使用基于FLANN匹配子的SIFT描述子和比率測試
import cv2
import numpy as np
from matplotlib import pyplot as pltimg1 = cv2.imread('myleft.jpg',0)? #queryimage # left image
img2 = cv2.imread('myright.jpg',0) #trainimage # right imagesift = cv2.SIFT()
# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)# FLANN parameters
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)flann = cv2.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)
現(xiàn)在我們有兩個圖片里最佳匹配的列表,我們來找基礎(chǔ)矩陣
pts1 = np.int32(pts1)
pts2 = np.int32(pts2)
F, mask = cv2.findFundamentalMat(pts1,pts2,cv2.FM_LMEDS)# We select only inlier points
pts1 = pts1[mask.ravel()==1]
pts2 = pts2[mask.ravel()==1]
下面我們找極線软能,極線在第一個圖像里對應的點畫在第二個圖像里迎捺。我們得到線的數(shù)組,我們定義一個新的函數(shù)來在圖像里畫這些線查排。
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 = cv2.cvtColor(img1,cv2.COLOR_GRAY2BGR)
? ? img2 = cv2.cvtColor(img2,cv2.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 = cv2.line(img1, (x0,y0), (x1,y1), color,1)
? ? ? ? img1 = cv2.circle(img1,tuple(pt1),5,color,-1)
? ? ? ? img2 = cv2.circle(img2,tuple(pt2),5,color,-1)
? ? return img1,img2
現(xiàn)在我們找同時在兩個圖像里的極線并畫出他們:
# Find epilines corresponding to points in right image (second image) and
# drawing its lines on left imagelines1 = cv2.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 = cv2.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()
下面是我們得到的結(jié)果
你可以看到在左邊的圖像里所有的極線匯聚在圖像外的點凳枝。那個匯聚點就是極點。
要得到更好的結(jié)果雹嗦,應該用有好的分辨率的圖像范舀,有很多非平面的點