Commit f2242f97 authored by jlaura's avatar jlaura Committed by GitHub
Browse files

Merge pull request #212 from evindunn/dev

"overlap" keypoint constraint & match mask
parents a0e50317 a619c5e7
Loading
Loading
Loading
Loading
+39 −12
Original line number Diff line number Diff line
@@ -115,14 +115,6 @@ class Edge(dict, MutableMapping):
        """
        pass

    def match_overlap(self, k=2, **kwargs):
        """
        Given two sets of descriptors, apply the matcher with the
        source and destination overlaps.
        """
        overlaps = [self['source_mbr'], self['destin_mbr']]
        self.match(k=k, overlap=overlaps, **kwargs)

    def decompose(self):
        """
        Apply coupled decomposition to the images and
@@ -153,6 +145,26 @@ class Edge(dict, MutableMapping):
        arr = node.geodata.read_array(pixels=pixels)
        node.extract_features(arr, xystart=xystart, *args, **kwargs)
    """

    def overlap_check(self):
        """Creates a mask for matches on the overlap"""
        if not (self["source_mbr"] and self["destin_mbr"]):
            warnings.warn(
                "Cannot use overlap constraint, minimum bounding rectangles"
                " have not been computed for one or more Nodes")
            return

        # Get overlapping keypts
        s_idx = self.get_keypoints(self.source, overlap=True).index
        d_idx = self.get_keypoints(self.destination, overlap=True).index

        # Create a mask from matches whose rows have both source idx &
        # dest idx in the overlapping keypts
        mask = pd.Series(False, index=self.matches.index)
        mask.loc[(self.matches["source_idx"].isin(s_idx)) &
                 (self.matches["destination_idx"].isin(d_idx))] = True
        self.masks['overlap'] = mask

    def symmetry_check(self):
        self.masks['symmetry'] = od.mirroring_test(self.matches)

@@ -201,18 +213,33 @@ class Edge(dict, MutableMapping):
            self.masks[maskname] = mask

    @utils.methodispatch
    def get_keypoints(self, node, index=None, homogeneous=False):
    def get_keypoints(self, node, index=None, homogeneous=False, overlap=False):
        if not hasattr(index, '__iter__') and index is not None:
            raise TypeError
        return node.get_keypoint_coordinates(index=index, homogeneous=homogeneous)
        keypts = node.get_keypoint_coordinates(index=index, homogeneous=homogeneous)
        # If we only want keypoints in the overlap
        if overlap:
            # Can't use overlap if we haven't computed MBRs
            if not (self["source_mbr"] and self["destin_mbr"]):
                warnings.warn(
                    "Cannot use overlap constraint, minimum bounding rectangles"
                    " have not been computed for one or more Nodes")
                return keypts
            # Create overlap's bounding polygon in pixel space
            bounds_poly = node.reproject_geom(self.overlap_latlon_coords)
            # Mask for node keypts based on bounding poly
            overlap_mask = cg.geom_mask(node.keypoints, bounds_poly)
            # Return masked keypts
            return keypts[overlap_mask]
        return keypts

    @get_keypoints.register(str)
    def _(self, node, index=None, homogeneous=False):
    def _(self, node, index=None, homogeneous=False, overlap=False):
        if not hasattr(index, '__iter__') and index is not None:
            raise TypeError
        node = node.lower()
        node = getattr(self, node)
        return node.get_keypoint_coordinates(index=index, homogeneous=homogeneous)
        return self.get_keypoints(node, index=index, homogeneous=homogeneous, overlap=overlap)

    def compute_fundamental_error(self, clean_keys=[]):
        """
+12 −0
Original line number Diff line number Diff line
@@ -442,6 +442,18 @@ class CandidateGraph(nx.Graph):
        '''
        self.apply_func_to_edges('ratio_check', *args, **kwargs)

    def compute_overlaps(self, *args, **kwargs):
        '''
        Computes overlap MBRs for all edges
        '''
        self.apply_func_to_edges('compute_overlap', *args, **kwargs)

    def overlap_checks(self, *args, **kwargs):
        '''
        Apply overlap check to all edges in the graph
        '''
        self.apply_func_to_edges('overlap_check', *args, **kwargs)

    def compute_homographies(self, *args, **kwargs):
        '''
        Compute homographies for all edges using identical parameters
+90 −1
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ import ogr
import numpy as np
import pandas as pd
from plio.io import io_gdal
from shapely.geometry import Polygon as Poly

from autocnet.matcher import cpu_outlier_detector as od
from autocnet.examples import get_path
@@ -135,7 +136,6 @@ class TestEdge(unittest.TestCase):
        e.source = source_node
        e.destination = destination_node


        e.clean = MagicMock(return_value=(matches_df, None))
        e.matches = matches_df

@@ -167,6 +167,41 @@ class TestEdge(unittest.TestCase):
                    self.assertTrue(out_df[0].iloc[row_idx][column] ==
                                            out_df[2].iloc[row_idx][column])

        # Test when overlap=True
        # edge["source_mbr"] and edge["destin_mbr"] haven't been calculated
        # yet, so return val should be unmasked df of node's keypts
        s_no_overlap = e.get_keypoints(e.source, overlap=True)
        d_no_overlap = e.get_keypoints(e.destination, overlap=True)
        self.assertTrue(s_no_overlap.equals(src_keypoint_df))
        self.assertTrue(d_no_overlap.equals(dst_keypoint_df))

        # Define the MBRs
        e.overlap_latlon_coords = 0, 0

        source_node.reproject_geom = MagicMock(return_value=Poly([(1, 6), (1, 8), (3, 6), (3, 8)]))
        source_node.keypoints = src_keypoint_df

        destination_node.reproject_geom = MagicMock(return_value=Poly([(31, 26), (31, 28), (33, 26), (33, 28)]))
        destination_node.keypoints = dst_keypoint_df

        # Only keep keypt vals w/I these bounding rects
        e["source_mbr"] = (0, 2, 8, 5)
        e["destin_mbr"] = (31, 33, 28, 26)

        # Grab the keypoints on our MBR overlaps
        s_overlap = e.get_keypoints(e.source, overlap=True)
        d_overlap = e.get_keypoints(e.destination, overlap=True)

        # Assert masked src keypts coords are equal
        s_expected = pd.DataFrame({'x': (1, 2, 3), 'y': (6, 7, 8)})
        self.assertTrue(np.array_equal(s_expected['x'], s_overlap['x'].values))
        self.assertTrue(np.array_equal(s_expected['y'], s_overlap['y'].values))

        # Assert masked dst keypt coords are equal
        d_expected = pd.DataFrame({'x': (33, 32, 31), 'y': (28, 27, 26)})
        self.assertTrue(np.array_equal(d_expected['x'], d_overlap['x'].values))
        self.assertTrue(np.array_equal(d_expected['y'], d_overlap['y'].values))

        # Assert type-checking in method throws proper errors
        with self.assertRaises(TypeError):
            e.get_keypoints("source", index = 456)
@@ -241,3 +276,57 @@ class TestEdge(unittest.TestCase):
        expected = list(od.distance_ratio(matches_df))
        e.ratio_check()
        self.assertEqual(expected, list(e.masks["ratio"]))

    def test_overlap_check(self):
        s = node.Node()
        d = node.Node()

        e = edge.Edge()
        e.source = s
        e.destination = d

        src_keypoint_df = pd.DataFrame({'x': (0, 1, 2, 3, 4), 'y': (5, 6, 7, 8, 9)})
        dst_keypoint_df = pd.DataFrame({'x': (34, 33, 32, 31, 30), 'y': (29, 28, 27, 26, 25)})

        # Create keypt matches
        keypoint_matches = [[0, 0, 1, 4, 5],
                            [0, 1, 1, 3, 5],
                            [0, 2, 1, 2, 5],
                            [0, 3, 1, 1, 5],
                            [0, 4, 1, 0, 5]]

        matches_df = pd.DataFrame(data=keypoint_matches,
                                  columns=['source_image', 'source_idx',
                                           'destination_image', 'destination_idx', 'distance'])
        s.keypoints = src_keypoint_df
        d.keypoints = dst_keypoint_df
        e.matches = matches_df

        s_overlap_keypts = pd.DataFrame({'x': (0, 1), 'y': (5, 6)})
        d_overlap_keypts = pd.DataFrame({'x': (31, 30), 'y': (26, 25)}, index=[3, 4])
        expected_mask = pd.Series(data=[True, True, False, False, False])

        # Mockup of the Edge.get_keypoints() method when overlap=True
        def mock_get_keypts(node, overlap=False):
            if node == s and overlap:
                return s_overlap_keypts
            elif node == d and overlap:
                return d_overlap_keypts
            else:
                return None

        e.get_keypoints = MagicMock(side_effect=mock_get_keypts)

        # Should fail if no src & dst mbrs on edge; Warns user & mask isn't
        # populated
        e.overlap_check()
        self.assertTrue("overlap" not in e.masks)

        # Should work after MBRs are set
        e["source_mbr"] = (1, 1, 1, 1)
        e["destin_mbr"] = (1, 1, 1, 1)
        e.overlap_check()
        overlap_matches, overlap_mask = e.clean(clean_keys=['overlap'])

        self.assertTrue(expected_mask.equals(overlap_mask))
        self.assertTrue(overlap_matches.equals(e.matches[overlap_mask]))
+3 −2
Original line number Diff line number Diff line
@@ -67,8 +67,9 @@ def decompose_and_match(self, k=2, maxiteration=3, size=18, buf_dist=3,**kwargs)
    dsize = ddata.shape

    # Grab all the available candidate keypoints
    skp = self.source.get_keypoints()
    dkp = self.destination.get_keypoints()
    overlap = kwargs.get("overlap", False)
    skp = self.get_keypoints(self.source, overlap=overlap)
    dkp = self.get_keypoints(self.destination, overlap=overlap)

    # Set up the membership arrays
    self.smembership = np.zeros(sdata.shape, dtype=np.int16)
+1 −10
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ FLANN_INDEX_KDTREE = 1 # Algorithm to set centers,
DEFAULT_FLANN_PARAMETERS = dict(algorithm=FLANN_INDEX_KDTREE, trees=3)


def match(self, k=2, overlap=[], **kwargs):
def match(self, k=2, **kwargs):
    """
    Given two sets of descriptors, utilize a FLANN (Approximate Nearest
    Neighbor KDTree) matcher to find the k nearest matches.  Nearness is
@@ -83,21 +83,12 @@ def match(self, k=2, overlap=[], **kwargs):
    if 'aidx' in kwargs.keys():
        aidx = kwargs['aidx']
        kwargs.pop('aidx')
    elif overlap:
        # Query the source keypoints for those in the MBR
        source_mbr = overlap[0]
        query_result = self.source.keypoints.query('x >= {} and x <= {} and y >= {} and y <= {}'.format(*source_mbr))
        aidx = query_result.index
    else:
        aidx = None
    
    if 'bidx' in kwargs.keys():
        bidx = kwargs['bidx']
        kwargs.pop('bidx')
    elif overlap:
        destin_mbr = overlap[1]
        query_result = self.destination.keypoints.query('x >= {} and x <= {} and y >= {} and y <= {}'.format(*destin_mbr))
        bidx = query_result.index
    else:
        bidx = None

Loading