Loading autocnet/fileio/io_gdal.py +26 −7 Original line number Diff line number Diff line Loading @@ -142,6 +142,11 @@ class GeoDataset(object): @property def geotransform(self): """ Where the array is in the form: [top left x, w-e pixel resolution, x-rotation, top left y, y-rotation, n-s pixel resolution] """ if not getattr(self, '_geotransform', None): self._geotransform = self.dataset.GetGeoTransform() return self._geotransform Loading Loading @@ -192,10 +197,10 @@ class GeoDataset(object): if not getattr(self, '_xy_extent', None): geotransform = self.geotransform minx = geotransform[0] maxy = geotransform[3] miny = geotransform[3] maxx = minx + geotransform[1] * self.dataset.RasterXSize miny = maxy + geotransform[5] * self.dataset.RasterYSize maxy = miny + geotransform[5] * self.dataset.RasterYSize self._xy_extent = [(minx, miny), (maxx, maxy)] Loading Loading @@ -328,7 +333,7 @@ class GeoDataset(object): The image band number to be extracted as a NumPy array. Default band=1. pixels : list [start, ystart, xstop, ystop]. Default pixels=None. [xstart, ystart, xstop, ystop]. Default pixels=None. dtype : str The NumPy dtype for the output array. Default dtype='float32'. Loading @@ -346,10 +351,24 @@ class GeoDataset(object): if not pixels: array = band.ReadAsArray().astype(dtype) else: xstart = pixels[0][0] ystart = pixels[0][1] xextent = pixels[1][0] - xstart yextent = pixels[1][1] - ystart xstart = pixels[0] if xstart < 0: xstart = 0 ystart = pixels[1] if ystart < 0: ystart = 0 xmax, ymax = map(int, self.xy_extent[1]) if pixels[2] > xmax: xextent = xmax - xstart else: xextent = pixels[2] - xstart if pixels[3] > ymax: yextent = ymax - ystart else: yextent = pixels[3] - ystart array = band.ReadAsArray(xstart, ystart, xextent, yextent).astype(dtype) return array Loading autocnet/fileio/tests/test_io_gdal.py +8 −12 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ class TestMercator(unittest.TestCase): self.assertEqual(self.dataset.unit_type, '') def test_get_xy_extent(self): self.assertEqual(self.dataset.xy_extent, [(0.0, -3921610.0), (10667520.0, 3921610.0)]) self.assertEqual(self.dataset.xy_extent, [(0.0, 3921610.0), (10667520.0, -3921610.0)]) def test_get_no_data_value(self): self.assertEqual(self.dataset.no_data_value, 0.0) Loading @@ -37,10 +37,10 @@ class TestMercator(unittest.TestCase): def test_xy_extent(self): xy_extent = self.dataset.xy_extent self.assertEqual(xy_extent, [(0.0, -3921610.0), (10667520.0, 3921610.0)]) self.assertEqual(xy_extent, [(0.0, 3921610.0), (10667520.0, -3921610.0)]) def test_latlon_extent(self): self.assertEqual(self.dataset.latlon_extent, [(90.0, 0.0), (-90.0, -150.4067721290261)]) self.assertEqual(self.dataset.latlon_extent, [(-90.0, 0.0), (90.0, -150.4067721290261)]) def test_spheroid(self): sphere = self.dataset.spheroid Loading Loading @@ -80,10 +80,6 @@ class TestMercator(unittest.TestCase): self.assertEqual(arr.shape, (1694, 2304)) self.assertEqual(arr.dtype, np.float32) def test_read_clipped_array(self): arr = self.dataset.read_array(pixels=((0,0), (100,100))) self.assertEqual(arr.shape, (100,100)) def test_read_array_set_dtype(self): arr = self.dataset.read_array(dtype='int8') self.assertEqual(arr.dtype, np.int8) Loading @@ -101,7 +97,7 @@ class TestLambert(unittest.TestCase): self.assertEqual(self.dataset.unit_type, '') def test_get_xy_extent(self): self.assertEqual(self.dataset.xy_extent, [(-464400.0, -1571220.0), (460530.0, -506970.0)]) self.assertEqual(self.dataset.xy_extent, [(-464400.0, -506970.0), (460530.0, -1571220.0)]) def test_get_no_data_value(self): self.assertEqual(self.dataset.no_data_value, 0.0) Loading @@ -123,10 +119,10 @@ class TestLambert(unittest.TestCase): def test_xy_extent(self): xy_extent = self.dataset.xy_extent self.assertEqual(xy_extent, [(-464400.0, -1571220.0), (460530.0, -506970.0)]) self.assertEqual(xy_extent, [(-464400.0, -506970.0), (460530.0, -1571220.0)]) def test_latlon_extent(self): self.assertEqual(self.dataset.latlon_extent, [(-89.98516988892511, -171.35800063907413), (-89.95883789218114, -178.8099427811737)]) self.assertEqual(self.dataset.latlon_extent, [(-89.95903191125286, -140.8933768668104),(-89.98515758604582, -148.48059053073257)]) class TestPolar(unittest.TestCase): def setUp(self): Loading @@ -140,7 +136,7 @@ class TestPolar(unittest.TestCase): self.assertEqual(self.dataset.unit_type, '') def test_get_xy_extent(self): self.assertEqual(self.dataset.xy_extent, [(-2129800.0, -2129800.0), (2129800.0, 2129800.0)]) self.assertEqual(self.dataset.xy_extent, [(-2129800.0, 2129800.0), (2129800.0, -2129800.0)]) def test_get_no_data_value(self): self.assertEqual(self.dataset.no_data_value, 0.0) Loading @@ -158,7 +154,7 @@ class TestPolar(unittest.TestCase): def test_xy_extent(self): xy_extent = self.dataset.xy_extent self.assertEqual(xy_extent, [(-2129800.0, -2129800.0), (2129800.0, 2129800.0)]) self.assertEqual(xy_extent, [(-2129800.0, 2129800.0), (2129800.0, -2129800.0)]) class TestWriter(unittest.TestCase): def setUp(self): Loading autocnet/graph/network.py +28 −22 Original line number Diff line number Diff line Loading @@ -165,7 +165,7 @@ class CandidateGraph(nx.Graph): """ self.node[nodeindex]['handle'] = GeoDataset(self.node[nodeindex]['image_path']) def get_array(self, nodeindex, downsampling=1): def get_array(self, nodeindex): """ Downsample the input image file by some amount using bicubic interpolation in order to reduce data sizes for visualization and analysis, e.g. feature detection Loading @@ -180,20 +180,19 @@ class CandidateGraph(nx.Graph): """ array = self.node[nodeindex]['handle'].read_array() newx_size = int(array.shape[0] / downsampling) newy_size = int(array.shape[1] / downsampling) resized_array = imresize(array, (newx_size, newy_size), interp='bicubic') self.node[nodeindex]['image'] = bytescale(resized_array) self.node[nodeindex]['image_downsampling'] = downsampling return bytescale(array) def extract_features(self, extractor_parameters={}, downsampling=1): def extract_features(self, method='orb', extractor_parameters={}, downsampling=1): """ Extracts features from each image in the graph and uses the result to assign the node attributes for 'handle', 'image', 'keypoints', and 'descriptors'. Parameters ---------- method : {'orb', 'sift', 'fast'} The descriptor method to be used extractor_parameters : dict A dictionary containing OpenCV SIFT parameters names and values. Loading @@ -202,9 +201,13 @@ class CandidateGraph(nx.Graph): """ for node, attributes in self.nodes_iter(data=True): self.get_geodataset(node) self.get_array(node, downsampling=downsampling) attributes['keypoints'], attributes['descriptors'] = fe.extract_features(attributes['image'], extractor_parameters) image = self.get_array(node) keypoints, descriptors = fe.extract_features(image, method=method, extractor_parameters=extractor_parameters) attributes['keypoints'] = keypoints attributes['descriptors'] = descriptors.astype(np.float32, copy=False) def add_matches(self, matches): """ Loading Loading @@ -337,26 +340,29 @@ class CandidateGraph(nx.Graph): matches = matches[mask] full_mask = np.where(mask == True) src_image = self.node[source]['image'] dest_image = self.node[destination]['image'] # Preallocate the numpy array to avoid appending and type conversion edge_offsets = np.empty((len(matches),3)) s_node = self.node[source] d_node = self.node[destination] s_image = s_node['handle'] d_image = d_node['handle'] # 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']) s_node = self.node[source] d_node = self.node[destination] s_keypoint = [s_node['keypoints'][s_idx].pt[0], s_node['keypoints'][s_idx].pt[1]] s_keypoint = s_node['keypoints'][s_idx].pt d_keypoint = d_node['keypoints'][d_idx].pt d_keypoint = [d_node['keypoints'][d_idx].pt[0], d_node['keypoints'][d_idx].pt[1]] # Get the template and search windows s_template = sp.clip_roi(src_image, s_keypoint, template_size) d_search = sp.clip_roi(dest_image, d_keypoint, search_size) s_template = sp.clip_roi(s_image, s_keypoint, template_size) d_search = sp.clip_roi(d_image, d_keypoint, search_size) edge_offsets[i] = sp.subpixel_offset(s_template, d_search, upsampling=upsampling) Loading Loading @@ -466,8 +472,8 @@ class CandidateGraph(nx.Graph): kp2y = kp2[m2[1]].pt[1] if 'subpixel' in clean_keys: kp2x += offsets['x_offset'].values[i] kp2y += offsets['y_offset'].values[i] kp2x += (offsets['x_offset'].values[i]) kp2y += (offsets['y_offset'].values[i]) values.append([kp2x, kp2y, m2, Loading autocnet/graph/tests/test_network.py +6 −1 Original line number Diff line number Diff line Loading @@ -34,12 +34,17 @@ class TestCandidateGraph(unittest.TestCase): except: pass def test_get_array(self): node_number = self.graph.node_name_map['AS15-M-0297_SML.png'] image = self.graph.get_array(node_number) self.assertEqual((1012, 1012), image.shape) self.assertEqual(np.uint8, image.dtype) def test_extract_features(self): # also tests get_geodataset() and get_keypoints self.graph.extract_features(extractor_parameters={'nfeatures':10}) node_number = self.graph.node_name_map['AS15-M-0297_SML.png'] node = self.graph.node[node_number] self.assertEquals(len(node['image']), 1012) self.assertEquals(len(node['keypoints']), 10) self.assertEquals(len(node['descriptors']), 10) self.assertIsInstance(node['keypoints'][0], type(cv2.KeyPoint())) Loading autocnet/matcher/feature_extractor.py +12 −3 Original line number Diff line number Diff line import cv2 def extract_features(array, extractor_parameters): def extract_features(array, method='orb', extractor_parameters=None): """ This method finds and extracts features from an image using the given dictionary of keyword arguments. The input image is represented as NumPy array and the output features are represented as keypoint IDs Loading @@ -12,6 +12,9 @@ def extract_features(array, extractor_parameters): array : ndarray a NumPy array that represents an image detector : {'orb', 'sift', 'fast', 'surf'} The detector method to be used extractor_parameters : dict A dictionary containing OpenCV SIFT parameters names and values. Loading @@ -21,5 +24,11 @@ def extract_features(array, extractor_parameters): in the form ([list of OpenCV KeyPoints], [NumPy array of descriptors as geometric vectors]) """ sift = cv2.xfeatures2d.SIFT_create(**extractor_parameters) return sift.detectAndCompute(array, None) detectors = {'fast': cv2.FastFeatureDetector_create, 'sift': cv2.xfeatures2d.SIFT_create, 'surf': cv2.xfeatures2d.SURF_create, 'orb': cv2.ORB_create} detector = detectors[method](**extractor_parameters) return detector.detectAndCompute(array, None) Loading
autocnet/fileio/io_gdal.py +26 −7 Original line number Diff line number Diff line Loading @@ -142,6 +142,11 @@ class GeoDataset(object): @property def geotransform(self): """ Where the array is in the form: [top left x, w-e pixel resolution, x-rotation, top left y, y-rotation, n-s pixel resolution] """ if not getattr(self, '_geotransform', None): self._geotransform = self.dataset.GetGeoTransform() return self._geotransform Loading Loading @@ -192,10 +197,10 @@ class GeoDataset(object): if not getattr(self, '_xy_extent', None): geotransform = self.geotransform minx = geotransform[0] maxy = geotransform[3] miny = geotransform[3] maxx = minx + geotransform[1] * self.dataset.RasterXSize miny = maxy + geotransform[5] * self.dataset.RasterYSize maxy = miny + geotransform[5] * self.dataset.RasterYSize self._xy_extent = [(minx, miny), (maxx, maxy)] Loading Loading @@ -328,7 +333,7 @@ class GeoDataset(object): The image band number to be extracted as a NumPy array. Default band=1. pixels : list [start, ystart, xstop, ystop]. Default pixels=None. [xstart, ystart, xstop, ystop]. Default pixels=None. dtype : str The NumPy dtype for the output array. Default dtype='float32'. Loading @@ -346,10 +351,24 @@ class GeoDataset(object): if not pixels: array = band.ReadAsArray().astype(dtype) else: xstart = pixels[0][0] ystart = pixels[0][1] xextent = pixels[1][0] - xstart yextent = pixels[1][1] - ystart xstart = pixels[0] if xstart < 0: xstart = 0 ystart = pixels[1] if ystart < 0: ystart = 0 xmax, ymax = map(int, self.xy_extent[1]) if pixels[2] > xmax: xextent = xmax - xstart else: xextent = pixels[2] - xstart if pixels[3] > ymax: yextent = ymax - ystart else: yextent = pixels[3] - ystart array = band.ReadAsArray(xstart, ystart, xextent, yextent).astype(dtype) return array Loading
autocnet/fileio/tests/test_io_gdal.py +8 −12 Original line number Diff line number Diff line Loading @@ -22,7 +22,7 @@ class TestMercator(unittest.TestCase): self.assertEqual(self.dataset.unit_type, '') def test_get_xy_extent(self): self.assertEqual(self.dataset.xy_extent, [(0.0, -3921610.0), (10667520.0, 3921610.0)]) self.assertEqual(self.dataset.xy_extent, [(0.0, 3921610.0), (10667520.0, -3921610.0)]) def test_get_no_data_value(self): self.assertEqual(self.dataset.no_data_value, 0.0) Loading @@ -37,10 +37,10 @@ class TestMercator(unittest.TestCase): def test_xy_extent(self): xy_extent = self.dataset.xy_extent self.assertEqual(xy_extent, [(0.0, -3921610.0), (10667520.0, 3921610.0)]) self.assertEqual(xy_extent, [(0.0, 3921610.0), (10667520.0, -3921610.0)]) def test_latlon_extent(self): self.assertEqual(self.dataset.latlon_extent, [(90.0, 0.0), (-90.0, -150.4067721290261)]) self.assertEqual(self.dataset.latlon_extent, [(-90.0, 0.0), (90.0, -150.4067721290261)]) def test_spheroid(self): sphere = self.dataset.spheroid Loading Loading @@ -80,10 +80,6 @@ class TestMercator(unittest.TestCase): self.assertEqual(arr.shape, (1694, 2304)) self.assertEqual(arr.dtype, np.float32) def test_read_clipped_array(self): arr = self.dataset.read_array(pixels=((0,0), (100,100))) self.assertEqual(arr.shape, (100,100)) def test_read_array_set_dtype(self): arr = self.dataset.read_array(dtype='int8') self.assertEqual(arr.dtype, np.int8) Loading @@ -101,7 +97,7 @@ class TestLambert(unittest.TestCase): self.assertEqual(self.dataset.unit_type, '') def test_get_xy_extent(self): self.assertEqual(self.dataset.xy_extent, [(-464400.0, -1571220.0), (460530.0, -506970.0)]) self.assertEqual(self.dataset.xy_extent, [(-464400.0, -506970.0), (460530.0, -1571220.0)]) def test_get_no_data_value(self): self.assertEqual(self.dataset.no_data_value, 0.0) Loading @@ -123,10 +119,10 @@ class TestLambert(unittest.TestCase): def test_xy_extent(self): xy_extent = self.dataset.xy_extent self.assertEqual(xy_extent, [(-464400.0, -1571220.0), (460530.0, -506970.0)]) self.assertEqual(xy_extent, [(-464400.0, -506970.0), (460530.0, -1571220.0)]) def test_latlon_extent(self): self.assertEqual(self.dataset.latlon_extent, [(-89.98516988892511, -171.35800063907413), (-89.95883789218114, -178.8099427811737)]) self.assertEqual(self.dataset.latlon_extent, [(-89.95903191125286, -140.8933768668104),(-89.98515758604582, -148.48059053073257)]) class TestPolar(unittest.TestCase): def setUp(self): Loading @@ -140,7 +136,7 @@ class TestPolar(unittest.TestCase): self.assertEqual(self.dataset.unit_type, '') def test_get_xy_extent(self): self.assertEqual(self.dataset.xy_extent, [(-2129800.0, -2129800.0), (2129800.0, 2129800.0)]) self.assertEqual(self.dataset.xy_extent, [(-2129800.0, 2129800.0), (2129800.0, -2129800.0)]) def test_get_no_data_value(self): self.assertEqual(self.dataset.no_data_value, 0.0) Loading @@ -158,7 +154,7 @@ class TestPolar(unittest.TestCase): def test_xy_extent(self): xy_extent = self.dataset.xy_extent self.assertEqual(xy_extent, [(-2129800.0, -2129800.0), (2129800.0, 2129800.0)]) self.assertEqual(xy_extent, [(-2129800.0, 2129800.0), (2129800.0, -2129800.0)]) class TestWriter(unittest.TestCase): def setUp(self): Loading
autocnet/graph/network.py +28 −22 Original line number Diff line number Diff line Loading @@ -165,7 +165,7 @@ class CandidateGraph(nx.Graph): """ self.node[nodeindex]['handle'] = GeoDataset(self.node[nodeindex]['image_path']) def get_array(self, nodeindex, downsampling=1): def get_array(self, nodeindex): """ Downsample the input image file by some amount using bicubic interpolation in order to reduce data sizes for visualization and analysis, e.g. feature detection Loading @@ -180,20 +180,19 @@ class CandidateGraph(nx.Graph): """ array = self.node[nodeindex]['handle'].read_array() newx_size = int(array.shape[0] / downsampling) newy_size = int(array.shape[1] / downsampling) resized_array = imresize(array, (newx_size, newy_size), interp='bicubic') self.node[nodeindex]['image'] = bytescale(resized_array) self.node[nodeindex]['image_downsampling'] = downsampling return bytescale(array) def extract_features(self, extractor_parameters={}, downsampling=1): def extract_features(self, method='orb', extractor_parameters={}, downsampling=1): """ Extracts features from each image in the graph and uses the result to assign the node attributes for 'handle', 'image', 'keypoints', and 'descriptors'. Parameters ---------- method : {'orb', 'sift', 'fast'} The descriptor method to be used extractor_parameters : dict A dictionary containing OpenCV SIFT parameters names and values. Loading @@ -202,9 +201,13 @@ class CandidateGraph(nx.Graph): """ for node, attributes in self.nodes_iter(data=True): self.get_geodataset(node) self.get_array(node, downsampling=downsampling) attributes['keypoints'], attributes['descriptors'] = fe.extract_features(attributes['image'], extractor_parameters) image = self.get_array(node) keypoints, descriptors = fe.extract_features(image, method=method, extractor_parameters=extractor_parameters) attributes['keypoints'] = keypoints attributes['descriptors'] = descriptors.astype(np.float32, copy=False) def add_matches(self, matches): """ Loading Loading @@ -337,26 +340,29 @@ class CandidateGraph(nx.Graph): matches = matches[mask] full_mask = np.where(mask == True) src_image = self.node[source]['image'] dest_image = self.node[destination]['image'] # Preallocate the numpy array to avoid appending and type conversion edge_offsets = np.empty((len(matches),3)) s_node = self.node[source] d_node = self.node[destination] s_image = s_node['handle'] d_image = d_node['handle'] # 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']) s_node = self.node[source] d_node = self.node[destination] s_keypoint = [s_node['keypoints'][s_idx].pt[0], s_node['keypoints'][s_idx].pt[1]] s_keypoint = s_node['keypoints'][s_idx].pt d_keypoint = d_node['keypoints'][d_idx].pt d_keypoint = [d_node['keypoints'][d_idx].pt[0], d_node['keypoints'][d_idx].pt[1]] # Get the template and search windows s_template = sp.clip_roi(src_image, s_keypoint, template_size) d_search = sp.clip_roi(dest_image, d_keypoint, search_size) s_template = sp.clip_roi(s_image, s_keypoint, template_size) d_search = sp.clip_roi(d_image, d_keypoint, search_size) edge_offsets[i] = sp.subpixel_offset(s_template, d_search, upsampling=upsampling) Loading Loading @@ -466,8 +472,8 @@ class CandidateGraph(nx.Graph): kp2y = kp2[m2[1]].pt[1] if 'subpixel' in clean_keys: kp2x += offsets['x_offset'].values[i] kp2y += offsets['y_offset'].values[i] kp2x += (offsets['x_offset'].values[i]) kp2y += (offsets['y_offset'].values[i]) values.append([kp2x, kp2y, m2, Loading
autocnet/graph/tests/test_network.py +6 −1 Original line number Diff line number Diff line Loading @@ -34,12 +34,17 @@ class TestCandidateGraph(unittest.TestCase): except: pass def test_get_array(self): node_number = self.graph.node_name_map['AS15-M-0297_SML.png'] image = self.graph.get_array(node_number) self.assertEqual((1012, 1012), image.shape) self.assertEqual(np.uint8, image.dtype) def test_extract_features(self): # also tests get_geodataset() and get_keypoints self.graph.extract_features(extractor_parameters={'nfeatures':10}) node_number = self.graph.node_name_map['AS15-M-0297_SML.png'] node = self.graph.node[node_number] self.assertEquals(len(node['image']), 1012) self.assertEquals(len(node['keypoints']), 10) self.assertEquals(len(node['descriptors']), 10) self.assertIsInstance(node['keypoints'][0], type(cv2.KeyPoint())) Loading
autocnet/matcher/feature_extractor.py +12 −3 Original line number Diff line number Diff line import cv2 def extract_features(array, extractor_parameters): def extract_features(array, method='orb', extractor_parameters=None): """ This method finds and extracts features from an image using the given dictionary of keyword arguments. The input image is represented as NumPy array and the output features are represented as keypoint IDs Loading @@ -12,6 +12,9 @@ def extract_features(array, extractor_parameters): array : ndarray a NumPy array that represents an image detector : {'orb', 'sift', 'fast', 'surf'} The detector method to be used extractor_parameters : dict A dictionary containing OpenCV SIFT parameters names and values. Loading @@ -21,5 +24,11 @@ def extract_features(array, extractor_parameters): in the form ([list of OpenCV KeyPoints], [NumPy array of descriptors as geometric vectors]) """ sift = cv2.xfeatures2d.SIFT_create(**extractor_parameters) return sift.detectAndCompute(array, None) detectors = {'fast': cv2.FastFeatureDetector_create, 'sift': cv2.xfeatures2d.SIFT_create, 'surf': cv2.xfeatures2d.SURF_create, 'orb': cv2.ORB_create} detector = detectors[method](**extractor_parameters) return detector.detectAndCompute(array, None)