【问题标题】:OpenCV Flann matching of feature point for multiple viewsOpenCV Flann 匹配多个视图的特征点
【发布时间】:2018-09-12 17:30:35
【问题描述】:

我正在尝试为捆绑调整准备重叠的航拍图像,以创建地形的 3D 重建。到目前为止,我有一个脚本可以计算哪些图像重叠并构建这些图像对的列表。我的下一阶段是检测和计算每个图像的 SIFT 特征,然后将特征点与数据集中重叠的任何图像进行匹配。为此,我正在使用 OpenCV 中提供的 FLANN Matcher。

我的问题是,捆绑调整输入需要为每个特征点提供唯一的点 ID。据我所知,FLANN 匹配器一次只能匹配两个图像的特征点。那么,如果我在 5 个摄像头中有一个相同的点可见,我怎样才能给这个点一个在 5 个摄像头中一致的 ID?如果我现在在匹配过程中保存该点时简单地给它一个 ID,那么同一点将具有不同的 ID,具体取决于用于计算它的摄像机集。

束调整输入的格式为:

camera_id (int), point_id(int), point_x(float), point_y(float)

我使用它是因为我所遵循的捆绑调整代码教程使用了BAL 数据集(即ceres solverscipy)。

我最初的想法是计算和描述所有图像中的所有 SIFT 点并添加到 1 个列表中。从这里我可以删除任何重复的关键点描述符。一旦我的数据集中有一个唯一 SIFT 点的列表,我就可以按顺序为每个点添加一个 ID。然后每次我匹配一个点时,我都可以在这个列表中查找点描述符并根据该列表分配点 ID。虽然我觉得这可行,但它似乎很慢,并且没有使用我用于匹配的 K-TREE 匹配方法。

所以最后,我的问题是......有没有办法在 OpenCV python 中使用 FLANN 匹配器来实现多个视图 (>2) 的特征匹配?或者……摄影测量/SLAM 社区有解决这个问题的通用方法吗?

到目前为止我的代码:

matches_list = []
sift = cv2.xfeatures2d.SIFT_create()

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)

for i in overlap_list:

    img1 = cv2.imread(i[0], 0)
    img2 = cv2.imread(i[1], 0)

    img1 = cv2.resize(img1, (img1.shape[1]/4, img1.shape[0]/4), interpolation=cv2.INTER_CUBIC)
    img2 = cv2.resize(img2, (img2.shape[1]/4, img2.shape[0]/4), interpolation=cv2.INTER_CUBIC)

    kp1, des1 = sift.detectAndCompute(img1,None)
    kp2, des2 = sift.detectAndCompute(img2,None)

    matches = flann.knnMatch(des1,des2,k=2)
    for j,(m,n) in enumerate(matches):
        if m.distance < 0.7*n.distance:
            pt1 = kp1[m.queryIdx].pt
            pt2 = kp2[m.trainIdx].pt
            matches_list.append([i[0], i[1], pt1, pt2])

这将返回一个具有以下结构的列表length = number of feature matches

matches_list[i] = [camera_1.jpg, camera_2.jpg, (cam1_x, cam1_y), (cam2_x, cam2_y)]

【问题讨论】:

  • 查看 Richard Szeliski 的 "Image Alignment and Stitching: A Tutorial"。真正伟大的资源概述了所有的拼接。捆绑调整从第 48 页(PDF 第 50 页)开始。您没有说明您使用的捆绑调整算法需要这些输入,但真正的捆绑调整会利用所有功能并且不会重复。您需要所有特征点,因为您试图将每个特征点映射到其他图像中的位置的错误最小化。
  • 感谢您的链接。这让我想起了我实际上有他的计算机视觉教科书!我正在研究使用 ceres 求解器捆绑调整以及 scipy 实现。他们在他们的教程中都使用了 BAL 数据集 (grail.cs.washington.edu/projects/bal),它为多个视图中的每个点使用一致的点 ID。你能推荐一个BA吗?
  • 是的,他的书很扎实——我喜欢这个教程,因为它不会太深,只涵盖了要点,所以它是一个很好的复习。我没有使用任何特定的 BA sw 包,所以我不确定它是如何工作的。我不确定这是否是 需要 做的,但是跨图像标记每个特征的方法是使用连接的组件。如果img1 中的特征xi1, yi1img2 中的xj2, yj2 匹配,并且xj2, yj2img3 中的xk3, yk3 匹配,则计算所有成对匹配并将其全部放入一个图中,每个连接组件是您可以识别的匹配特征。

标签: python opencv


【解决方案1】:

这个问题要问的确切问题是“当我只有成对匹配时,如何唯一地标记一组多个匹配?”

这是一个标准的图论问题:从边集到连通分量。

只是为了一些直觉:

这个想法是你有边缘(特征匹配对)。因此,例如在上图中,(2, 1) 是一条边。以及 (1, 3) 和 (5, 6) 等等。因此,既然 2 与 1 匹配,而 1 与 3 匹配,那么实际上,1、2 和 3 可能都是相同的特征。因此,您可以通过查找此图中连接在一起的所有组件来将相同的功能组合在一起。请注意,图形只需要由这些对来描述,仅此而已。

您已经有了计算匹配的代码。我将提供一些代码来计算连接的组件。不能保证此代码特别快,但它应该对您使用的任何类型的数据都具有鲁棒性。但是请注意,您发送的每个不同节点都必须具有不同的数据,因为这使用集合。

def conncomp(edges):
    """Finds the connected components in a graph.

    Parameters
    ---------- 
    edges : sequence
        A sequence of pairs where the pair represents an undirected edge.

    Returns
    -------
    components : list
        A list with each component as a list of nodes. Only includes single
        nodes if the node is paired with itself in edges.
    """

    # group edge pairs together into a dict
    pair_dict = defaultdict(set)
    nodes = set([num for pair in edges for num in pair])
    for node in nodes:
        for pair in edges:
            if node in pair:
                pair_dict[node] = pair_dict[node].union(set(pair))

    # run BFS on the dict
    components = []
    nodes_to_explore = set(pair_dict.keys())
    while nodes_to_explore:  # while nodes_to_explore is not empty
        node = nodes_to_explore.pop()
        component = {node}
        neighbors = pair_dict[node]
        while neighbors:  # while neighbors is non-emtpy
            next_node = neighbors.pop()
            if next_node in nodes_to_explore:
                nodes_to_explore.remove(next_node)
            next_nodes = set([val for val in pair_dict[next_node] if val not in component])
            neighbors = neighbors.union(next_nodes)
            component.add(next_node)
        components.append(list(component))

    return components

如上所述,这个函数的输入是一个对(元组)的列表。我只需发送配对 ID 列表,例如:

edges = [(img1_feat_i, img2_feat_j), ...]

其中img1_feat_iimg2_feat_j 匹配来自knnMatchBFMatch 或任何您喜欢使用的功能的ID。

该函数将返回一个组件列表,如

[[img1_feat_i, img2_feat_j, img3_feat_k, ...], ...]

每个组件(即每个子列表)在图像中都是相同的特征,因此您可以将所有这些不同的 id 映射到该组件的一个唯一 id。

【讨论】:

  • 感谢这真的很有帮助!我仍然遇到的唯一问题是 knnMatch 没有功能 ID。这意味着我仍然无法使用您提供的代码。我通过执行由图像名称、x 坐标和 y 坐标组成的 sha256 哈希函数来解决它。我使用 sha256 作为功能 ID,一切都按预期工作。干杯!
  • 啊,我想您可能可以使用描述符本身。但是哈希函数会更好:)
猜你喜欢
  • 1970-01-01
  • 2022-06-18
  • 2014-04-11
  • 2018-02-22
  • 2017-08-13
  • 2017-10-05
  • 1970-01-01
  • 2018-07-03
  • 2014-05-02
相关资源
最近更新 更多