Loading autocnet/__init__.py +37 −8 Changes for autocnet/__init__.py: 37 added lines, 8 removed lines. Original line number Diff line number Diff line import os import autocnet __version__ = "0.1.0" def get_data(filename): packagdir = autocnet.__path__[0] dirname = os.path.join(os.path.dirname(packagdir), 'data') fullname = os.path.join(dirname, filename) return fullname import autocnet.examples import autocnet.camera import autocnet.cg Loading @@ -18,3 +10,40 @@ import autocnet.matcher import autocnet.transformation import autocnet.utils import autocnet.utils __version__ = "0.1.0" def get_data(filename): packagdir = autocnet.__path__[0] dirname = os.path.join(os.path.dirname(packagdir), 'data') fullname = os.path.join(dirname, filename) return fullname def cuda(enable=False, gpu=0): # Classes/Methods that can vary if GPU is available from autocnet.graph.node import Node from autocnet.graph.edge import Edge if enable: print('Enabling CUDA') try: import cudasift as cs cs.PyInitCuda(gpu) # Here is where the GPU methods get patched into the class from autocnet.matcher.cuda_extractor import extract_features Node._extract_features = staticmethod(extract_features) from autocnet.matcher.cuda_matcher import match Edge.match = match except Exception: print('Failed to enable cuda') return print('CUDA Disabled') # Here is where the CPU methods get patched into the class from autocnet.matcher.feature_extractor import extract_features Node._extract_features = staticmethod(extract_features) from autocnet.matcher.feature_matcher import match Edge.match = match cuda() autocnet/graph/edge.py +33 −40 Changes for autocnet/graph/edge.py: 33 added lines, 40 removed lines. Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import numpy as np import pandas as pd from scipy.spatial.distance import cdist import autocnet from autocnet.utils import utils from autocnet.matcher import health from autocnet.matcher import outlier_detector as od Loading Loading @@ -77,9 +78,13 @@ class Edge(dict, MutableMapping): # state, dynamically draw the mask from the object. for c in self._masks.columns: if c in mask_lookup: try: truncated_mask = getattr(self, mask_lookup[c]).mask self._masks[c] = False self._masks[c].iloc[truncated_mask.index] = truncated_mask except Exception: #TODO: Get rid of state pass return self._masks @masks.setter Loading Loading @@ -342,48 +347,36 @@ class Edge(dict, MutableMapping): k : int The number of neighbors to find """ def mono_matches(a, b, aidx=None, bidx=None): """ Apply the FLANN match_features Parameters ---------- a : object A node object b : object A node object aidx : iterable An index for the descriptors to subset pass bidx : iterable An index for the descriptors to subset def cuda_match(self, ratio=0.8, **kwargs): """ # Subset if requested if aidx is not None: ad = a.descriptors[aidx] else: ad = a.descriptors if bidx is not None: bd = b.descriptors[bidx] else: bd = b.descriptors # Load, train, and match fl.add(ad, a.node_id, index=aidx) fl.train() matches = fl.query(bd, b.node_id, k, index=bidx) self._add_matches(matches) fl.clear() fl = FlannMatcher() mono_matches(self.source, self.destination) mono_matches(self.destination, self.source) Apply a composite CUDA matcher and ratio check. If this method is used, no additional ratio check is necessary and no symmetry check is required. The ratio check is embedded on the cuda side and returned as an ambiguity value. In testing symmetry is not required as it is expensive without significant gain in accuracy when using this implementation. """ if not autocnet.cudasift: warnings.warn('CudaSift is not available, please use the standard matcher.') s_siftdata = autocnet.cs.PySiftData.from_data_frame(self.source.get_keypoints(), self.source.descriptors) d_siftdata = autocnet.cs.PySiftData.from_data_frame(self.destination.get_keypoints(), self.destination.descriptors) autocnet.cs.PyMatchSiftData(s_siftdata, d_siftdata) matches, _ = s_siftdata.to_data_frame() source = np.empty(len(matches)) source[:] = self.source.node_id destination = np.empty(len(matches)) destination[:] = self.destination.node_id df = pd.concat([pd.Series(source), pd.Series(matches.index), pd.Series(destination), matches.match, matches.score, matches.ambiguity], axis=1) df.columns = ['source_image', 'source_idx', 'destination_image', 'destination_idx', 'score', 'ambiguity'] print(df) self.matches = df def _add_matches(self, matches): """ Given a dataframe of matches, either append to an existing Loading autocnet/graph/network.py +5 −3 Changes for autocnet/graph/network.py: 5 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -206,7 +206,7 @@ class CandidateGraph(nx.Graph): raise NotImplementedError def extract_features(self, method='orb', extractor_parameters={}): def extract_features(self, *args, **kwargs): """ Extracts features from each image in the graph and uses the result to assign the node attributes for 'handle', 'image', 'keypoints', and 'descriptors'. Loading @@ -224,8 +224,7 @@ class CandidateGraph(nx.Graph): """ for i, node in self.nodes_iter(data=True): image = node.get_array() node.extract_features(image, method=method, extractor_parameters=extractor_parameters) node.extract_features(image, *args, **kwargs), def save_features(self, out_path, nodes=[]): """ Loading Loading @@ -295,6 +294,9 @@ class CandidateGraph(nx.Graph): """ self.apply_func_to_edges('match', *args, **kwargs) def cuda_match(self, *args, **kwargs): self.apply_func_to_edges('cuda_match', *args, **kwargs) def decompose_and_match_features(self, *args, **kwargs): """ For all edges in the graph, apply coupled decomposition followed by Loading autocnet/graph/node.py +6 −2 Changes for autocnet/graph/node.py: 6 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -227,7 +227,8 @@ class Node(dict, MutableMapping): return keypoints def extract_features(self, array, **kwargs): @staticmethod def _extract_features(*args, **kwargs): """ Extract features for the node Loading @@ -239,7 +240,10 @@ class Node(dict, MutableMapping): kwargs passed to autocnet.feature_extractor.extract_features """ self._keypoints, self.descriptors = fe.extract_features(array, **kwargs) pass def extract_features(self, *args, **kwargs): self._keypoints, self.descriptors = Node._extract_features(*args, **kwargs) def load_features(self, in_path): """ Loading autocnet/matcher/cuda_extractor.py 0 → 100644 +19 −0 Changes for autocnet/matcher/cuda_extractor.py: 19 added lines, 0 removed lines. Original line number Diff line number Diff line import warnings import cudasift as cs def extract_features(array, nfeatures=None): if not nfeatures: nfeatures = int(max(array.shape) / 1.75) else: warnings.warn('NFeatures specified with the CudaSift implementation. Please ensure the distribution of keypoints is what you expect.') siftdata = cs.PySiftData(nfeatures) cs.ExtractKeypoints(array, siftdata) keypoints, descriptors = siftdata.to_data_frame() keypoints = keypoints[['xpos', 'ypos', 'scale', 'sharpness', 'edgeness', 'orientation', 'score', 'ambiguity']] # Set the columns that have unfilled values to zero to avoid confusion keypoints['score'] = 0.0 keypoints['ambiguity'] = 0.0 return keypoints, descriptors Loading
autocnet/__init__.py +37 −8 Changes for autocnet/__init__.py: 37 added lines, 8 removed lines. Original line number Diff line number Diff line import os import autocnet __version__ = "0.1.0" def get_data(filename): packagdir = autocnet.__path__[0] dirname = os.path.join(os.path.dirname(packagdir), 'data') fullname = os.path.join(dirname, filename) return fullname import autocnet.examples import autocnet.camera import autocnet.cg Loading @@ -18,3 +10,40 @@ import autocnet.matcher import autocnet.transformation import autocnet.utils import autocnet.utils __version__ = "0.1.0" def get_data(filename): packagdir = autocnet.__path__[0] dirname = os.path.join(os.path.dirname(packagdir), 'data') fullname = os.path.join(dirname, filename) return fullname def cuda(enable=False, gpu=0): # Classes/Methods that can vary if GPU is available from autocnet.graph.node import Node from autocnet.graph.edge import Edge if enable: print('Enabling CUDA') try: import cudasift as cs cs.PyInitCuda(gpu) # Here is where the GPU methods get patched into the class from autocnet.matcher.cuda_extractor import extract_features Node._extract_features = staticmethod(extract_features) from autocnet.matcher.cuda_matcher import match Edge.match = match except Exception: print('Failed to enable cuda') return print('CUDA Disabled') # Here is where the CPU methods get patched into the class from autocnet.matcher.feature_extractor import extract_features Node._extract_features = staticmethod(extract_features) from autocnet.matcher.feature_matcher import match Edge.match = match cuda()
autocnet/graph/edge.py +33 −40 Changes for autocnet/graph/edge.py: 33 added lines, 40 removed lines. Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import numpy as np import pandas as pd from scipy.spatial.distance import cdist import autocnet from autocnet.utils import utils from autocnet.matcher import health from autocnet.matcher import outlier_detector as od Loading Loading @@ -77,9 +78,13 @@ class Edge(dict, MutableMapping): # state, dynamically draw the mask from the object. for c in self._masks.columns: if c in mask_lookup: try: truncated_mask = getattr(self, mask_lookup[c]).mask self._masks[c] = False self._masks[c].iloc[truncated_mask.index] = truncated_mask except Exception: #TODO: Get rid of state pass return self._masks @masks.setter Loading Loading @@ -342,48 +347,36 @@ class Edge(dict, MutableMapping): k : int The number of neighbors to find """ def mono_matches(a, b, aidx=None, bidx=None): """ Apply the FLANN match_features Parameters ---------- a : object A node object b : object A node object aidx : iterable An index for the descriptors to subset pass bidx : iterable An index for the descriptors to subset def cuda_match(self, ratio=0.8, **kwargs): """ # Subset if requested if aidx is not None: ad = a.descriptors[aidx] else: ad = a.descriptors if bidx is not None: bd = b.descriptors[bidx] else: bd = b.descriptors # Load, train, and match fl.add(ad, a.node_id, index=aidx) fl.train() matches = fl.query(bd, b.node_id, k, index=bidx) self._add_matches(matches) fl.clear() fl = FlannMatcher() mono_matches(self.source, self.destination) mono_matches(self.destination, self.source) Apply a composite CUDA matcher and ratio check. If this method is used, no additional ratio check is necessary and no symmetry check is required. The ratio check is embedded on the cuda side and returned as an ambiguity value. In testing symmetry is not required as it is expensive without significant gain in accuracy when using this implementation. """ if not autocnet.cudasift: warnings.warn('CudaSift is not available, please use the standard matcher.') s_siftdata = autocnet.cs.PySiftData.from_data_frame(self.source.get_keypoints(), self.source.descriptors) d_siftdata = autocnet.cs.PySiftData.from_data_frame(self.destination.get_keypoints(), self.destination.descriptors) autocnet.cs.PyMatchSiftData(s_siftdata, d_siftdata) matches, _ = s_siftdata.to_data_frame() source = np.empty(len(matches)) source[:] = self.source.node_id destination = np.empty(len(matches)) destination[:] = self.destination.node_id df = pd.concat([pd.Series(source), pd.Series(matches.index), pd.Series(destination), matches.match, matches.score, matches.ambiguity], axis=1) df.columns = ['source_image', 'source_idx', 'destination_image', 'destination_idx', 'score', 'ambiguity'] print(df) self.matches = df def _add_matches(self, matches): """ Given a dataframe of matches, either append to an existing Loading
autocnet/graph/network.py +5 −3 Changes for autocnet/graph/network.py: 5 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -206,7 +206,7 @@ class CandidateGraph(nx.Graph): raise NotImplementedError def extract_features(self, method='orb', extractor_parameters={}): def extract_features(self, *args, **kwargs): """ Extracts features from each image in the graph and uses the result to assign the node attributes for 'handle', 'image', 'keypoints', and 'descriptors'. Loading @@ -224,8 +224,7 @@ class CandidateGraph(nx.Graph): """ for i, node in self.nodes_iter(data=True): image = node.get_array() node.extract_features(image, method=method, extractor_parameters=extractor_parameters) node.extract_features(image, *args, **kwargs), def save_features(self, out_path, nodes=[]): """ Loading Loading @@ -295,6 +294,9 @@ class CandidateGraph(nx.Graph): """ self.apply_func_to_edges('match', *args, **kwargs) def cuda_match(self, *args, **kwargs): self.apply_func_to_edges('cuda_match', *args, **kwargs) def decompose_and_match_features(self, *args, **kwargs): """ For all edges in the graph, apply coupled decomposition followed by Loading
autocnet/graph/node.py +6 −2 Changes for autocnet/graph/node.py: 6 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -227,7 +227,8 @@ class Node(dict, MutableMapping): return keypoints def extract_features(self, array, **kwargs): @staticmethod def _extract_features(*args, **kwargs): """ Extract features for the node Loading @@ -239,7 +240,10 @@ class Node(dict, MutableMapping): kwargs passed to autocnet.feature_extractor.extract_features """ self._keypoints, self.descriptors = fe.extract_features(array, **kwargs) pass def extract_features(self, *args, **kwargs): self._keypoints, self.descriptors = Node._extract_features(*args, **kwargs) def load_features(self, in_path): """ Loading
autocnet/matcher/cuda_extractor.py 0 → 100644 +19 −0 Changes for autocnet/matcher/cuda_extractor.py: 19 added lines, 0 removed lines. Original line number Diff line number Diff line import warnings import cudasift as cs def extract_features(array, nfeatures=None): if not nfeatures: nfeatures = int(max(array.shape) / 1.75) else: warnings.warn('NFeatures specified with the CudaSift implementation. Please ensure the distribution of keypoints is what you expect.') siftdata = cs.PySiftData(nfeatures) cs.ExtractKeypoints(array, siftdata) keypoints, descriptors = siftdata.to_data_frame() keypoints = keypoints[['xpos', 'ypos', 'scale', 'sharpness', 'edgeness', 'orientation', 'score', 'ambiguity']] # Set the columns that have unfilled values to zero to avoid confusion keypoints['score'] = 0.0 keypoints['ambiguity'] = 0.0 return keypoints, descriptors