Loading autocnet/graph/network.py +26 −5 Original line number Diff line number Diff line Loading @@ -67,11 +67,6 @@ class CandidateGraph(nx.Graph): e = self.edge[s][d] e.source = self.node[s] e.destination = self.node[d] #del self.adj[d][s] # Add the Edge class as a edge data structure #for s, d, edge in self.edges_iter(data=True): #self.edge[s][d] = Edge(self.node[s], self.node[d]) @classmethod def from_graph(cls, graph): Loading Loading @@ -373,6 +368,32 @@ class CandidateGraph(nx.Graph): """ _, self.clusters = func(self, *args, **kwargs) def compute_triangular_cycles(self): """ Find all cycles of length 3. This is similar to cycle_basis (networkX), but returns all cycles. As opposed to all basis cycles. Returns ------- cycles : list A list of cycles in the form [(a,b,c), (c,d,e)], where letrers indicate node identifiers Examples -------- >>> g = CandidateGraph() >>> g.add_edges_from([(0,1), (0,2), (1,2), (0,3), (1,3), (2,3)]) >>> g.compute_triangular_cycles() [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)] """ cycles = [] for s, d in self.edges_iter(): for n in self.nodes(): if(s,n) in self.edges() and (d,n) in self.edges(): cycles.append((s,d,n)) return cycles def apply_func_to_edges(self, function, *args, **kwargs): """ Iterates over edges using an optional mask and and applies the given function. Loading autocnet/graph/node.py +12 −7 Original line number Diff line number Diff line Loading @@ -144,13 +144,13 @@ class Node(dict, MutableMapping): """ if hasattr(self, '_keypoints'): try: return self._keypoints.iloc[index] return self._keypoints.loc[index] except: return self._keypoints else: return None def get_keypoint_coordinates(self, index=None): def get_keypoint_coordinates(self, index=None, homogeneous=False): """ Return the coordinates of the keypoints without any ancillary data Loading @@ -159,16 +159,21 @@ class Node(dict, MutableMapping): index : iterable indices for of the keypoints to return homogeneous : bool If True, return homogeneous coordinates in the form [x, y, 1]. Default: False Returns ------- : dataframe A pandas dataframe of keypoint coordinates """ keypoints = self.get_keypoints(index=index) try: return keypoints[['x', 'y']] except: return None keypoints = self.get_keypoints(index=index)[['x', 'y']] if homogeneous: keypoints['homogeneous'] = 1 return keypoints def extract_features(self, array, **kwargs): """ Loading autocnet/matcher/outlier_detector.py +16 −18 Original line number Diff line number Diff line Loading @@ -138,17 +138,17 @@ class SpatialSuppression(Observable): """ def __init__(self, df, domain, min_radius=2, k=250, error_k=0.1): def __init__(self, df, domain, min_radius=1, k=250, error_k=0.1): columns = df.columns for i in ['x', 'y', 'strength']: if i not in columns: raise ValueError('The dataframe is missing a {} column.'.format(i)) self._df = df.sort_values(by=['strength'], ascending=False).copy() self.df = df.sort_values(by=['strength'], ascending=False).copy() self.max_radius = max(domain) self.min_radius = min_radius self.domain = domain self.mask = None self._k = k self.k = k self._error_k = error_k self.attrs = ['mask', 'k', 'error_k'] Loading @@ -164,14 +164,6 @@ class SpatialSuppression(Observable): except: return None @property def k(self): return self._k @k.setter def k(self, v): self._k = v @property def error_k(self): return self._error_k Loading @@ -180,23 +172,23 @@ class SpatialSuppression(Observable): def error_k(self, v): self._error_k = v @property def df(self): return self._df def suppress(self): """ Suppress subpixel registered points to that k +- k * error_k points, with good spatial distribution, remain """ process = True if self.k > len(self.df): warnings.warn('Only {} valid points, but {} points requested'.format(len(self.df), self.k)) self.k = len(self.df) result = self.df.index process = False search_space = np.linspace(self.min_radius, self.max_radius / 16, 250) cell_sizes = (search_space / math.sqrt(2)).astype(np.int) min_idx = 0 max_idx = len(search_space) - 1 while True: while process: mid_idx = int((min_idx + max_idx) / 2) r = search_space[mid_idx] Loading Loading @@ -228,6 +220,7 @@ class SpatialSuppression(Observable): pts.append((p[['x', 'y']])) if len(result) > self.k + self.k * self.error_k: # Too many points, break print('TooMany') min_idx = mid_idx break Loading @@ -253,13 +246,18 @@ class SpatialSuppression(Observable): # Check break conditions if self.k - self.k * self.error_k <= len(result) <= self.k + self.k * self.error_k: break print('Just right') process = False elif len(result) < self.k: # The radius is too large print('TOOFEW') max_idx = mid_idx if min_idx == max_idx: process = False elif min_idx == mid_idx or mid_idx == max_idx: warnings.warn('Unable to optimally solve. Returning with {} points'.format(len(result))) break process = False self.mask = pd.Series(False, self.df.index) self.mask.loc[list(result)] = True Loading autocnet/utils/tests/test_utils.py +10 −1 Original line number Diff line number Diff line Loading @@ -93,7 +93,6 @@ class TestUtils(unittest.TestCase): self.assertEqual(pts.shape, (25,3)) np.testing.assert_array_equal(pts[:, -1], np.ones(25)) def test_remove_field_name(self): starray = np.array([(1 ,2.,'String'), (2, 3.,"String2")], dtype=[('index', 'i4'),('bar', 'f4'), ('baz', 'S10')]) Loading @@ -101,3 +100,13 @@ class TestUtils(unittest.TestCase): dtype=[('bar', 'f4'), ('baz', 'S10')]) cleaned_array = utils.remove_field_name(starray, 'index') np.testing.assert_array_equal(cleaned_array, truth) def test_normalize_vector(self): x = np.array([1,1,1], dtype=np.float) y = utils.normalize_vector(x) np.testing.assert_array_almost_equal(np.array([ 0.70710678, 0.70710678, 0.70710678]), y) x = np.repeat(np.arange(4), 3).reshape(-1, 3) y = utils.normalize_vector(x) truth = np.tile(np.array([ 0.70710678, 0.70710678, 0.70710678]), 4) np.testing.assert_array_almost_equal(truth, y) autocnet/utils/utils.py +34 −0 Original line number Diff line number Diff line from functools import reduce import numpy as np import pandas as pd def normalize_vector(line): """ Normalize a standard form line Parameters ---------- line : ndarray Standard line form coefficients Returns ------- line : ndarray The normalized line Examples -------- >>> x = np.random.random((3,3)) >>> normalize_vector(x) array([[ 0.88280225, 0.4697448 , 0.11460811], [ 0.26090555, 0.96536433, 0.91648305], [ 0.58271501, 0.81267657, 0.30796395]]) """ if isinstance(line, pd.DataFrame): line = line.values try: n = np.sqrt(line[:, 0]**2 + line[:, 1]**2).reshape(-1, 1) except: n = np.sqrt(line[0]**2 + line[1]**2) line /= n return line def getnearest(iterable, value): """ Loading Loading
autocnet/graph/network.py +26 −5 Original line number Diff line number Diff line Loading @@ -67,11 +67,6 @@ class CandidateGraph(nx.Graph): e = self.edge[s][d] e.source = self.node[s] e.destination = self.node[d] #del self.adj[d][s] # Add the Edge class as a edge data structure #for s, d, edge in self.edges_iter(data=True): #self.edge[s][d] = Edge(self.node[s], self.node[d]) @classmethod def from_graph(cls, graph): Loading Loading @@ -373,6 +368,32 @@ class CandidateGraph(nx.Graph): """ _, self.clusters = func(self, *args, **kwargs) def compute_triangular_cycles(self): """ Find all cycles of length 3. This is similar to cycle_basis (networkX), but returns all cycles. As opposed to all basis cycles. Returns ------- cycles : list A list of cycles in the form [(a,b,c), (c,d,e)], where letrers indicate node identifiers Examples -------- >>> g = CandidateGraph() >>> g.add_edges_from([(0,1), (0,2), (1,2), (0,3), (1,3), (2,3)]) >>> g.compute_triangular_cycles() [(0, 1, 2), (0, 1, 3), (0, 2, 3), (1, 2, 3)] """ cycles = [] for s, d in self.edges_iter(): for n in self.nodes(): if(s,n) in self.edges() and (d,n) in self.edges(): cycles.append((s,d,n)) return cycles def apply_func_to_edges(self, function, *args, **kwargs): """ Iterates over edges using an optional mask and and applies the given function. Loading
autocnet/graph/node.py +12 −7 Original line number Diff line number Diff line Loading @@ -144,13 +144,13 @@ class Node(dict, MutableMapping): """ if hasattr(self, '_keypoints'): try: return self._keypoints.iloc[index] return self._keypoints.loc[index] except: return self._keypoints else: return None def get_keypoint_coordinates(self, index=None): def get_keypoint_coordinates(self, index=None, homogeneous=False): """ Return the coordinates of the keypoints without any ancillary data Loading @@ -159,16 +159,21 @@ class Node(dict, MutableMapping): index : iterable indices for of the keypoints to return homogeneous : bool If True, return homogeneous coordinates in the form [x, y, 1]. Default: False Returns ------- : dataframe A pandas dataframe of keypoint coordinates """ keypoints = self.get_keypoints(index=index) try: return keypoints[['x', 'y']] except: return None keypoints = self.get_keypoints(index=index)[['x', 'y']] if homogeneous: keypoints['homogeneous'] = 1 return keypoints def extract_features(self, array, **kwargs): """ Loading
autocnet/matcher/outlier_detector.py +16 −18 Original line number Diff line number Diff line Loading @@ -138,17 +138,17 @@ class SpatialSuppression(Observable): """ def __init__(self, df, domain, min_radius=2, k=250, error_k=0.1): def __init__(self, df, domain, min_radius=1, k=250, error_k=0.1): columns = df.columns for i in ['x', 'y', 'strength']: if i not in columns: raise ValueError('The dataframe is missing a {} column.'.format(i)) self._df = df.sort_values(by=['strength'], ascending=False).copy() self.df = df.sort_values(by=['strength'], ascending=False).copy() self.max_radius = max(domain) self.min_radius = min_radius self.domain = domain self.mask = None self._k = k self.k = k self._error_k = error_k self.attrs = ['mask', 'k', 'error_k'] Loading @@ -164,14 +164,6 @@ class SpatialSuppression(Observable): except: return None @property def k(self): return self._k @k.setter def k(self, v): self._k = v @property def error_k(self): return self._error_k Loading @@ -180,23 +172,23 @@ class SpatialSuppression(Observable): def error_k(self, v): self._error_k = v @property def df(self): return self._df def suppress(self): """ Suppress subpixel registered points to that k +- k * error_k points, with good spatial distribution, remain """ process = True if self.k > len(self.df): warnings.warn('Only {} valid points, but {} points requested'.format(len(self.df), self.k)) self.k = len(self.df) result = self.df.index process = False search_space = np.linspace(self.min_radius, self.max_radius / 16, 250) cell_sizes = (search_space / math.sqrt(2)).astype(np.int) min_idx = 0 max_idx = len(search_space) - 1 while True: while process: mid_idx = int((min_idx + max_idx) / 2) r = search_space[mid_idx] Loading Loading @@ -228,6 +220,7 @@ class SpatialSuppression(Observable): pts.append((p[['x', 'y']])) if len(result) > self.k + self.k * self.error_k: # Too many points, break print('TooMany') min_idx = mid_idx break Loading @@ -253,13 +246,18 @@ class SpatialSuppression(Observable): # Check break conditions if self.k - self.k * self.error_k <= len(result) <= self.k + self.k * self.error_k: break print('Just right') process = False elif len(result) < self.k: # The radius is too large print('TOOFEW') max_idx = mid_idx if min_idx == max_idx: process = False elif min_idx == mid_idx or mid_idx == max_idx: warnings.warn('Unable to optimally solve. Returning with {} points'.format(len(result))) break process = False self.mask = pd.Series(False, self.df.index) self.mask.loc[list(result)] = True Loading
autocnet/utils/tests/test_utils.py +10 −1 Original line number Diff line number Diff line Loading @@ -93,7 +93,6 @@ class TestUtils(unittest.TestCase): self.assertEqual(pts.shape, (25,3)) np.testing.assert_array_equal(pts[:, -1], np.ones(25)) def test_remove_field_name(self): starray = np.array([(1 ,2.,'String'), (2, 3.,"String2")], dtype=[('index', 'i4'),('bar', 'f4'), ('baz', 'S10')]) Loading @@ -101,3 +100,13 @@ class TestUtils(unittest.TestCase): dtype=[('bar', 'f4'), ('baz', 'S10')]) cleaned_array = utils.remove_field_name(starray, 'index') np.testing.assert_array_equal(cleaned_array, truth) def test_normalize_vector(self): x = np.array([1,1,1], dtype=np.float) y = utils.normalize_vector(x) np.testing.assert_array_almost_equal(np.array([ 0.70710678, 0.70710678, 0.70710678]), y) x = np.repeat(np.arange(4), 3).reshape(-1, 3) y = utils.normalize_vector(x) truth = np.tile(np.array([ 0.70710678, 0.70710678, 0.70710678]), 4) np.testing.assert_array_almost_equal(truth, y)
autocnet/utils/utils.py +34 −0 Original line number Diff line number Diff line from functools import reduce import numpy as np import pandas as pd def normalize_vector(line): """ Normalize a standard form line Parameters ---------- line : ndarray Standard line form coefficients Returns ------- line : ndarray The normalized line Examples -------- >>> x = np.random.random((3,3)) >>> normalize_vector(x) array([[ 0.88280225, 0.4697448 , 0.11460811], [ 0.26090555, 0.96536433, 0.91648305], [ 0.58271501, 0.81267657, 0.30796395]]) """ if isinstance(line, pd.DataFrame): line = line.values try: n = np.sqrt(line[:, 0]**2 + line[:, 1]**2).reshape(-1, 1) except: n = np.sqrt(line[0]**2 + line[1]**2) line /= n return line def getnearest(iterable, value): """ Loading