@@ -303,3 +303,180 @@ axis = np.float32([[0,0,0], [0,3,0], [3,3,0], [3,0,0],
303303
304304如果你对计算机图形学和增强现实等感兴趣,可以使用OpenGL渲染更复杂的图形。
305305
306+ ## 三、对极几何
307+ 308+ ***
309+ 310+ ### 目标:
311+ 312+ 本章节你需要学习以下内容:
313+ 314+ *我们将了解多视角几何的基础知识
315+ *我们将看到什么是极点,极线,对极约束等。
316+ 317+ ### 1、基础
318+ 319+ 当我们使用针孔相机拍摄图像时,我们会丢失一些重要的信息,比如图像的深度。或者是图像中的每个点距离相机有多远,因为它是3D到2D的转换。因此一个重要的问题就产生了,使用这样的摄像机我们能否计算除深度信息呢?答案是我们需要使用多个摄像头。我们的眼睛以类似的方式工作,我们使用两个相机(两只眼睛)来判断物体的距离,称为立体视觉。那么让我们看看OpenCV在这个领域提供了什么。
320+ 321+ (《学习 OpenCV》一书有大量相关知识)
322+ 323+ 在进入深度图像之前,让我们先了解多视图几何中的一些基本概念。在本节中,我们将讨论极线几何。请参见下图,其中显示了使用两个相机拍摄同一场景图像的基本设置。
324+ 325+ ![ image6] ( https://raw.githubusercontent.com/TonyStark1997/OpenCV-Python/master/7.Camera%20Calibration%20and%203D%20Reconstruction/Image/image6.jpg )
326+ 327+ 如果我们仅使用左侧相机,则无法找到与图像中的点x对应的3D点,因为线OX上的每个点都投影到图像平面上的相同点。但是如果我们也考虑上右侧图像的话,直线 OX 上的点将投影到右侧图像上的不同位置(x')。所以根据这两幅图像,我们就可以使用三角测量计算出 3D 空间中的点到摄像机的距离(深度)。这就是整个思路。
328+ 329+ OX上不同点的投影在右平面(线l')上形成一条线。我们称它为对应于点x的极线。也就是说,要在右侧图像上找到点x,需要沿着此极线搜索。它应该在这个一维直线的某个地方(想象一下,要找到其他图像中的匹配点,你不需要搜索整个图像,只需沿着极线搜索。因此它提供了更好的性能和准确性)。这称为极线约束。类似地,所有点将在另一图像中具有其对应的极线。平面XOO'称为极线平面。
330+ 331+ O和O'是摄像机中心。从上面给出的设置中,可以看到右侧摄像机O'的投影在该点的左侧图像上看到,例如。它被称为极点。极点是通过摄像机中心和图像平面的线的交叉点。类似地,e'是左相机的极点。在某些情况下,你将无法在图像中找到极点,它们可能位于图像之外(这意味着,一台摄像机看不到另一台摄像机)。
332+ 333+ 所有的极线都通过它的极点。所以为了找到极点的位置,我们可以先找到多条极线,这些极线的交点就是极点。
334+ 335+ 所以在本小节中,我们的重点就是寻找极线和极点。但要找到它们,我们需要另外两个元素,本征矩阵(F)和基本矩阵(E)。本征矩阵包含了物理空间中两个摄像机相关的旋转和平移信息。如下图所示(本图来源自:学习 OpenCV):
336+ 337+ ![ image7] ( https://raw.githubusercontent.com/TonyStark1997/OpenCV-Python/master/7.Camera%20Calibration%20and%203D%20Reconstruction/Image/image7.jpg )
338+ 339+ 但我们更喜欢用像素坐标进行测量,对吧?基础矩阵 F 除了包含 E 的信息外还包含了两个摄像机的内参数。由于 F包含了这些内参数,因此它可以它在像素坐标系将两台摄像机关联起来。(如果使用是校正之后的图像并通过除以焦距进行了归一化,F=E)。简单来说,基础矩阵 F 将一副图像中的点映射到另一幅图像中的线(极线)上。这是通过匹配两幅图像上的点来实现的。要计算基础矩阵至少需要 8 个点(使用 8 点算法)。点越多越好,可以使用 RANSAC 算法得到更加稳定的结果。
340+ 341+ ### 2、代码实现
342+ 343+ 首先,我们需要在两个图像之间找到尽可能多的匹配,以找到基本矩阵。为此,我们使用SIFT描述符和基于FLANN的匹配器和比率测试。
344+ 345+ ``` python
346+ import numpy as np
347+ import cv2 as cv
348+ from matplotlib import pyplot as plt
349+ 350+ img1 = cv.imread(' myleft.jpg' ,0 ) # queryimage # left image
351+ img2 = cv.imread(' myright.jpg' ,0 ) # trainimage # right image
352+ 353+ sift = cv.SIFT()
354+ 355+ # find the keypoints and descriptors with SIFT
356+ kp1, des1 = sift.detectAndCompute(img1,None )
357+ kp2, des2 = sift.detectAndCompute(img2,None )
358+ 359+ # FLANN parameters
360+ FLANN_INDEX_KDTREE = 1
361+ index_params = dict (algorithm = FLANN_INDEX_KDTREE , trees = 5 )
362+ search_params = dict (checks = 50 )
363+ 364+ flann = cv.FlannBasedMatcher(index_params,search_params)
365+ matches = flann.knnMatch(des1,des2,k = 2 )
366+ 367+ good = []
368+ pts1 = []
369+ pts2 = []
370+ 371+ # ratio test as per Lowe's paper
372+ for i,(m,n) in enumerate (matches):
373+ if m.distance < 0.8 * n.distance:
374+ good.append(m)
375+ pts2.append(kp2[m.trainIdx].pt)
376+ pts1.append(kp1[m.queryIdx].pt)
377+ ```
378+ 379+ 现在我们有两张图片的最佳匹配列表。让我们找到基本矩阵。
380+ 381+ ``` python
382+ pts1 = np.int32(pts1)
383+ pts2 = np.int32(pts2)
384+ F, mask = cv.findFundamentalMat(pts1,pts2,cv.FM_LMEDS )
385+ 386+ # We select only inlier points
387+ pts1 = pts1[mask.ravel()== 1 ]
388+ pts2 = pts2[mask.ravel()== 1 ]
389+ ```
390+ 391+ 下一步我们要找到极线。我们会得到一个包含很多线的数组。所以我们要定义一个新的函数将这些线绘制到图像中。
392+ 393+ ``` python
394+ def drawlines (img1 ,img2 ,lines ,pts1 ,pts2 ):
395+ ''' img1 - image on which we draw the epilines for the points in img2
396+ lines - corresponding epilines '''
397+ r,c = img1.shape
398+ img1 = cv.cvtColor(img1,cv.COLOR_GRAY2BGR )
399+ img2 = cv.cvtColor(img2,cv.COLOR_GRAY2BGR )
400+ for r,pt1,pt2 in zip (lines,pts1,pts2):
401+ color = tuple (np.random.randint(0 ,255 ,3 ).tolist())
402+ x0,y0 = map (int , [0 , - r[2 ]/ r[1 ] ])
403+ x1,y1 = map (int , [c, - (r[2 ]+ r[0 ]* c)/ r[1 ] ])
404+ img1 = cv.line(img1, (x0,y0), (x1,y1), color,1 )
405+ img1 = cv.circle(img1,tuple (pt1),5 ,color,- 1 )
406+ img2 = cv.circle(img2,tuple (pt2),5 ,color,- 1 )
407+ return img1,img2
408+ ```
409+ 410+ 现在我们在两个图像中找到了极线并绘制它们。
411+ 412+ ``` python
413+ # Find epilines corresponding to points in right image (second image) and
414+ # drawing its lines on left image
415+ lines1 = cv.computeCorrespondEpilines(pts2.reshape(- 1 ,1 ,2 ), 2 ,F)
416+ lines1 = lines1.reshape(- 1 ,3 )
417+ img5,img6 = drawlines(img1,img2,lines1,pts1,pts2)
418+ 419+ # Find epilines corresponding to points in left image (first image) and
420+ # drawing its lines on right image
421+ lines2 = cv.computeCorrespondEpilines(pts1.reshape(- 1 ,1 ,2 ), 1 ,F)
422+ lines2 = lines2.reshape(- 1 ,3 )
423+ img3,img4 = drawlines(img2,img1,lines2,pts2,pts1)
424+ 425+ plt.subplot(121 ),plt.imshow(img5)
426+ plt.subplot(122 ),plt.imshow(img3)
427+ plt.show()
428+ ```
429+ 430+ 结果如下图所示;
431+ 432+ ![ image8] ( https://raw.githubusercontent.com/TonyStark1997/OpenCV-Python/master/7.Camera%20Calibration%20and%203D%20Reconstruction/Image/image8.jpg )
433+ 434+ 你可以在左侧图像中看到所有的极线都会聚合在右侧图像外部的一个点上,这个点就是极点。
435+ 436+ 为了得到更好的结果,我们应该使用分辨率比较高和很多非平面点的图像。
437+ 438+ ## 四、立体图像的深度图
439+ 440+ ***
441+ 442+ ### 目标:
443+ 444+ 本章节你需要学习以下内容:
445+ 446+ *我们将学习如何从立体图像创建深度图。
447+ 448+ ### 1、基础
449+ 450+ 在上一节中我们学习了对极约束的基本概念和相关术语。如果同一场景有两幅图像的话我们在直觉上就可以获得图像的深度信息。下面是的这幅图和其中的数学公式证明我们的直觉是对的。(图像来源 image courtesy)
451+ 452+ ![ image9] ( https://raw.githubusercontent.com/TonyStark1997/OpenCV-Python/master/7.Camera%20Calibration%20and%203D%20Reconstruction/Image/image9.jpg )
453+ 454+ 上图包含等效三角形。编写等效方程将产生以下结果:
455+ 456+ $$ disparity = x - x' = \frac{Bf}{Z} $$
457+ 458+ x 和 x' 分别是图像中的点到 3D 空间中的点和到摄像机中心的距离。B 是这两个摄像机之间的距离,f 是摄像机的焦距。上边的等式告诉我们点的深度与x 和 x' 的差成反比。所以根据这个等式我们就可以得到图像中所有点的深度图。
459+ 460+ 这样就可以找到两幅图像中的匹配点了。前面我们已经知道了对极约束可以使这个操作更快更准。一旦找到了匹配,就可以计算出 disparity 了。让我们看看在 OpenCV 中怎样做吧。
461+ 462+ ### 2、代码实现
463+ 464+ 下面的代码片段显示了创建视差图的简单过程。
465+ 466+ ``` python
467+ import numpy as np
468+ import cv2 as cv
469+ from matplotlib import pyplot as plt
470+ 471+ imgL = cv.imread(' tsukuba_l.png' ,0 )
472+ imgR = cv.imread(' tsukuba_r.png' ,0 )
473+ 474+ stereo = cv.StereoBM_create(numDisparities = 16 , blockSize = 15 )
475+ disparity = stereo.compute(imgL,imgR)
476+ plt.imshow(disparity,' gray' )
477+ plt.show()
478+ ```
479+ 480+ 下图包含原始图像(左)及其视差图(右)。如图所见,结果受到高度噪音的污染。通过调整numDisparities和blockSize的值,你可以获得更好的结果。
481+ 482+ ![ image10] ( https://raw.githubusercontent.com/TonyStark1997/OpenCV-Python/master/7.Camera%20Calibration%20and%203D%20Reconstruction/Image/image10.jpg )
0 commit comments