Loading autocnet/graph/edge.py +86 −59 Changes for autocnet/graph/edge.py: 86 added lines, 59 removed lines. Original line number Diff line number Diff line from functools import wraps import warnings from collections import MutableMapping Loading @@ -8,7 +9,7 @@ from scipy.spatial.distance import cdist import autocnet from autocnet.utils import utils from autocnet.matcher import outlier_detector as od from autocnet.matcher import cpu_outlier_detector as od from autocnet.matcher import suppression_funcs as spf from autocnet.matcher import subpixel as sp from autocnet.transformation import fundamental_matrix as fm Loading @@ -17,6 +18,7 @@ from autocnet.vis.graph_view import plot_edge, plot_node, plot_edge_decompositio from autocnet.cg import cg class Edge(dict, MutableMapping): """ Attributes Loading @@ -40,8 +42,11 @@ class Edge(dict, MutableMapping): self.destination = destination self['homography'] = None self['fundamental_matrix'] = None self.matches = None self.matches = pd.DataFrame() self.masks = pd.DataFrame() self['weights'] = {} self['source_mbr'] = None self['destin_mbr'] = None def __repr__(self): return """ Loading Loading @@ -69,7 +74,7 @@ class Edge(dict, MutableMapping): return eq @property """@property def masks(self): mask_lookup = {'fundamental': 'fundamental_matrix'} if not hasattr(self, '_masks'): Loading @@ -95,10 +100,7 @@ class Edge(dict, MutableMapping): def masks(self, v): column_name = v[0] boolean_mask = v[1] self.masks[column_name] = boolean_mask def decompose_and_match(*args, **kwargs): pass self.masks[column_name] = boolean_mask""" def match(self, k=2, **kwargs): Loading @@ -116,22 +118,44 @@ class Edge(dict, MutableMapping): """ pass def decompose(self): """ Apply coupled decomposition to the images and match identified sub-images """ pass def decompose_and_match(*args, **kwargs): pass def extract_subset(self, *args, **kwargs): self.compute_overlap() # Extract the source minx, maxx, miny, maxy = self['source_mbr'] xystart = (minx, miny) pixels=[minx, miny, maxx-minx, maxy-miny] node = self.source arr = node.geodata.read_array(pixels=pixels) node.extract_features(arr, xystart=xystart, *args, **kwargs) # Extract the destination minx, maxx, miny, maxy = self['destin_mbr'] xystart = (minx, miny) pixels=[minx, miny, maxx-minx, maxy-miny] node = self.destination arr = node.geodata.read_array(pixels=pixels) node.extract_features(arr, xystart=xystart, *args, **kwargs) def symmetry_check(self): if hasattr(self, 'matches'): mask = od.mirroring_test(self.matches) self.masks = ('symmetry', mask) else: raise AttributeError('No matches have been computed for this edge.') self.masks['symmetry'] = od.mirroring_test(self.matches) def ratio_check(self, clean_keys=[], **kwargs): if hasattr(self, 'matches'): def ratio_check(self, clean_keys=[], maskname='ratio', **kwargs): matches, mask = self.clean(clean_keys) distance_mask = od.distance_ratio(matches, **kwargs) self.masks = ('ratio', distance_mask) else: raise AttributeError('No matches have been computed for this edge.') self.masks[maskname] = od.distance_ratio(matches, **kwargs) def compute_fundamental_matrix(self, clean_keys=[], **kwargs): def compute_fundamental_matrix(self, clean_keys=[], maskname='fundamental', **kwargs): """ Estimate the fundamental matrix (F) using the correspondences tagged to this edge. Loading @@ -151,9 +175,6 @@ class Edge(dict, MutableMapping): autocnet.transformation.transformations.FundamentalMatrix """ if not hasattr(self, 'matches'): raise AttributeError('Matches have not been computed for this edge') return matches, mask = self.clean(clean_keys) # TODO: Homogeneous is horribly inefficient here, use Numpy array notation Loading @@ -174,9 +195,38 @@ class Edge(dict, MutableMapping): mask[mask] = fmask # Set the initial state of the fundamental mask in the masks self.masks = ('fundamental', mask) self.masks[maskname] = mask def compute_homography(self, method='ransac', clean_keys=[], pid=None, **kwargs): def compute_fundamental_error(self, clean_keys=[]): """ Given a fundamental matrix, compute the reprojective error between a two sets of keypoints. Parameters ---------- clean_keys : list of string keys to masking arrays (created by calling outlier detection) Returns ------- error : pd.Series of reprojective error indexed to the matches data frame """ if self['fundamental_matrix'] is None: warning.warn('No fundamental matrix has been compute for this edge.' ) matches, masks = self.clean(clean_keys) source_kps = self.source.get_keypoint_coordinates(index=matches['source_idx']) destination_kps = self.destination.get_keypoint_coordinates(index=matches['destination_idx']) error = fm.compute_fundamental_error(self['fundamental_matrix'], source_kps, destination_kps) error = pd.Series(error, index=matches.index) return error def compute_homography(self, method='ransac', clean_keys=[], pid=None, maskname='homography', **kwargs): """ For each edge in the (sub) graph, compute the homography Parameters Loading @@ -195,12 +245,6 @@ class Edge(dict, MutableMapping): mask : ndarray Boolean array of the outliers """ if hasattr(self, 'matches'): matches = self.matches else: raise AttributeError('Matches have not been computed for this edge') matches, mask = self.clean(clean_keys) s_keypoints = self.source.get_keypoint_coordinates(index=matches['source_idx']) Loading @@ -210,7 +254,7 @@ class Edge(dict, MutableMapping): # Convert the truncated RANSAC mask back into a full length mask mask[mask] = hmask self.masks = ('ransac', mask) self.masks['homography'] = mask def subpixel_register(self, clean_keys=[], threshold=0.8, template_size=19, search_size=53, max_x_shift=1.0, Loading Loading @@ -288,19 +332,18 @@ class Edge(dict, MutableMapping): threshold_mask = self.matches['correlation'] >= threshold # Compute the mask for the point shifts that are too large query_string = 'x_offset <= -{0} or x_offset >= {0} or y_offset <= -{1} or y_offset >= {1}'.format(max_x_shift, max_y_shift) query_string = 'x_offset <= -{0} or x_offset >= {0} or y_offset <= -{1} or y_offset >= {1}'.format(max_x_shift,max_y_shift) sp_shift_outliers = self.matches.query(query_string) shift_mask = pd.Series(True, index=self.matches.index) shift_mask.loc[sp_shift_outliers.index] = False # Generate the composite mask and write the masks to the mask data structure mask = threshold_mask & shift_mask self.masks = ('shift', shift_mask) self.masks = ('threshold', threshold_mask) self.masks = ('subpixel', mask) self.masks['shift'] = shift_mask self.masks['threshold'] = threshold_mask self.masks['subpixel'] = mask def suppress(self, suppression_func=spf.correlation, clean_keys=[], **kwargs): def suppress(self, suppression_func=spf.correlation, clean_keys=[], maskname='suppression', **kwargs): """ Apply a disc based suppression algorithm to get a good spatial distribution of high quality points, where the user defines some Loading Loading @@ -334,7 +377,7 @@ class Edge(dict, MutableMapping): smask, k = od.spatial_suppression(merged, domain, **kwargs) mask[mask] = smask self.masks = ('suppression', mask) self.masks[maskname] = mask def plot_source(self, ax=None, clean_keys=[], **kwargs): # pragma: no cover matches, mask = self.clean(clean_keys=clean_keys) Loading Loading @@ -417,9 +460,6 @@ class Edge(dict, MutableMapping): returns the overlap area covered by the keypoints """ if self.matches is None: raise AttributeError('Edge needs to have features extracted and matched') return matches, mask = self.clean(clean_keys) source_array = self.source.get_keypoint_coordinates(index=matches['source_idx']).values Loading Loading @@ -458,24 +498,11 @@ class Edge(dict, MutableMapping): voronoi = cg.vor(self, clean_keys, **kwargs) self.matches = pd.concat([self.matches, voronoi[1]['vor_weights']], axis=1) def decompose(self, maxiterations=3): """ Apply coupled decomposition to the images and match identified sub-images Parameters ---------- maxiterations : int The number of iterations. Appropriate values: | Number of megapixels | k | |----------------------|---| | m < 10 |1-2| | 10 < m < 30 | 3 | | 30 < m < 100 | 4 | | 100 < m < 1000 | 5 | | m > 1000 | 6 | def compute_overlap(self, **kwargs): """ pass Estimate a source and destination minimum bounding rectangle, in pixel space """ self.overlap_latlon_coords, self["source_mbr"], self["destin_mbr"] = self.source.geodata.compute_overlap(self.destination.geodata, **kwargs) autocnet/graph/tests/test_edge.py +4 −6 Changes for autocnet/graph/tests/test_edge.py: 4 added lines, 6 removed lines. Original line number Diff line number Diff line import unittest from unittest.mock import Mock from unittest.mock import MagicMock from unittest.mock import Mock, MagicMock import ogr import pandas as pd Loading Loading @@ -47,8 +46,7 @@ class TestEdge(unittest.TestCase): def test_compute_fundamental_matrix(self): with self.assertRaises(AttributeError): self.edge.compute_fundamental_matrix() pass def test_edge_overlap(self): e = edge.Edge() Loading Loading @@ -86,7 +84,7 @@ class TestEdge(unittest.TestCase): [0, 3, 1, 3], [0, 4, 1, 4]] matches_df = pd.DataFrame(data = keypoint_matches, columns = ['source_image', 'source_idx', 'destination_image', 'destination_idx']) matches_df = pd.DataFrame(keypoint_matches, columns = ['source_image', 'source_idx', 'destination_image', 'destination_idx']) e = edge.Edge() source_node = MagicMock(spec = node.Node()) destination_node = MagicMock(spec = node.Node()) Loading Loading @@ -126,7 +124,7 @@ class TestEdge(unittest.TestCase): e.matches = matches_df self.assertRaises(AttributeError, cg.edge[0][1].coverage) #self.assertRaises(AttributeError, cg.edge[0][1].coverage) self.assertEqual(e.coverage(), 0.3) def test_voronoi_transform(self): Loading autocnet/io/network.py +10 −9 Changes for autocnet/io/network.py: 10 added lines, 9 removed lines. Original line number Diff line number Diff line Loading @@ -51,12 +51,13 @@ def save(network, projectname): # Write the array node_attributes to hdf for n, data in network.nodes_iter(data=True): if data.descriptors is not None: grp = data['node_id'] np.savez('{}.npz'.format(data['node_id']), descriptors=data.descriptors, keypoints=data._keypoints, keypoints_idx=data._keypoints.index, keypoints_columns=data._keypoints.columns) keypoints=data.keypoints, keypoints_idx=data.keypoints.index, keypoints_columns=data.keypoints.columns) pzip.write('{}.npz'.format(data['node_id'])) os.remove('{}.npz'.format(data['node_id'])) Loading @@ -64,14 +65,15 @@ def save(network, projectname): for s, d, data in network.edges_iter(data=True): if s > d: s, d = d, s if data.matches is not None: grp = str((s,d)) np.savez('{}_{}.npz'.format(s, d), matches=data.matches, matches_idx=data.matches.index, matches_columns=data.matches.columns, _masks=data._masks, _masks_idx=data._masks.index, _masks_columns=data._masks.columns) masks=data.masks, masks_idx=data.masks.index, masks_columns=data.masks.columns) pzip.write('{}_{}.npz'.format(s, d)) os.remove('{}_{}.npz'.format(s, d)) Loading @@ -87,7 +89,6 @@ def json_numpy_obj_hook(dct): return dct def load(projectname): with ZipFile(projectname, 'r') as pzip: # Read the graph object with pzip.open('graph.json', 'r') as g: Loading @@ -102,6 +103,7 @@ def load(projectname): for d in data['nodes']: n = Node(image_name=d['image_name'], image_path=d['image_path'], node_id=d['id']) n['hash'] = d['hash'] n['downsample_amount'] = d.get('downsample_amount', 1) try: # Load the byte stream for the nested npz file into memory and then unpack n.load_features(BytesIO(pzip.read('{}.npz'.format(d['id'])))) Loading @@ -118,11 +120,10 @@ def load(projectname): edge['weights'] = e['weights'] try: nzf = np.load(BytesIO(pzip.read('{}_{}.npz'.format(e['source'], e['target'])))) edge._masks = pd.DataFrame(nzf['_masks'], index=nzf['_masks_idx'], columns=nzf['_masks_columns']) edge.masks = pd.DataFrame(nzf['masks'], index=nzf['masks_idx'], columns=nzf['masks_columns']) edge.matches = pd.DataFrame(nzf['matches'], index=nzf['matches_idx'], columns=nzf['matches_columns']) except: pass # Add a mock edge cg.edge[e['source']][e['target']] = edge return cg autocnet/vis/graph_view.py +27 −2 Changes for autocnet/vis/graph_view.py: 27 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -4,6 +4,13 @@ import networkx as nx from matplotlib import pyplot as plt import matplotlib from scipy.misc import imresize def downsample(array, amount): return imresize(array, (int(array.shape[0] / amount), int(array.shape[1] / amount)), interp='lanczos') def plot_graph(graph, ax=None, cmap='Spectral', labels=False, font_size=12, clusters=None, **kwargs): """ Loading Loading @@ -48,7 +55,7 @@ def plot_graph(graph, ax=None, cmap='Spectral', labels=False, font_size=12, clus return ax def plot_node(node, ax=None, clean_keys=[], index_mask=None, **kwargs): def plot_node(node, ax=None, clean_keys=[], index_mask=None, downsampling=1, **kwargs): """ Plot the array and keypoints for a given node. Loading Loading @@ -83,6 +90,11 @@ def plot_node(node, ax=None, clean_keys=[], index_mask=None, **kwargs): array = node.get_array(band) if isinstance(downsampling, bool): downsampling = node['downsample_amount'] array = downsample(array, downsampling) ax.set_title(node['image_name']) ax.margins(tight=True) ax.axis('off') Loading Loading @@ -182,7 +194,7 @@ def plot_edge_decomposition(edge, ax=None, clean_keys=[], image_space=100, return ax def plot_edge(edge, ax=None, clean_keys=[], image_space=100, def plot_edge(edge, ax=None, clean_keys=[], image_space=100, downsampling=1, scatter_kwargs={}, line_kwargs={}, image_kwargs={}): """ Plot the correspondences for a given edge Loading @@ -201,6 +213,8 @@ def plot_edge(edge, ax=None, clean_keys=[], image_space=100, image_space : int The number of pixels to insert between the images downsample : bool scatter_kwargs : dict of MatPlotLib arguments to be applied to the scatter plots Loading @@ -227,8 +241,19 @@ def plot_edge(edge, ax=None, clean_keys=[], image_space=100, ax.axis('off') # Image plotting if isinstance(downsampling, bool): downsample_source = edge.source['downsample_amount'] else: downsample_source = downsampling source_array = edge.source.get_array() source_array = downsample(source_array, downsample_source) if isinstance(downsampling, bool): downsample_destin = edge.destination['downsample_amount'] else: downsample_destin = downsampling destination_array = edge.destination.get_array() destination_array = downsample(destination_array, downsample_destin) s_shape = source_array.shape d_shape = destination_array.shape Loading Loading
autocnet/graph/edge.py +86 −59 Changes for autocnet/graph/edge.py: 86 added lines, 59 removed lines. Original line number Diff line number Diff line from functools import wraps import warnings from collections import MutableMapping Loading @@ -8,7 +9,7 @@ from scipy.spatial.distance import cdist import autocnet from autocnet.utils import utils from autocnet.matcher import outlier_detector as od from autocnet.matcher import cpu_outlier_detector as od from autocnet.matcher import suppression_funcs as spf from autocnet.matcher import subpixel as sp from autocnet.transformation import fundamental_matrix as fm Loading @@ -17,6 +18,7 @@ from autocnet.vis.graph_view import plot_edge, plot_node, plot_edge_decompositio from autocnet.cg import cg class Edge(dict, MutableMapping): """ Attributes Loading @@ -40,8 +42,11 @@ class Edge(dict, MutableMapping): self.destination = destination self['homography'] = None self['fundamental_matrix'] = None self.matches = None self.matches = pd.DataFrame() self.masks = pd.DataFrame() self['weights'] = {} self['source_mbr'] = None self['destin_mbr'] = None def __repr__(self): return """ Loading Loading @@ -69,7 +74,7 @@ class Edge(dict, MutableMapping): return eq @property """@property def masks(self): mask_lookup = {'fundamental': 'fundamental_matrix'} if not hasattr(self, '_masks'): Loading @@ -95,10 +100,7 @@ class Edge(dict, MutableMapping): def masks(self, v): column_name = v[0] boolean_mask = v[1] self.masks[column_name] = boolean_mask def decompose_and_match(*args, **kwargs): pass self.masks[column_name] = boolean_mask""" def match(self, k=2, **kwargs): Loading @@ -116,22 +118,44 @@ class Edge(dict, MutableMapping): """ pass def decompose(self): """ Apply coupled decomposition to the images and match identified sub-images """ pass def decompose_and_match(*args, **kwargs): pass def extract_subset(self, *args, **kwargs): self.compute_overlap() # Extract the source minx, maxx, miny, maxy = self['source_mbr'] xystart = (minx, miny) pixels=[minx, miny, maxx-minx, maxy-miny] node = self.source arr = node.geodata.read_array(pixels=pixels) node.extract_features(arr, xystart=xystart, *args, **kwargs) # Extract the destination minx, maxx, miny, maxy = self['destin_mbr'] xystart = (minx, miny) pixels=[minx, miny, maxx-minx, maxy-miny] node = self.destination arr = node.geodata.read_array(pixels=pixels) node.extract_features(arr, xystart=xystart, *args, **kwargs) def symmetry_check(self): if hasattr(self, 'matches'): mask = od.mirroring_test(self.matches) self.masks = ('symmetry', mask) else: raise AttributeError('No matches have been computed for this edge.') self.masks['symmetry'] = od.mirroring_test(self.matches) def ratio_check(self, clean_keys=[], **kwargs): if hasattr(self, 'matches'): def ratio_check(self, clean_keys=[], maskname='ratio', **kwargs): matches, mask = self.clean(clean_keys) distance_mask = od.distance_ratio(matches, **kwargs) self.masks = ('ratio', distance_mask) else: raise AttributeError('No matches have been computed for this edge.') self.masks[maskname] = od.distance_ratio(matches, **kwargs) def compute_fundamental_matrix(self, clean_keys=[], **kwargs): def compute_fundamental_matrix(self, clean_keys=[], maskname='fundamental', **kwargs): """ Estimate the fundamental matrix (F) using the correspondences tagged to this edge. Loading @@ -151,9 +175,6 @@ class Edge(dict, MutableMapping): autocnet.transformation.transformations.FundamentalMatrix """ if not hasattr(self, 'matches'): raise AttributeError('Matches have not been computed for this edge') return matches, mask = self.clean(clean_keys) # TODO: Homogeneous is horribly inefficient here, use Numpy array notation Loading @@ -174,9 +195,38 @@ class Edge(dict, MutableMapping): mask[mask] = fmask # Set the initial state of the fundamental mask in the masks self.masks = ('fundamental', mask) self.masks[maskname] = mask def compute_homography(self, method='ransac', clean_keys=[], pid=None, **kwargs): def compute_fundamental_error(self, clean_keys=[]): """ Given a fundamental matrix, compute the reprojective error between a two sets of keypoints. Parameters ---------- clean_keys : list of string keys to masking arrays (created by calling outlier detection) Returns ------- error : pd.Series of reprojective error indexed to the matches data frame """ if self['fundamental_matrix'] is None: warning.warn('No fundamental matrix has been compute for this edge.' ) matches, masks = self.clean(clean_keys) source_kps = self.source.get_keypoint_coordinates(index=matches['source_idx']) destination_kps = self.destination.get_keypoint_coordinates(index=matches['destination_idx']) error = fm.compute_fundamental_error(self['fundamental_matrix'], source_kps, destination_kps) error = pd.Series(error, index=matches.index) return error def compute_homography(self, method='ransac', clean_keys=[], pid=None, maskname='homography', **kwargs): """ For each edge in the (sub) graph, compute the homography Parameters Loading @@ -195,12 +245,6 @@ class Edge(dict, MutableMapping): mask : ndarray Boolean array of the outliers """ if hasattr(self, 'matches'): matches = self.matches else: raise AttributeError('Matches have not been computed for this edge') matches, mask = self.clean(clean_keys) s_keypoints = self.source.get_keypoint_coordinates(index=matches['source_idx']) Loading @@ -210,7 +254,7 @@ class Edge(dict, MutableMapping): # Convert the truncated RANSAC mask back into a full length mask mask[mask] = hmask self.masks = ('ransac', mask) self.masks['homography'] = mask def subpixel_register(self, clean_keys=[], threshold=0.8, template_size=19, search_size=53, max_x_shift=1.0, Loading Loading @@ -288,19 +332,18 @@ class Edge(dict, MutableMapping): threshold_mask = self.matches['correlation'] >= threshold # Compute the mask for the point shifts that are too large query_string = 'x_offset <= -{0} or x_offset >= {0} or y_offset <= -{1} or y_offset >= {1}'.format(max_x_shift, max_y_shift) query_string = 'x_offset <= -{0} or x_offset >= {0} or y_offset <= -{1} or y_offset >= {1}'.format(max_x_shift,max_y_shift) sp_shift_outliers = self.matches.query(query_string) shift_mask = pd.Series(True, index=self.matches.index) shift_mask.loc[sp_shift_outliers.index] = False # Generate the composite mask and write the masks to the mask data structure mask = threshold_mask & shift_mask self.masks = ('shift', shift_mask) self.masks = ('threshold', threshold_mask) self.masks = ('subpixel', mask) self.masks['shift'] = shift_mask self.masks['threshold'] = threshold_mask self.masks['subpixel'] = mask def suppress(self, suppression_func=spf.correlation, clean_keys=[], **kwargs): def suppress(self, suppression_func=spf.correlation, clean_keys=[], maskname='suppression', **kwargs): """ Apply a disc based suppression algorithm to get a good spatial distribution of high quality points, where the user defines some Loading Loading @@ -334,7 +377,7 @@ class Edge(dict, MutableMapping): smask, k = od.spatial_suppression(merged, domain, **kwargs) mask[mask] = smask self.masks = ('suppression', mask) self.masks[maskname] = mask def plot_source(self, ax=None, clean_keys=[], **kwargs): # pragma: no cover matches, mask = self.clean(clean_keys=clean_keys) Loading Loading @@ -417,9 +460,6 @@ class Edge(dict, MutableMapping): returns the overlap area covered by the keypoints """ if self.matches is None: raise AttributeError('Edge needs to have features extracted and matched') return matches, mask = self.clean(clean_keys) source_array = self.source.get_keypoint_coordinates(index=matches['source_idx']).values Loading Loading @@ -458,24 +498,11 @@ class Edge(dict, MutableMapping): voronoi = cg.vor(self, clean_keys, **kwargs) self.matches = pd.concat([self.matches, voronoi[1]['vor_weights']], axis=1) def decompose(self, maxiterations=3): """ Apply coupled decomposition to the images and match identified sub-images Parameters ---------- maxiterations : int The number of iterations. Appropriate values: | Number of megapixels | k | |----------------------|---| | m < 10 |1-2| | 10 < m < 30 | 3 | | 30 < m < 100 | 4 | | 100 < m < 1000 | 5 | | m > 1000 | 6 | def compute_overlap(self, **kwargs): """ pass Estimate a source and destination minimum bounding rectangle, in pixel space """ self.overlap_latlon_coords, self["source_mbr"], self["destin_mbr"] = self.source.geodata.compute_overlap(self.destination.geodata, **kwargs)
autocnet/graph/tests/test_edge.py +4 −6 Changes for autocnet/graph/tests/test_edge.py: 4 added lines, 6 removed lines. Original line number Diff line number Diff line import unittest from unittest.mock import Mock from unittest.mock import MagicMock from unittest.mock import Mock, MagicMock import ogr import pandas as pd Loading Loading @@ -47,8 +46,7 @@ class TestEdge(unittest.TestCase): def test_compute_fundamental_matrix(self): with self.assertRaises(AttributeError): self.edge.compute_fundamental_matrix() pass def test_edge_overlap(self): e = edge.Edge() Loading Loading @@ -86,7 +84,7 @@ class TestEdge(unittest.TestCase): [0, 3, 1, 3], [0, 4, 1, 4]] matches_df = pd.DataFrame(data = keypoint_matches, columns = ['source_image', 'source_idx', 'destination_image', 'destination_idx']) matches_df = pd.DataFrame(keypoint_matches, columns = ['source_image', 'source_idx', 'destination_image', 'destination_idx']) e = edge.Edge() source_node = MagicMock(spec = node.Node()) destination_node = MagicMock(spec = node.Node()) Loading Loading @@ -126,7 +124,7 @@ class TestEdge(unittest.TestCase): e.matches = matches_df self.assertRaises(AttributeError, cg.edge[0][1].coverage) #self.assertRaises(AttributeError, cg.edge[0][1].coverage) self.assertEqual(e.coverage(), 0.3) def test_voronoi_transform(self): Loading
autocnet/io/network.py +10 −9 Changes for autocnet/io/network.py: 10 added lines, 9 removed lines. Original line number Diff line number Diff line Loading @@ -51,12 +51,13 @@ def save(network, projectname): # Write the array node_attributes to hdf for n, data in network.nodes_iter(data=True): if data.descriptors is not None: grp = data['node_id'] np.savez('{}.npz'.format(data['node_id']), descriptors=data.descriptors, keypoints=data._keypoints, keypoints_idx=data._keypoints.index, keypoints_columns=data._keypoints.columns) keypoints=data.keypoints, keypoints_idx=data.keypoints.index, keypoints_columns=data.keypoints.columns) pzip.write('{}.npz'.format(data['node_id'])) os.remove('{}.npz'.format(data['node_id'])) Loading @@ -64,14 +65,15 @@ def save(network, projectname): for s, d, data in network.edges_iter(data=True): if s > d: s, d = d, s if data.matches is not None: grp = str((s,d)) np.savez('{}_{}.npz'.format(s, d), matches=data.matches, matches_idx=data.matches.index, matches_columns=data.matches.columns, _masks=data._masks, _masks_idx=data._masks.index, _masks_columns=data._masks.columns) masks=data.masks, masks_idx=data.masks.index, masks_columns=data.masks.columns) pzip.write('{}_{}.npz'.format(s, d)) os.remove('{}_{}.npz'.format(s, d)) Loading @@ -87,7 +89,6 @@ def json_numpy_obj_hook(dct): return dct def load(projectname): with ZipFile(projectname, 'r') as pzip: # Read the graph object with pzip.open('graph.json', 'r') as g: Loading @@ -102,6 +103,7 @@ def load(projectname): for d in data['nodes']: n = Node(image_name=d['image_name'], image_path=d['image_path'], node_id=d['id']) n['hash'] = d['hash'] n['downsample_amount'] = d.get('downsample_amount', 1) try: # Load the byte stream for the nested npz file into memory and then unpack n.load_features(BytesIO(pzip.read('{}.npz'.format(d['id'])))) Loading @@ -118,11 +120,10 @@ def load(projectname): edge['weights'] = e['weights'] try: nzf = np.load(BytesIO(pzip.read('{}_{}.npz'.format(e['source'], e['target'])))) edge._masks = pd.DataFrame(nzf['_masks'], index=nzf['_masks_idx'], columns=nzf['_masks_columns']) edge.masks = pd.DataFrame(nzf['masks'], index=nzf['masks_idx'], columns=nzf['masks_columns']) edge.matches = pd.DataFrame(nzf['matches'], index=nzf['matches_idx'], columns=nzf['matches_columns']) except: pass # Add a mock edge cg.edge[e['source']][e['target']] = edge return cg
autocnet/vis/graph_view.py +27 −2 Changes for autocnet/vis/graph_view.py: 27 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -4,6 +4,13 @@ import networkx as nx from matplotlib import pyplot as plt import matplotlib from scipy.misc import imresize def downsample(array, amount): return imresize(array, (int(array.shape[0] / amount), int(array.shape[1] / amount)), interp='lanczos') def plot_graph(graph, ax=None, cmap='Spectral', labels=False, font_size=12, clusters=None, **kwargs): """ Loading Loading @@ -48,7 +55,7 @@ def plot_graph(graph, ax=None, cmap='Spectral', labels=False, font_size=12, clus return ax def plot_node(node, ax=None, clean_keys=[], index_mask=None, **kwargs): def plot_node(node, ax=None, clean_keys=[], index_mask=None, downsampling=1, **kwargs): """ Plot the array and keypoints for a given node. Loading Loading @@ -83,6 +90,11 @@ def plot_node(node, ax=None, clean_keys=[], index_mask=None, **kwargs): array = node.get_array(band) if isinstance(downsampling, bool): downsampling = node['downsample_amount'] array = downsample(array, downsampling) ax.set_title(node['image_name']) ax.margins(tight=True) ax.axis('off') Loading Loading @@ -182,7 +194,7 @@ def plot_edge_decomposition(edge, ax=None, clean_keys=[], image_space=100, return ax def plot_edge(edge, ax=None, clean_keys=[], image_space=100, def plot_edge(edge, ax=None, clean_keys=[], image_space=100, downsampling=1, scatter_kwargs={}, line_kwargs={}, image_kwargs={}): """ Plot the correspondences for a given edge Loading @@ -201,6 +213,8 @@ def plot_edge(edge, ax=None, clean_keys=[], image_space=100, image_space : int The number of pixels to insert between the images downsample : bool scatter_kwargs : dict of MatPlotLib arguments to be applied to the scatter plots Loading @@ -227,8 +241,19 @@ def plot_edge(edge, ax=None, clean_keys=[], image_space=100, ax.axis('off') # Image plotting if isinstance(downsampling, bool): downsample_source = edge.source['downsample_amount'] else: downsample_source = downsampling source_array = edge.source.get_array() source_array = downsample(source_array, downsample_source) if isinstance(downsampling, bool): downsample_destin = edge.destination['downsample_amount'] else: downsample_destin = downsampling destination_array = edge.destination.get_array() destination_array = downsample(destination_array, downsample_destin) s_shape = source_array.shape d_shape = destination_array.shape Loading