The PyDenseCRF package by Lucas Beyer is unfortunately not designed in a particularly pythonic fashion. My goal with this project is to make his wrapper for the DenseCRF C++ code by Philip Krähenbühl easier to use.
This is a tool for post-processing of image segmentation masks, and before using it you should have precomputed class probabilities for each label. Training is unfortunately not supported by this tool.
Open a terminal window and navigate to the folder that you want to download PyDense2 into. Write git clone https://github.com/yngvem/PyDense2.git
in the terminal window, followed by cd PyDense2
and at last python setup.py install
. PyDense2 can then be imported into any Python file.
If you get a cryptic error on import, then you are probably missing libgcc as part of your Python installation. If you are using Anaconda this can be fixed by typing conda install libgcc
in a terminal window.
The unary potentials must come from precomputed class probabilities. In addition to this, two different pairwise potentials are supported. Gaussian potentials and bilateral potentials. Gaussian are given by the function , where i and j are two pixel indices, the y-variables are the pixel labels, the x-variables are the pixel positions, is a hyperparameter deciding how fast the potential should decline in the different spatial directions and is a function (called the compatibility function) that signify how big the potential for a pair of pixel should be given their labels. Oddily enough, two labels with a high compatibility function value yield a high potential, a better name for this would therefore be incompatibility function. Bilateral potentials are given by a similar function, namely . The only new variables in this equation is the c-values, which are the colour values of the corresponding pixels and the , which plays the same role as , but for colour distance instead of spatial distance.
Here is a simple example where the probability mask and image is stored as numpy array files. This will work with images of any dimensions (1d signals, 2d images, 3d images, videos, etc).
import numpy as np
import matplotlib.pyplot as plt
from pydense2 import crf_model, potentials
# Create unary potential
unary = potentials.UnaryPotentialFromProbabilities(
one_class_probabilities=False # There are one set of probabilities per class,
# this can only be true in the binary class problem
)
# Create pairwise potentials
bilateral_pairwise = potentials.BilateralPotential(
spatial_sigma=10, # Diagonal sigma matrix, where all the diagonal values are 10
colour_sigma=1, # Diagonal sigma matrix, where all the diagonal values are 1
compatibility=4 # A potts like compatibility function.
)
gaussian_pairwise = potentials.GaussianPotential(
sigma=10, # Diagonal sigma matrix, where all the diagonal values are 10
compatibility=2 # A potts like compatibility function
)
# Create CRF model and add potentials
crf = crf_model.DenseCRF(
num_classes=2, # The number of output classes
unary_potential=unary,
binary_potentials=[bilateral_pairwise, gaussian_pairwise]
)
# Load image and probabilities
image = np.load('image.npy')
probabilities = np.load('image.npy')
# Set the image for the CRF model to start refining the mask
crf.set_image(
image=image,
probabilities=probabilities,
colour_axis=-1, # The axis corresponding to colour is the last axis in the image
class_axis=-1 # The axis corresponding to which class the probabilities are for is the last axis in the probabilities array
)
# Refine the mask, doing 10 iterations
crf.perform_inference(10)
refined_mask10 = crf.segmentation_map
# Refine the mask some more, performing another 10 iterations
crf.perform_inference(10) # The CRF model will continue where it last left off.
refined_mask20 = crf.segmentation_map
# Plot the results
plt.subplot(121)
plt.title('Segmentation mask after 10 iterations')
plt.imshow(refined_mask10)
plt.subplot(122)
plt.title('Segmentation mask after 20 iterations')
plt.imshow(refined_mask20)
A separate class is supplied for evaluating a CRF model (currently, only the Dice metric is usable, more might be added later). It takes a list of test images and computes the evaluation metric for each of those images in parallel. Joblib is used to perform these computations (if n_jobs
is not set to 1). This requires all objects to be pickleable, however, the GaussianPotentials are not (this is a weakness of the PyDenseCRF package) so n_jobs
must be set to 1 if you want to use the CRF evaluator with Gaussian potentials.
Examples of the evaluator will come later.