Пример #1
0
    def test_filter_non_relevant_node_from_tree(self):
        g = hg.get_4_adjacency_graph((1, 8))
        edge_weights = np.asarray((0, 2, 0, 0, 1, 0, 0))
        tree, altitudes = hg.bpt_canonical(g, edge_weights)

        def functor(tree, altitudes):
            area = hg.attribute_area(tree)
            area_min_children = hg.accumulate_parallel(tree, area, hg.Accumulators.min)
            return area_min_children < 3

        res_tree, res_altitudes = hg.filter_non_relevant_node_from_tree(tree, altitudes, functor, canonize_tree=False)

        sm = hg.saliency(res_tree, res_altitudes)
        sm_ref = np.asarray((0, 0, 0, 0, 1, 0, 0))
        self.assertTrue(np.all(sm == sm_ref))

        ref_parents = (8, 8, 9, 9, 10, 11, 11, 12, 13, 10, 13, 12, 14, 14, 14)
        ref_altitudes = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
        self.assertTrue(hg.CptBinaryHierarchy.validate(res_tree))
        self.assertTrue(np.all(ref_parents == res_tree.parents()))
        self.assertTrue(np.all(res_altitudes == ref_altitudes))

        res_tree, res_altitudes = hg.filter_non_relevant_node_from_tree(tree, altitudes, functor, canonize_tree=True)

        sm = hg.saliency(res_tree, res_altitudes)
        sm_ref = np.asarray((0, 0, 0, 0, 1, 0, 0))
        self.assertTrue(np.all(sm == sm_ref))

        ref_parents = (8, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10)
        ref_tree = hg.Tree(ref_parents)
        ref_altitudes = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
        self.assertFalse(hg.CptBinaryHierarchy.validate(res_tree))
        self.assertTrue(hg.test_tree_isomorphism(res_tree, ref_tree))
        self.assertTrue(np.all(res_altitudes == ref_altitudes))
Пример #2
0
    def test_saliency(self):
        graph = hg.get_4_adjacency_graph((2, 3))
        edge_weights = np.asarray((1, 0, 2, 1, 1, 1, 2))
        sm = hg.saliency(*hg.bpt_canonical(graph, edge_weights))

        self.assertTrue(np.all(sm == edge_weights))

        labels = np.asarray(((1, 2, 3), (1, 4, 5)))
        rag = hg.make_region_adjacency_graph_from_labelisation(graph, labels)
        rag_edge_weights = np.asarray((1, 2, 1, 1, 1, 2))
        sm = hg.saliency(*hg.bpt_canonical(rag, rag_edge_weights))
        self.assertTrue(np.all(sm == edge_weights))
Пример #3
0
def filter_non_relevant_node_from_tree(tree,
                                       altitudes,
                                       non_relevant_functor,
                                       leaf_graph,
                                       canonize_tree=True):
    """
    Filter the given tree according to a functor telling if nodes are relevant or not.

    In a binary a tree, each inner node (non leaf node) is associated to the frontier separating its two children.
    If a the frontier associated to a node is considered as non relevant (for example because on of the two children
    of the node is too small) then the corresponding frontier is removed effectively merging its two children.

    This function returns a binary partition tree such that:

        - the frontiers associated to nodes marked *non-relevant* do not exist anymore;
        - the regions of the new tree are either regions of the initial tree or regions obtained by merging adjacent
          regions of the initial tree.

    If :attr:`tree` does not satisfy the concept :class:`~higra.CptBinaryHierarchy`, the given tree is first transformed
    into a binary tree (arbitrary choices are made).

    :attr:`non_relevant_functor` must be a function that accepts two arguments, a binary tree and its node altitudes,
    and must return a boolean node attribute for the given tree (ie a 1d array of boolean-ish values of size
    ``tree.num_vertices()``. A value of ``True`` is interpreted as *this node is not relevant and its associated
    frontier must be removed*.

    :See:

        :func:`~higra.filter_small_nodes_from_tree`
        :func:`~higra.filter_weak_frontier_nodes_from_tree`

    :param tree: input tree (Concept :class:`~higra.CptHierarchy`)
    :param altitudes: node altitudes of the input tree
    :param non_relevant_functor: a function that computes an attribute on a binary tree
    :param leaf_graph: graph of the tree leaves (deduced from :class:`~higra.CptHierarchy`)
    :param canonize_tree: if ``True`` (default), the resulting hierarchy is canonized (see function :func:`~higra.canonize_hierarchy`),
           otherwise the returned hierarchy is a binary tree
    :return: a tree (Concept :class:`~higra.CptHierarchy` is ``True`` and :class:`~higra.CptBinaryHierarchy` otherwise)
             and its node altitudes
    """

    if not hg.CptBinaryHierarchy.validate(tree):
        saliency = hg.saliency(tree, altitudes, leaf_graph, handle_rag=False)
        tree, altitudes = hg.bpt_canonical(leaf_graph, saliency)

    mst = hg.CptBinaryHierarchy.get_mst(tree)
    deleted_frontier_nodes = non_relevant_functor(tree, altitudes)

    mst_edge_weights = altitudes[tree.num_leaves():]
    mst_edge_weights[deleted_frontier_nodes[tree.num_leaves():]] = 0
    tree, altitudes = hg.bpt_canonical(mst, mst_edge_weights)

    if canonize_tree:
        tree, altitudes = hg.canonize_hierarchy(tree, altitudes)

    return tree, altitudes
Пример #4
0
    def test_filter_weak_frontier_nodes_from_tree(self):
        g = hg.get_4_adjacency_graph((1, 8))
        edge_weights = np.asarray((0, 2, 0, 0, 1, 0, 0))
        tree, altitudes = hg.bpt_canonical(g, edge_weights)

        res_tree, res_altitudes = hg.filter_weak_frontier_nodes_from_tree(tree, altitudes, edge_weights, 2)

        sm = hg.saliency(res_tree, res_altitudes)
        sm_ref = np.asarray((0, 2, 0, 0, 0, 0, 0))
        self.assertTrue(np.all(sm == sm_ref))
Пример #5
0
    def test_filter_non_relevant_node_from_tree(self):
        g = hg.get_4_adjacency_graph((1, 8))
        edge_weights = np.asarray((0, 2, 0, 0, 1, 0, 0))
        tree, altitudes = hg.quasi_flat_zone_hierarchy(g, edge_weights)

        res_tree, res_altitudes = hg.filter_small_nodes_from_tree(tree, altitudes, 3)

        sm = hg.saliency(res_tree, res_altitudes)
        sm_ref = np.asarray((0, 0, 0, 0, 1, 0, 0))
        self.assertTrue(np.all(sm == sm_ref))
Пример #6
0
    def test_filter_small_node_from_tree_on_rag(self):
        g = hg.get_4_adjacency_graph((2, 8))
        labels = np.asarray(((0, 1, 2, 3, 4, 5, 6, 7),
                             (0, 1, 2, 3, 4, 5, 6, 7)))

        rag = hg.make_region_adjacency_graph_from_labelisation(g, labels)
        edge_weights = np.asarray((0, 2, 0, 0, 1, 0, 0))

        tree, altitudes = hg.quasi_flat_zone_hierarchy(rag, edge_weights)

        res_tree, res_altitudes = hg.filter_small_nodes_from_tree(tree, altitudes, 5)

        sm = hg.saliency(res_tree, res_altitudes, handle_rag=False)
        sm_ref = np.asarray((0, 0, 0, 0, 1, 0, 0))
        self.assertTrue(np.all(sm == sm_ref))

        sm = hg.saliency(res_tree, res_altitudes, handle_rag=True)
        sm_ref = np.asarray((0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0))
        self.assertTrue(np.all(sm == sm_ref))
Пример #7
0
    def get_contour_masked(self, output, masked):
        import numpy as np
        import higra as hg
        hh = np.copy(masked)
        hh[hh > 0] = 255

        from scipy import signal

        Ix = signal.correlate2d(hh[:, :, 0],
                                [[1, 2, 1], [0, 0, 0], [-1, -2, -1]],
                                mode='same',
                                boundary='symm')
        Iy = signal.correlate2d(hh[:, :, 0],
                                [[1, 0, -1], [2, 0, -2], [1, 0, -1]],
                                mode='same',
                                boundary='symm')

        G = np.hypot(Ix, Iy)

        G = G / G.max()

        size = hh.shape[:2]

        gradient_coarse = np.array([output[1], G]).max(axis=0)
        gradient_fine = np.array([output[0], G]).max(axis=0)
        gradient_orientation = output[2]

        graph = hg.get_4_adjacency_graph(size)
        edge_weights_fine = hg.weight_graph(graph, gradient_fine,
                                            hg.WeightFunction.mean)
        edge_weights_coarse = hg.weight_graph(graph, gradient_coarse,
                                              hg.WeightFunction.mean)
        edge_weights_hig = hg.weight_graph(graph, G, hg.WeightFunction.mean)

        # special handling for angles to wrap around the trigonometric cycle...
        edge_orientations_source = hg.weight_graph(graph, gradient_orientation,
                                                   hg.WeightFunction.source)
        edge_orientations_target = hg.weight_graph(graph, gradient_orientation,
                                                   hg.WeightFunction.target)
        edge_orientations = hg.mean_angle_mod_pi(edge_orientations_source,
                                                 edge_orientations_target)

        combined_hierarchy1, altitudes_combined1 = hg.multiscale_mean_pb_hierarchy(
            graph,
            edge_weights_fine,
            others_edge_weights=(edge_weights_coarse, ),
            edge_orientations=edge_orientations)

        return hg.graph_4_adjacency_2_khalimsky(
            graph, hg.saliency(combined_hierarchy1, altitudes_combined1))
Пример #8
0
    def test_filter_non_relevant_node_from_tree(self):
        g = hg.get_4_adjacency_graph((1, 8))
        edge_weights = np.asarray((0, 2, 0, 0, 1, 0, 0))
        tree, altitudes = hg.bpt_canonical(g, edge_weights)

        def functor(tree, altitudes):
            area = hg.attribute_area(tree)
            area_min_children = hg.accumulate_parallel(tree, area, hg.Accumulators.min)
            return area_min_children < 3

        res_tree, res_altitudes = hg.filter_non_relevant_node_from_tree(tree, altitudes, functor)

        sm = hg.saliency(res_tree, res_altitudes)
        sm_ref = np.asarray((0, 0, 0, 0, 1, 0, 0))
        self.assertTrue(np.all(sm == sm_ref))
Пример #9
0
def ultrametric_open(graph, edge_weights):
    """
    Subdominant ultrametric of the given edge weighted graph.

    The subdominant ultrametric relative to a given dissimilarity measure (here the graph edge weights)
    is defined as the largest ultrametric smaller than the dissimilarity measure.

    In the case of an edge weighted undirected graph, the value of the subdominant ultrametric on the
    edge :math:`e_{xy}` is given by the min-max distance between :math:`x` and :math:`y`.

    Complexity: :math:`\mathcal{O}(n*log(n))` with :math:`n` the number of edges in the graph

    :param graph: Input graph
    :param edge_weights: Graph edge weights
    :return: edge weights corresponding to the subdominant ultrametric
    """
    tree, altitudes = hg.bpt_canonical(edge_weights, graph)
    return hg.saliency(altitudes)
Пример #10
0
    def test_random_binary_partition_tree_concepts(self):
        size = 32
        tree, altitudes = hg.random_binary_partition_tree(size, 0)
        leaf_graph = hg.CptHierarchy.get_leaf_graph(tree)

        self.assertTrue(leaf_graph.num_vertices() == size)
        self.assertTrue(leaf_graph.num_edges() == size - 1)

        sm = hg.saliency(tree, altitudes)
        self.assertTrue(np.all(sm == altitudes[size:]))

        mst = hg.CptBinaryHierarchy.get_mst(tree)
        self.assertTrue(
            np.all(
                hg.CptMinimumSpanningTree.get_edge_map(mst) ==
                hg.CptBinaryHierarchy.get_mst_edge_map(tree)))
        self.assertTrue(mst == leaf_graph)
        self.assertTrue(
            leaf_graph == hg.CptMinimumSpanningTree.get_base_graph(mst))
Пример #11
0
def multiscale_mean_pb_hierarchy(graph,
                                 fine_edge_weights,
                                 others_edge_weights,
                                 shape,
                                 edge_orientations=None):
    """
    Multiscale mean probability boundary hierarchy.

    The method is described in:

        J. Pont-Tuset, P. Arbeláez, J. Barron, F. Marques, and J. Malik
        Multiscale Combinatorial Grouping for Image Segmentation and Object Proposal Generation
        IEEE Transactions on Pattern Analysis and Machine Intelligence (TPAMI), vol. 39, no. 1, pp. 128 - 140, 2017.

    and in:

        K.K. Maninis, J. Pont-Tuset, P. Arbeláez and L. Van Gool
        Convolutional Oriented Boundaries: From Image Segmentation to High-Level Tasks
        IEEE Transactions on Pattern Analysis and Machine Intelligence (TPAMI), vol. 40, no. 4, pp. 819 - 833, 2018.

    This does not include gradient estimation.

    The returned hierarchy is defined on the gradient watershed super-pixels.

    The final sigmoid scaling of the hierarchy altitude is not performed.

    :param graph: must be a 4 adjacency graph (Concept :class:`~higra.CptGridGraph`)
    :param fine_edge_weights: edge weights of the finest gradient
    :param others_edge_weights: tuple of gradient value on edges
    :param shape: shape of the graph, i.e. a pair (height, width) (deduced from :class:`~higra.CptGridGraph`)
    :param edge_orientations: estimated orientation of the gradient on edges (optional)
    :return: a tree (Concept :class:`~higra.CptHierarchy`) and its node altitudes
    """
    shape = hg.normalize_shape(shape)
    tree_fine, altitudes_fine = hg.mean_pb_hierarchy(
        graph,
        fine_edge_weights,
        shape=shape,
        edge_orientations=edge_orientations)
    saliency_fine = hg.saliency(tree_fine, altitudes_fine)
    super_vertex_fine = hg.labelisation_hierarchy_supervertices(
        tree_fine, altitudes_fine)

    other_hierarchies = []
    for edge_weights in others_edge_weights:
        tree_coarse, altitudes_coarse = hg.mean_pb_hierarchy(
            graph,
            edge_weights,
            shape=shape,
            edge_orientations=edge_orientations)
        other_hierarchies.append((tree_coarse, altitudes_coarse))

    aligned_saliencies = hg.align_hierarchies(graph, super_vertex_fine,
                                              other_hierarchies)

    for saliency in aligned_saliencies:
        saliency_fine += saliency

    saliency_fine *= (1.0 / (1 + len(others_edge_weights)))

    return hg.mean_pb_hierarchy(graph, saliency_fine, shape=shape)