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

Merge branch 'dev' into master

parents b22f547d f717f4e8
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -44,8 +44,8 @@ install:
  - conda config --add channels jlaura
  - conda config --set ssl_verify false
  - conda install python=$PYTHON_VERSION
  - conda install -c conda-forge numpy
  - conda install -c jlaura plio opencv3=3.0.0
  - conda install -c conda-forge numpy opencv
  - conda install -c jlaura plio
  - conda install -c conda-forge vlfeat
  - conda install -c menpo cyvlfeat
  - pip install pillow pysal
@@ -66,8 +66,6 @@ install:

script:
  - pytest --cov=autocnet
  # clean up any remaining processes...
  - if [ $TRAVIS_OS_NAME == "linux" ]; then killall5; fi

after_success:
  # Upload to anaconda and push to coveralls
+1 −1
Original line number Diff line number Diff line
@@ -37,6 +37,6 @@ We suggest using Anaconda Python to install Autocnet within a virtual environmen
  * ``conda create -n <your_environment_name> python=3 && source activate <your_environment_name>``
1. Bring up a command line and add three channels to your conda config (``~/condarc``):
  * ``conda config --add channels conda-forge``
  * ``conda condig --add channels jlaura``
  * ``conda config --add channels jlaura``
  * ``conda config --add channels menpo``
1. Finally, install autocnet: ``conda install -c jlaura autocnet-dev``
+20 −0
Original line number Diff line number Diff line
@@ -75,3 +75,23 @@ class TestC(unittest.TestCase):
    def test_to_dataframe(self):
        self.C.to_dataframe()

    def test_point_repr(self):
        expected = 0
        p = control.Point(expected)
        self.assertEqual(str(expected), p.__repr__())

    def test_correspondence_repr(self):
        expected = 0
        c = control.Correspondence(expected, 1, 1)
        self.assertEqual(str(expected), c.__repr__())

    def test_correspondence_eq(self):
        expected = 0
        c = control.Correspondence(expected, 1, 1)
        self.assertTrue(c == expected)

    def test_correspondence_hash(self):
        expected = 200
        c = control.Correspondence(expected, 1, 1)
        self.assertEqual(hash(expected), hash(c))
+71 −19
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ import pandas as pd
from scipy.spatial.distance import cdist

import autocnet
from autocnet.graph.node import Node
from autocnet.utils import utils
from autocnet.matcher import outlier_detector as od
from autocnet.matcher import suppression_funcs as spf
@@ -73,22 +74,11 @@ class Edge(dict, MutableMapping):
    def masks(self):
        mask_lookup = {'fundamental': 'fundamental_matrix'}
        if not hasattr(self, '_masks'):
            if self.matches is not None:
            if isinstance(self.matches, pd.DataFrame):
                self._masks = pd.DataFrame(True, columns=['symmetry'],
                                           index=self.matches.index)
            else:
                self._masks = pd.DataFrame()
        # If the mask is coming form another object that tracks
        # state, dynamically draw the mask from the object.
        for c in self._masks.columns:
            if c in mask_lookup:
                try:
                    truncated_mask = getattr(self, mask_lookup[c]).mask
                    self._masks[c] = False
                    self._masks[c].iloc[truncated_mask.index] = truncated_mask
                except Exception:
                    #TODO: Get rid of state
                    pass
        return self._masks

    @masks.setter
@@ -117,14 +107,14 @@ class Edge(dict, MutableMapping):
        pass

    def symmetry_check(self):
        if hasattr(self, 'matches'):
        if isinstance(self.matches, pd.DataFrame):
            mask = od.mirroring_test(self.matches)
            self.masks = ('symmetry', mask)
        else:
            raise AttributeError('No matches have been computed for this edge.')

    def ratio_check(self, clean_keys=[], **kwargs):
        if hasattr(self, 'matches'):
        if isinstance(self.matches, pd.DataFrame):
            matches, mask = self.clean(clean_keys)
            distance_mask = od.distance_ratio(matches, **kwargs)
            self.masks = ('ratio', distance_mask)
@@ -151,7 +141,7 @@ class Edge(dict, MutableMapping):
        autocnet.transformation.transformations.FundamentalMatrix

        """
        if not hasattr(self, 'matches'):
        if not isinstance(self.matches, pd.DataFrame):
            raise AttributeError('Matches have not been computed for this edge')
            return
        matches, mask = self.clean(clean_keys)
@@ -196,7 +186,7 @@ class Edge(dict, MutableMapping):
               Boolean array of the outliers
        """

        if hasattr(self, 'matches'):
        if isinstance(self.matches, pd.DataFrame):
            matches = self.matches
        else:
            raise AttributeError('Matches have not been computed for this edge')
@@ -320,7 +310,7 @@ class Edge(dict, MutableMapping):
                     of mask keys to be used to reduce the total size
                     of the matches dataframe.
        """
        if not hasattr(self, 'matches'):
        if not isinstance(self.matches, pd.DataFrame):
            raise AttributeError('This edge does not yet have any matches computed.')

        matches, mask = self.clean(clean_keys)
@@ -417,7 +407,7 @@ class Edge(dict, MutableMapping):
                                   returns the overlap area
                                   covered by the keypoints
        """
        if self.matches is None:
        if not isinstance(self.matches, pd.DataFrame):
            raise AttributeError('Edge needs to have features extracted and matched')
            return
        matches, mask = self.clean(clean_keys)
@@ -453,7 +443,7 @@ class Edge(dict, MutableMapping):
                     Of strings used to apply masks to omit correspondences

        """
        if self.matches is None:
        if not isinstance(self.matches, pd.DataFrame):
            raise AttributeError('Matches have not been computed for this edge')
        voronoi = cg.vor(self, clean_keys, **kwargs)
        self.matches = pd.concat([self.matches, voronoi[1]['vor_weights']], axis=1)
@@ -479,3 +469,65 @@ class Edge(dict, MutableMapping):

        """
        pass

    def get_keypoints(self, node, clean_keys):
        """

        Returns a list of keypoint coordinates that match the specified
        paramaters

        Parameters
        ----------
        node :      str or Node
                    Can be "source" or "destination" based on which node we're
                    pulling keypoint data for; Also can pass Node obj itself

        clean_keys :    list
                        List of clean key strings

        Return
        ------
        masked_keypts : Dataframe
                        Dataframe of keypoints that match the specified masks
                        on the specified node
        """

        # Assert parameter types are correct
        try:
            assert (isinstance(node, str) or isinstance(node, Node))
        except AssertionError:
            raise TypeError('Parameter "node" must be of type str or type Node')
        try:
            assert isinstance(clean_keys, list)
        except AssertionError:
            raise TypeError('Parameter "clean_keys" must be of type list')

        # If node param is a string, make sure it's one of the right strings
        if isinstance(node, str):
            try:
                assert (node in ["source", "destination"])
                # Define the node if str is passed as param
                if node == "source":
                    node = self.source
                elif node == "destination":
                    node = self.destination
            except AssertionError:
                raise KeyError('node" parameter must be "source"' +
                               'or "destination"')

        # Get cleaned, combined src & dst keypt df for this edge ("matches")
        matches, mask = self.clean(clean_keys)

        # Grab the keypt indices filtered by clean_keys as ints, pandas
        # complains when you use them as indicies if they're not ints
        if node == self.source:
            keypt_indices = matches["source_idx"].astype(int)
        elif node == self.destination:
            keypt_indices = matches["destination_idx"].astype(int)

        # Get all keypts for the specified node
        all_keypts = node.get_keypoints()
        # Return keypts @ masked indecies for the node
        masked_keypts = all_keypts.iloc[keypt_indices].sort_index()

        return masked_keypts
+179 −0
Original line number Diff line number Diff line
@@ -3,9 +3,11 @@ from unittest.mock import Mock
from unittest.mock import MagicMock

import ogr
import numpy as np
import pandas as pd
from plio.io import io_gdal

from autocnet.matcher import outlier_detector as od
from autocnet.examples import get_path
from autocnet.graph.network import CandidateGraph
from autocnet.utils.utils import array_to_poly
@@ -44,6 +46,33 @@ class TestEdge(unittest.TestCase):

    def test_masks(self):
        self.assertIsInstance(self.edge.masks, pd.DataFrame)
        matches = [[0, 0, 1, 0],
                   [0, 1, 1, 1],
                   [0, 2, 1, 2],
                   [0, 3, 1, 3],
                   [0, 4, 1, 4]]
        matches_df = pd.DataFrame(data=matches,
                                  columns=['source_image', 'source_idx',
                                           'destination_image',
                                           'destination_idx'])
        e = edge.Edge()
        e.matches = matches_df

        # Test empty masks df on an edge with computed matches
        expected = pd.DataFrame(True, columns=['symmetry'],
                                index=matches_df.index)
        self.assertTrue(expected.equals(e.masks))

        # Test the masks setter, changing a given row
        new_symmetry_rows = [True, False, True, False, True]
        e.masks = "symmetry", new_symmetry_rows

        self.assertEqual(new_symmetry_rows, list(e.masks.loc[:, "symmetry"]))

        # Test the masks setter, inserting a new row
        e.masks = "fundamental", new_symmetry_rows
        self.assertEqual(new_symmetry_rows, list(e.masks.loc[:, "fundamental"]))



    def test_compute_fundamental_matrix(self):
@@ -249,3 +278,153 @@ class TestEdge(unittest.TestCase):
        for i in e.matches['vor_weights']:
            self.assertAlmostEquals(i, weights['vor_weights'][k])
            k += 1

    def test_get_keypoints(self):
        src_keypoint_df = pd.DataFrame({'x': (0, 1, 2, 3, 4), 'y': (5, 6, 7, 8, 9),
                                        'response': (10, 11, 12, 13, 14), 'size': (15, 16, 17, 18, 19),
                                        'angle': (20, 21, 22, 23, 24), 'octave': (25, 26, 27, 28, 29),
                                        'layer': (30, 31, 32, 33, 34)})

        dst_keypoint_df = pd.DataFrame({'x': (34, 33, 32, 31, 30), 'y': (29, 28, 27, 26, 25),
                                        'response': (24, 23, 22, 21, 20), 'size': (19, 18, 17, 16, 15),
                                        'angle': (14, 13, 12, 11, 10), 'octave': (9, 8, 7, 6, 5),
                                        'layer': (4, 3, 2, 1, 0)})

        keypoint_matches = [[0, 0, 1, 4],
                            [0, 1, 1, 3],
                            [0, 2, 1, 2],
                            [0, 3, 1, 1],
                            [0, 4, 1, 0]]

        matches_df = pd.DataFrame(data=keypoint_matches, columns=['source_image', 'source_idx',
                                                                  'destination_image', 'destination_idx'])

        e = edge.Edge()
        source_node = MagicMock(spec=node.Node())
        destination_node = MagicMock(spec=node.Node())

        source_node.get_keypoints = MagicMock(return_value=src_keypoint_df)
        destination_node.get_keypoints = MagicMock(return_value=dst_keypoint_df)

        e.source = source_node
        e.destination = destination_node

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

        clean_keys = ["fundamental", "ratio", "symmetry"]

        # Test all uses for edge.get_keypoints()
        src_matched_keypts = e.get_keypoints("source", clean_keys)
        src_matched_keypts2 = e.get_keypoints(e.source, clean_keys)
        dst_matched_keypts = e.get_keypoints("destination", clean_keys)
        dst_matched_keypts2 = e.get_keypoints(e.destination, clean_keys)

        # [output df to test] [name of node] [df to test against]
        to_test = [[src_matched_keypts, "source", src_keypoint_df],
                   [src_matched_keypts2, "source", src_keypoint_df],
                   [dst_matched_keypts, "destination", dst_keypoint_df],
                   [dst_matched_keypts2, "destination", dst_keypoint_df]]

        for out_df in to_test:
            # For each row index in the appropriate column of the matches_df,
            # assert that row index exists in the function's returned df
            [self.assertIn(row_idx, out_df[0].index.values)
             for row_idx in matches_df[out_df[1] + '_idx']]
            # For each row index in the returned df
            for row_idx in out_df[0].index.values:
                # Assert that row_idx exists in the matches_df's appropriate
                # column
                self.assertIn(row_idx, matches_df[out_df[1] + '_idx'])
                # Assert that all row_idx[column] vals returned by function
                # match their counterpart in orig df
                for column in out_df[0].columns:
                    self.assertTrue(out_df[0].iloc[row_idx][column] ==
                                    out_df[2].iloc[row_idx][column])

        # Assert type-checking in method throws proper errors
        with self.assertRaises(TypeError):
            e.get_keypoints("source", 1)
        with self.assertRaises(TypeError):
            e.get_keypoints(1, clean_keys)
        # Check key error thrown when string arg != "source" or "destination"
        with self.assertRaises(KeyError):
            e.get_keypoints("string", clean_keys)

    def test_eq(self):
        edge1 = edge.Edge()
        edge2 = edge.Edge()
        edge3 = edge.Edge()

        # Test edges w/ different keys are not equal, ones with same keys are
        edge1.__dict__["key"] = 1
        edge2.__dict__["key"] = 1
        edge3.__dict__["not_key"] = 1

        self.assertTrue(edge1 == edge2)
        self.assertFalse(edge1 == edge3)

        # Test edges with same keys, but diff df values
        edge1.__dict__["key"] = pd.DataFrame({'x': (0, 1, 2, 3, 4)})
        edge2.__dict__["key"] = pd.DataFrame({'x': (0, 1, 2, 3, 4)})
        edge3.__dict__["key"] = pd.DataFrame({'x': (0, 1, 2, 3, 5)})

        self.assertTrue(edge1 == edge2)
        self.assertFalse(edge1 == edge3)

        # Test edges with same keys, but diff np array vals
        # edge.__eq__ calls ndarray.all(), which checks that
        # all values in an array eval to true
        edge1.__dict__["key"] = np.array([True, True, True], dtype=np.bool)
        edge2.__dict__["key"] = np.array([True, True, True], dtype=np.bool)
        edge3.__dict__["key"] = np.array([True, True, False], dtype=np.bool)

        self.assertTrue(edge1 == edge2)
        self.assertFalse(edge1 == edge3)

    def test_repr(self):
        src = node.Node()
        dst = node.Node()
        masks = pd.DataFrame()

        e = edge.Edge()
        e.source = src
        e.destination = dst

        expected = """
        Source Image Index: {}
        Destination Image Index: {}
        Available Masks: {}
        """.format(src, dst, masks)

        self.assertEqual(expected, e.__repr__())

    def test_symmetry_check(self):
        # Matches is init to None
        e = edge.Edge()
        e.source = node.Node()
        e.destination = node.Node()
        # If there are no matches, should raise attrib err
        with (self.assertRaises(AttributeError)):
            e.symmetry_check()

    def test_ratio_check(self):
        # Matches is init to None
        e = edge.Edge()
        # If there are no matches, should raise attrib err
        with (self.assertRaises(AttributeError)):
            e.ratio_check()

        # If there are 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'])
        e.matches = matches_df
        expected = list(od.distance_ratio(matches_df))
        e.ratio_check()
        self.assertEqual(expected, list(e.masks["ratio"]))
Loading