Loading autocnet/graph/network.py +58 −30 Original line number Diff line number Diff line Loading @@ -161,11 +161,12 @@ class Edge(object): # for each edge, calculate this for each keypoint pair for i, (idx, row) in enumerate(matches.iterrows()): s_idx = int(row['source_idx']) d_idx = int(row['destination_idx']) s_keypoint = self.source.keypoints[s_idx][['x', 'y']].values d_keypoint = self.destination.keypoints[d_idx][['x', 'y']].values s_keypoint = self.source.keypoints.iloc[s_idx][['x', 'y']].values d_keypoint = self.destination.keypoints.iloc[d_idx][['x', 'y']].values # Get the template and search windows s_template = sp.clip_roi(self.source.handle, s_keypoint, template_size) Loading @@ -188,6 +189,26 @@ class Edge(object): 'correlation']) self.masks = ('subpixel', mask) def convex_hull_coverage(self, clean_keys=[]): """ Compute the ratio $area_{convexhull} / area_{imageoverlap}$. Returns ------- ratio : float """ if not self.homography: raise(AttributeError, 'A homography has not been computed. Unable to determine image overlap.') matches = self.matches # Build up a composite mask from all of the user specified masks if clean_keys: mask = np.prod([self._mask_arrays[i] for i in clean_keys], axis=0, dtype=np.bool) matches = matches[mask] print(matches ) def update(self, *args): # Added for NetworkX pass Loading Loading @@ -284,7 +305,7 @@ class Node(object): mask = od.adaptive_non_max_suppression(self.keypoints,nfeatures,robust) self.masks = ('anms', mask) def convex_hull_ratio(self): def convex_hull_ratio(self, clean_keys=[]): """ Compute the ratio $area_{convexhull} / area_{total}$ Loading @@ -297,6 +318,12 @@ class Node(object): if not hasattr(self, 'keypoints'): raise AttributeError('Keypoints must be extracted already, they have not been.') if clean_keys: mask = np.prod([self._mask_arrays[i] for i in clean_keys], axis=0, dtype=np.bool) keypoints = self.keypoints[mask] keypoints = self.keypoints[['x', 'y']].values ratio = convex_hull_ratio(keypoints, ideal_area) return ratio Loading Loading @@ -490,9 +517,9 @@ class CandidateGraph(nx.Graph): source_key = dest_group['source_image'].values[0] destination_key = dest_group['destination_image'].values[0] try: edge = self[source_key][destination_key] edge = self.edge[source_key][destination_key] except: # pragma: no cover edge = self[destination_key][source_key] edge = self.edge[destination_key][source_key] if hasattr(edge, 'matches'): df = edge.matches Loading @@ -500,6 +527,20 @@ class CandidateGraph(nx.Graph): else: edge.matches = dest_group def symmetry_checks(self): """ Perform a symmetry check on all edges in the graph """ for s, d, edge in self.edges_iter(data=True): edge.symmetry_check() def ratio_checks(self, ratio=0.8): """ Perform a ratio check on all edges in the graph """ for s, d, edge in self.edges_iter(data=True): edge.ratio_check(ratio=ratio) def compute_homographies(self, outlier_algorithm=cv2.RANSAC, clean_keys=[]): """ Compute homographies for all edges using identical parameters Loading @@ -518,7 +559,6 @@ class CandidateGraph(nx.Graph): edge.compute_homography(outlier_algorithm=outlier_algorithm, clean_keys=clean_keys) def compute_subpixel_offsets(self, clean_keys=[], threshold=0.8, upsampling=10, template_size=9, search_size=27): """ Loading Loading @@ -599,27 +639,32 @@ class CandidateGraph(nx.Graph): if 'subpixel' in clean_keys: offsets = edge.subpixel_offsets kp1 = self.node[source].keypoints kp2 = self.node[destination].keypoints pt_idx = 0 values = [] for i, (idx, row) in enumerate(matches.iterrows()): # Composite matching key (node_id, point_id) m1_pid = int(row['source_idx']) m2_pid = int(row['destination_idx']) m1 = (source, int(row['source_idx'])) m2 = (destination, int(row['destination_idx'])) values.append([kp1[['x', 'y']].values, values.append([kp1.iloc[m1_pid]['x'], kp1.iloc[m1_pid]['y'], m1, pt_idx, source]) kp2x = kp2['x'] kp2y = kp2['y'] kp2x = kp2.iloc[m2_pid]['x'] kp2y = kp2.iloc[m2_pid]['y'] if 'subpixel' in clean_keys: kp2x += (offsets['x_offset'].values[i]) kp2y += (offsets['y_offset'].values[i]) kp2x += offsets['x_offset'].values[i] kp2y += offsets['y_offset'].values[i] values.append([kp2x, kp2y, m2, Loading Loading @@ -653,7 +698,7 @@ class CandidateGraph(nx.Graph): # Final validation to remove any correspondence with multiple correspondences in the same image merged_cnet = _validate_cnet(merged_cnet) print(merged_cnet) return merged_cnet def to_json_file(self, outputfile): Loading Loading @@ -692,20 +737,3 @@ class CandidateGraph(nx.Graph): A list of connected sub-graphs of nodes, with the largest sub-graph first. Each subgraph is a set. """ return sorted(nx.connected_components(self), key=len, reverse=True) def covered_area(self, idx): node = self.node[nodeindex] keypoint_objs = self.get_keypoints(nodeindex) handle = node['handle'] image_area = handle.pixel_area print(image_area) pts = np.empty((len(keypoint_objs), 2)) for i, j in enumerate(keypoint_objs): pts[i] = j.pt[0], j.pt[1] print(pts) hull = ConvexHull(pts) return hull.volume No newline at end of file functional_tests/test_three_image.py +1 −1 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ class TestThreeImageMatching(unittest.TestCase): # Step update the serial numbers nid_to_serial = {} for i, node in cg.nodes_iter(data=True): nid_to_serial[node] = self.serial_numbers[node.image_name] nid_to_serial[i] = self.serial_numbers[node.image_name] cnet.replace({'nid': nid_to_serial}, inplace=True) # Step: Output a control network Loading functional_tests/test_two_image.py +8 −3 Original line number Diff line number Diff line Loading @@ -48,6 +48,12 @@ class TestTwoImageMatching(unittest.TestCase): for i, node in cg.nodes_iter(data=True): self.assertIn(node.nkeypoints, range(490, 511)) #Step: Compute the coverage ratios truth_ratios = [0.95351579, 0.93595664] for i, node in cg.nodes_iter(data=True): ratio = node.convex_hull_ratio() self.assertIn(round(ratio,8), truth_ratios) # Step: apply Adaptive non-maximal suppression for i, node in cg.nodes_iter(data=True): pass Loading @@ -72,7 +78,7 @@ class TestTwoImageMatching(unittest.TestCase): # Perform the ratio test edge.ratio_check(ratio=0.8) self.assertIn(edge._mask_arrays['ratio'].sum(), range(20, 100)) self.assertIn(edge._mask_arrays['ratio'].sum(), range(150, 350)) # Step: Compute the homographies and apply RANSAC cg.compute_homographies(clean_keys=['symmetry', 'ratio']) Loading @@ -90,10 +96,9 @@ class TestTwoImageMatching(unittest.TestCase): # Step update the serial numbers nid_to_serial = {} for i, node in cg.nodes_iter(data=True): nid_to_serial[node] = self.serial_numbers[node.image_name] nid_to_serial[i] = self.serial_numbers[node.image_name] cnet.replace({'nid': nid_to_serial}, inplace=True) # Step: Output a control network to_isis('TestTwoImageMatching.net', cnet, mode='wb', networkid='TestTwoImageMatching', targetname='Moon') Loading Loading
autocnet/graph/network.py +58 −30 Original line number Diff line number Diff line Loading @@ -161,11 +161,12 @@ class Edge(object): # for each edge, calculate this for each keypoint pair for i, (idx, row) in enumerate(matches.iterrows()): s_idx = int(row['source_idx']) d_idx = int(row['destination_idx']) s_keypoint = self.source.keypoints[s_idx][['x', 'y']].values d_keypoint = self.destination.keypoints[d_idx][['x', 'y']].values s_keypoint = self.source.keypoints.iloc[s_idx][['x', 'y']].values d_keypoint = self.destination.keypoints.iloc[d_idx][['x', 'y']].values # Get the template and search windows s_template = sp.clip_roi(self.source.handle, s_keypoint, template_size) Loading @@ -188,6 +189,26 @@ class Edge(object): 'correlation']) self.masks = ('subpixel', mask) def convex_hull_coverage(self, clean_keys=[]): """ Compute the ratio $area_{convexhull} / area_{imageoverlap}$. Returns ------- ratio : float """ if not self.homography: raise(AttributeError, 'A homography has not been computed. Unable to determine image overlap.') matches = self.matches # Build up a composite mask from all of the user specified masks if clean_keys: mask = np.prod([self._mask_arrays[i] for i in clean_keys], axis=0, dtype=np.bool) matches = matches[mask] print(matches ) def update(self, *args): # Added for NetworkX pass Loading Loading @@ -284,7 +305,7 @@ class Node(object): mask = od.adaptive_non_max_suppression(self.keypoints,nfeatures,robust) self.masks = ('anms', mask) def convex_hull_ratio(self): def convex_hull_ratio(self, clean_keys=[]): """ Compute the ratio $area_{convexhull} / area_{total}$ Loading @@ -297,6 +318,12 @@ class Node(object): if not hasattr(self, 'keypoints'): raise AttributeError('Keypoints must be extracted already, they have not been.') if clean_keys: mask = np.prod([self._mask_arrays[i] for i in clean_keys], axis=0, dtype=np.bool) keypoints = self.keypoints[mask] keypoints = self.keypoints[['x', 'y']].values ratio = convex_hull_ratio(keypoints, ideal_area) return ratio Loading Loading @@ -490,9 +517,9 @@ class CandidateGraph(nx.Graph): source_key = dest_group['source_image'].values[0] destination_key = dest_group['destination_image'].values[0] try: edge = self[source_key][destination_key] edge = self.edge[source_key][destination_key] except: # pragma: no cover edge = self[destination_key][source_key] edge = self.edge[destination_key][source_key] if hasattr(edge, 'matches'): df = edge.matches Loading @@ -500,6 +527,20 @@ class CandidateGraph(nx.Graph): else: edge.matches = dest_group def symmetry_checks(self): """ Perform a symmetry check on all edges in the graph """ for s, d, edge in self.edges_iter(data=True): edge.symmetry_check() def ratio_checks(self, ratio=0.8): """ Perform a ratio check on all edges in the graph """ for s, d, edge in self.edges_iter(data=True): edge.ratio_check(ratio=ratio) def compute_homographies(self, outlier_algorithm=cv2.RANSAC, clean_keys=[]): """ Compute homographies for all edges using identical parameters Loading @@ -518,7 +559,6 @@ class CandidateGraph(nx.Graph): edge.compute_homography(outlier_algorithm=outlier_algorithm, clean_keys=clean_keys) def compute_subpixel_offsets(self, clean_keys=[], threshold=0.8, upsampling=10, template_size=9, search_size=27): """ Loading Loading @@ -599,27 +639,32 @@ class CandidateGraph(nx.Graph): if 'subpixel' in clean_keys: offsets = edge.subpixel_offsets kp1 = self.node[source].keypoints kp2 = self.node[destination].keypoints pt_idx = 0 values = [] for i, (idx, row) in enumerate(matches.iterrows()): # Composite matching key (node_id, point_id) m1_pid = int(row['source_idx']) m2_pid = int(row['destination_idx']) m1 = (source, int(row['source_idx'])) m2 = (destination, int(row['destination_idx'])) values.append([kp1[['x', 'y']].values, values.append([kp1.iloc[m1_pid]['x'], kp1.iloc[m1_pid]['y'], m1, pt_idx, source]) kp2x = kp2['x'] kp2y = kp2['y'] kp2x = kp2.iloc[m2_pid]['x'] kp2y = kp2.iloc[m2_pid]['y'] if 'subpixel' in clean_keys: kp2x += (offsets['x_offset'].values[i]) kp2y += (offsets['y_offset'].values[i]) kp2x += offsets['x_offset'].values[i] kp2y += offsets['y_offset'].values[i] values.append([kp2x, kp2y, m2, Loading Loading @@ -653,7 +698,7 @@ class CandidateGraph(nx.Graph): # Final validation to remove any correspondence with multiple correspondences in the same image merged_cnet = _validate_cnet(merged_cnet) print(merged_cnet) return merged_cnet def to_json_file(self, outputfile): Loading Loading @@ -692,20 +737,3 @@ class CandidateGraph(nx.Graph): A list of connected sub-graphs of nodes, with the largest sub-graph first. Each subgraph is a set. """ return sorted(nx.connected_components(self), key=len, reverse=True) def covered_area(self, idx): node = self.node[nodeindex] keypoint_objs = self.get_keypoints(nodeindex) handle = node['handle'] image_area = handle.pixel_area print(image_area) pts = np.empty((len(keypoint_objs), 2)) for i, j in enumerate(keypoint_objs): pts[i] = j.pt[0], j.pt[1] print(pts) hull = ConvexHull(pts) return hull.volume No newline at end of file
functional_tests/test_three_image.py +1 −1 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ class TestThreeImageMatching(unittest.TestCase): # Step update the serial numbers nid_to_serial = {} for i, node in cg.nodes_iter(data=True): nid_to_serial[node] = self.serial_numbers[node.image_name] nid_to_serial[i] = self.serial_numbers[node.image_name] cnet.replace({'nid': nid_to_serial}, inplace=True) # Step: Output a control network Loading
functional_tests/test_two_image.py +8 −3 Original line number Diff line number Diff line Loading @@ -48,6 +48,12 @@ class TestTwoImageMatching(unittest.TestCase): for i, node in cg.nodes_iter(data=True): self.assertIn(node.nkeypoints, range(490, 511)) #Step: Compute the coverage ratios truth_ratios = [0.95351579, 0.93595664] for i, node in cg.nodes_iter(data=True): ratio = node.convex_hull_ratio() self.assertIn(round(ratio,8), truth_ratios) # Step: apply Adaptive non-maximal suppression for i, node in cg.nodes_iter(data=True): pass Loading @@ -72,7 +78,7 @@ class TestTwoImageMatching(unittest.TestCase): # Perform the ratio test edge.ratio_check(ratio=0.8) self.assertIn(edge._mask_arrays['ratio'].sum(), range(20, 100)) self.assertIn(edge._mask_arrays['ratio'].sum(), range(150, 350)) # Step: Compute the homographies and apply RANSAC cg.compute_homographies(clean_keys=['symmetry', 'ratio']) Loading @@ -90,10 +96,9 @@ class TestTwoImageMatching(unittest.TestCase): # Step update the serial numbers nid_to_serial = {} for i, node in cg.nodes_iter(data=True): nid_to_serial[node] = self.serial_numbers[node.image_name] nid_to_serial[i] = self.serial_numbers[node.image_name] cnet.replace({'nid': nid_to_serial}, inplace=True) # Step: Output a control network to_isis('TestTwoImageMatching.net', cnet, mode='wb', networkid='TestTwoImageMatching', targetname='Moon') Loading