Loading autocnet/fileio/io_gdal.py +2 −1 Original line number Diff line number Diff line Loading @@ -392,7 +392,8 @@ class GeoDataset(object): lon, lat, _ = self.coordinate_transformation.TransformPoint(x, y) except: lat = lon = None warnings.warn('Unable to compute pixel to geographic conversion without projection information.') warnings.warn('Unable to compute pixel to geographic conversion without ' 'projection information for {}'.format(self.base_name)) return lat, lon Loading autocnet/fileio/io_utils.py +9 −0 Original line number Diff line number Diff line Loading @@ -22,3 +22,12 @@ def delete_dir(dir): Remove a directory """ shutil.rmtree(dir) def file_to_list(file): with open(file, 'r') as f: file_list = f.readlines() file_list = map(str.rstrip, file_list) file_list = filter(None, file_list) return file_list autocnet/graph/edge.py +7 −7 Original line number Diff line number Diff line Loading @@ -228,11 +228,11 @@ class Edge(dict, MutableMapping): # Grab the full images, or handles if tiled is True: s_img = self.source.handle d_img = self.destination.handle s_img = self.source.geodata d_img = self.destination.geodata else: s_img = self.source.handle.read_array() d_img = self.destination.handle.read_array() s_img = self.source.geodata.read_array() d_img = self.destination.geodata.read_array() source_image = (matches.iloc[0]['source_image']) Loading Loading @@ -292,7 +292,7 @@ class Edge(dict, MutableMapping): raise AttributeError('This edge does not yet have any matches computed.') matches, mask = self._clean(clean_keys) domain = self.source.handle.raster_size domain = self.source.geodata.raster_size # Massage the dataframe into the correct structure coords = self.source.get_keypoint_coordinates() Loading Loading @@ -357,8 +357,8 @@ class Edge(dict, MutableMapping): The estimated area """ source_geom = self.source.handle.pixel_polygon destination_geom = self.destination.handle.pixel_polygon source_geom = self.source.geodata.pixel_polygon destination_geom = self.destination.geodata.pixel_polygon # Project using the homography vertices_to_project = destination_geom.vertices Loading autocnet/graph/network.py +33 −33 Original line number Diff line number Diff line import itertools import math import os import warnings import dill as pickle import networkx as nx import numpy as np import pandas as pd from autocnet.fileio.io_gdal import GeoDataset from autocnet.fileio import io_hdf from autocnet.control.control import C from autocnet.fileio import io_hdf from autocnet.fileio import io_json from autocnet.matcher.matcher import FlannMatcher import autocnet.matcher.suppression_funcs as spf from autocnet.fileio import io_utils 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.graph import markov_cluster from autocnet.matcher.matcher import FlannMatcher from autocnet.vis.graph_view import plot_graph Loading Loading @@ -44,7 +46,6 @@ class CandidateGraph(nx.Graph): self.node_counter = 0 node_labels = {} self.node_name_map = {} self.graph_masks = pd.DataFrame() for node_name in self.nodes(): image_name = os.path.basename(node_name) Loading Loading @@ -107,10 +108,8 @@ class CandidateGraph(nx.Graph): : object A Network graph object """ if not isinstance(filelist, list): with open(filelist, 'r') as f: filelist = f.readlines() filelist = map(str.rstrip, filelist) if isinstance(filelist, str): filelist = io_utils.file_to_list(filelist) # TODO: Reject unsupported file formats + work with more file formats if basepath: Loading @@ -120,22 +119,29 @@ class CandidateGraph(nx.Graph): # This is brute force for now, could swap to an RTree at some point. adjacency_dict = {} valid_datasets = [] for i, j in itertools.permutations(datasets,2): if not i.file_name in adjacency_dict.keys(): for i in datasets: adjacency_dict[i.file_name] = [] if not j.file_name in adjacency_dict.keys(): adjacency_dict[j.file_name] = [] fp = i.footprint if fp and fp.IsValid(): valid_datasets.append(i) else: warnings.warn('Missing or invalid geospatial data for {}'.format(i.base_name)) # Grab the footprints and test for intersection for i, j in itertools.permutations(valid_datasets, 2): i_fp = i.footprint j_fp = j.footprint try: if i_fp.Intersects(j_fp): adjacency_dict[i.file_name].append(j.file_name) adjacency_dict[j.file_name].append(i.file_name) except: # no geospatial information embedded in the images pass except: warnings.warn('Failed to calculated intersection between {} and {}'.format(i, j)) return cls(adjacency_dict) Loading Loading @@ -297,6 +303,11 @@ class CandidateGraph(nx.Graph): descriptors = node.descriptors # Load the neighbors of the current node into the FLANN matcher neighbors = self.neighbors(i) # if node has no neighbors, skip if not neighbors: continue for n in neighbors: neighbor_descriptors = self.node[n].descriptors self._fl.add(neighbor_descriptors, n) Loading Loading @@ -362,7 +373,7 @@ class CandidateGraph(nx.Graph): """ _, self.clusters = func(self, *args, **kwargs) def apply_func_to_edges(self, function, *args, graph_mask_keys=[], **kwargs): def apply_func_to_edges(self, function, *args, **kwargs): """ Iterates over edges using an optional mask and and applies the given function. If func is not an attribute of Edge, raises AttributeError Loading @@ -373,20 +384,12 @@ class CandidateGraph(nx.Graph): graph_mask_keys : list of keys in graph_masks """ if graph_mask_keys: merged_graph_mask = self.graph_masks[graph_mask_keys].all(axis=1) edges_to_iter = merged_graph_mask[merged_graph_mask].index else: edges_to_iter = self.edges() if not isinstance(function, str): function = function.__name__ for s, d in edges_to_iter: curr_edge = self.get_edge_data(s, d) for s, d, edge in self.edges_iter(data=True): try: func = getattr(curr_edge, function) func = getattr(edge, function) except: raise AttributeError(function, ' is not an attribute of Edge') else: Loading @@ -403,11 +406,8 @@ class CandidateGraph(nx.Graph): boolean mask for edges in the minimum spanning tree """ graph_mask = pd.Series(False, index=self.edges()) self.graph_masks['mst'] = graph_mask mst = nx.minimum_spanning_tree(self) self.graph_masks['mst'][mst.edges()] = True return self.create_edge_subgraph(mst.edges()) def to_filelist(self): """ Loading autocnet/graph/node.py +8 −8 Original line number Diff line number Diff line Loading @@ -26,7 +26,7 @@ class Node(dict, MutableMapping): Name of the image, with extension image_path : str Relative or absolute PATH to the image handle : object geodata : object File handle to the object keypoints : dataframe With columns, x, y, and response Loading Loading @@ -59,10 +59,10 @@ class Node(dict, MutableMapping): """.format(self.node_id, self.image_name, self.image_path, self.nkeypoints, self.masks, self.__class__) @property def handle(self): if not getattr(self, '_handle', None): self._handle = GeoDataset(self.image_path) return self._handle def geodata(self): if not getattr(self, '_geodata', None): self._geodata = GeoDataset(self.image_path) return self._geodata @property def nkeypoints(self): Loading Loading @@ -123,7 +123,7 @@ class Node(dict, MutableMapping): The band to read, default 1 """ array = self.handle.read_array(band=band) array = self.geodata.read_array(band=band) return bytescale(array) def get_keypoints(self, index=None): Loading Loading @@ -272,7 +272,7 @@ class Node(dict, MutableMapping): if not hasattr(self, '_keypoints'): raise AttributeError('No keypoints extracted for this node.') domain = self.handle.raster_size domain = self.geodata.raster_size self._keypoints['strength'] = self._keypoints.apply(func, axis=1) if not hasattr(self, 'suppression'): Loading @@ -297,7 +297,7 @@ class Node(dict, MutableMapping): ratio : float The ratio of convex hull area to total area. """ ideal_area = self.handle.pixel_area ideal_area = self.geodata.pixel_area if not hasattr(self, '_keypoints'): raise AttributeError('Keypoints must be extracted already, they have not been.') Loading Loading
autocnet/fileio/io_gdal.py +2 −1 Original line number Diff line number Diff line Loading @@ -392,7 +392,8 @@ class GeoDataset(object): lon, lat, _ = self.coordinate_transformation.TransformPoint(x, y) except: lat = lon = None warnings.warn('Unable to compute pixel to geographic conversion without projection information.') warnings.warn('Unable to compute pixel to geographic conversion without ' 'projection information for {}'.format(self.base_name)) return lat, lon Loading
autocnet/fileio/io_utils.py +9 −0 Original line number Diff line number Diff line Loading @@ -22,3 +22,12 @@ def delete_dir(dir): Remove a directory """ shutil.rmtree(dir) def file_to_list(file): with open(file, 'r') as f: file_list = f.readlines() file_list = map(str.rstrip, file_list) file_list = filter(None, file_list) return file_list
autocnet/graph/edge.py +7 −7 Original line number Diff line number Diff line Loading @@ -228,11 +228,11 @@ class Edge(dict, MutableMapping): # Grab the full images, or handles if tiled is True: s_img = self.source.handle d_img = self.destination.handle s_img = self.source.geodata d_img = self.destination.geodata else: s_img = self.source.handle.read_array() d_img = self.destination.handle.read_array() s_img = self.source.geodata.read_array() d_img = self.destination.geodata.read_array() source_image = (matches.iloc[0]['source_image']) Loading Loading @@ -292,7 +292,7 @@ class Edge(dict, MutableMapping): raise AttributeError('This edge does not yet have any matches computed.') matches, mask = self._clean(clean_keys) domain = self.source.handle.raster_size domain = self.source.geodata.raster_size # Massage the dataframe into the correct structure coords = self.source.get_keypoint_coordinates() Loading Loading @@ -357,8 +357,8 @@ class Edge(dict, MutableMapping): The estimated area """ source_geom = self.source.handle.pixel_polygon destination_geom = self.destination.handle.pixel_polygon source_geom = self.source.geodata.pixel_polygon destination_geom = self.destination.geodata.pixel_polygon # Project using the homography vertices_to_project = destination_geom.vertices Loading
autocnet/graph/network.py +33 −33 Original line number Diff line number Diff line import itertools import math import os import warnings import dill as pickle import networkx as nx import numpy as np import pandas as pd from autocnet.fileio.io_gdal import GeoDataset from autocnet.fileio import io_hdf from autocnet.control.control import C from autocnet.fileio import io_hdf from autocnet.fileio import io_json from autocnet.matcher.matcher import FlannMatcher import autocnet.matcher.suppression_funcs as spf from autocnet.fileio import io_utils 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.graph import markov_cluster from autocnet.matcher.matcher import FlannMatcher from autocnet.vis.graph_view import plot_graph Loading Loading @@ -44,7 +46,6 @@ class CandidateGraph(nx.Graph): self.node_counter = 0 node_labels = {} self.node_name_map = {} self.graph_masks = pd.DataFrame() for node_name in self.nodes(): image_name = os.path.basename(node_name) Loading Loading @@ -107,10 +108,8 @@ class CandidateGraph(nx.Graph): : object A Network graph object """ if not isinstance(filelist, list): with open(filelist, 'r') as f: filelist = f.readlines() filelist = map(str.rstrip, filelist) if isinstance(filelist, str): filelist = io_utils.file_to_list(filelist) # TODO: Reject unsupported file formats + work with more file formats if basepath: Loading @@ -120,22 +119,29 @@ class CandidateGraph(nx.Graph): # This is brute force for now, could swap to an RTree at some point. adjacency_dict = {} valid_datasets = [] for i, j in itertools.permutations(datasets,2): if not i.file_name in adjacency_dict.keys(): for i in datasets: adjacency_dict[i.file_name] = [] if not j.file_name in adjacency_dict.keys(): adjacency_dict[j.file_name] = [] fp = i.footprint if fp and fp.IsValid(): valid_datasets.append(i) else: warnings.warn('Missing or invalid geospatial data for {}'.format(i.base_name)) # Grab the footprints and test for intersection for i, j in itertools.permutations(valid_datasets, 2): i_fp = i.footprint j_fp = j.footprint try: if i_fp.Intersects(j_fp): adjacency_dict[i.file_name].append(j.file_name) adjacency_dict[j.file_name].append(i.file_name) except: # no geospatial information embedded in the images pass except: warnings.warn('Failed to calculated intersection between {} and {}'.format(i, j)) return cls(adjacency_dict) Loading Loading @@ -297,6 +303,11 @@ class CandidateGraph(nx.Graph): descriptors = node.descriptors # Load the neighbors of the current node into the FLANN matcher neighbors = self.neighbors(i) # if node has no neighbors, skip if not neighbors: continue for n in neighbors: neighbor_descriptors = self.node[n].descriptors self._fl.add(neighbor_descriptors, n) Loading Loading @@ -362,7 +373,7 @@ class CandidateGraph(nx.Graph): """ _, self.clusters = func(self, *args, **kwargs) def apply_func_to_edges(self, function, *args, graph_mask_keys=[], **kwargs): def apply_func_to_edges(self, function, *args, **kwargs): """ Iterates over edges using an optional mask and and applies the given function. If func is not an attribute of Edge, raises AttributeError Loading @@ -373,20 +384,12 @@ class CandidateGraph(nx.Graph): graph_mask_keys : list of keys in graph_masks """ if graph_mask_keys: merged_graph_mask = self.graph_masks[graph_mask_keys].all(axis=1) edges_to_iter = merged_graph_mask[merged_graph_mask].index else: edges_to_iter = self.edges() if not isinstance(function, str): function = function.__name__ for s, d in edges_to_iter: curr_edge = self.get_edge_data(s, d) for s, d, edge in self.edges_iter(data=True): try: func = getattr(curr_edge, function) func = getattr(edge, function) except: raise AttributeError(function, ' is not an attribute of Edge') else: Loading @@ -403,11 +406,8 @@ class CandidateGraph(nx.Graph): boolean mask for edges in the minimum spanning tree """ graph_mask = pd.Series(False, index=self.edges()) self.graph_masks['mst'] = graph_mask mst = nx.minimum_spanning_tree(self) self.graph_masks['mst'][mst.edges()] = True return self.create_edge_subgraph(mst.edges()) def to_filelist(self): """ Loading
autocnet/graph/node.py +8 −8 Original line number Diff line number Diff line Loading @@ -26,7 +26,7 @@ class Node(dict, MutableMapping): Name of the image, with extension image_path : str Relative or absolute PATH to the image handle : object geodata : object File handle to the object keypoints : dataframe With columns, x, y, and response Loading Loading @@ -59,10 +59,10 @@ class Node(dict, MutableMapping): """.format(self.node_id, self.image_name, self.image_path, self.nkeypoints, self.masks, self.__class__) @property def handle(self): if not getattr(self, '_handle', None): self._handle = GeoDataset(self.image_path) return self._handle def geodata(self): if not getattr(self, '_geodata', None): self._geodata = GeoDataset(self.image_path) return self._geodata @property def nkeypoints(self): Loading Loading @@ -123,7 +123,7 @@ class Node(dict, MutableMapping): The band to read, default 1 """ array = self.handle.read_array(band=band) array = self.geodata.read_array(band=band) return bytescale(array) def get_keypoints(self, index=None): Loading Loading @@ -272,7 +272,7 @@ class Node(dict, MutableMapping): if not hasattr(self, '_keypoints'): raise AttributeError('No keypoints extracted for this node.') domain = self.handle.raster_size domain = self.geodata.raster_size self._keypoints['strength'] = self._keypoints.apply(func, axis=1) if not hasattr(self, 'suppression'): Loading @@ -297,7 +297,7 @@ class Node(dict, MutableMapping): ratio : float The ratio of convex hull area to total area. """ ideal_area = self.handle.pixel_area ideal_area = self.geodata.pixel_area if not hasattr(self, '_keypoints'): raise AttributeError('Keypoints must be extracted already, they have not been.') Loading