本文中我們準備探索允許3D重建在實踐中工作的核心概念,。具體來說,,我們將了解多視圖或對極幾何的基礎知識。 讓我們來看一下現(xiàn)代針孔相機的功能,。當一幅圖像被拍攝時,,它的信息呈現(xiàn)在二維平面上,在二維平面上,,每個點(或像素)與相機鏡頭之間的距離是不存在的,。但這正是我們?yōu)榱耸褂?D圖像執(zhí)行3D重建所需的信息。我們的解決方案的關鍵在于使用第二個攝像頭拍攝同一物體的照片,,并比較每個圖像來提取深度信息,。我們的眼睛在類似的情況下執(zhí)行著同樣的任務。不過,,OpenCV包含的工具可以幫助我們在非人類針孔相機拍攝的圖像中看到深度,。 我們先分析下面這張圖,該圖說明了兩個攝像機拍攝同一場景照片的場景中多視圖幾何的一些基本概念,。 通過檢查從左側攝像機的中心點發(fā)出的上圖所示的OX線,,我們可以看到該沿線有3個點,我們需要知道它們與O點的距離,。如前所述我們并不能夠僅從左側相機中提取此信息,。然而,對于右側相機,,我們可以看到X的3個點可以沿其圖像平面上的一條直線投影(直線用:l '表示),。這條線被稱為核線(epiline); 必須出現(xiàn)沿著OX線的任何點X的線。重要的是,,當我們在右側相機的圖像中搜索X的匹配點時,,我們已經(jīng)知道它將沿著該核線出現(xiàn),因此大大減少了我們的搜索工作量,。此外,,我們可以假設在左側圖像中出現(xiàn)的每個點將始終具有在右側圖像中找到的伴隨的核線,。這被稱為極線約束。 需要注意的另一個重要方面是核點(epipole),。核點是連接到每個攝像機中心點的線在核面內(nèi)的交點。在上面的圖中,,0和0 '分別表示在兩端,。點e和e'是對方相機的中心點出現(xiàn)在對應圖像中的點。通過將點X(我們試圖提取其深度值的點)包含在核點線上,,我們可以導出這個平面表示為X00'的平面,。該平面稱為極線平面。 我們還可以在上圖中看到,,右側圖像中的X點的投影與相對的相機(e')的核點相交,。這顯示了所有的極線將如何通過圖像中找到的核點(相對照相機的真實中心點將出現(xiàn)的圖像點)。這一點的重要性在于我們能夠通過找到多個核線相交的圖像點來精確計算出核點在我們的圖像中的位置,。我們還應該注意,在很多情況下,,在相對圖像的幀中可能沒有捕獲一個或兩個相機中心點,,這意味著該極點將落在其2D像素矩陣之外。盡管如此,,我們?nèi)匀豢梢酝ㄟ^分析核線精確地計算出虛構的核點位置,,從而達到提取深度信息的最終目標。 由于我們現(xiàn)在已經(jīng)涵蓋了對極幾何的基礎知識,,我們現(xiàn)在可以解決另外兩個元素,,以便找到核點和核線。 基本矩陣包含有關平移和旋轉的信息,,它描述了相機相對于其對應物的位置,。下圖展示了基本矩陣。 但是對于我們的解決方案,,我們希望根據(jù)相機像素坐標進行計算,。這是基本矩陣發(fā)揮作用的地方,因為它包含與實際矩陣相同的信息,,但也包括關于兩個相機的內(nèi)在信息,,以便我們可以用這樣的術語將兩者聯(lián)系起來。雖然基本矩陣中的信息來源于我們相機的真實世界定位,,但實際矩陣必須自己計算,。基本矩陣允許我們做的是將一個圖像中的單個點映射到另一個圖像中的相應的極線,,為我們提供一個起點,,以便之后找到兩個攝像機中心點和極平面之間的核線,。 但是我們?nèi)绾斡嬎銓嶋H矩陣?我們可以使用OpenCV從每個圖像中的一組已知匹配點推導出這個矩陣,。這基本上是一個校準過程,,建議從圖像中找到最少8個匹配點,并用于達到所需的準確度,。為此,,我們使用OpenCV分析兩個圖像并提取其最佳匹配像素坐標。在下面的Python代碼中,,我們使用SIFT描述符和基于FLANN的matcher和ratio文本提取這些點,。 import cv2import numpy as npfrom matplotlib import pyplot as pltimg1 = cv2.imread('myleft.jpg',0) #queryimage # left imageimg2 = cv2.imread('myright.jpg',0) #trainimage # right imagesift = cv2.SIFT()# find the keypoints and descriptors with SIFTkp1, des1 = sift.detectAndCompute(img1,None)kp2, des2 = sift.detectAndCompute(img2,None)# FLANN parametersFLANN_INDEX_KDTREE = 0index_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 = []for i,(m,n) in enumerate(matches): if m.distance < 0.8*n.distance:="" good.append(m)="" pts2.append(kp2[m.trainidx].pt)=""> 這將為我們提供兩個圖像中最佳像素匹配的列表,,然后可以將其發(fā)送以計算實際矩陣,,我們將在下面使用OpenCV的功能進行計算。Python代碼如下: pts1 = np.int32(pts1)pts2 = np.int32(pts2)F, mask = cv2.findFundamentalMat(pts1,pts2,cv2.FM_LMEDS)# We select only inlier pointspts1 = pts1[mask.ravel()==1]pts2 = pts2[mask.ravel()==1] 讓我們花點時間創(chuàng)建一個Python函數(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)在我們繼續(xù)計算與相對圖像中的點相對應的核線。在這個階段,,我們還需要注意我們正在使用哪個相機,因為我們將在一個圖像中找到點,,并在其對應物上繪制核線,。下面的代碼將生成一個行數(shù)組,,我們可以隨后將其發(fā)送到繪圖函數(shù),。Python代碼如下: # 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 imagelines2 = 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() 結果顯示兩幅帶有核線的圖像,,它們代表了來自相反圖像的點數(shù)組,。我們可以注意到,每一組線的集合點都在攝像機視圖之外,。這個虛構的點是核點(即在三維空間中,,相對攝像機的中心點)。 最后,,我們應該認識到在嘗試進行3D重建時相機分辨率的重要性,。這是由于在我們的過程中,第一步我們必須使用一種算法來識別每幅圖像中非常匹配的像素,,這樣我們就可以產(chǎn)生一個精確的基本矩陣,。分辨率越低,,我們處理的信息就越少,因此極大地阻礙了保持精度的能力,。盡管如此,,我們?nèi)栽谟昧Ⅲw圖像進行全面三維重建的,我們的最后一步是使用第二個相應的極線計算第一個圖像上的點的深度,。 |
|