Skip to content

Commit

Permalink
implemented widget for generateing a mask
Browse files Browse the repository at this point in the history
  • Loading branch information
niksirbi committed Oct 31, 2023
1 parent 3094076 commit 571be9c
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 77 deletions.
8 changes: 2 additions & 6 deletions brainglobe_template_builder/napari/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@
__version__ = "unknown"

from brainglobe_template_builder.napari._reader import napari_get_reader
from brainglobe_template_builder.napari._widget import (
ExampleQWidget,
gaussian_blur_widget,
)
from brainglobe_template_builder.napari._widget import mask_widget

__all__ = (
"napari_get_reader",
"ExampleQWidget",
"gaussian_blur_widget",
"mask_widget",
)
117 changes: 62 additions & 55 deletions brainglobe_template_builder/napari/_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,88 +6,95 @@
Replace code below according to your needs.
"""
from typing import TYPE_CHECKING
from typing import Literal, Union

import numpy as np
from magicgui import magic_factory
from napari_plugin_engine import napari_hook_implementation
from qtpy.QtWidgets import QHBoxLayout, QPushButton, QWidget

if TYPE_CHECKING:
import napari

from napari import Viewer
from napari.layers import Image
from napari.types import LayerDataTuple


class ExampleQWidget(QWidget):
# your QWidget.__init__ can optionally request the napari viewer instance
# in one of two ways:
# 1. use a parameter called `napari_viewer`, as done here
# 2. use a type annotation of 'napari.viewer.Viewer' for any parameter
def __init__(self, napari_viewer):
super().__init__()
self.viewer = napari_viewer

btn = QPushButton("Click me!")
btn.clicked.connect(self._on_click)

self.setLayout(QHBoxLayout())
self.layout().addWidget(btn)

def _on_click(self):
print("napari has", len(self.viewer.layers), "layers")
from napari_plugin_engine import napari_hook_implementation


@magic_factory(
call_button="gaussian blur",
sigma={"widget_type": "FloatSlider", "max": 10, "min": 0, "step": 1},
tooltips={"sigma": "Standard deviation for Gaussian kernel (in pixels)"},
# do not show the viewer argument, as it is not a magicgui argument
call_button="generate mask",
gauss_sigma={"widget_type": "FloatSlider", "max": 10, "min": 0, "step": 1},
threshold_method={"choices": ["triangle", "otsu"]},
erosion_size={"widget_type": "Slider", "max": 10, "min": 0, "step": 1},
)
def gaussian_blur_widget(
viewer: Viewer, image: Image, sigma: float = 3
) -> LayerDataTuple:
"""Smooth image with a gaussian filter and add to napari viewer.
def mask_widget(
image: Image,
gauss_sigma: float = 3,
threshold_method: Literal["triangle", "otsu"] = "triangle",
erosion_size: int = 5,
) -> Union[LayerDataTuple, None]:
"""Threshold image and create a mask for the largest object.
The mask is generated by applying a Gaussian filter to the image,
thresholding the smoothed image, keeping only the largest object, and
eroding the resulting mask.
Parameters
----------
image : Image
A napari image layer to smooth.
sigma : float
Standard deviation for Gaussian kernel (in pixels).
A napari image layer to threshold.
gauss_sigma : float
Standard deviation for Gaussian kernel (in pixels) to smooth image
before thresholding. Set to 0 to skip smoothing.
threshold_method : str
Thresholding method to use. Options are 'triangle' and 'otsu'.
erosion_size : int
Size of the erosion footprint (in pixels) to apply to the mask.
Set to 0 to skip erosion.
Returns
-------
image_smoothed : Image
A napari image layer with the smoothed image.
layers : list[LayerDataTuple]
A list of napari layers to add to the viewer.
The first layer is the mask, and the second layer is the smoothed
image (if smoothing was applied).
"""

if image is not None:
assert isinstance(image, Image), "image must be a napari Image layer"
else:
print("Please select an image layer")
return
return None

from skimage import filters, measure, morphology

from skimage import filters
# Apply gaussian filter to image
if gauss_sigma > 0:
data_smoothed = filters.gaussian(image.data, sigma=gauss_sigma)
else:
data_smoothed = image.data

# Threshold the (smoothed) image
if threshold_method == "triangle":
thresholded = filters.threshold_triangle(data_smoothed)
elif threshold_method == "otsu":
thresholded = filters.threshold_otsu(data_smoothed)
else:
raise ValueError(f"Unknown thresholding method {threshold_method}")

image_smoothed = filters.gaussian(image.data, sigma=sigma)
binary = data_smoothed > thresholded

# return the smoothed image layer
return (
image_smoothed,
{"name": f"{image.name}_smoothed"},
"image",
)
# Keep only the largest object
labeled_image = measure.label(binary)
regions = measure.regionprops(labeled_image)
largest_region = max(regions, key=lambda region: region.area)
# Create a binary mask for the largest object
mask = labeled_image == largest_region.label

# Erode the mask
if erosion_size > 0:
mask = morphology.binary_erosion(
mask, footprint=np.ones((erosion_size,) * image.ndim)
)

# Uses the `autogenerate: true` flag in the plugin manifest
# to indicate it should be wrapped as a magicgui to autogenerate
# a widget.
def example_function_widget(img_layer: "napari.layers.Image"):
print(f"you have selected {img_layer}")
# return the mask as a napari Labels layer
return (mask, {"name": f"{image.name}_mask", "opacity": 0.5}, "labels")


@napari_hook_implementation
def napari_experimental_provide_dock_widget():
return gaussian_blur_widget
return mask_widget
21 changes: 5 additions & 16 deletions brainglobe_template_builder/napari/napari.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,15 @@ contributions:
- id: brainglobe-template-builder.get_reader
python_name: brainglobe_template_builder.napari._reader:napari_get_reader
title: Open data with brainglobe-template-builder
- id: brainglobe-template-builder.make_qwidget
python_name: brainglobe_template_builder.napari._widget:ExampleQWidget
title: Make example QWidget
- id: brainglobe-template-builder.make_blur_widget
python_name: brainglobe_template_builder.napari._widget:gaussian_blur_widget
title: Apply Gaussian blur to image
- id: brainglobe-template-builder.make_func_widget
python_name: brainglobe_template_builder.napari._widget:example_function_widget
title: Make example function widget
- id: brainglobe-template-builder.make_mask_widget
python_name: brainglobe_template_builder.napari._widget:mask_widget
title: Generate organ mask
readers:
- command: brainglobe-template-builder.get_reader
accepts_directories: false
filename_patterns:
- '*.tif'
- '*.tiff'
widgets:
- command: brainglobe-template-builder.make_qwidget
display_name: Example QWidget
- command: brainglobe-template-builder.make_blur_widget
display_name: Gaussian Blur
- command: brainglobe-template-builder.make_func_widget
autogenerate: true
display_name: Example Function Widget
- command: brainglobe-template-builder.make_mask_widget
display_name: Generate Mask

0 comments on commit 571be9c

Please sign in to comment.