Ejemplo n.º 1
0
def constrained_connectivity_hierarchy_strong_connection(graph, edge_weights):
    """
    Strongly constrained connectivity hierarchy based on the given edge weighted graph.

    Let :math:`X` be a set of vertices, the range of :math:`X` is the maximal weight of the edges linking two vertices inside :math:`X`.

    Let :math:`\\alpha` be a positive real number, a set of vertices :math:`X` is :math:`\\alpha`-connected, if for any two vertices
    :math:`i` and :math:`j` in :math:`X`, there exists a path from :math:`i` to :math:`j` in :math:`X` composed of edges of weights
    lower than or equal to :math:`\\alpha`.

    Let :math:`\\alpha`  be a positive real numbers, the :math:`\\alpha`-strongly connected components of the graph are
    the maximal :math:`\\alpha'`-connected sets of vertices with a range lower than or equal to :math:`\\alpha` with :math:`\\alpha'\leq\\alpha`.

    Finally, the strongly constrained connectivity hierarchy is defined as the hierarchy composed of all the
    :math:`\\alpha`- strongly connected components for all positive :math:`\\alpha`.

    The definition used follows the one given in:

        P. Soille,
        "Constrained connectivity for hierarchical image partitioning and simplification,"
        in IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 30, no. 7, pp. 1132-1145, July 2008.
        doi: 10.1109/TPAMI.2007.70817

    The algorithm runs in time :math:`\mathcal{O}(n\log(n))` and proceeds by filtering a quasi-flat zone hierarchy (see :func:`~higra.quasi_flat_zones_hierarchy`)

    :param graph: input graph
    :param edge_weights: edge_weights: edge weights of the input graph
    :return: a tree (Concept :class:`~higra.CptHierarchy`) and its node altitudes
    """

    tree, altitudes = hg.quasi_flat_zone_hierarchy(graph, edge_weights)
    altitude_parents = altitudes[tree.parents()]

    # max edge weights inside each region
    lca_map = hg.attribute_lca_map(tree)
    max_edge_weights = np.zeros((tree.num_vertices(),), dtype=edge_weights.dtype)
    np.maximum.at(max_edge_weights, lca_map, edge_weights)
    max_edge_weights = hg.accumulate_and_max_sequential(tree,
                                                        max_edge_weights,
                                                        max_edge_weights[:tree.num_leaves()],
                                                        hg.Accumulators.max)

    # parent node can't be deleted
    altitude_parents[tree.root()] = max(altitudes[tree.root()], max_edge_weights[tree.root()])

    # nodes whith a range greater than the altitudes of their parent have to be deleted
    violated_constraints = max_edge_weights >= altitude_parents

    # the altitude of nodes with a range greater than their altitude but lower than the one of their parent must be changed
    reparable_node_indices = np.nonzero(
        np.logical_and(max_edge_weights > altitudes, max_edge_weights < altitude_parents))
    altitudes[reparable_node_indices] = max_edge_weights[reparable_node_indices]

    # final  result construction
    tree, node_map = hg.simplify_tree(tree, violated_constraints)
    altitudes = altitudes[node_map]
    hg.CptHierarchy.link(tree, graph)

    return tree, altitudes
Ejemplo n.º 2
0
 def test_dendrogram_purity_random(self):
     g = hg.get_4_adjacency_graph((10, 10))
     np.random.seed(42)
     for i in range(5):
         ew = np.random.randint(0, 20, g.num_edges())
         tree, _ = hg.quasi_flat_zone_hierarchy(g, ew)
         labels = np.random.randint(0, 10, (100, ))
         v1 = hg.dendrogram_purity(tree, labels)
         v2 = dendrogram_purity_naif(tree, labels)
         self.assertTrue(np.allclose(v1, v2))
Ejemplo n.º 3
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))
Ejemplo n.º 4
0
    def test_attribute_tree_sampling_probability_edge_model(self):
        g = hg.get_4_adjacency_graph((3, 3))
        edge_weights = np.asarray((0, 6, 2, 6, 0, 0, 5, 4, 5, 3, 2, 2))
        tree, altitudes = hg.quasi_flat_zone_hierarchy(g, edge_weights)
        res = hg.attribute_tree_sampling_probability(tree,
                                                     g,
                                                     edge_weights,
                                                     model='edge')

        ref = np.asarray((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 3,
                          26)) / np.sum(edge_weights)
        self.assertTrue(np.allclose(ref, res))
Ejemplo n.º 5
0
    def test_QFZ(self):
        graph = hg.get_4_adjacency_graph((2, 3))

        edge_weights = np.asarray((1, 0, 2, 1, 1, 1, 2))

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

        tref = hg.Tree(
            np.asarray((6, 7, 8, 6, 7, 8, 7, 9, 9, 9), dtype=np.int64))

        self.assertTrue(hg.test_tree_isomorphism(tree, tref))
        self.assertTrue(np.allclose(altitudes, (0, 0, 0, 0, 0, 0, 0, 1, 1, 2)))
Ejemplo n.º 6
0
    def test_tree_sampling_divergence(self):
        g = hg.get_4_adjacency_graph((3, 3))
        edge_weights = np.asarray((0, 6, 2, 6, 0, 0, 5, 4, 5, 3, 2, 2))
        tree, altitudes = hg.quasi_flat_zone_hierarchy(g, edge_weights)

        cost = hg.tree_sampling_divergence(tree, edge_weights)

        p = [0., 0., 0., 0.05714286, 0.11428571, 0.08571429, 0.74285714]
        q = [
            0.03918367, 0.01142857, 0.13469388, 0.10285714, 0.11673469,
            0.39428571, 0.93387755
        ]

        ref_cost = p[3] * np.log(p[3] / q[3]) + p[4] * np.log(
            p[4] / q[4]) + p[5] * np.log(p[5] / q[5]) + p[6] * np.log(
                p[6] / q[6])
        self.assertTrue(np.isclose(cost, ref_cost))
Ejemplo n.º 7
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))
Ejemplo n.º 8
0
    def test_attribute_tree_sampling_probability_null_model(self):
        g = hg.get_4_adjacency_graph((3, 3))
        edge_weights = np.asarray((0, 6, 2, 6, 0, 0, 5, 4, 5, 3, 2, 2))
        tree, altitudes = hg.quasi_flat_zone_hierarchy(g, edge_weights)
        res = hg.attribute_tree_sampling_probability(tree,
                                                     g,
                                                     edge_weights,
                                                     model='null')

        Z = np.sum(edge_weights)
        ref = np.asarray(
            (0, 0, 0, 0, 0, 0, 0, 0, 0,
             6 * 8, 2 * 7,
             11 * 15,
             6 * 2 + 6 * 7 + 8 * 2 + 8 * 7,
             7 * 9 + 7 * 5 + 9 * 5,
             6 * 7 + 6 * 9 + 6 * 5 + 8 * 7 + 8 * 9 + 8 * 5 + 2 * 7 + 2 * 9 + 2 * 5 + 7 * 7 + 7 * 9 + 7 * 5,
             6 * 11 + 6 * 15 + 8 * 11 + 8 * 15 + 2 * 11 + 2 * 15 + 11 * 7 + 11 * 7 + 11 * 9 + 11 * 5 + 15 * 7 + 15 * 7 + 15 * 9 + 15 * 5)) / \
              (Z * Z)
        self.assertTrue(np.allclose(ref, res))
Ejemplo n.º 9
0
def constrained_connectivity_hierarchy_alpha_omega(graph, vertex_weights):
    """
    Alpha-omega constrained connectivity hierarchy based on the given vertex weighted graph.

    For  :math:`(i,j)` be an edge of the graph, we define :math:`w(i,j)=|w(i) - w(j)|`, the weight of this edge.
    Let :math:`X` be a set of vertices, the range of :math:`X` is the maximal absolute difference between the weights of any two vertices in :math:`X`:
    :math:`R(X) = \max\{|w(i) - w(j)|, (i,j)\in X^2\}`

    Let :math:`\\alpha` be a positive real number, a set of vertices :math:`X` is :math:`\\alpha`-connected, if for any two vertices
    :math:`i` and :math:`j` in :math:`X`, there exists a path from :math:`i` to :math:`j` in :math:`X` composed of edges of weights
    lower than or equal to :math:`\\alpha`.

    Let :math:`\\alpha`  and :math:`\omega` be a two positive real numbers, the :math:`\\alpha-\omega`-connected components of the graph are
    the maximal :math:`\\alpha'`-connected sets of vertices with a range lower than or equal to :math:`\omega`, with :math:`\\alpha'\leq\\alpha`.

    Finally, the alpha-omega constrained connectivity hierarchy is defined as the hierarchy composed of all the :math:`k-k`-connected components for all positive :math:`k`.

    The definition used follows the one given in:

        P. Soille,
        "Constrained connectivity for hierarchical image partitioning and simplification,"
        in IEEE Transactions on Pattern Analysis and Machine Intelligence, vol. 30, no. 7, pp. 1132-1145, July 2008.
        doi: 10.1109/TPAMI.2007.70817

    The algorithm runs in time :math:`\mathcal{O}(n\log(n))` and proceeds by filtering a quasi-flat zone hierarchy (see :func:`~higra.quasi_flat_zones_hierarchy`)

    :param graph: input graph
    :param vertex_weights: edge_weights: edge weights of the input graph
    :return: a tree (Concept :class:`~higra.CptHierarchy`) and its node altitudes
    """

    vertex_weights = hg.linearize_vertex_weights(vertex_weights, graph)
    if vertex_weights.ndim != 1:
        raise ValueError("constrainted_connectivity_hierarchy_alpha_omega only works for scalar vertex weights.")

    # QFZ on the L1 distance weighted graph
    edge_weights = hg.weight_graph(graph, vertex_weights, hg.WeightFunction.L1)
    tree, altitudes = hg.quasi_flat_zone_hierarchy(graph, edge_weights)
    altitude_parents = altitudes[tree.parents()]

    # vertex value range inside each region
    min_value = hg.accumulate_sequential(tree, vertex_weights, hg.Accumulators.min)
    max_value = hg.accumulate_sequential(tree, vertex_weights, hg.Accumulators.max)
    value_range = max_value - min_value

    # parent node can't be deleted
    altitude_parents[tree.root()] = max(altitudes[tree.root()], value_range[tree.root()])

    # nodes whith a range greater than the altitudes of their parent have to be deleted
    violated_constraints = value_range >= altitude_parents

    # the altitude of nodes with a range greater than their altitude but lower than the one of their parent must be changed
    reparable_node_indices = np.nonzero(np.logical_and(value_range > altitudes, value_range < altitude_parents))
    altitudes[reparable_node_indices] = value_range[reparable_node_indices]

    # final  result construction
    tree, node_map = hg.simplify_tree(tree, violated_constraints)
    altitudes = altitudes[node_map]
    hg.CptHierarchy.link(tree, graph)

    return tree, altitudes