我正在尝试为包调整准备重叠的航拍图像,以创建地形的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 solver和scipy)。
我最初的想法是计算和描述所有图像中的所有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)]
答案 0 :(得分:2)
这个问题提出的确切问题是&#34;如果我只有成对匹配,我怎样才能唯一地标记一组多个匹配?&#34;
这是一个标准的图论理论问题:从边缘集到连通分量。
只是为了一些直觉:
这个想法是你有边(特征匹配对)。因此对于例如在上图中,(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_i
和img2_feat_j
匹配来自knnMatch
或BFMatch
或您想要使用的任何内容的ID。
该函数将返回一个组件列表,如
[[img1_feat_i, img2_feat_j, img3_feat_k, ...], ...]
每个组件(即每个子列表)在图像中都具有相同的功能,因此您可以将所有这些不同的ID映射到组件的一个唯一ID。