超详讲解图像拼接/全景图原理和应用 | 附源码


点击上方AI算法与图像处理”,选择加"星标"或“置顶”html

重磅干货,第一时间送达640?wx_fmt=jpegpython

研究好玩又有用的技术第 008 linux


在学习中发现快乐,在应用找到价值。这是我第八期分享图像技术应用的文章。git

前七期欢迎阅读和分享:github

概述

做者:Thalles Silva  编译:AI算法与图像处理

图像拼接是计算机视觉中最成功的应用之一。现在,很难找到不包含此功能的手机或图像处理API。

在这篇文章中,咱们将讨论如何使用Python和OpenCV执行图像拼接。鉴于一对共享一些共同区域的图像,咱们的目标是“缝合”它们并建立全景图像场景。

在整篇文章中,咱们将介绍一些最着名的计算机视觉技术。这些包括:

  • 关键点检测

  • 局部不变描述符(SIFT,SURF等)

  • 特征匹配

  • 使用RANSAC进行的旋转估计(Homography estimation)

  • 透视畸变(Perspective warping)

咱们探索了许多特征提取算子,如SIFT,SURF,BRISK和ORB。你可使用这款Colab笔记本,甚至能够用你的照片试试。[这里我已经调试好源码并上传到github上面]

640?wx_fmt=png

特征检测和提取

给定一对像上面那样的图像,咱们想要把它们拼接起来建立一个全景场景。值得注意的是,两个图像都须要共享一些共同的区域

此外,即便图片在如下一个或多个方面存在差别,咱们的解决方案也必须强大:

  • Scaling

  • Angle

  • Spacial position

  • Capturing devices

朝这个方向迈出的第一步是提取一些感兴趣的关键点和特征。可是,这些功能须要具备一些特殊属性。

咱们首先考虑一个简单的解决方案。

关键点检测

一开始可能使用简单些的方法,诸如使用Harris Corners之类的算法提取关键点。而后,咱们能够尝试根据欧几里德距离之类的类似度量来匹配相应的关键点。咱们知道,角点(corner)有一个很好的属性:它们对旋转是不变的。 这意味着,一旦咱们检测到一个角点,若是咱们旋转图像,那个角点仍将存在。

可是,若是咱们旋转而后缩放图像怎么办?在这种状况下,咱们会很难,由于角点不是规模不变的。也就是说,若是咱们放大图像,先前检测到的角可能会变成一条线!

总之,咱们须要对旋转和缩放不变的特征。这就是SIFT,SURF和ORB等更强大的方法的用武之地。


关键点和描述子

像SIFT和SURF这样的方法试图解决角点检测算法的局限性。
一般,角点检测器算法使用固定大小的内核来检测图像上的感兴趣区域(角点)。很容易看出,当咱们缩放图像时,这个内核可能会变得过小或太大。

为了解决这个限制,像SIFT这样的方法使用高斯差分(DoD)。咱们的想法是在同一图像的不一样比例版本上应用DoD。它还使用相邻像素信息来查找和细化关键点和相应的描述子。

首先,咱们须要加载2个图像,查询图片和训练图片。最初,咱们首先从二者中提取关键点和描述符。咱们能够经过使用OpenCV detectAndCompute()函数一步完成。请注意,为了使用detectAndCompute(),咱们须要一个关键点检测器和描述符对象的实例。它能够是ORB,SIFT或SURF等。另外,在将图像馈送到detectAndCompute()以前,咱们将它们转换为灰度。

def detectAndDescribe(image, method=None):
  """
  Compute key points and feature descriptors using an specific method
  """
   
  assert method is not None, "You need to define a feature detection method. Values are: 'sift', 'surf'"
   
  # detect and extract features from the image
  if method == 'sift':
      descriptor = cv2.xfeatures2d.SIFT_create()
  elif method == 'surf':
      descriptor = cv2.xfeatures2d.SURF_create()
  elif method == 'brisk':
      descriptor = cv2.BRISK_create()
  elif method == 'orb':
      descriptor = cv2.ORB_create()
       
  # get keypoints and descriptors
  (kps, features) = descriptor.detectAndCompute(image, None)
   
  return (kps, features)

咱们对查询和训练的图片都运行detectAndCompute()。此时,咱们为这两个图像提供了一组关键点和描述子。若是咱们使用SIFT做为特征提取器,它将为每一个关键点返回128维特征向量。若是选择SURF,咱们将得到64维特征向量。如下图像显示了使用SIFT,SURF,BRISK和ORB提取的一些功能。

640?wx_fmt=png

使用SIFT检测关键点和描述子

640?wx_fmt=png

使用SURF检测关键点和描述子

640?wx_fmt=png

使用BRISK和汉明距离检测关键点和描述子

640?wx_fmt=png

使用ORB和汉明距离检测关键点和描述子


特征匹配

咱们能够看到,咱们从两个图像中都有大量的特征。
如今,咱们想比较两组特征并以线段相连的形式显示更多类似性的特征点对。
使用OpenCV,功能匹配须要Matcher对象。在这里,咱们探索两种方法:


  • Brute Force Matcher(暴力匹配法)

  • KNN(k-最近邻)

BruteForce(BF)Matcher正如其名称所代表的那样。给定2组特征(来自图片A和图片B),来自集合A的每一个特征与集合B中的全部特征进行比较。默认状况下,BF匹配器计算 两点之间的欧几里德距离。所以,对于集合A中的每一个特征,它返回集合B中最接近的特征。对于SIFT和SURF,OpenCV建议使用欧几里德距离。对于其余特征提取器,如ORB和BRISK,建议使用汉明距离。
要使用OpenCV建立BruteForce Matcher,咱们只须要指定2个参数。第一个是距离度量。第二个是crossCheck布尔参数。


  def createMatcher(method,crossCheck):
  "Create and return a Matcher Object"
   
  if method == 'sift' or method == 'surf':
      bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck=crossCheck)
  elif method == 'orb' or method == 'brisk':
      bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=crossCheck)
  return bf


crossCheck bool参数指示两个特征是否必须相互匹配才能被视为有效。换句话说,对于被认为有效的一对特征(f1,f2),f1须要匹配f2,而且f2也必须匹配f1做为最接近的匹配。此过程可确保更强大的匹配功能集,并在原始SIFT文章中进行了描述。
可是,对于咱们想要考虑多个候选匹配的状况,咱们可使用基于KNN的匹配过程。

KNN不返回给定特征的单个最佳匹配,而是返回k个最佳匹配

注意,k的值必须是由用户预先定义的。如咱们预期的那样,KNN提供了一个更的的候选特征集合。可是,咱们须要确保全部这些匹配对在进一步发展以前都是健壮的。


比率测试(Ratio Testing)

为了确保KNN返回的特征具备良好的可比性,SIFT论文的做者提出了一种称为比率测试(Ratio Testing)的技术。基本上,咱们迭代KNN返回的每一个对并执行距离测试。对于每对特征(f1,f2),若是f1和f2之间的距离在必定比例内,咱们保留它,不然,咱们将它丢弃。此外,必须手动选择比率值。

从本质上讲,比率测试与BruteForce Matcher的交叉检查选项完成相同的工做。二者都确保一对检测到的特征确实足够近以致于被认为是类似的。下面的2个数字显示了BF和KNN Matcher对SIFT特征的结果。咱们选择仅显示100个匹配点以清除可视化。


640?wx_fmt=png

使用KNN和RIFT测试对SIFT特征进行特征匹配

640?wx_fmt=png

在SIFT功能上使用Brute Force Matcher进行特征匹配


请注意,即便在KNN中交叉验证——暴力匹配(Brute force)和比率测试以后,某些功能也没法正确匹配。
然而,Matcher算法将为咱们提供两个图像中最好的(更类似的)特征集。如今,咱们须要获取这些点并找到基于匹配点将2个图像拼接在一块儿的变换矩阵。

这种转换称为Homography matrix(单应性矩阵)。简而言之,若是Homography是3x3矩阵,可用于许多应用,例如相机姿态估计,透视校订和图像拼接。若是Homography是2D变换。它将点从一个平面(图像)映射到另外一个平面。让咱们看看咱们是如何获得它的。


Estimating the Homograph


RANdom SAmple Consensus或RANSAC是一种适合线性模型的迭代算法。与其余线性回归器不一样,RANSAC设计为对异常值具备鲁棒性。
像线性回归这样的模型使用最小二乘估计来使最佳模型适合数据。然而,普通最小二乘法对异常值很是敏感。所以,若是异常值的数量很大,它可能会失败。
RANSAC经过仅使用数据中的 内部子集估计参数来解决此问题。下图显示了线性回归和RANSAC之间的比较。首先,请注意数据集包含至关多的异常值。

咱们能够看到线性回归模型很容易受到异常值的影响。那是由于它试图减小平均偏差。所以,它倾向于支持最小化从全部数据点到模型自己的总距离的模型。这包括异常值。

相反,RANSAC仅将模型拟合到被识别为内点的点子集上。

这个特性对咱们的用例很是重要。在这里,咱们将使用RANSAC来估计Homography矩阵。事实证实,Homography对咱们传递给它的数据质量很是敏感。所以,重要的是有一个算法(RANSAC)能够过滤明显不属于数据分布的点。


640?wx_fmt=png

最小二乘与RANSAC模型拟合的比较。请注意数据中的大量异常值


一旦咱们获得 estimated Homography,咱们须要将其中一个图像变换到一个共同的平面。
在这里,咱们将对其中一个图像应用透视变换。基本上,透视变换能够组合一个或多个操做,例如旋转,缩放,平移或剪切。这个想法是转换其中一个图像,使两个图像合并为一个。为此,咱们可使用OpenCV warpPerspective()函数。它采用图像和homography做为输入。而后,它根据homography将源图像变换到目的平面上。


  # Apply panorama correction
width = trainImg.shape[1] + queryImg.shape[1]
height = trainImg.shape[0] + queryImg.shape[0]

result = cv2.warpPerspective(trainImg, H, (width, height))
result[0:queryImg.shape[0], 0:queryImg.shape[1]] = queryImg

plt.figure(figsize=(20,10))
plt.imshow(result)

plt.axis('off')
plt.show()


生成的全景图像以下所示。如咱们所见,结果中有几个工件。更具体地说,咱们能够看到与图像边界处的照明条件和边缘效应有关的一些问题。理想状况下,咱们能够执行后处理技术来标准化直方图匹配等强度。这可能会使结果看起来更加真实。

谢谢阅读!


效果图


640?wx_fmt=png

demo 2


640?wx_fmt=jpeg


640?wx_fmt=jpeg




可能遇到bug

可能存在的报错

(1)Qt 连接报错 version `Qt_5' not found]

python3: relocation error: /usr/lib/x86_64-linux-gnu/libQt5XcbQpa.so.5: symbol _ZN20QPlatformIntegration11screenAddedEP15QPlatformScreenb version Qt_5_PRIVATE_API not defined in file libQt5Gui.so.5 with link time reference

解决方案:https://www.cnblogs.com/sunchaothu/p/9962675.html

(2)AttributeError: module 'cv2.cv2' has no attribute 'xfeatures2d'

须要回退版本到3.4.2.16,记得要先卸载以前安装好的opencv-python

解决方案:https://blog.csdn.net/weixin_43167047/article/details/82841750


代码:https://github.com/DWCTOD/AI_study


原文连接:

https://towardsdatascience.com/image-panorama-stitching-with-opencv-2402bde6b46c


640?

有热门推荐?


1.

2.

3.


加群交流

欢迎小伙伴加群交流,目前已有交流群的方向包括:AI学习交流群,目标检测,秋招互助,资料下载等等;

加群可扫描并回复感兴趣方向便可


640?wx_fmt=png
你点的每一个“在看”,我都认真当成了喜欢