{
"cells": [
{
"cell_type": "markdown",
"id": "50f6743a-03a3-43b5-8e8a-5bb32ff41f6f",
"metadata": {},
"source": [
"# Merging labels according to edge-to-edge-distances\n",
"In this notebook we will merge labels in a label image according to their edge-to-edge distances to each other. Labels close-by will be merged.\n",
"\n",
"See also\n",
"* [Image.sc discussion](https://forum.image.sc/t/measure-distances-between-labels/79125)\n",
"* [Merging labels using napari-accelerated-pixel-and-object-classifiers](https://github.com/haesleinhuepf/napari-accelerated-pixel-and-object-classification#merging-objects)"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "626d0d82-dbbb-4092-bf57-80eda98de375",
"metadata": {},
"outputs": [],
"source": [
"import pyclesperanto_prototype as cle\n",
"from skimage.io import imread\n",
"import numpy as np"
]
},
{
"cell_type": "markdown",
"id": "f6585d60-e431-4902-bed9-901afcf4f6ba",
"metadata": {},
"source": [
"For demonstration purposes, we use a modified version of the labels derived from the blobs example-image. We artificially introduce gaps between them."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "878e3c57-593c-4ad3-9c3c-096f1eef2c6b",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"\n",
" \n",
" | \n",
"\n",
"cle._ image \n",
"\n",
"shape | (254, 256) | \n",
"dtype | uint32 | \n",
"size | 254.0 kB | \n",
"min | 0.0 | max | 47.0 | \n",
" \n",
"\n",
" | \n",
"
\n",
"
"
],
"text/plain": [
"cl.OCLArray([[ 0, 0, 0, ..., 45, 45, 45],\n",
" [ 0, 0, 0, ..., 45, 45, 45],\n",
" [ 0, 0, 0, ..., 45, 45, 45],\n",
" ...,\n",
" [ 0, 0, 0, ..., 0, 0, 0],\n",
" [ 0, 0, 0, ..., 0, 0, 0],\n",
" [ 0, 0, 0, ..., 0, 0, 0]], dtype=uint32)"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"image = imread(\"../../data/blobs.tif\")\n",
"image[:, 80:150] = 0\n",
"image[80:130, 100:] = 0\n",
"\n",
"image = cle.asarray(image)\n",
"labels = cle.voronoi_otsu_labeling(image, spot_sigma=4, outline_sigma=3)\n",
"labels"
]
},
{
"cell_type": "markdown",
"id": "457c3bd0-1641-4021-affb-06b22649b324",
"metadata": {},
"source": [
"First, we dilate the labels by half of the maximum distance the edges are allowed to have."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "20258bd4-9f74-479e-a7bd-1d5399a6ef21",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"\n",
" \n",
" | \n",
"\n",
"cle._ image \n",
"\n",
"shape | (254, 256) | \n",
"dtype | uint32 | \n",
"size | 254.0 kB | \n",
"min | 0.0 | max | 47.0 | \n",
" \n",
"\n",
" | \n",
"
\n",
"
"
],
"text/plain": [
"cl.OCLArray([[ 0, 0, 0, ..., 45, 45, 45],\n",
" [ 0, 0, 0, ..., 45, 45, 45],\n",
" [ 0, 0, 0, ..., 45, 45, 45],\n",
" ...,\n",
" [ 0, 0, 0, ..., 0, 0, 0],\n",
" [ 0, 0, 0, ..., 0, 0, 0],\n",
" [ 0, 0, 0, ..., 0, 0, 0]], dtype=uint32)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"maximum_distance = 12\n",
"\n",
"dilated_labels = cle.dilate_labels(labels, radius=maximum_distance/2)\n",
"dilated_labels"
]
},
{
"cell_type": "markdown",
"id": "e07254cf-accc-425e-af51-d992f481d1fc",
"metadata": {},
"source": [
"We then merge the labels if the touch."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "7298aa54-71b4-46f6-bff3-6eb22547d329",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"\n",
" \n",
" | \n",
"\n",
"cle._ image \n",
"\n",
"shape | (254, 256) | \n",
"dtype | uint32 | \n",
"size | 254.0 kB | \n",
"min | 0.0 | max | 5.0 | \n",
" \n",
"\n",
" | \n",
"
\n",
"
"
],
"text/plain": [
"cl.OCLArray([[0, 0, 0, ..., 4, 4, 4],\n",
" [0, 0, 0, ..., 4, 4, 4],\n",
" [0, 0, 0, ..., 4, 4, 4],\n",
" ...,\n",
" [0, 0, 0, ..., 0, 0, 0],\n",
" [0, 0, 0, ..., 0, 0, 0],\n",
" [0, 0, 0, ..., 0, 0, 0]], dtype=uint32)"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"merged_dilated_labels = cle.merge_touching_labels(dilated_labels)\n",
"merged_dilated_labels"
]
},
{
"cell_type": "markdown",
"id": "d850ccfc-eaee-4647-ac52-3fbfaac5b1c9",
"metadata": {},
"source": [
"Afterwards, we mask the merged labels with the original label's shape. We also convert the result of this operation to 32-bit integer, so that the visualization as label-image works."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "b41bd861-c9fb-4e81-b20a-91cc8920e379",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"\n",
" \n",
" | \n",
"\n",
"cle._ image \n",
"\n",
"shape | (254, 256) | \n",
"dtype | uint32 | \n",
"size | 254.0 kB | \n",
"min | 0.0 | max | 5.0 | \n",
" \n",
"\n",
" | \n",
"
\n",
"
"
],
"text/plain": [
"cl.OCLArray([[0, 0, 0, ..., 4, 4, 4],\n",
" [0, 0, 0, ..., 4, 4, 4],\n",
" [0, 0, 0, ..., 4, 4, 4],\n",
" ...,\n",
" [0, 0, 0, ..., 0, 0, 0],\n",
" [0, 0, 0, ..., 0, 0, 0],\n",
" [0, 0, 0, ..., 0, 0, 0]], dtype=uint32)"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"merged_labels = (merged_dilated_labels * (labels > 0)).astype(np.uint32)\n",
"merged_labels"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cf56cb38-aaaa-4e79-b762-21e87ec78045",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.15"
}
},
"nbformat": 4,
"nbformat_minor": 5
}