Loading .travis.yml +7 −5 Original line number Diff line number Diff line Loading @@ -27,13 +27,15 @@ install: - conda info -a # Create a virtual env and install dependencies - conda create -y -q -n test-env python=$TRAVIS_PYTHON_VERSION nose gdal numpy pillow scipy pandas networkx scikit-image - conda create -y -q -n test-env python=$TRAVIS_PYTHON_VERSION nose numpy pillow scipy pandas networkx scikit-image # Activate the env - source activate test-env # Install the non-conda packages if required, requirements.txt duplicates are ignored - conda install -c https://conda.anaconda.org/jlaura opencv3=3.0.0 - conda install -c https://conda.anaconda.org/osgeo gdal - conda install -c osgeo proj4 - conda upgrade numpy - pip install -r requirements.txt - pip install coverage - pip install coveralls Loading @@ -41,6 +43,9 @@ install: script: - nosetests --with-coverage --cover-package=autocnet after_success: - coveralls notifications: email: recipients: Loading @@ -50,6 +55,3 @@ notifications: - jwbacker@usgs.gov on_success: always on_failure: always No newline at end of file after_success: - coveralls autocnet/fileio/io_gdal.py +4 −2 Original line number Diff line number Diff line import os import numpy as np from osgeo import gdal from osgeo import osr import gdal import osr from autocnet.fileio import extract_metadata gdal.UseExceptions() NP2GDAL_CONVERSION = { "uint8": 1, "int8": 1, Loading autocnet/graph/network.py +73 −18 Original line number Diff line number Diff line Loading @@ -258,7 +258,6 @@ class CandidateGraph(nx.Graph): transformation_matrix, ransac_mask = od.compute_homography(s_coords, d_coords) ransac_mask = ransac_mask.ravel() # Convert the truncated RANSAC mask back into a full length mask if clean_keys: Loading @@ -266,34 +265,78 @@ class CandidateGraph(nx.Graph): else: mask = ransac_mask # Compute the error in the homography s_trans = np.hstack((s_coords[::-1],np.ones(s_coords.shape[0]).reshape(-1,1))).T s_trans = np.dot(transformation_matrix, s_trans).T #Normalize the error for i in range(3): s_trans[i] /= s_trans[2] d_trans = np.hstack((d_coords[::-1], np.ones(d_coords.shape[0]).reshape(-1,1))) attributes['homography_error'] = np.sqrt(np.sum((d_trans - s_trans)**2, axis=1)) attributes['homography'] = transformation_matrix attributes['ransac'] = mask def compute_subpixel_offsets(self): def compute_subpixel_offsets(self, clean_keys=[], threshold=0.8, upsampling=10): """ For the entire graph, compute the subpixel offsets using pattern-matching and add the result as an attribute to each edge of the graph. Returns ------- subpixel_offsets : ndarray A numpy array containing all the subpixel offsets for the entire graph. Parameters ---------- clean_keys : list of string keys to masking arrays (created by calling outlier detection) threshold : float On the range [-1, 1]. Values less than or equal to this threshold are masked and can be considered outliers """ subpixel_offsets = [] for source, destination, attributes in self.edges_iter(data=True): #for each edge matches = attributes['matches'] #grab the matches for source, destination, attributes in self.edges_iter(data=True): matches = attributes['matches'] full_offsets = np.zeros((len(matches), 3)) # Build up a composite mask from all of the user specified masks if clean_keys: mask = np.prod([attributes[i] for i in clean_keys], axis=0, dtype=np.bool) matches = matches[mask] full_mask = np.where(mask == True) src_image = self.node[source]['image'] dest_image = self.node[destination]['image'] edge_offsets = [] for i, (idx, row) in enumerate(matches.iterrows()): #for each edge, calculate this for each keypoint pair # Preallocate the numpy array to avoid appending and type conversion edge_offsets = np.empty((len(matches),3)) # for each edge, calculate this for each keypoint pair for i, (idx, row) in enumerate(matches.iterrows()): s_idx = int(row['source_idx']) d_idx = int(row['destination_idx']) src_keypoint = self.node[source]['keypoints'][s_idx] dest_keypoint = self.node[destination]['keypoints'][d_idx] edge_offsets.append(sp.subpixel_offset(src_keypoint, dest_keypoint, src_image, dest_image)) attributes['subpixel_offsets'] = np.array(edge_offsets) subpixel_offsets.append(np.array(edge_offsets)) return subpixel_offsets # Compute the subpixel offset edge_offsets[i] = sp.subpixel_offset(src_keypoint, dest_keypoint, src_image, dest_image, upsampling=upsampling) # Compute the mask for correlations less than the threshold threshold_mask = edge_offsets[edge_offsets[:,-1] >= threshold] # Convert the truncated mask back into a full length mask if clean_keys: mask[full_mask] = threshold_mask full_offsets[full_mask] = edge_offsets else: mask = threshold_mask attributes['subpixel_offsets'] = pd.DataFrame(full_offsets, columns=['x_offset', 'y_offset', 'correlation']) attributes['subpixel'] = mask def to_cnet(self, clean_keys=[]): """ Loading Loading @@ -349,12 +392,15 @@ class CandidateGraph(nx.Graph): mask = np.prod([attributes[i] for i in clean_keys], axis=0, dtype=np.bool) matches = matches[mask] if 'subpixel' in clean_keys: offsets = attributes['subpixel_offsets'][attributes['subpixel']] print(offsets) kp1 = self.node[source]['keypoints'] kp2 = self.node[destination]['keypoints'] pt_idx = 0 values = [] for idx, row in matches.iterrows(): for i, (idx, row) in enumerate(matches.iterrows()): # Composite matching key (node_id, point_id) m1 = (source, int(row['source_idx'])) m2 = (destination, int(row['destination_idx'])) Loading @@ -365,8 +411,17 @@ class CandidateGraph(nx.Graph): pt_idx, source]) values.append([kp2[m2[1]].pt[0], kp2[m2[1]].pt[1], kp2x = kp2[m2[1]].pt[0] kp2y = kp2[m2[1]].pt[1] if 'subpixel' in clean_keys: print(idx) print(kp2x, kp2y) kp2x += offsets['x_offset'].values[i] kp2y += offsets['y_offset'].values[i] print(kp2x, kp2y) values.append([kp2x, kp2y, m2, pt_idx, destination]) Loading autocnet/graph/tests/test_network.py +9 −7 Original line number Diff line number Diff line Loading @@ -13,8 +13,9 @@ from .. import network class TestCandidateGraph(unittest.TestCase): def setUp(self): self.graph = network.CandidateGraph.from_adjacency(get_path('adjacency.json')) @classmethod def setUpClass(cls): cls.graph = network.CandidateGraph.from_adjacency(get_path('adjacency.json')) def test_get_name(self): node_number = self.graph.node_name_map['AS15-M-0297_SML.png'] Loading @@ -28,6 +29,10 @@ class TestCandidateGraph(unittest.TestCase): def test_to_json_file(self): self.graph.to_json_file('test_graph_to_json.json') self.assertTrue(os.path.exists('test_graph_to_json.json')) try: os.remove('test_graph_to_json.json') except: pass def test_extract_features(self): # also tests get_geodataset() and get_keypoints Loading @@ -42,7 +47,4 @@ class TestCandidateGraph(unittest.TestCase): self.assertEquals(self.graph.get_keypoints(node_number), node['keypoints']) def tearDown(self): try: os.remove('test_graph_to_json.json') except: pass autocnet/matcher/outlier_detector.py +2 −0 Original line number Diff line number Diff line Loading @@ -148,3 +148,5 @@ def compute_homography(kp1, kp2, outlier_algorithm=cv2.RANSAC, reproj_threshold= reproj_threshold) mask = mask.astype(bool) return transformation_matrix, mask Loading
.travis.yml +7 −5 Original line number Diff line number Diff line Loading @@ -27,13 +27,15 @@ install: - conda info -a # Create a virtual env and install dependencies - conda create -y -q -n test-env python=$TRAVIS_PYTHON_VERSION nose gdal numpy pillow scipy pandas networkx scikit-image - conda create -y -q -n test-env python=$TRAVIS_PYTHON_VERSION nose numpy pillow scipy pandas networkx scikit-image # Activate the env - source activate test-env # Install the non-conda packages if required, requirements.txt duplicates are ignored - conda install -c https://conda.anaconda.org/jlaura opencv3=3.0.0 - conda install -c https://conda.anaconda.org/osgeo gdal - conda install -c osgeo proj4 - conda upgrade numpy - pip install -r requirements.txt - pip install coverage - pip install coveralls Loading @@ -41,6 +43,9 @@ install: script: - nosetests --with-coverage --cover-package=autocnet after_success: - coveralls notifications: email: recipients: Loading @@ -50,6 +55,3 @@ notifications: - jwbacker@usgs.gov on_success: always on_failure: always No newline at end of file after_success: - coveralls
autocnet/fileio/io_gdal.py +4 −2 Original line number Diff line number Diff line import os import numpy as np from osgeo import gdal from osgeo import osr import gdal import osr from autocnet.fileio import extract_metadata gdal.UseExceptions() NP2GDAL_CONVERSION = { "uint8": 1, "int8": 1, Loading
autocnet/graph/network.py +73 −18 Original line number Diff line number Diff line Loading @@ -258,7 +258,6 @@ class CandidateGraph(nx.Graph): transformation_matrix, ransac_mask = od.compute_homography(s_coords, d_coords) ransac_mask = ransac_mask.ravel() # Convert the truncated RANSAC mask back into a full length mask if clean_keys: Loading @@ -266,34 +265,78 @@ class CandidateGraph(nx.Graph): else: mask = ransac_mask # Compute the error in the homography s_trans = np.hstack((s_coords[::-1],np.ones(s_coords.shape[0]).reshape(-1,1))).T s_trans = np.dot(transformation_matrix, s_trans).T #Normalize the error for i in range(3): s_trans[i] /= s_trans[2] d_trans = np.hstack((d_coords[::-1], np.ones(d_coords.shape[0]).reshape(-1,1))) attributes['homography_error'] = np.sqrt(np.sum((d_trans - s_trans)**2, axis=1)) attributes['homography'] = transformation_matrix attributes['ransac'] = mask def compute_subpixel_offsets(self): def compute_subpixel_offsets(self, clean_keys=[], threshold=0.8, upsampling=10): """ For the entire graph, compute the subpixel offsets using pattern-matching and add the result as an attribute to each edge of the graph. Returns ------- subpixel_offsets : ndarray A numpy array containing all the subpixel offsets for the entire graph. Parameters ---------- clean_keys : list of string keys to masking arrays (created by calling outlier detection) threshold : float On the range [-1, 1]. Values less than or equal to this threshold are masked and can be considered outliers """ subpixel_offsets = [] for source, destination, attributes in self.edges_iter(data=True): #for each edge matches = attributes['matches'] #grab the matches for source, destination, attributes in self.edges_iter(data=True): matches = attributes['matches'] full_offsets = np.zeros((len(matches), 3)) # Build up a composite mask from all of the user specified masks if clean_keys: mask = np.prod([attributes[i] for i in clean_keys], axis=0, dtype=np.bool) matches = matches[mask] full_mask = np.where(mask == True) src_image = self.node[source]['image'] dest_image = self.node[destination]['image'] edge_offsets = [] for i, (idx, row) in enumerate(matches.iterrows()): #for each edge, calculate this for each keypoint pair # Preallocate the numpy array to avoid appending and type conversion edge_offsets = np.empty((len(matches),3)) # for each edge, calculate this for each keypoint pair for i, (idx, row) in enumerate(matches.iterrows()): s_idx = int(row['source_idx']) d_idx = int(row['destination_idx']) src_keypoint = self.node[source]['keypoints'][s_idx] dest_keypoint = self.node[destination]['keypoints'][d_idx] edge_offsets.append(sp.subpixel_offset(src_keypoint, dest_keypoint, src_image, dest_image)) attributes['subpixel_offsets'] = np.array(edge_offsets) subpixel_offsets.append(np.array(edge_offsets)) return subpixel_offsets # Compute the subpixel offset edge_offsets[i] = sp.subpixel_offset(src_keypoint, dest_keypoint, src_image, dest_image, upsampling=upsampling) # Compute the mask for correlations less than the threshold threshold_mask = edge_offsets[edge_offsets[:,-1] >= threshold] # Convert the truncated mask back into a full length mask if clean_keys: mask[full_mask] = threshold_mask full_offsets[full_mask] = edge_offsets else: mask = threshold_mask attributes['subpixel_offsets'] = pd.DataFrame(full_offsets, columns=['x_offset', 'y_offset', 'correlation']) attributes['subpixel'] = mask def to_cnet(self, clean_keys=[]): """ Loading Loading @@ -349,12 +392,15 @@ class CandidateGraph(nx.Graph): mask = np.prod([attributes[i] for i in clean_keys], axis=0, dtype=np.bool) matches = matches[mask] if 'subpixel' in clean_keys: offsets = attributes['subpixel_offsets'][attributes['subpixel']] print(offsets) kp1 = self.node[source]['keypoints'] kp2 = self.node[destination]['keypoints'] pt_idx = 0 values = [] for idx, row in matches.iterrows(): for i, (idx, row) in enumerate(matches.iterrows()): # Composite matching key (node_id, point_id) m1 = (source, int(row['source_idx'])) m2 = (destination, int(row['destination_idx'])) Loading @@ -365,8 +411,17 @@ class CandidateGraph(nx.Graph): pt_idx, source]) values.append([kp2[m2[1]].pt[0], kp2[m2[1]].pt[1], kp2x = kp2[m2[1]].pt[0] kp2y = kp2[m2[1]].pt[1] if 'subpixel' in clean_keys: print(idx) print(kp2x, kp2y) kp2x += offsets['x_offset'].values[i] kp2y += offsets['y_offset'].values[i] print(kp2x, kp2y) values.append([kp2x, kp2y, m2, pt_idx, destination]) Loading
autocnet/graph/tests/test_network.py +9 −7 Original line number Diff line number Diff line Loading @@ -13,8 +13,9 @@ from .. import network class TestCandidateGraph(unittest.TestCase): def setUp(self): self.graph = network.CandidateGraph.from_adjacency(get_path('adjacency.json')) @classmethod def setUpClass(cls): cls.graph = network.CandidateGraph.from_adjacency(get_path('adjacency.json')) def test_get_name(self): node_number = self.graph.node_name_map['AS15-M-0297_SML.png'] Loading @@ -28,6 +29,10 @@ class TestCandidateGraph(unittest.TestCase): def test_to_json_file(self): self.graph.to_json_file('test_graph_to_json.json') self.assertTrue(os.path.exists('test_graph_to_json.json')) try: os.remove('test_graph_to_json.json') except: pass def test_extract_features(self): # also tests get_geodataset() and get_keypoints Loading @@ -42,7 +47,4 @@ class TestCandidateGraph(unittest.TestCase): self.assertEquals(self.graph.get_keypoints(node_number), node['keypoints']) def tearDown(self): try: os.remove('test_graph_to_json.json') except: pass
autocnet/matcher/outlier_detector.py +2 −0 Original line number Diff line number Diff line Loading @@ -148,3 +148,5 @@ def compute_homography(kp1, kp2, outlier_algorithm=cv2.RANSAC, reproj_threshold= reproj_threshold) mask = mask.astype(bool) return transformation_matrix, mask