Commit 5df0a1c8 authored by Kristin Berry's avatar Kristin Berry
Browse files

Merge pull request #38 from jlaura/offset_validation

👍 
parents 42b416a2 26a0ea19
Loading
Loading
Loading
Loading
+26 −7
Original line number Diff line number Diff line
@@ -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
@@ -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)]

@@ -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'.
@@ -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
+8 −12
Original line number Diff line number Diff line
@@ -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)
@@ -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
@@ -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)
@@ -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)
@@ -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):
@@ -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)
@@ -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):
+28 −22
Original line number Diff line number Diff line
@@ -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
@@ -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.

@@ -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):
        """
@@ -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)

@@ -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,
+6 −1
Original line number Diff line number Diff line
@@ -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()))
+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 
@@ -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. 

@@ -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