Loading autocnet/fileio/io_controlnetwork.py +0 −2 Original line number Diff line number Diff line Loading @@ -132,7 +132,6 @@ class IsisStore(object): """ point_sizes = [] point_messages = [] print(cnet) for pid, point in cnet.groupby('pid'): # Instantiate the proto spec point_spec = cnf.ControlPointFileEntryV0002() Loading @@ -146,7 +145,6 @@ class IsisStore(object): # A single extend call is cheaper than many add calls to pack points measure_iterable = [] print(point) for name, row in point.iterrows(): measure_spec = point_spec.Measure() measure_spec.serialnumber = row.nid Loading autocnet/graph/network.py +11 −1 Original line number Diff line number Diff line import operator import os import networkx as nx Loading Loading @@ -236,7 +237,7 @@ class CandidateGraph(nx.Graph): else: return ('','') def to_cnet(self): def to_cnet(self, clean_keys=[]): """ Generate a control network (C) object from a graph Loading @@ -244,11 +245,20 @@ class CandidateGraph(nx.Graph): ------- merged_cnet : C A control network object clean_keys : list of strings identifying the masking arrays to use, e.g. ratio, symmetry """ merged_cnet = None for source, destination, attributes in self.edges_iter(data=True): matches = attributes['matches'] # Merge all of the masks if clean_keys: mask = np.array(list(map(operator.mul, *[attributes[i] for i in clean_keys]))) matches = matches[mask] kp1 = self.node[source]['keypoints'] kp2 = self.node[destination]['keypoints'] Loading autocnet/matcher/matcher.py +1 −101 Original line number Diff line number Diff line Loading @@ -76,6 +76,7 @@ def pattern_match(template, image, upsampling=10, return x, y, strength class FlannMatcher(object): """ A wrapper to the OpenCV Flann based matcher class that adds Loading Loading @@ -168,104 +169,3 @@ class FlannMatcher(object): return pd.DataFrame(matched, columns=['source_image', 'source_idx', 'destination_image', 'destination_idx', 'distance']) class OutlierDetector(object): """ A class which contains several outlier detection methods which all return True/False masks as pandas data series, which can be used as masks for the "matches" pandas dataframe which stores match information for each edge of the graph. Attributes ---------- """ def __init__(self): pass # (query only takes care of literal self-matches on a keypoint basis, not self-matches for the whole image) def self_neighbors(self, matches): """ Returns a pandas data series intended to be used as a mask. Each row is True if it is not matched to a point in the same image (good) and False if it is (bad.) Parameters ---------- matches : dataframe the matches dataframe stored along the edge of the graph containing matched points with columns containing: matched image name, query index, train index, and descriptor distance Returns ------- : dataseries Intended to mask the matches dataframe. True means the row is not matched to a point in the same image and false the row is. """ return matches.source_image != matches.destination_image def distance_ratio(self, matches, ratio=0.8): """ Compute and return a mask for the matches dataframe stored on each edge of the graph using the ratio test and distance_ratio set during initialization. Parameters ---------- matches : dataframe the matches dataframe stored along the edge of the graph containing matched points with columns containing: matched image name, query index, train index, and descriptor distance. ***Will only work as expected if matches already has dropped duplicates*** ratio: float the ratio between the first and second-best match distances for each keypoint to use as a bound for marking the first keypoint as "good." Returns ------- : dataseries Intended to mask the matches dataframe. Rows are True if the associated keypoint passes the ratio test and false otherwise. Keypoints without more than one match are True by default, since the ratio test will not work for them. """ #0.8 is Lowe's paper value -- can be changed. mask = [] temp_matches = matches.drop_duplicates() #don't want to deal with duplicates... for key, group in temp_matches.groupby('source_idx'): #won't work if there's only 1 match for each queryIdx if len(group) < 2: mask.append(True) else: if group['distance'].iloc[0] < ratio * group['distance'].iloc[1]: #this means distance _0_ is good and can drop all other distances mask.append(True) for i in range(len(group['distance']-1)): mask.append(False) else: for i in range(len(group['distance'])): mask.append(False) return pd.Series(mask) def mirroring_test(self, matches): """ Compute and return a mask for the matches dataframe on each edge of the graph which will keep only entries in which there is both a source -> destination match and a destination -> source match. Parameters ---------- matches : dataframe the matches dataframe stored along the edge of the graph containing matched points with columns containing: matched image name, query index, train index, and descriptor distance Returns ------- : dataseries Intended to mask the matches dataframe. Rows are True if the associated keypoint passes the mirroring test and false otherwise. That is, if 1->2, 2->1, both rows will be True, otherwise, they will be false. Keypoints with only one match will be False. Removes duplicate rows. """ return matches.duplicated(keep='first') autocnet/matcher/outlier_detector.py 0 → 100644 +113 −0 Original line number Diff line number Diff line import numpy as np def self_neighbors(matches): """ Returns a pandas data series intended to be used as a mask. Each row is True if it is not matched to a point in the same image (good) and False if it is (bad.) Parameters ---------- matches : dataframe the matches dataframe stored along the edge of the graph containing matched points with columns containing: matched image name, query index, train index, and descriptor distance Returns ------- : dataseries Intended to mask the matches dataframe. True means the row is not matched to a point in the same image and false the row is. """ return matches.source_image != matches.destination_image def distance_ratio(matches, ratio=0.8): """ Compute and return a mask for a matches dataframe using Lowe's ratio test. Lowe (2004) [Lowe2004]_ Parameters ---------- matches : dataframe the matches dataframe stored along the edge of the graph containing matched points with columns containing: matched image name, query index, train index, and descriptor distance. ratio : float the ratio between the first and second-best match distances for each keypoint to use as a bound for marking the first keypoint as "good". Default: 0.8 Returns ------- mask : ndarray Intended to mask the matches dataframe. Rows are True if the associated keypoint passes the ratio test and false otherwise. Keypoints without more than one match are True by default, since the ratio test will not work for them. """ mask = np.zeros(len(matches), dtype=bool) # Pre-allocate the mask counter = 0 for i, group in matches.groupby('source_idx'): group_size = len(group) # If we can not perform the ratio check because all matches are symmetrical if len(group['destination_idx'].unique()) == 1: mask[counter:counter + group_size] = False counter += group_size else: # Otherwise, we can perform the ratio test sorted = group.sort_values(by=['distance']) unique = sorted['distance'].unique() if unique[0] < ratio * unique[1]: mask[counter] = True mask[counter + 1:counter + group_size] = False counter += group_size else: mask[counter: counter + group_size] = False counter += group_size ''' # won't work if there's only 1 match for each queryIdx if len(group) < 2: mask.append(True) else: if group['distance'].iloc[0] < ratio * group['distance'].iloc[1]: # this means distance _0_ is good and can drop all other distances mask.append(True) for i in range(len(group['distance']-1)): mask.append(False) else: for i in range(len(group['distance'])): mask.append(False) ''' return mask def mirroring_test(matches): """ Compute and return a mask for the matches dataframe on each edge of the graph which will keep only entries in which there is both a source -> destination match and a destination -> source match. Parameters ---------- matches : dataframe the matches dataframe stored along the edge of the graph containing matched points with columns containing: matched image name, query index, train index, and descriptor distance Returns ------- duplicates : dataseries Intended to mask the matches dataframe. Rows are True if the associated keypoint passes the mirroring test and false otherwise. That is, if 1->2, 2->1, both rows will be True, otherwise, they will be false. Keypoints with only one match will be False. Removes duplicate rows. """ duplicates = matches.duplicated(keep='first').values duplicates.astype(bool, copy=False) return duplicates autocnet/matcher/tests/test_feature_extractor.py +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ class TestFeatureExtractor(unittest.TestCase): def test_extract_features(self): features = feature_extractor.extract_features(self.data_array, self.parameters) self.assertEquals(len(features), 2) self.assertEqual(len(features[0]), 11) # OpenCV +1 to self.assertIn(len(features[0]), range(8,11)) self.assertIsInstance(features[0][0], type(cv2.KeyPoint())) self.assertIsInstance(features[1][0], np.ndarray) Loading
autocnet/fileio/io_controlnetwork.py +0 −2 Original line number Diff line number Diff line Loading @@ -132,7 +132,6 @@ class IsisStore(object): """ point_sizes = [] point_messages = [] print(cnet) for pid, point in cnet.groupby('pid'): # Instantiate the proto spec point_spec = cnf.ControlPointFileEntryV0002() Loading @@ -146,7 +145,6 @@ class IsisStore(object): # A single extend call is cheaper than many add calls to pack points measure_iterable = [] print(point) for name, row in point.iterrows(): measure_spec = point_spec.Measure() measure_spec.serialnumber = row.nid Loading
autocnet/graph/network.py +11 −1 Original line number Diff line number Diff line import operator import os import networkx as nx Loading Loading @@ -236,7 +237,7 @@ class CandidateGraph(nx.Graph): else: return ('','') def to_cnet(self): def to_cnet(self, clean_keys=[]): """ Generate a control network (C) object from a graph Loading @@ -244,11 +245,20 @@ class CandidateGraph(nx.Graph): ------- merged_cnet : C A control network object clean_keys : list of strings identifying the masking arrays to use, e.g. ratio, symmetry """ merged_cnet = None for source, destination, attributes in self.edges_iter(data=True): matches = attributes['matches'] # Merge all of the masks if clean_keys: mask = np.array(list(map(operator.mul, *[attributes[i] for i in clean_keys]))) matches = matches[mask] kp1 = self.node[source]['keypoints'] kp2 = self.node[destination]['keypoints'] Loading
autocnet/matcher/matcher.py +1 −101 Original line number Diff line number Diff line Loading @@ -76,6 +76,7 @@ def pattern_match(template, image, upsampling=10, return x, y, strength class FlannMatcher(object): """ A wrapper to the OpenCV Flann based matcher class that adds Loading Loading @@ -168,104 +169,3 @@ class FlannMatcher(object): return pd.DataFrame(matched, columns=['source_image', 'source_idx', 'destination_image', 'destination_idx', 'distance']) class OutlierDetector(object): """ A class which contains several outlier detection methods which all return True/False masks as pandas data series, which can be used as masks for the "matches" pandas dataframe which stores match information for each edge of the graph. Attributes ---------- """ def __init__(self): pass # (query only takes care of literal self-matches on a keypoint basis, not self-matches for the whole image) def self_neighbors(self, matches): """ Returns a pandas data series intended to be used as a mask. Each row is True if it is not matched to a point in the same image (good) and False if it is (bad.) Parameters ---------- matches : dataframe the matches dataframe stored along the edge of the graph containing matched points with columns containing: matched image name, query index, train index, and descriptor distance Returns ------- : dataseries Intended to mask the matches dataframe. True means the row is not matched to a point in the same image and false the row is. """ return matches.source_image != matches.destination_image def distance_ratio(self, matches, ratio=0.8): """ Compute and return a mask for the matches dataframe stored on each edge of the graph using the ratio test and distance_ratio set during initialization. Parameters ---------- matches : dataframe the matches dataframe stored along the edge of the graph containing matched points with columns containing: matched image name, query index, train index, and descriptor distance. ***Will only work as expected if matches already has dropped duplicates*** ratio: float the ratio between the first and second-best match distances for each keypoint to use as a bound for marking the first keypoint as "good." Returns ------- : dataseries Intended to mask the matches dataframe. Rows are True if the associated keypoint passes the ratio test and false otherwise. Keypoints without more than one match are True by default, since the ratio test will not work for them. """ #0.8 is Lowe's paper value -- can be changed. mask = [] temp_matches = matches.drop_duplicates() #don't want to deal with duplicates... for key, group in temp_matches.groupby('source_idx'): #won't work if there's only 1 match for each queryIdx if len(group) < 2: mask.append(True) else: if group['distance'].iloc[0] < ratio * group['distance'].iloc[1]: #this means distance _0_ is good and can drop all other distances mask.append(True) for i in range(len(group['distance']-1)): mask.append(False) else: for i in range(len(group['distance'])): mask.append(False) return pd.Series(mask) def mirroring_test(self, matches): """ Compute and return a mask for the matches dataframe on each edge of the graph which will keep only entries in which there is both a source -> destination match and a destination -> source match. Parameters ---------- matches : dataframe the matches dataframe stored along the edge of the graph containing matched points with columns containing: matched image name, query index, train index, and descriptor distance Returns ------- : dataseries Intended to mask the matches dataframe. Rows are True if the associated keypoint passes the mirroring test and false otherwise. That is, if 1->2, 2->1, both rows will be True, otherwise, they will be false. Keypoints with only one match will be False. Removes duplicate rows. """ return matches.duplicated(keep='first')
autocnet/matcher/outlier_detector.py 0 → 100644 +113 −0 Original line number Diff line number Diff line import numpy as np def self_neighbors(matches): """ Returns a pandas data series intended to be used as a mask. Each row is True if it is not matched to a point in the same image (good) and False if it is (bad.) Parameters ---------- matches : dataframe the matches dataframe stored along the edge of the graph containing matched points with columns containing: matched image name, query index, train index, and descriptor distance Returns ------- : dataseries Intended to mask the matches dataframe. True means the row is not matched to a point in the same image and false the row is. """ return matches.source_image != matches.destination_image def distance_ratio(matches, ratio=0.8): """ Compute and return a mask for a matches dataframe using Lowe's ratio test. Lowe (2004) [Lowe2004]_ Parameters ---------- matches : dataframe the matches dataframe stored along the edge of the graph containing matched points with columns containing: matched image name, query index, train index, and descriptor distance. ratio : float the ratio between the first and second-best match distances for each keypoint to use as a bound for marking the first keypoint as "good". Default: 0.8 Returns ------- mask : ndarray Intended to mask the matches dataframe. Rows are True if the associated keypoint passes the ratio test and false otherwise. Keypoints without more than one match are True by default, since the ratio test will not work for them. """ mask = np.zeros(len(matches), dtype=bool) # Pre-allocate the mask counter = 0 for i, group in matches.groupby('source_idx'): group_size = len(group) # If we can not perform the ratio check because all matches are symmetrical if len(group['destination_idx'].unique()) == 1: mask[counter:counter + group_size] = False counter += group_size else: # Otherwise, we can perform the ratio test sorted = group.sort_values(by=['distance']) unique = sorted['distance'].unique() if unique[0] < ratio * unique[1]: mask[counter] = True mask[counter + 1:counter + group_size] = False counter += group_size else: mask[counter: counter + group_size] = False counter += group_size ''' # won't work if there's only 1 match for each queryIdx if len(group) < 2: mask.append(True) else: if group['distance'].iloc[0] < ratio * group['distance'].iloc[1]: # this means distance _0_ is good and can drop all other distances mask.append(True) for i in range(len(group['distance']-1)): mask.append(False) else: for i in range(len(group['distance'])): mask.append(False) ''' return mask def mirroring_test(matches): """ Compute and return a mask for the matches dataframe on each edge of the graph which will keep only entries in which there is both a source -> destination match and a destination -> source match. Parameters ---------- matches : dataframe the matches dataframe stored along the edge of the graph containing matched points with columns containing: matched image name, query index, train index, and descriptor distance Returns ------- duplicates : dataseries Intended to mask the matches dataframe. Rows are True if the associated keypoint passes the mirroring test and false otherwise. That is, if 1->2, 2->1, both rows will be True, otherwise, they will be false. Keypoints with only one match will be False. Removes duplicate rows. """ duplicates = matches.duplicated(keep='first').values duplicates.astype(bool, copy=False) return duplicates
autocnet/matcher/tests/test_feature_extractor.py +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ class TestFeatureExtractor(unittest.TestCase): def test_extract_features(self): features = feature_extractor.extract_features(self.data_array, self.parameters) self.assertEquals(len(features), 2) self.assertEqual(len(features[0]), 11) # OpenCV +1 to self.assertIn(len(features[0]), range(8,11)) self.assertIsInstance(features[0][0], type(cv2.KeyPoint())) self.assertIsInstance(features[1][0], np.ndarray)