diff --git a/src/napari_segment/_widget.py b/src/napari_segment/_widget.py
index 9bab1f7cfa0916c8168c9bc1ce77116035df3bd1..204e25f61e72ff01af439d2bfea0662955be2fb0 100644
--- a/src/napari_segment/_widget.py
+++ b/src/napari_segment/_widget.py
@@ -14,13 +14,12 @@ import numpy as np
from magicgui import magic_factory
from qtpy.QtWidgets import QHBoxLayout, QPushButton, QWidget
from scipy.ndimage import (
- binary_dilation,
binary_erosion,
binary_fill_holes,
gaussian_filter,
label,
)
-from skimage.measure import regionprops_table
+from skimage.measure import regionprops
# import matplotlib.pyplot as plt
@@ -65,67 +64,118 @@ def save_values(val):
"max": 0.5,
},
erode={"widget_type": "Slider", "min": 1, "max": 10},
+ min_diam={"widget_type": "Slider", "min": 10, "max": 250},
+ max_diam={"widget_type": "Slider", "min": 150, "max": 1000},
+ max_ecc={
+ "label": "Eccentricity",
+ "widget_type": "FloatSlider",
+ "min": 0.0,
+ "max": 1.0,
+ },
)
def segment_organoid(
BF_layer: "napari.layers.Image",
fluo_layer: "napari.layers.Image",
- thr: float = 0.4,
+ thr: float = 0.3,
erode: int = 10,
- donut=10,
+ min_diam=150,
+ max_diam=550,
+ max_ecc=0.7,
+ show_detections=True,
) -> napari.types.LayerDataTuple:
# frame = napari.current_viewer().cursor.position[0]
kwargs = {}
ddata = BF_layer.data
- labels = ddata.map_blocks(
- partial(segment_bf, thr=thr, erode=erode), dtype=ddata.dtype
+ smooth_gradient = ddata.map_blocks(
+ partial(get_gradient), dtype=ddata.dtype
)
- selected_labels = dask.array.map_blocks(filter_biggest, labels, donut)
- print(selected_labels.shape)
- return [
- (labels, {"name": "raw_labels", "visible": False, **kwargs}, "labels"),
- (selected_labels, {"name": "selected labels", **kwargs}, "labels"),
- ]
-
-
-def filter_biggest(labels, donut=10):
- props = regionprops_table(
+ labels = smooth_gradient.map_blocks(
+ partial(threshold_gradient, thr=thr, erode=erode),
+ dtype=ddata.dtype,
+ )
+ try:
+ selected_labels = dask.array.map_blocks(
+ filter_labels,
+ labels,
+ min_diam,
+ max_diam,
+ max_ecc,
+ dtype=ddata.dtype,
+ )
+ # print(selected_labels.shape)
+ return [
+ (
+ labels,
+ {"name": "Detections", "visible": show_detections, **kwargs},
+ "labels",
+ ),
+ (selected_labels, {"name": "selected labels", **kwargs}, "labels"),
+ ]
+ except TypeError:
+ return [
+ (
+ labels,
+ {"name": "Detections", "visible": True, **kwargs},
+ "labels",
+ ),
+ ]
+
+
+def filter_labels(labels, min_diam=50, max_diam=150, max_ecc=0.2):
+ if max_diam <= min_diam:
+ raise ValueError(
+ "min value is greater than max value for the diameter filter"
+ )
+ props = regionprops(
labels[0],
- properties=(
- "label",
- "area",
- ),
)
- biggest_prop_index = np.argmax(props["area"])
- label_of_biggest_object = props["label"][biggest_prop_index]
- spheroid_mask = labels[0] == label_of_biggest_object
- bg_mask = np.bitwise_xor(
- binary_dilation(spheroid_mask, structure=np.ones((donut, donut))),
- spheroid_mask,
+ good_props = filter(
+ lambda p: (d := p.major_axis_length) > min_diam
+ and d < max_diam
+ and p.eccentricity < max_ecc,
+ props,
)
- return (
- spheroid_mask.astype("uint16") + 2 * bg_mask.astype("uint16")
- ).reshape(labels.shape)
+ good_labels = [p.label for p in good_props]
+ if len(good_labels) < 1:
+ return np.zeros_like(labels)
+ # print(f'good_labels {good_labels}')
+ mask = np.sum([labels == v for v in good_labels], axis=0)
+ # print(mask.shape)
+ return (label(mask)[0].astype("uint16")).reshape(labels.shape)
-def segment_bf(well, thr=0.2, smooth=10, erode=10, fill=True, plot=False):
+def get_gradient(bf_data: np.ndarray, smooth=10):
"""
- Serments input 2d array using thresholded gradient with filling
+ Removes first dimension,
+ Computes gradient of the image,
+ applies gaussian filter
Returns SegmentedImage object
"""
- grad = get_2d_gradient(well[0])
- sm = gaussian_filter(grad, smooth)
+ assert (
+ bf_data[0].ndim == 2
+ ), f"expected 2D shape, got shape {bf_data[0].shape}"
+ gradient = get_2d_gradient(bf_data[0])
+ smoothed_gradient = gaussian_filter(gradient, smooth)
# sm = multiwell.gaussian_filter(well, smooth)
+ return smoothed_gradient.reshape(bf_data.shape)
- regions = sm > thr * sm.max()
+
+def threshold_gradient(
+ smoothed_gradient: np.ndarray,
+ thr: float = 0.4,
+ fill: bool = True,
+ erode: int = 1,
+):
+ regions = smoothed_gradient[0] > thr * smoothed_gradient[0].max()
if fill:
regions = binary_fill_holes(regions)
- if erode:
+ if erode and erode > 0:
regions = binary_erosion(regions, iterations=erode)
labels, _ = label(regions)
- return labels.reshape(well.shape)
+ return labels.reshape(smoothed_gradient.shape)
def get_2d_gradient(xy):