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
+6 −4
Changes for .travis.yml: 6 added lines, 4 removed lines.
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
Changes for autocnet/fileio/io_gdal.py: 4 added lines, 2 removed lines.
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
Changes for autocnet/graph/network.py: 73 added lines, 18 removed lines.
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])
+7 −5
Changes for autocnet/graph/tests/test_network.py: 7 added lines, 5 removed lines.
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
Changes for autocnet/matcher/outlier_detector.py: 2 added lines, 0 removed lines.
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