Commit 8e862b85 authored by Kristin Berry's avatar Kristin Berry
Browse files

Merge pull request #32 from jlaura/subpixel

Subpixel
parents 4d6db623 c45678ab
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -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
@@ -41,6 +43,9 @@ install:
script:
  - nosetests --with-coverage --cover-package=autocnet

after_success:
  - coveralls

notifications:
    email:
        recipients:
@@ -50,6 +55,3 @@ notifications:
            - jwbacker@usgs.gov
        on_success: always
        on_failure: always
 No newline at end of file

after_success:
  - coveralls
+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,
+73 −18
Original line number Diff line number Diff line
@@ -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:
@@ -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=[]):
        """
@@ -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']))
@@ -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])
+9 −7
Original line number Diff line number Diff line
@@ -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']
@@ -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
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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