Loading autocnet/graph/network.py +62 −17 Original line number Diff line number Diff line Loading @@ -258,31 +258,64 @@ class CandidateGraph(nx.Graph): attributes['homography'] = transformation_matrix attributes['ransac'] = mask def compute_subpixel_offsets(self): def compute_subpixel_offsets(self, clean_keys=[], threshold=0.8, upsampling=10): """ For the entire graph, compute the subpixel offsets using pattern-matching and add the result as an attribute to each edge of the graph. Returns ------- subpixel_offsets : ndarray A numpy array containing all the subpixel offsets for the entire graph. Parameters ---------- clean_keys : list of string keys to masking arrays (created by calling outlier detection) threshold : float On the range [-1, 1]. Values less than or equal to this threshold are masked and can be considered outliers """ subpixel_offsets = [] for source, destination, attributes in self.edges_iter(data=True): #for each edge matches = attributes['matches'] #grab the matches for source, destination, attributes in self.edges_iter(data=True): matches = attributes['matches'] full_offsets = np.zeros((len(matches), 3)) # Build up a composite mask from all of the user specified masks if clean_keys: mask = np.prod([attributes[i] for i in clean_keys], axis=0, dtype=np.bool) matches = matches[mask] full_mask = np.where(mask == True) src_image = self.node[source]['image'] dest_image = self.node[destination]['image'] edge_offsets = [] for i, (idx, row) in enumerate(matches.iterrows()): #for each edge, calculate this for each keypoint pair # Preallocate the numpy array to avoid appending and type conversion edge_offsets = np.empty((len(matches),3)) # 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']) src_keypoint = self.node[source]['keypoints'][s_idx] dest_keypoint = self.node[destination]['keypoints'][d_idx] edge_offsets.append(sp.subpixel_offset(src_keypoint, dest_keypoint, src_image, dest_image)) attributes['subpixel_offsets'] = np.array(edge_offsets) subpixel_offsets.append(np.array(edge_offsets)) return subpixel_offsets # Compute the subpixel offset edge_offsets[i] = sp.subpixel_offset(src_keypoint, dest_keypoint, src_image, dest_image, upsampling=upsampling) # Compute the mask for correlations less than the threshold threshold_mask = edge_offsets[edge_offsets[:,-1] >= threshold] # Convert the truncated mask back into a full length mask if clean_keys: mask[full_mask] = threshold_mask full_offsets[full_mask] = edge_offsets else: mask = threshold_mask attributes['subpixel_offsets'] = pd.DataFrame(full_offsets, columns=['x_offset', 'y_offset', 'correlation']) attributes['subpixel'] = mask def to_cnet(self, clean_keys=[]): """ Loading Loading @@ -338,12 +371,15 @@ class CandidateGraph(nx.Graph): mask = np.prod([attributes[i] for i in clean_keys], axis=0, dtype=np.bool) matches = matches[mask] if 'subpixel' in clean_keys: offsets = attributes['subpixel_offsets'][attributes['subpixel']] print(offsets) kp1 = self.node[source]['keypoints'] kp2 = self.node[destination]['keypoints'] pt_idx = 0 values = [] for idx, row in matches.iterrows(): for i, (idx, row) in enumerate(matches.iterrows()): # Composite matching key (node_id, point_id) m1 = (source, int(row['source_idx'])) m2 = (destination, int(row['destination_idx'])) Loading @@ -354,8 +390,17 @@ class CandidateGraph(nx.Graph): pt_idx, source]) values.append([kp2[m2[1]].pt[0], kp2[m2[1]].pt[1], kp2x = kp2[m2[1]].pt[0] kp2y = kp2[m2[1]].pt[1] if 'subpixel' in clean_keys: print(idx) print(kp2x, kp2y) kp2x += offsets['x_offset'].values[i] kp2y += offsets['y_offset'].values[i] print(kp2x, kp2y) values.append([kp2x, kp2y, m2, pt_idx, destination]) Loading autocnet/graph/tests/test_network.py +9 −7 Original line number Diff line number Diff line Loading @@ -13,8 +13,9 @@ from .. import network class TestCandidateGraph(unittest.TestCase): def setUp(self): self.graph = network.CandidateGraph.from_adjacency_file(get_path('adjacency.json')) @classmethod def setUpClass(cls): cls.graph = network.CandidateGraph.from_adjacency_file(get_path('adjacency.json')) def test_get_name(self): node_number = self.graph.node_name_map['AS15-M-0297_SML.png'] Loading @@ -28,6 +29,10 @@ class TestCandidateGraph(unittest.TestCase): def test_to_json_file(self): self.graph.to_json_file('test_graph_to_json.json') self.assertTrue(os.path.exists('test_graph_to_json.json')) try: os.remove('test_graph_to_json.json') except: pass def test_extract_features(self): # also tests get_geodataset() and get_keypoints Loading @@ -42,7 +47,4 @@ class TestCandidateGraph(unittest.TestCase): self.assertEquals(self.graph.get_keypoints(node_number), node['keypoints']) def tearDown(self): try: os.remove('test_graph_to_json.json') except: pass autocnet/matcher/outlier_detector.py +1 −0 Original line number Diff line number Diff line Loading @@ -149,6 +149,7 @@ def compute_homography(kp1, kp2, outlier_algorithm=cv2.RANSAC, reproj_threshold= mask = mask.astype(bool) return transformation_matrix, mask def homography_test(kp1, kp2, homography, threshold=3.0): """ Utilize the transformation matrix (homography) to check whether Loading autocnet/matcher/subpixel.py +4 −2 Original line number Diff line number Diff line import pandas as pd from autocnet.matcher import matcher from scipy.misc import imresize # TODO: look into KeyPoint.size and perhaps use to determine an appropriately-sized search/template. # TODO: do not allow even sizes Loading Loading @@ -31,7 +33,7 @@ Parameters The returned tuple is of form: (x_offset, y_offset, strength). The offsets are from the search to the template keypoint. """ def subpixel_offset(template_kp, search_kp, template_img, search_img, template_size=9, search_size=27): def subpixel_offset(template_kp, search_kp, template_img, search_img, template_size=9, search_size=27, upsampling=10): # Get the x,y coordinates temp_x, temp_y = map(int, template_kp.pt) search_x, search_y = map(int, search_kp.pt) Loading @@ -46,7 +48,7 @@ def subpixel_offset(template_kp, search_kp, template_img, search_img, template_s results = (None, None, None) try: results = matcher.pattern_match(template, search) results = matcher.pattern_match(template, search, upsampling=upsampling) except ValueError: # the match fails if the template or search point is near an edge of the image # TODO: come up with a better solution? Loading functional_tests/test_two_image.py +4 −4 Original line number Diff line number Diff line Loading @@ -75,14 +75,14 @@ class TestTwoImageMatching(unittest.TestCase): mask = np.array(ratio_mask * symmetry_mask) self.assertIn(len(matches.loc[mask]), range(75,101)) # Step: Compute the homographies and apply RANSAC cg.compute_homographies(clean_keys=['symmetry', 'ratio']) #compute subpixel offsets for the entire graph offsets = cg.compute_subpixel_offsets() self.assertEqual(len(offsets), cg.number_of_edges()) # Step: Compute subpixel offsets for candidate points cg.compute_subpixel_offsets(clean_keys=['symmetry', 'ratio', 'ransac']) # Step: And create a C object cnet = cg.to_cnet(clean_keys=['symmetry', 'ratio', 'ransac']) cnet = cg.to_cnet(clean_keys=['symmetry', 'ratio', 'ransac', 'subpixel']) # Step update the serial numbers nid_to_serial = {} for node, attributes in cg.nodes_iter(data=True): Loading Loading
autocnet/graph/network.py +62 −17 Original line number Diff line number Diff line Loading @@ -258,31 +258,64 @@ class CandidateGraph(nx.Graph): attributes['homography'] = transformation_matrix attributes['ransac'] = mask def compute_subpixel_offsets(self): def compute_subpixel_offsets(self, clean_keys=[], threshold=0.8, upsampling=10): """ For the entire graph, compute the subpixel offsets using pattern-matching and add the result as an attribute to each edge of the graph. Returns ------- subpixel_offsets : ndarray A numpy array containing all the subpixel offsets for the entire graph. Parameters ---------- clean_keys : list of string keys to masking arrays (created by calling outlier detection) threshold : float On the range [-1, 1]. Values less than or equal to this threshold are masked and can be considered outliers """ subpixel_offsets = [] for source, destination, attributes in self.edges_iter(data=True): #for each edge matches = attributes['matches'] #grab the matches for source, destination, attributes in self.edges_iter(data=True): matches = attributes['matches'] full_offsets = np.zeros((len(matches), 3)) # Build up a composite mask from all of the user specified masks if clean_keys: mask = np.prod([attributes[i] for i in clean_keys], axis=0, dtype=np.bool) matches = matches[mask] full_mask = np.where(mask == True) src_image = self.node[source]['image'] dest_image = self.node[destination]['image'] edge_offsets = [] for i, (idx, row) in enumerate(matches.iterrows()): #for each edge, calculate this for each keypoint pair # Preallocate the numpy array to avoid appending and type conversion edge_offsets = np.empty((len(matches),3)) # 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']) src_keypoint = self.node[source]['keypoints'][s_idx] dest_keypoint = self.node[destination]['keypoints'][d_idx] edge_offsets.append(sp.subpixel_offset(src_keypoint, dest_keypoint, src_image, dest_image)) attributes['subpixel_offsets'] = np.array(edge_offsets) subpixel_offsets.append(np.array(edge_offsets)) return subpixel_offsets # Compute the subpixel offset edge_offsets[i] = sp.subpixel_offset(src_keypoint, dest_keypoint, src_image, dest_image, upsampling=upsampling) # Compute the mask for correlations less than the threshold threshold_mask = edge_offsets[edge_offsets[:,-1] >= threshold] # Convert the truncated mask back into a full length mask if clean_keys: mask[full_mask] = threshold_mask full_offsets[full_mask] = edge_offsets else: mask = threshold_mask attributes['subpixel_offsets'] = pd.DataFrame(full_offsets, columns=['x_offset', 'y_offset', 'correlation']) attributes['subpixel'] = mask def to_cnet(self, clean_keys=[]): """ Loading Loading @@ -338,12 +371,15 @@ class CandidateGraph(nx.Graph): mask = np.prod([attributes[i] for i in clean_keys], axis=0, dtype=np.bool) matches = matches[mask] if 'subpixel' in clean_keys: offsets = attributes['subpixel_offsets'][attributes['subpixel']] print(offsets) kp1 = self.node[source]['keypoints'] kp2 = self.node[destination]['keypoints'] pt_idx = 0 values = [] for idx, row in matches.iterrows(): for i, (idx, row) in enumerate(matches.iterrows()): # Composite matching key (node_id, point_id) m1 = (source, int(row['source_idx'])) m2 = (destination, int(row['destination_idx'])) Loading @@ -354,8 +390,17 @@ class CandidateGraph(nx.Graph): pt_idx, source]) values.append([kp2[m2[1]].pt[0], kp2[m2[1]].pt[1], kp2x = kp2[m2[1]].pt[0] kp2y = kp2[m2[1]].pt[1] if 'subpixel' in clean_keys: print(idx) print(kp2x, kp2y) kp2x += offsets['x_offset'].values[i] kp2y += offsets['y_offset'].values[i] print(kp2x, kp2y) values.append([kp2x, kp2y, m2, pt_idx, destination]) Loading
autocnet/graph/tests/test_network.py +9 −7 Original line number Diff line number Diff line Loading @@ -13,8 +13,9 @@ from .. import network class TestCandidateGraph(unittest.TestCase): def setUp(self): self.graph = network.CandidateGraph.from_adjacency_file(get_path('adjacency.json')) @classmethod def setUpClass(cls): cls.graph = network.CandidateGraph.from_adjacency_file(get_path('adjacency.json')) def test_get_name(self): node_number = self.graph.node_name_map['AS15-M-0297_SML.png'] Loading @@ -28,6 +29,10 @@ class TestCandidateGraph(unittest.TestCase): def test_to_json_file(self): self.graph.to_json_file('test_graph_to_json.json') self.assertTrue(os.path.exists('test_graph_to_json.json')) try: os.remove('test_graph_to_json.json') except: pass def test_extract_features(self): # also tests get_geodataset() and get_keypoints Loading @@ -42,7 +47,4 @@ class TestCandidateGraph(unittest.TestCase): self.assertEquals(self.graph.get_keypoints(node_number), node['keypoints']) def tearDown(self): try: os.remove('test_graph_to_json.json') except: pass
autocnet/matcher/outlier_detector.py +1 −0 Original line number Diff line number Diff line Loading @@ -149,6 +149,7 @@ def compute_homography(kp1, kp2, outlier_algorithm=cv2.RANSAC, reproj_threshold= mask = mask.astype(bool) return transformation_matrix, mask def homography_test(kp1, kp2, homography, threshold=3.0): """ Utilize the transformation matrix (homography) to check whether Loading
autocnet/matcher/subpixel.py +4 −2 Original line number Diff line number Diff line import pandas as pd from autocnet.matcher import matcher from scipy.misc import imresize # TODO: look into KeyPoint.size and perhaps use to determine an appropriately-sized search/template. # TODO: do not allow even sizes Loading Loading @@ -31,7 +33,7 @@ Parameters The returned tuple is of form: (x_offset, y_offset, strength). The offsets are from the search to the template keypoint. """ def subpixel_offset(template_kp, search_kp, template_img, search_img, template_size=9, search_size=27): def subpixel_offset(template_kp, search_kp, template_img, search_img, template_size=9, search_size=27, upsampling=10): # Get the x,y coordinates temp_x, temp_y = map(int, template_kp.pt) search_x, search_y = map(int, search_kp.pt) Loading @@ -46,7 +48,7 @@ def subpixel_offset(template_kp, search_kp, template_img, search_img, template_s results = (None, None, None) try: results = matcher.pattern_match(template, search) results = matcher.pattern_match(template, search, upsampling=upsampling) except ValueError: # the match fails if the template or search point is near an edge of the image # TODO: come up with a better solution? Loading
functional_tests/test_two_image.py +4 −4 Original line number Diff line number Diff line Loading @@ -75,14 +75,14 @@ class TestTwoImageMatching(unittest.TestCase): mask = np.array(ratio_mask * symmetry_mask) self.assertIn(len(matches.loc[mask]), range(75,101)) # Step: Compute the homographies and apply RANSAC cg.compute_homographies(clean_keys=['symmetry', 'ratio']) #compute subpixel offsets for the entire graph offsets = cg.compute_subpixel_offsets() self.assertEqual(len(offsets), cg.number_of_edges()) # Step: Compute subpixel offsets for candidate points cg.compute_subpixel_offsets(clean_keys=['symmetry', 'ratio', 'ransac']) # Step: And create a C object cnet = cg.to_cnet(clean_keys=['symmetry', 'ratio', 'ransac']) cnet = cg.to_cnet(clean_keys=['symmetry', 'ratio', 'ransac', 'subpixel']) # Step update the serial numbers nid_to_serial = {} for node, attributes in cg.nodes_iter(data=True): Loading