Source code for detectree.refine
"""Methods to refine the pixel-level classification."""
import maxflow as mf
import numpy as np
MOORE_NEIGHBORHOOD_ARR = np.array([[0, 0, 0], [0, 0, 1], [1, 1, 1]])
[docs]
def maxflow_refine(
p_tree_img,
tree_val,
nontree_val,
*,
refine_int_rescale=10000,
refine_beta=50,
):
"""Refine the pixel-level classification using a graph max-flow algorithm.
Parameters
----------
p_tree_img : numpy.ndarray
The probability image of the pixel being a tree, as a two-dimensional numpy
array with values between 0 and 1.
tree_val, nontree_val : int, optional
The values that designate tree and non-tree pixels respectively in the output
array.
refine_int_rescale : int, optional
Parameter of the refinement procedure that controls the precision of the
transformation of float to integer edge weights, required for the employed
graph cuts algorithm. Larger values lead to greater precision.
refine_beta : int, optional
Parameter of the refinement procedure that controls the smoothness of the
labelling. Larger values lead to smoother shapes.
Returns
-------
img : numpy.ndarray
The refined pixel-level classification as a two-dimensional numpy array with
the same shape as `p_tree_img`.
"""
# p_nontree, p_tree = np.hsplit(p_pred, 2)
g = mf.Graph[int]()
node_ids = g.add_grid_nodes(p_tree_img.shape)
# P_nontree = p_nontree.reshape(img_shape)
# P_tree = p_tree.reshape(img_shape)
# The classifier probabilities are floats between 0 and 1, and the graph
# cuts algorithm requires an integer representation. Therefore, we multiply
# the probabilities by an arbitrary large number and then transform the
# result to integers. For instance, we could use a `refine_int_rescale` of
# `100` so that the probabilities are rescaled into integers between 0 and
# 100 like percentages). The larger `refine_int_rescale`, the greater the
# precision.
# ACHTUNG: the data term when the pixel is a tree is `log(1 - P_tree)`,
# i.e., `log(P_nontree)`, so the two lines below are correct
D_tree = (refine_int_rescale * np.log(1 - p_tree_img)).astype(int)
D_nontree = (refine_int_rescale * np.log(p_tree_img)).astype(int)
# TODO: option to choose Moore/Von Neumann neighborhood?
g.add_grid_edges(node_ids, refine_beta, structure=MOORE_NEIGHBORHOOD_ARR)
g.add_grid_tedges(node_ids, D_tree, D_nontree)
g.maxflow()
# y_pred = g.get_grid_segments(node_ids)
# transform boolean `g.get_grid_segments(node_ids)` to an array of
# `self.tree_val` and `self.nontree_val`
y_pred = np.full_like(p_tree_img, nontree_val)
y_pred[g.get_grid_segments(node_ids)] = tree_val
return y_pred