Example #1
0
def attribute_vertex_perimeter(graph, edge_length=None):
    """
    Vertex perimeter of the given graph.
    The perimeter of a vertex is defined as the sum of the length of out-edges of the vertex.

    If the input graph has an attribute value `no_border_vertex_out_degree`, then each vertex perimeter is assumed to be
    equal to this attribute value. This is a convenient method to handle image type graphs where an outer border has to be
    considered.

    :param graph: input graph
    :param edge_length: length of the edges of the input graph (provided by :func:`~higra.attribute_edge_length` on `graph`)
    :return: a nd array
    """
    if edge_length is None:
        edge_length = hg.attribute_edge_length(graph)

    special_case_border_graph = hg.get_attribute(
        graph, "no_border_vertex_out_degree")

    if special_case_border_graph is not None:
        res = np.full((graph.num_vertices(), ),
                      special_case_border_graph,
                      dtype=np.float64)
        res = hg.delinearize_vertex_weights(res, graph)
        return res

    res = hg.accumulate_graph_edges(graph, edge_length, hg.Accumulators.sum)
    res = hg.delinearize_vertex_weights(res, graph)
    return res
Example #2
0
def rag_back_project_vertex_weights(graph, vertex_weights):
    """
    Projects rag vertex weights onto original graph vertices.
    The result is an array weighting the vertices of the original graph of the rag such that:
    for any vertex :math:`v` of the original graph, its weight is equal to the weight of the vertex of the rag that represents
    the region that contains :math:`v`.

    For any vertex index :math:`i`,
    :math:`result[i] = rag\_vertex\_weight[rag\_vertex_map[i]]`

    :param graph: input region adjacency graph
    :param vertex_weights: vertex weights on the input region adjacency graph
    :return: vertex weights on the original graph
    """

    rag = hg.CptRegionAdjacencyGraph.construct(graph)
    if graph.num_vertices() != vertex_weights.shape[0]:
        raise Exception("vertex_weights size does not match graph size.")

    new_weights = hg.cpp._rag_back_project_weights(rag["vertex_map"],
                                                   vertex_weights)

    new_weights = hg.delinearize_vertex_weights(new_weights, rag["pre_graph"])

    return new_weights
Example #3
0
File: tree.py Project: higra/Higra
def labelisation_hierarchy_supervertices(tree,
                                         altitudes,
                                         leaf_graph=None,
                                         handle_rag=True):
    """
    Labelize the tree leaves into supervertices.
    The altitudes must be increasing, i.e. for any nodes :math:`i, j` such that :math:`j` is an ancestor of :math:`i`,
    then :math:`altitudes[i] \leq altitudes[j]`.
    Two leaves are in the same supervertex if they have a common ancestor at altitude 0.

    If we consider that the pair :math:`(tree, altitudes)` represents a dendrogram, i.e. that it defines a
    pseudo-ultrametric on the set of leaves, a supervertex is a maximal cluster such that the distance between
    any pair of points in the cluster is equal to 0.

    This functions guaranties that the labels are in the range :math:`[0, num\_supervertices-1]`.

    :param tree: input tree (Concept :class:`~higra.CptHierarchy`)
    :param altitudes: node altitudes of the input tree
    :param leaf_graph:  graph of the tree leaves (optional, deduced from :class:`~higra.CptHierarchy`)
    :param handle_rag: if True and the provided tree has been built on a region adjacency graph, then the labelisation corresponding to the rag regions is returned.
    :return: Leaf labels
    """

    if hg.CptRegionAdjacencyGraph.validate(leaf_graph) and handle_rag:
        return hg.CptRegionAdjacencyGraph.construct(leaf_graph)["vertex_map"]

    leaf_labels = hg.cpp._labelisation_hierarchy_supervertices(tree, altitudes)

    if leaf_graph is not None:
        leaf_labels = hg.delinearize_vertex_weights(leaf_labels, leaf_graph)

    return leaf_labels
Example #4
0
def labelisation_hierarchy_supervertices(tree,
                                         altitudes,
                                         leaf_graph=None,
                                         handle_rag=True):
    """
    Labelize the tree leaves into supervertices.

    Two leaves are in the same supervertex if they have a common ancestor of altitude 0.

    This functions guaranties that the labels are in the range [0, num_supervertices-1].

    :param tree: input tree (Concept :class:`~higra.CptHierarchy`)
    :param altitudes: node altitudes of the input tree
    :param leaf_graph:  graph of the tree leaves (optional, deduced from :class:`~higra.CptHierarchy`)
    :param handle_rag: if True and the provided tree has been built on a region adjacency graph, then the labelisation corresponding to the rag regions is returned.
    :return: Leaf labels

    """

    if hg.CptRegionAdjacencyGraph.validate(leaf_graph) and handle_rag:
        return hg.CptRegionAdjacencyGraph.construct(leaf_graph)["vertex_map"]

    leaf_labels = hg.cpp._labelisation_hierarchy_supervertices(tree, altitudes)

    if leaf_graph is not None:
        leaf_labels = hg.delinearize_vertex_weights(leaf_labels, leaf_graph)

    return leaf_labels
Example #5
0
File: tree.py Project: higra/Higra
def binary_labelisation_from_markers(tree,
                                     object_marker,
                                     background_marker,
                                     leaf_graph=None):
    """
    Given two binary markers :math:`o` (object) and :math:`b` (background) (given by their indicator functions)
    on the leaves of a tree :math:`T`, the corresponding binary labelization of the leaves of :math:`T` is defined as
    the union of all the nodes intersecting :math:`o` but not :math:`b`:

    .. math::

        res = \\bigcup \{R \in T \mid R \cap o \\neq \emptyset, \\textrm{ and } R \cap b = \emptyset\}

    :param tree: input tree (Concept :class:`~higra.CptHierarchy`)
    :param object_marker: indicator function of the object marker: 1d array of size tree.num_leaves() where non zero values correspond to the object marker
    :param background_marker: indicator function of the background marker: 1d array of size tree.num_leaves() where non zero values correspond to the background marker
    :param leaf_graph: graph on the leaves of the input tree (optional, deduced from :class:`~higra.CptHierarchy`)
    :return: Leaf labels
    """

    if leaf_graph is not None:
        object_marker = hg.linearize_vertex_weights(object_marker, leaf_graph)
        background_marker = hg.linearize_vertex_weights(
            background_marker, leaf_graph)

    object_marker, background_marker = hg.cast_to_common_type(
        object_marker, background_marker)
    labels = hg.cpp._binary_labelisation_from_markers(tree, object_marker,
                                                      background_marker)

    if leaf_graph is not None:
        labels = hg.delinearize_vertex_weights(labels, leaf_graph)

    return labels
Example #6
0
File: tree.py Project: higra/Higra
def labelisation_horizontal_cut_from_threshold(tree,
                                               altitudes,
                                               threshold,
                                               leaf_graph=None):
    """
    Labelize tree leaves according to an horizontal cut of the tree given by its altitude.

    Two leaves are in the same region (ie. have the same label) if
    the altitude of their lowest common ancestor is strictly greater
    than the specified threshold.

    Consider using the class :class:`~higra.HorizontalCutExplorer` if you plan to compute several horizontal cuts from a
    same hierarchy.

    :param tree: input tree (deduced from :class:`~higra.CptHierarchy`)
    :param altitudes: node altitudes of the input tree
    :param threshold: a threshold level
    :param leaf_graph: graph of the tree leaves (optional, deduced from :class:`~higra.CptHierarchy`)
    :return: Leaf labels
    """

    leaf_labels = hg.cpp._labelisation_horizontal_cut_from_threshold(
        tree, float(threshold), altitudes)

    if leaf_graph is not None:
        leaf_labels = hg.delinearize_vertex_weights(leaf_labels, leaf_graph)

    return leaf_labels
Example #7
0
def labelisation_watershed(graph, edge_weights):
    """
    Watershed cut of the given edge weighted graph.

    The definition and algorithm used are described in:

        J. Cousty, G. Bertrand, L. Najman and M. Couprie.
        `Watershed cuts: minimum spanning forests, and the drop of water principle <https://hal-upec-upem.archives-ouvertes.fr/hal-00622410/document>`_.
        IEEE Trans. on Pattern Analysis and Machine Intelligence, 31(8): 1362-1374, 2009.

    The watershed cut is represented by a labelisation of the graph vertices.

    :Complexity:

    This algorithm has a linear runtime complexity :math:`\mathcal{O}(n)` with :math:`n` the number of edges in the graph.


    :param graph: input graph
    :param edge_weights: Weights on the edges of the graph
    :return: A labelisation of the graph vertices
   """
    vertex_labels = hg.cpp._labelisation_watershed(graph, edge_weights)

    vertex_labels = hg.delinearize_vertex_weights(vertex_labels, graph)

    return vertex_labels
Example #8
0
def read_graph_pink(filename):
    """
    Read a graph file stored in pink ascii format

    :param filename: path to the graph file
    :return: a tuple (graph, vertex_weights, edge_weights)
    """
    graph, vertex_weights, edge_weights, shape = hg.cpp._read_graph_pink(
        filename)

    hg.CptGridGraph.link(graph, shape)
    vertex_weights = hg.delinearize_vertex_weights(vertex_weights, graph,
                                                   shape)

    return graph, vertex_weights, edge_weights
Example #9
0
def graph_cut_2_labelisation(graph, edge_weights):
    """
    Labelises graph vertices according to the given graph cut.

    Each edge having a non zero value in the given edge_weights
    are assumed to be part of the cut.

    :param graph: Input graph
    :param edge_weights: Weights on the edges of the graph
    :return: A labelisation of the graph vertices
    """
    vertex_labels = hg.cpp._graph_cut_2_labelisation(graph, edge_weights)

    vertex_labels = hg.delinearize_vertex_weights(vertex_labels, graph)

    return vertex_labels
Example #10
0
def reconstruct_leaf_data(tree, altitudes, deleted_nodes, leaf_graph=None):
    """
    Each leaf of the tree takes the altitude of its closest non deleted ancestor.

    :param tree: input tree (Concept :class:`~higra.CptHierarchy`)
    :param altitudes: node altitudes of the input tree
    :param deleted_nodes: binary node weights indicating which nodes are deleted
    :param leaf_graph: graph of the tree leaves (optional, deduced from :class:`~higra.CptHierarchy`)
    :return: Leaf weights
    """
    reconstruction = hg.propagate_sequential(tree, altitudes, deleted_nodes)
    leaf_weights = reconstruction[0:tree.num_leaves(), ...]

    if leaf_graph is not None:
        leaf_weights = hg.delinearize_vertex_weights(leaf_weights, leaf_graph)

    return leaf_weights
Example #11
0
def __reconstruct_leaf_data(self, tree, altitudes, leaf_graph, handle_rag=True):
    """
    Reconstruct the cut at leaf level of the provided tree.
    A leaf of the tree is valued by the altitude of the single cut node which is an ancestor of the leaf.

    :param tree: input tree (Concept :class:`~higra.CptHierarchy`)
    :param altitudes: node altitudes of the input tree
    :param leaf_graph: graph on the tree leaves (deduced from :class:`~higra.CptHierarchy`)
    :param handle_rag: if `True` and if `leaf_graph` is a region adjacency graph then the cut is given for the original graph (the pre-graph of the region adjacency graph).
    :return: leaf weights
    """
    leaf_weights = self._reconstruct_leaf_data(tree, altitudes)

    if hg.CptRegionAdjacencyGraph.validate(leaf_graph) and handle_rag:
        leaf_weights = hg.rag_back_project_vertex_weights(leaf_graph, leaf_weights)
    else:
        leaf_weights = hg.delinearize_vertex_weights(leaf_weights, leaf_graph)

    return leaf_weights
Example #12
0
def __labelisation_leaves(self, tree, leaf_graph, handle_rag=True):
    """
    Labelize tree leaves according to the horizontal cut.
    Two leaves are in the same region (ie. have the same label) if their lowest common ancestor is a subset or equal to
    one the node of the cut.,

    :param tree: input tree (Concept :class:`~higra.CptHierarchy`)
    :param leaf_graph: graph on the tree leaves (deduced from :class:`~higra.CptHierarchy`)
    :param handle_rag: if `True` and if `leaf_graph` is a region adjacency graph then the labels are given for the original graph (the pre-graph of the region adjacency graph).
    :return: a 1d array
    """
    labels = self._labelisation_leaves(tree)

    if hg.CptRegionAdjacencyGraph.validate(leaf_graph) and handle_rag:
        labels = hg.rag_back_project_vertex_weights(leaf_graph, labels)
    else:
        labels = hg.delinearize_vertex_weights(labels, leaf_graph)

    return labels
Example #13
0
def attribute_vertex_area(graph):
    """
    Vertex area of the given graph.

    In general the area of a vertex if simply equal to 1. But, if the graph is a region adjacency graph then the area of
    a region is equal to the sum of the area of the vertices inside the region (obtained with a recursive call to
    ``attribute_vertex_area`` on the original graph).

    :param graph: input graph
    :return: a 1d array
    """
    if hg.CptRegionAdjacencyGraph.validate(graph):  # this is a rag like graph
        pre_graph = hg.CptRegionAdjacencyGraph.get_pre_graph(graph)
        pre_graph_vertex_area = attribute_vertex_area(pre_graph)
        return hg.rag_accumulate_on_vertices(
            graph, hg.Accumulators.sum, vertex_weights=pre_graph_vertex_area)
    res = np.ones((graph.num_vertices(), ), dtype=np.float64)
    res = hg.delinearize_vertex_weights(res, graph)
    return res
Example #14
0
File: tree.py Project: higra/Higra
def labelisation_horizontal_cut_from_num_regions(tree,
                                                 altitudes,
                                                 num_regions,
                                                 mode="at_least",
                                                 leaf_graph=None):
    """
    Labelize tree leaves according to an horizontal cut of the tree given by its number of regions.

    If :attr:`mode` is ``"at_least"`` (default), the the smallest horizontal cut having at least the given number of
    regions is considered.
    If :attr:`mode` is ``"at_most"``, the the largest horizontal cut having at most the given number of
    regions is considered.

    Consider using the class :class:`~higra.HorizontalCutExplorer` if you plan to compute several horizontal cuts from a
    same hierarchy.

    :param tree: input tree (deduced from :class:`~higra.CptHierarchy`)
    :param altitudes: node altitudes of the input tree
    :param num_regions: a number of regions
    :param mode: ``"at_least"`` or ``"at_most"``
    :param leaf_graph: graph of the tree leaves (optional, deduced from :class:`~higra.CptHierarchy`)
    :return: Leaf labels
    """

    num_regions = int(num_regions)

    if mode == "at_least":
        modeb = True
    elif mode == "at_most":
        modeb = False
    else:
        raise ValueError("Incorrect mode")

    hc = hg.HorizontalCutExplorer(tree, altitudes)
    cut = hc.horizontal_cut_from_num_regions(num_regions, modeb)

    leaf_labels = cut.labelisation_leaves(tree)

    if leaf_graph is not None:
        leaf_labels = hg.delinearize_vertex_weights(leaf_labels, leaf_graph)

    return leaf_labels
Example #15
0
def labelisation_optimal_cut_from_energy(tree,
                                         energy_attribute,
                                         accumulator=hg.Accumulators.sum,
                                         leaf_graph=None):
    """
    Labelisation of the input tree leaves corresponding to the optimal cut according to the given energy attribute.

    Given a node :math:`i`, the value :math:`energy(i)` represents the energy fo the partial partition composed of the single region :math:`i`.
    Given a node :math:`i`, the energy of the partial partition composed of the children of :math:`i` is given by :math:`accumulator(energy(children(i)))`
    This function computes the partition (ie. a set of node forming a cut of the tree) that has a minimal energy
    according to the definition above.

    Supported accumulators are `hg.Accumulators.sum`, `hg.Accumulators.min`, and `hg.Accumulators.max`.

    The algorithm used is based on dynamic programming and runs in linear time :math:`\mathcal{O}(n)` w.r.t. to the number of nodes in the tree.

    See:

        Laurent Guigues, Jean Pierre Cocquerez, Hervé Le Men.
        `Scale-sets Image Analysis. International <https://hal.archives-ouvertes.fr/hal-00705364/file/ijcv_scale-setV11.pdf>`_
        Journal of Computer Vision, Springer Verlag, 2006, 68 (3), pp.289-317

    and

        Bangalore Ravi Kiran, Jean Serra.
        `Global-local optimizations by hierarchical cuts and climbing energies. <https://hal.archives-ouvertes.fr/hal-00802978v2/document>`_
        Pattern Recognition Letters, Elsevier, 2014, 47 (1), pp.12-24.


    :param tree: input tree (Concept :class:`~higra.CptHierarchy`)
    :param energy_attribute: energy value of each node of the input tree
    :param accumulator: see :class:`~higra.Accumulators`
    :param leaf_graph: leaf graph of the input tree (deduced from :class:`~higra.CptHierarchy`)
    :return: a labelisation of the leaves of the tree
    """
    labels = hg.cpp._labelisation_optimal_cut_from_energy(
        tree, energy_attribute, accumulator)

    if leaf_graph is not None:
        labels = hg.delinearize_vertex_weights(labels, leaf_graph)

    return labels
Example #16
0
def attribute_vertex_coordinates(graph, shape):
    """
    Coordinates of the vertices of the given grid graph.

    Example
    =======

    >>> g = hg.get_4_adjacency_graph((2, 3))
    >>> c = hg.attribute_vertex_coordinates(g)
    (((0, 0), (0, 1), (0, 2)),
     ((1, 0), (1, 1), (1, 2)))

    :param graph: Input graph (Concept :class:`~higra.CptGridGraph`)
    :param shape: (deduced from :class:`~higra.CptGridGraph`)
    :return: a nd array
    """
    coords = np.meshgrid(np.arange(shape[1]), np.arange(shape[0]))
    coords = [c.reshape((-1, )) for c in coords]
    attribute = np.stack(list(reversed(coords)), axis=1)
    attribute = hg.delinearize_vertex_weights(attribute, graph)
    return attribute
Example #17
0
File: tree.py Project: higra/Higra
def reconstruct_leaf_data(tree,
                          altitudes,
                          deleted_nodes=None,
                          leaf_graph=None):
    """
    Each leaf of the tree takes the altitude of its closest non deleted ancestor.

    The root node is never deleted.
    In a component tree, leaves are always deleted.

    If :attr:`deleted_nodes` is ``None`` then its default value is set to `np.zeros((tree.numvertices(),)`
    (no nodes are deleted).

    :param tree: input tree (Concept :class:`~higra.CptHierarchy`)
    :param altitudes: node altitudes of the input tree
    :param deleted_nodes: binary node weights indicating which nodes are deleted (optional)
    :param leaf_graph: graph of the tree leaves (optional, deduced from :class:`~higra.CptHierarchy`)
    :return: Leaf weights
    """

    if deleted_nodes is None:
        if tree.category() == hg.TreeCategory.PartitionTree:
            leaf_weights = altitudes[0:tree.num_leaves(), ...]
        elif tree.category() == hg.TreeCategory.ComponentTree:
            parents = tree.parents()
            leaf_weights = altitudes[parents[np.arange(tree.num_leaves())],
                                     ...]
    else:
        if tree.category() == hg.TreeCategory.ComponentTree:
            deleted_nodes[:tree.num_leaves()] = True

        reconstruction = hg.propagate_sequential(tree, altitudes,
                                                 deleted_nodes)
        leaf_weights = reconstruction[0:tree.num_leaves(), ...]

    if leaf_graph is not None:
        leaf_weights = hg.delinearize_vertex_weights(leaf_weights, leaf_graph)

    return leaf_weights
Example #18
0
def labelisation_seeded_watershed(graph,
                                  edge_weights,
                                  vertex_seeds,
                                  background_label=0):
    """
    Seeded watershed cut on an edge weighted graph.
    Seeds and associated labels are given in :attr:`vertex_seeds`.
    A vertex :math:`v`, such that :math:`vertex\_seeds(v)\\neq background\_label` is a seed with associated label :math:`vertex\_seeds(v)`.

    The label of a vertex of the graph is then defined equal to the label of the closest seed in the edge weighted graph for the min-max distance.
    If several such seeds exist (eg. on a plateus between two seeds), an arbitrary and consistent choice is made ensuring that:

    - each flat zone of level :math:`k` of the final labelling contains at least one seed with the label :math:`k`; and
    - each seed is contained in a flat zone whose level is equal to the seed label.

    :Complexity:

    This algorithm has a runtime complexity in :math:`\mathcal{O}(n \log n)` with :math:`n` the number of edges in the graph.

    :param graph: Input graph
    :param edge_weights: Weights on the edges of the graph
    :param vertex_seeds: Seeds with integer label values on the vertices of the graph
    :param background_label: Vertices whose values are equal to :attr:`background_label` (default 0) in :attr:`vertex_seeds` are not considered as seeds
    :return: A labelisation of the graph vertices
    """
    if not issubclass(vertex_seeds.dtype.type, np.integer):
        raise ValueError("vertex_seeds must be an array of integers")

    vertex_seeds = hg.linearize_vertex_weights(vertex_seeds, graph)

    vertex_seeds = hg.cast_to_dtype(vertex_seeds, np.int64)

    labels = hg.cpp._labelisation_seeded_watershed(graph, edge_weights,
                                                   vertex_seeds,
                                                   background_label)

    labels = hg.delinearize_vertex_weights(labels, graph)
    return labels
Example #19
0
    def test_delinearize_vertex_weights(self):
        g = hg.get_4_adjacency_graph((4, 5))

        r = hg.delinearize_vertex_weights(np.ones((20, )), g, (4, 5))
        self.assertTrue(r.shape == (4, 5))

        r = hg.delinearize_vertex_weights(np.ones((4, 5)), g, (4, 5))
        self.assertTrue(r.shape == (4, 5))

        r = hg.delinearize_vertex_weights(np.ones((4, 5, 10, 12)), g, (4, 5))
        self.assertTrue(r.shape == (4, 5, 10, 12))

        r = hg.delinearize_vertex_weights(np.ones((20, 4, 5, 2, 3)), g, (4, 5))
        self.assertTrue(r.shape == (4, 5, 4, 5, 2, 3))

        with self.assertRaises(ValueError):
            hg.delinearize_vertex_weights(np.ones((5, 4)), g, (4, 5))

        with self.assertRaises(ValueError):
            hg.delinearize_vertex_weights(np.ones((25, )), g, (4, 5))

        shape = (1, 4)
        g = hg.get_4_adjacency_graph(shape)
        r = hg.delinearize_vertex_weights(np.ones((4, )), g)
        self.assertTrue(r.shape == shape)

        r = hg.delinearize_vertex_weights(np.ones(shape), g)
        self.assertTrue(r.shape == shape)

        r = hg.delinearize_vertex_weights(np.ones((1, 4, 10, 12)), g)
        self.assertTrue(r.shape == (1, 4, 10, 12))

        r = hg.delinearize_vertex_weights(np.ones((4, 5, 2, 3)), g)
        self.assertTrue(r.shape == (1, 4, 5, 2, 3))

        with self.assertRaises(ValueError):
            hg.delinearize_vertex_weights(np.ones((5, 4)), g)

        with self.assertRaises(ValueError):
            hg.delinearize_vertex_weights(np.ones((8, )), g)

        shape = (4, 1)
        g = hg.get_4_adjacency_graph(shape)
        r = hg.delinearize_vertex_weights(np.ones((4, )), g)
        self.assertTrue(r.shape == shape)

        r = hg.delinearize_vertex_weights(np.ones(shape), g)
        self.assertTrue(r.shape == shape)

        r = hg.delinearize_vertex_weights(np.ones((4, 1, 10, 12)), g)
        self.assertTrue(r.shape == (4, 1, 10, 12))

        r = hg.delinearize_vertex_weights(np.ones((4, 5, 2, 3)), g)
        self.assertTrue(r.shape == (4, 1, 5, 2, 3))

        with self.assertRaises(ValueError):
            hg.delinearize_vertex_weights(np.ones((5, 4)), g)

        with self.assertRaises(ValueError):
            hg.delinearize_vertex_weights(np.ones((8, )), g)