Loading autocnet/camera/tests/test_camera.py +4 −3 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ class TestCamera(unittest.TestCase): c = camera.triangulate(coords1, coords2, p, p1) np.testing.assert_array_almost_equal(c, truth) truth = np.array([ 0.078723, 0.228164, 0.127505, 0.333851, 0.009718]) c = camera.projection_error(p1, p, coords1, coords2) np.testing.assert_array_almost_equal(c, truth) No newline at end of file truth = np.array([ 3.09866357e-02, 2.60295132e-01, 8.12871690e-02, 5.57281224e-01, 4.72226586e-04]) residuals, reproj_error = camera.projection_error(p1, p, coords1, coords2) np.testing.assert_array_almost_equal(residuals, truth) No newline at end of file autocnet/fileio/tests/test_io_controlnetwork.py +12 −16 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ from autocnet.utils.utils import find_in_dict from autocnet.control.control import CorrespondenceNetwork from autocnet.graph.edge import Edge from autocnet.graph.node import Node from autocnet.graph.network import CandidateGraph sys.path.insert(0, os.path.abspath('..')) Loading @@ -22,13 +23,15 @@ class TestWriteIsisControlNetwork(unittest.TestCase): @classmethod def setUpClass(cls): cls.npts = 5 serial_times = {295: '1971-07-31T01:24:11.754', 296: '1971-07-31T01:24:36.970'} cls.serials = ['APOLLO15/METRIC/{}'.format(i) for i in serial_times.values()] net = CandidateGraph({'a': ['b'], 'b': ['a']}) for i, n in net.nodes_iter(data=True): n._keypoints = pd.DataFrame(np.arange(10).reshape(cls.npts,-1), columns=['x', 'y']) n._isis_serial = cls.serials[i] # Create an edge and a set of matches cls.npts = 5 coords = pd.DataFrame(np.arange(cls.npts * 2).reshape(-1, 2)) source = np.zeros(cls.npts) destination = np.ones(cls.npts) pid = np.arange(cls.npts) Loading @@ -38,19 +41,12 @@ class TestWriteIsisControlNetwork(unittest.TestCase): 'destination_image', 'destination_idx']) edge = Mock(spec=Edge) edge.source = Mock(spec=Node) edge.destination = Mock(spec=Node) edge.source.isis_serial = cls.serials[0] edge.destination.isis_serial = cls.serials[1] edge.source.get_keypoint_coordinates = MagicMock(return_value=coords) edge.destination.get_keypoint_coordinates = MagicMock(return_value=coords) cnet = CorrespondenceNetwork() cnet.add_correspondences(edge, matches) cls.creation_date = cnet.creationdate cls.modified_date = cnet.modifieddate io_controlnetwork.to_isis('test.net', cnet, mode='wb', targetname='Moon') net.edge[0][1].matches = matches net.generate_cnet(clean_keys=[]) cls.creation_date = net.creationdate cls.modified_date = net.modifieddate io_controlnetwork.to_isis('test.net', net, mode='wb', targetname='Moon') cls.header_message_size = 98 cls.point_start_byte = 65634 Loading autocnet/graph/edge.py +29 −9 Original line number Diff line number Diff line Loading @@ -68,7 +68,7 @@ class Edge(dict, MutableMapping): mask_lookup = {'fundamental': 'fundamental_matrix', 'ratio': 'distance_ratio'} if not hasattr(self, '_masks'): if hasattr(self, 'matches'): if self.matches is not None: self._masks = pd.DataFrame(True, columns=['symmetry'], index=self.matches.index) else: Loading @@ -93,7 +93,21 @@ class Edge(dict, MutableMapping): return self._health.health def match(self, k=2): """ Given two sets of descriptors, utilize a FLANN (Approximate Nearest Neighbor KDTree) matcher to find the k nearest matches. Nearness is the euclidean distance between descriptors. The matches are then added as an attribute to the edge object. Parameters ---------- k : int The number of neighbors to find Returns ------- """ def mono_matches(a, b): fl.add(a.descriptors, a.node_id) fl.train() Loading @@ -105,6 +119,15 @@ class Edge(dict, MutableMapping): mono_matches(self.destination, self.source) def _add_matches(self, matches): """ Given a dataframe of matches, either append to an existing matches edge attribute or initially populate said attribute. Parameters ---------- matches : dataframe A dataframe of matches """ if self.matches is None: self.matches = matches else: Loading Loading @@ -160,6 +183,7 @@ class Edge(dict, MutableMapping): return matches, mask = self._clean(clean_keys) # TODO: Homogeneous is horribly inefficient here, use Numpy array notation s_keypoints = self.source.get_keypoint_coordinates(index=matches['source_idx'], homogeneous=True) d_keypoints = self.destination.get_keypoint_coordinates(index=matches['destination_idx'], Loading Loading @@ -219,17 +243,13 @@ class Edge(dict, MutableMapping): s_keypoints = self.source.get_keypoint_coordinates(index=matches['source_idx']) d_keypoints = self.destination.get_keypoint_coordinates(index=matches['destination_idx']) transformation_matrix, ransac_mask = od.compute_homography(s_keypoints.values, d_keypoints.values, **kwargs) self.homography = Homography(np.zeros((3,3)), index=self.masks.index) self.homography.compute(s_keypoints.values, d_keypoints.values) # Convert the truncated RANSAC mask back into a full length mask mask[mask] = ransac_mask.ravel() mask[mask] = self.homography.mask.ravel() self.masks = ('ransac', mask) self.homography = Homography(transformation_matrix, s_keypoints[ransac_mask], d_keypoints[ransac_mask], mask=mask[mask].index) # Finalize the array to get custom attrs to propagate self.homography.__array_finalize__(self.homography) Loading autocnet/graph/network.py +1 −2 Original line number Diff line number Diff line Loading @@ -13,7 +13,6 @@ from autocnet.fileio.io_gdal import GeoDataset from autocnet.graph import markov_cluster from autocnet.graph.edge import Edge from autocnet.graph.node import Node from autocnet.matcher.add_depth import deepen_correspondences from autocnet.vis.graph_view import plot_graph Loading Loading @@ -661,7 +660,7 @@ class CandidateGraph(nx.Graph): # get all edges that have matches matches = [(u, v) for u, v, edge in self.edges_iter(data=True) if hasattr(edge, 'matches') and not edge.matches.empty] not edge.matches is None] return self.create_edge_subgraph(matches) Loading autocnet/graph/node.py +13 −13 Original line number Diff line number Diff line Loading @@ -22,21 +22,31 @@ from autocnet.utils import utils class Node(dict, MutableMapping): """ This class represents a node in a graph and is synonymous with an image. The node (image) stores PATH information, an accessor to the on-disk data set, and correspondences information that references the image. Attributes ---------- image_name : str Name of the image, with extension image_path : str Relative or absolute PATH to the image geodata : object File handle to the object keypoints : dataframe With columns, x, y, and response nkeypoints : int The number of keypoints found for this image descriptors : ndarray 32-bit array of feature descriptors returned by OpenCV masks : set A list of the available masking arrays Loading @@ -51,6 +61,7 @@ class Node(dict, MutableMapping): self.node_id = node_id self._mask_arrays = {} self.point_to_correspondence = defaultdict(set) self.nkeypoints = 0 def __repr__(self): return """ Loading @@ -69,17 +80,6 @@ class Node(dict, MutableMapping): self._geodata = GeoDataset(self.image_path) return self._geodata @property def nkeypoints(self): if hasattr(self, '_keypoints'): return len(self._keypoints) else: return 0 @nkeypoints.setter def nkeypoints(self, v): self._nkeypoints = v @property def masks(self): mask_lookup = {'suppression': 'suppression'} Loading Loading @@ -231,7 +231,7 @@ class Node(dict, MutableMapping): keypoints[i] = kpt.pt[0], kpt.pt[1], kpt.response, kpt.size, kpt.angle, octave, layer # y, x self._keypoints = pd.DataFrame(keypoints, columns=['x', 'y', 'response', 'size', 'angle', 'octave', 'layer']) self._nkeypoints = len(self._keypoints) self.nkeypoints = len(self._keypoints) self.descriptors = descriptors.astype(np.float32) def load_features(self, in_path): Loading Loading
autocnet/camera/tests/test_camera.py +4 −3 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ class TestCamera(unittest.TestCase): c = camera.triangulate(coords1, coords2, p, p1) np.testing.assert_array_almost_equal(c, truth) truth = np.array([ 0.078723, 0.228164, 0.127505, 0.333851, 0.009718]) c = camera.projection_error(p1, p, coords1, coords2) np.testing.assert_array_almost_equal(c, truth) No newline at end of file truth = np.array([ 3.09866357e-02, 2.60295132e-01, 8.12871690e-02, 5.57281224e-01, 4.72226586e-04]) residuals, reproj_error = camera.projection_error(p1, p, coords1, coords2) np.testing.assert_array_almost_equal(residuals, truth) No newline at end of file
autocnet/fileio/tests/test_io_controlnetwork.py +12 −16 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ from autocnet.utils.utils import find_in_dict from autocnet.control.control import CorrespondenceNetwork from autocnet.graph.edge import Edge from autocnet.graph.node import Node from autocnet.graph.network import CandidateGraph sys.path.insert(0, os.path.abspath('..')) Loading @@ -22,13 +23,15 @@ class TestWriteIsisControlNetwork(unittest.TestCase): @classmethod def setUpClass(cls): cls.npts = 5 serial_times = {295: '1971-07-31T01:24:11.754', 296: '1971-07-31T01:24:36.970'} cls.serials = ['APOLLO15/METRIC/{}'.format(i) for i in serial_times.values()] net = CandidateGraph({'a': ['b'], 'b': ['a']}) for i, n in net.nodes_iter(data=True): n._keypoints = pd.DataFrame(np.arange(10).reshape(cls.npts,-1), columns=['x', 'y']) n._isis_serial = cls.serials[i] # Create an edge and a set of matches cls.npts = 5 coords = pd.DataFrame(np.arange(cls.npts * 2).reshape(-1, 2)) source = np.zeros(cls.npts) destination = np.ones(cls.npts) pid = np.arange(cls.npts) Loading @@ -38,19 +41,12 @@ class TestWriteIsisControlNetwork(unittest.TestCase): 'destination_image', 'destination_idx']) edge = Mock(spec=Edge) edge.source = Mock(spec=Node) edge.destination = Mock(spec=Node) edge.source.isis_serial = cls.serials[0] edge.destination.isis_serial = cls.serials[1] edge.source.get_keypoint_coordinates = MagicMock(return_value=coords) edge.destination.get_keypoint_coordinates = MagicMock(return_value=coords) cnet = CorrespondenceNetwork() cnet.add_correspondences(edge, matches) cls.creation_date = cnet.creationdate cls.modified_date = cnet.modifieddate io_controlnetwork.to_isis('test.net', cnet, mode='wb', targetname='Moon') net.edge[0][1].matches = matches net.generate_cnet(clean_keys=[]) cls.creation_date = net.creationdate cls.modified_date = net.modifieddate io_controlnetwork.to_isis('test.net', net, mode='wb', targetname='Moon') cls.header_message_size = 98 cls.point_start_byte = 65634 Loading
autocnet/graph/edge.py +29 −9 Original line number Diff line number Diff line Loading @@ -68,7 +68,7 @@ class Edge(dict, MutableMapping): mask_lookup = {'fundamental': 'fundamental_matrix', 'ratio': 'distance_ratio'} if not hasattr(self, '_masks'): if hasattr(self, 'matches'): if self.matches is not None: self._masks = pd.DataFrame(True, columns=['symmetry'], index=self.matches.index) else: Loading @@ -93,7 +93,21 @@ class Edge(dict, MutableMapping): return self._health.health def match(self, k=2): """ Given two sets of descriptors, utilize a FLANN (Approximate Nearest Neighbor KDTree) matcher to find the k nearest matches. Nearness is the euclidean distance between descriptors. The matches are then added as an attribute to the edge object. Parameters ---------- k : int The number of neighbors to find Returns ------- """ def mono_matches(a, b): fl.add(a.descriptors, a.node_id) fl.train() Loading @@ -105,6 +119,15 @@ class Edge(dict, MutableMapping): mono_matches(self.destination, self.source) def _add_matches(self, matches): """ Given a dataframe of matches, either append to an existing matches edge attribute or initially populate said attribute. Parameters ---------- matches : dataframe A dataframe of matches """ if self.matches is None: self.matches = matches else: Loading Loading @@ -160,6 +183,7 @@ class Edge(dict, MutableMapping): return matches, mask = self._clean(clean_keys) # TODO: Homogeneous is horribly inefficient here, use Numpy array notation s_keypoints = self.source.get_keypoint_coordinates(index=matches['source_idx'], homogeneous=True) d_keypoints = self.destination.get_keypoint_coordinates(index=matches['destination_idx'], Loading Loading @@ -219,17 +243,13 @@ class Edge(dict, MutableMapping): s_keypoints = self.source.get_keypoint_coordinates(index=matches['source_idx']) d_keypoints = self.destination.get_keypoint_coordinates(index=matches['destination_idx']) transformation_matrix, ransac_mask = od.compute_homography(s_keypoints.values, d_keypoints.values, **kwargs) self.homography = Homography(np.zeros((3,3)), index=self.masks.index) self.homography.compute(s_keypoints.values, d_keypoints.values) # Convert the truncated RANSAC mask back into a full length mask mask[mask] = ransac_mask.ravel() mask[mask] = self.homography.mask.ravel() self.masks = ('ransac', mask) self.homography = Homography(transformation_matrix, s_keypoints[ransac_mask], d_keypoints[ransac_mask], mask=mask[mask].index) # Finalize the array to get custom attrs to propagate self.homography.__array_finalize__(self.homography) Loading
autocnet/graph/network.py +1 −2 Original line number Diff line number Diff line Loading @@ -13,7 +13,6 @@ from autocnet.fileio.io_gdal import GeoDataset from autocnet.graph import markov_cluster from autocnet.graph.edge import Edge from autocnet.graph.node import Node from autocnet.matcher.add_depth import deepen_correspondences from autocnet.vis.graph_view import plot_graph Loading Loading @@ -661,7 +660,7 @@ class CandidateGraph(nx.Graph): # get all edges that have matches matches = [(u, v) for u, v, edge in self.edges_iter(data=True) if hasattr(edge, 'matches') and not edge.matches.empty] not edge.matches is None] return self.create_edge_subgraph(matches) Loading
autocnet/graph/node.py +13 −13 Original line number Diff line number Diff line Loading @@ -22,21 +22,31 @@ from autocnet.utils import utils class Node(dict, MutableMapping): """ This class represents a node in a graph and is synonymous with an image. The node (image) stores PATH information, an accessor to the on-disk data set, and correspondences information that references the image. Attributes ---------- image_name : str Name of the image, with extension image_path : str Relative or absolute PATH to the image geodata : object File handle to the object keypoints : dataframe With columns, x, y, and response nkeypoints : int The number of keypoints found for this image descriptors : ndarray 32-bit array of feature descriptors returned by OpenCV masks : set A list of the available masking arrays Loading @@ -51,6 +61,7 @@ class Node(dict, MutableMapping): self.node_id = node_id self._mask_arrays = {} self.point_to_correspondence = defaultdict(set) self.nkeypoints = 0 def __repr__(self): return """ Loading @@ -69,17 +80,6 @@ class Node(dict, MutableMapping): self._geodata = GeoDataset(self.image_path) return self._geodata @property def nkeypoints(self): if hasattr(self, '_keypoints'): return len(self._keypoints) else: return 0 @nkeypoints.setter def nkeypoints(self, v): self._nkeypoints = v @property def masks(self): mask_lookup = {'suppression': 'suppression'} Loading Loading @@ -231,7 +231,7 @@ class Node(dict, MutableMapping): keypoints[i] = kpt.pt[0], kpt.pt[1], kpt.response, kpt.size, kpt.angle, octave, layer # y, x self._keypoints = pd.DataFrame(keypoints, columns=['x', 'y', 'response', 'size', 'angle', 'octave', 'layer']) self._nkeypoints = len(self._keypoints) self.nkeypoints = len(self._keypoints) self.descriptors = descriptors.astype(np.float32) def load_features(self, in_path): Loading