Segmentierungsalgorithmen optimieren#

Das Napari-Plugin napari-workflow-optimizer ermöglicht die bequeme Optimierung von Bildsegemtierungsworkflows.

from napari_workflow_optimizer import JaccardLabelImageOptimizer, Workflow

from skimage.io import imread
import pyclesperanto_prototype as cle
import matplotlib.pyplot as plt

Um den Optimierer zu verwenden, müssen wir unseren Workflow mit einem Workflow-Objekt definieren. Es funktioniert wie ein Wörterbuch mit Bildnamen als Schlüsselwörtern und einer Liste von Operationen und Parametern als Werte. Die zugrunde liegende Infrastruktur basiert auf dask-Graphen.

w = Workflow()
# define background subtraction
w.set("blurred", cle.gaussian_blur, "input", sigma_x=5, sigma_y=5)
# define segmentation
w.set("binarized", cle.threshold_otsu, "blurred")
w.set("labeled", cle.label, "binarized")

Diese Workflows können erkundet werden. Zum Beispiel können wir daraus lesen, welche Bildparameter zum Starten benötigt werden.

w.roots()
['input']

Wir können auch ermitteln, was die Ergebnisbilder des Workflows sind.

w.leafs()
['labeled']

Nachdem wir die Eingaben festgelegt haben, können wir den Workflow bitten, Ergebnisse zu berechnen.

w.set("input", imread("../../data/blobs.tif"))
result = w.get("labeled")

cle.imshow(result, labels=True)
../_images/cab4e38e044535463178fcf1e62cd36e32965a28fc1ce952db31796b70694b0d.png

Für die Optimierung eines solchen Workflows benötigen wir ein Ground-Truth-Annotationsbild. Eine spärliche Annotation einiger Objekte ist dafür ausreichend.

ground_truth = imread("../../data/blobs_sparse_labels.tif")
cle.imshow(ground_truth, labels=True)
../_images/6d517202339c1dd6f59a9e24fd84fc8fd2f0acd0d2aba8012dd49289052ea53f.png

Der JaccardLabelImageOptimizer verarbeitet einen Workflow und kann Parameter in Bezug auf eine spärliche Ground Truth optimieren. Seine optimize-Funktion gibt einen Satz von Parametern zurück, der allen numerischen Parametern des Workflows entspricht.

jlio = JaccardLabelImageOptimizer(w)
best_param = jlio.optimize("labeled", ground_truth, maxiter=20)
best_param
array([4.80023582e+00, 4.44562637e+00, 3.84861161e-04])

Wir können dann den Optimierer verwenden, um diese Parameter für den Workflow festzulegen und anschließend lesen, wo im Workflow die Parameter gesetzt wurden.

jlio.set_numeric_parameters(best_param)

# before printing the workflow, we quickly remove the input image
w.remove('input')
print(w)
Workflow:
blurred <- (<function gaussian_blur at 0x0000023723085A60>, 'input', None, 4.8002358210977345, 4.445626372447739, 0.00038486116050511713)
binarized <- (<function threshold_otsu at 0x00000237232EADC0>, 'blurred')
labeled <- (<function connected_components_labeling_box at 0x000002372316AB80>, 'binarized')

Nachdem wir die Eingabe erneut festgelegt haben, können wir den Workflow auch auf das Bild anwenden und das Ergebnis visuell überprüfen.

w.set("input", imread("../../data/blobs.tif"))
cle.imshow(w.get("labeled"), labels=True)
../_images/9670bee784477649f403b50b1b7d253b75fa69d0f8cbcf5b779cd6bd7dba88f7.png

Weitere Einblicke in die Optimierung#

Der JaccardLabelImageOptimizer ermöglicht es auch, einen Blick auf den zuvor angewendeten Optimierungsprozess zu werfen.

attempt, quality = jlio.get_plot()

plt.plot(attempt, quality)
[<matplotlib.lines.Line2D at 0x23735c22c70>]
../_images/9a651798ca88ed0ba7674d01f32cc6492c4842b881cac216f5d56edf5751055c.png