Commit ad9dc7f3 authored by jay's avatar jay
Browse files

Updates for testing and test coverage, equality logic.

parent 39d2a58c
Loading
Loading
Loading
Loading
+2 −18
Original line number Diff line number Diff line
@@ -59,24 +59,8 @@ class Edge(dict, MutableMapping):
        """.format(self.source, self.destination, self.masks)

    def __eq__(self, other):
        eq = True
        d = self.__dict__
        o = other.__dict__
        for k, v in d.items():
            # If the attribute key is missing they can not be equal
            if not k in o.keys():
                eq = False
                return eq
            if isinstance(v, pd.DataFrame):
                if not v.equals(o[k]):
                    eq = False
                    print(k)
            elif isinstance(v, np.ndarray):
                if not v.all() == o[k].all():
                    eq = False
                    print(k)

        return eq
        return utils.compare_dicts(self.__dict__, other.__dict__) *\
               utils.compare_dicts(self, other)

    def match(self, k=2, **kwargs):

+7 −4
Original line number Diff line number Diff line
@@ -89,15 +89,18 @@ class CandidateGraph(nx.Graph):
        self.graph['modifieddate'] = strftime("%Y-%m-%d %H:%M:%S", gmtime())

    def __eq__(self, other):
        eq = True
        # Check the nodes
        if sorted(self.nodes()) != sorted(other.nodes()):
            return False
        for n in self.nodes_iter():
            if not self.node[n] == other.node[n]:
                eq = False
                return False
        if sorted(self.edges()) != sorted(other.edges()):
            return False
        for s, d in self.edges_iter():
            if not self.edge[s][d] == other.edge[s][d]:
                eq = False
        return eq
                return False
        return True

    def _order_adjacency(self):  # pragma: no cover
        self.adj = OrderedDict(sorted(self.adj.items()))
+15 −43
Original line number Diff line number Diff line
@@ -65,7 +65,6 @@ class Node(dict, MutableMapping):
        self['image_path'] = image_path
        self['node_id'] = node_id
        self['hash'] = image_name
        self._mask_arrays = {}
        self.descriptors = None
        self.keypoints = pd.DataFrame()
        self.masks = pd.DataFrame()
@@ -81,7 +80,7 @@ class Node(dict, MutableMapping):
        """.format(self['node_id'], self['image_name'], self['image_path'],
                   self.nkeypoints, self.masks, self.__class__)

    def __hash__(self):
    def __hash__(self): #pragma: no cover
        return hash(repr(self))

    def __gt__(self, other):
@@ -89,7 +88,7 @@ class Node(dict, MutableMapping):
        oid = other['node_id']
        return myid > oid

    def __geq__(self, other):
    def __ge__(self, other):
        myid = self['node_id']
        oid = other['node_id']
        return myid >= oid
@@ -99,7 +98,7 @@ class Node(dict, MutableMapping):
        oid = other['node_id']
        return myid < oid

    def __leq__(self, other):
    def __le__(self, other):
        myid = self['node_id']
        oid = other['node_id']
        return myid <= oid
@@ -108,19 +107,9 @@ class Node(dict, MutableMapping):
        return str(self['node_id'])

    def __eq__(self, other):
        eq = True
        d = self.__dict__
        o = other.__dict__
        for k, v in d.items():
            if isinstance(v, pd.DataFrame):
                if not v.equals(o[k]):
                    print('NODE', k)
                    eq = False
            elif isinstance(v, np.ndarray):
                if not v.all() == o[k].all():
                    print('NODE', k)
                    eq = False
        return eq
        return utils.compare_dicts(self.__dict__, other.__dict__) *\
               utils.compare_dicts(self, other)


    @property
    def geodata(self):
@@ -169,7 +158,7 @@ class Node(dict, MutableMapping):
        Returns
        -------
        coverage_area :  float
                        Area covered by the generated
                         percentage area covered by the generated
                         keypoints
        """

@@ -182,9 +171,7 @@ class Node(dict, MutableMapping):

        total_area = max_x * max_y

        self.coverage_area = (hull_area/total_area)*100

        return self.coverage_area
        return hull_area / total_area

    def get_byte_array(self, band=1):
        """
@@ -258,17 +245,21 @@ class Node(dict, MutableMapping):

        return keypoints

    def get_raw_keypoint_coordinates(self, index):
    def get_raw_keypoint_coordinates(self, index=slice(None)):
        """
        The performance of get_keypoint_coordinates can be slow
        due to the ability for fancier indexing.  This method
        returns coordinates using numpy array accessors.

        Parameters
        ----------
        index : iterable
                positional indices to return from the global keypoints dataframe
        """
        index = index.astype(np.int)
        return self.keypoints.values[index,:2]

    @staticmethod
    def _extract_features(array, *args, **kwargs):
    def _extract_features(array, *args, **kwargs):  # pragma: no cover
        """
        Extract features for the node

@@ -417,25 +408,6 @@ class Node(dict, MutableMapping):
        io_keypoints.to_npy(self.keypoints, self.descriptors,
                            out_path + '_{}.npz'.format(self['node_id']))

    def coverage_ratio(self, clean_keys=[]):
        """
        Compute the ratio $area_{convexhull} / area_{total}$

        Returns
        -------
        ratio : float
                The ratio of convex hull area to total area.
        """
        ideal_area = self.geodata.pixel_area
        if not hasattr(self, 'keypoints'):
            raise AttributeError('Keypoints must be extracted already, they have not been.')

        #TODO: clean_keys are disabled - re-enable.
        keypoints = self.get_keypoint_coordinates()

        ratio = convex_hull_ratio(keypoints, ideal_area)
        return ratio

    def plot(self, clean_keys=[], **kwargs):  # pragma: no cover
        return plot_node(self, clean_keys=clean_keys, **kwargs)

+64 −2
Original line number Diff line number Diff line
import copy
import os
import time
import sys

import pandas as pd
import pytest
import unittest

from unittest.mock import patch, PropertyMock, MagicMock

@@ -41,13 +42,49 @@ def geo_graph():
def disconnected_graph():
    return network.CandidateGraph.from_adjacency(get_path('adjacency.json'))

@pytest.fixture()
def candidategraph(node_a, node_b, node_c):
    # TODO: Getting this fixture from the global conf is causing deepycopy
    # to fail.  Why?
    cg = network.CandidateGraph()

    # Create a candidategraph object - we instantiate a real CandidateGraph to
    # have access of networkx functionality we do not want to test and then
    # mock all autocnet functionality to control test behavior.
    edges = [(0,1), (0,2), (1,2)]
    cg.add_edges_from(edges)

    match_indices = [([0,1,2,3,4,5,6,7], [0,1,2,3,4,5,6,7]),
                     ([0,1,2,3,4,5,8,9], [0,1,2,3,4,5,8,9]),
                     ([0,1,2,3,4,5,8,9], [0,1,2,3,4,5,6,7])]

    matches = []
    for i, e in enumerate(edges):
        c = match_indices[i]
        source_image = np.repeat(e[0], 8)
        destin_image = np.repeat(e[1], 8)
        coords = np.zeros(8)
        data = np.vstack((source_image, c[0], destin_image, c[1],
                          coords, coords, coords, coords)).T
        matches_df = pd.DataFrame(data, columns=['source_image', 'source_idx', 'destination_image', 'destination_idx',
                                                 'source_x', 'source_y', 'destination_x', 'destination_y'])
        matches.append(matches_df)

    # Mock in autocnet methods
    cg.get_matches = MagicMock(return_value=matches)

    # Mock in the node objects onto the candidate graph
    cg.node[0] = node_a
    cg.node[1] = node_b
    cg.node[2] = node_c

    return cg

def test_get_name(graph):
    node_number = graph.graph['node_name_map']['AS15-M-0297_SML.png']
    name = graph.get_name(node_number)
    assert name == 'AS15-M-0297_SML.png'


def test_size(graph):
    assert graph.size() == graph.number_of_edges()
    for u, v, e in graph.edges_iter(data=True):
@@ -79,6 +116,31 @@ def test_add_image(graph):
    with pytest.raises(NotImplementedError):
        graph.add_image()

def test_equal(candidategraph):
    cg = copy.deepcopy(candidategraph)
    assert candidategraph == cg

    cg = copy.deepcopy(candidategraph)
    cg.remove_edge(0,1)
    assert candidategraph != cg

    cg = copy.deepcopy(candidategraph)
    cg.remove_node(0)
    assert candidategraph != cg

    cg = copy.deepcopy(candidategraph)
    cg.node[0]['image_name'] = 'foo'
    assert candidategraph != cg

    cg = copy.deepcopy(candidategraph)
    cg.edge[0][1]['fundamental_matrix'] = np.random.random((3,3))
    assert candidategraph != cg

def test_get_matches(candidategraph):
    matches = candidategraph.get_matches()
    assert len(matches) == 3
    assert len(matches[0]) == 8
    assert isinstance(matches[0], pd.DataFrame)

def test_island_nodes(disconnected_graph):
    assert len(disconnected_graph.island_nodes()) == 1
+47 −2
Original line number Diff line number Diff line
@@ -40,6 +40,18 @@ class TestNode(object):
        assert (1012, 1012) == image.shape
        assert np.uint8 == image.dtype

    def test_equalities(self,node_a, node_b):
        assert node_a < node_b
        assert node_a <= node_b
        assert not (node_a > node_b)
        assert not (node_a >= node_b)
        assert not (node_a == node_b)

        node_a.random_attr = np.arange(10)
        node_b.random_attr = np.arange(10)

        assert not (node_a == node_b)

    def test_get_array(self, node):
        image = node.get_array()
        assert (1012, 1012) == image.shape
@@ -74,6 +86,9 @@ class TestNode(object):
        assert kps['y'].min() < tilesize
        assert len(kps) == pytest.approx(90, 3)

        with pytest.raises(ValueError) as e_info:
            node.extract_features_with_tiling(tilesize=10, overlap=20)

    def test_masks(self, node):
        image = node.get_array()
        node.extract_features(image, extractor_parameters={'nfeatures': 5})
@@ -120,7 +135,7 @@ class TestNode(object):
        image = node.get_array()
        node.extract_features(image, extractor_method='sift', extractor_parameters={'nfeatures': 10})
        coverage_percn = node.coverage()
        assert coverage_percn == pytest.approx(38.06139557, 2)
        assert coverage_percn == pytest.approx(0.3806139557, 2)

    def test_clean(self, node):
        with pytest.raises(AttributeError):
@@ -132,6 +147,36 @@ class TestNode(object):
        matches, mask = node._clean(clean_keys=['a'])
        assert mask.equals(pd.Series([True, True, True, False, False]))

    def test_footprint(self, geo_node):
    def test_get_keypoints(self, node):
        image = node.get_array()
        node.extract_features(image, extractor_parameters={'nfeatures':5})
        kps = node.get_keypoints(index=[1,3])
        assert len(kps) == 2
        assert 1 in kps.index and 3 in kps.index

    def test_get_keypoint_coordinates(self, node):
        image = node.get_array()
        node.extract_features(image, extractor_parameters={'nfeatures':5})
        kpc = node.get_keypoint_coordinates()
        assert 'x' in kpc.columns
        assert 'y' in kpc.columns
        kpc = node.get_keypoint_coordinates(index=[2,4])
        assert len(kpc) == 2
        kpc = node.get_keypoint_coordinates(homogeneous=True)
        assert (kpc.homogeneous == 1).all()

    def test_get_raw_keypoint_coordinates(self, node):
        image = node.get_array()
        node.extract_features(image, extractor_parameters={'nfeatures':5})
        kpc = node.get_raw_keypoint_coordinates()
        assert isinstance(kpc, np.ndarray)
        assert kpc.shape == (5,2)

        kpc = node.get_raw_keypoint_coordinates(-1)
        assert kpc.shape == (2,)


    def test_footprint(self, geo_node, node_a):
        # Esnure that a shapely compliant poly is being returned
        assert isinstance(geo_node.footprint, Polygon)
        assert node_a.footprint == None
Loading