diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7d452ebae8ad0d1ae9b3d7009569042a3e8c4ed7..dcc9a2b78e06b63b2a4a0c995aaf41bab730caf2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + rev: v4.1.0 hooks: - id: check-docstring-first - id: end-of-file-fixer @@ -24,11 +24,11 @@ repos: hooks: - id: isort - repo: https://github.com/psf/black - rev: 21.11b1 + rev: 22.1.0 hooks: - id: black - repo: https://github.com/asottile/pyupgrade - rev: v2.29.1 + rev: v2.31.1 hooks: - id: pyupgrade args: [--py38-plus, --keep-runtime-typing] diff --git a/src/napari_segment/_reader.py b/src/napari_segment/_reader.py index 8c324fc583bceae3fc721491f5debc0ea059df72..80560e03a6cbe1d78a2691df8e208b294537307c 100644 --- a/src/napari_segment/_reader.py +++ b/src/napari_segment/_reader.py @@ -5,6 +5,7 @@ It implements the Reader specification, but your plugin may choose to implement multiple readers or even other plugin contributions. see: https://napari.org/plugins/stable/guides.html#readers """ +import nd2 import numpy as np @@ -29,13 +30,30 @@ def napari_get_reader(path): path = path[0] # if we know we cannot read the file, we immediately return None. - if not path.endswith(".npy"): - return None + if path.endswith(".nd2"): + return read_nd2 # otherwise we return the *function* that can read ``path``. return reader_function +def read_nd2(path): + data = nd2.ND2File(path) + ddata = data.to_dask() + return [ + ( + ddata, + dict( + channel_axis=1, + name=["BF", "fluo"], + colormap=["gray", "green"], + contrast_limits=[(8500, 35000), (150, 20000)], + ), + "image", + ) + ] + + def reader_function(path): """Take a path or list of paths and return a list of LayerData tuples. @@ -54,7 +72,8 @@ def reader_function(path): A list of LayerData tuples where each tuple in the list contains (data, metadata, layer_type), where data is a numpy array, metadata is a dict of keyword arguments for the corresponding viewer.add_* method - in napari, and layer_type is a lower-case string naming the type of layer. + in napari, and layer_type is a lower-case string naming the type of + layer. Both "meta", and "layer_type" are optional. napari will default to layer_type=="image" if not provided """ diff --git a/src/napari_segment/_widget.py b/src/napari_segment/_widget.py index 7f2fc5d116fb41de6ace8fb15e7a359906cca142..a7c0317830f4a87d4a7acac4a22ad8502ed17aaa 100644 --- a/src/napari_segment/_widget.py +++ b/src/napari_segment/_widget.py @@ -6,8 +6,16 @@ see: https://napari.org/plugins/stable/guides.html#widgets Replace code below according to your needs. """ -from qtpy.QtWidgets import QWidget, QHBoxLayout, QPushButton +from functools import partial +from multiprocessing import Pool + +import napari +import numpy as np from magicgui import magic_factory +from qtpy.QtWidgets import QHBoxLayout, QPushButton, QWidget +from scipy.ndimage import binary_dilation +from segment.seg import segment_bf +from skimage.measure import regionprops_table class ExampleQWidget(QWidget): @@ -37,5 +45,53 @@ def example_magic_widget(img_layer: "napari.layers.Image"): # 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}") +@magic_factory() +def segment_organoid( + BF_layer: "napari.layers.Image", + fluo_layer: "napari.layers.Image", + donut=10, + thr: float = 0.4, +) -> napari.types.LabelsData: + # frame = napari.current_viewer().cursor.position[0] + try: + p = Pool() + stack = list( + p.map( + partial(seg_frame, thr=thr, donut=donut), + zip(BF_layer.data.compute(), fluo_layer.data.compute()), + ) + ) + except Exception as e: + print(e.args) + finally: + p.close() + + return np.array(stack) + # (bg_mask.astype('uint8'), {"name": "donut", **kwargs}, 'image') + + +def seg_frame(data, thr, donut): + bf, fluo = data + label = segment_bf(bf, thr=thr, plot=False) + print(bf.dtype, bf.shape) + props = regionprops_table( + label, + intensity_image=fluo, + properties=( + "label", + "centroid", + "bbox", + "mean_intensity", + "area", + "major_axis_length", + ), + ) + # print(props) + biggest_prop_index = np.argmax(props["area"]) + label_of_biggest_object = props["label"][biggest_prop_index] + spheroid_mask = label == label_of_biggest_object + bg_mask = np.bitwise_xor( + binary_dilation(spheroid_mask, structure=np.ones((donut, donut))), + spheroid_mask, + ) + return spheroid_mask.astype("uint16") + 2 * bg_mask.astype("uint8") diff --git a/src/napari_segment/napari.yaml b/src/napari_segment/napari.yaml index 45fe55a77c234b219e3c9e59dfa8031c7124df16..14dfbbb0fcd2f2613d67de57761b8998efdd758b 100644 --- a/src/napari_segment/napari.yaml +++ b/src/napari_segment/napari.yaml @@ -13,36 +13,36 @@ contributions: title: Save image data with Segment organoid - id: napari-segment.make_sample_data python_name: napari_segment._sample_data:make_sample_data - title: Load sample data from Segment organoid - - id: napari-segment.make_qwidget - python_name: napari_segment._widget:ExampleQWidget - title: Make example QWidget - - id: napari-segment.make_magic_widget - python_name: napari_segment._widget:example_magic_widget - title: Make example magic widget - - id: napari-segment.make_func_widget - python_name: napari_segment._widget:example_function_widget - title: Make example function widget + title: Load sample data from Segment organoid + # - id: napari-segment.make_qwidget + # python_name: napari_segment._widget:ExampleQWidget + # title: Make example QWidget + # - id: napari-segment.make_magic_widget + # python_name: napari_segment._widget:example_magic_widget + # title: Make example magic widget + - id: napari-segment.segment_organoid + python_name: napari_segment._widget:segment_organoid + title: Segment organoid readers: - command: napari-segment.get_reader accepts_directories: false - filename_patterns: ['*.npy'] + filename_patterns: ['*.npy', '*.nd2'] writers: - command: napari-segment.write_multiple layer_types: ['image*','labels*'] filename_extensions: [] - command: napari-segment.write_single_image layer_types: ['image'] - filename_extensions: ['.npy'] + filename_extensions: ['.npy'] sample_data: - command: napari-segment.make_sample_data display_name: Segment organoid - key: unique_id.1 + key: unique_id.1 widgets: - - command: napari-segment.make_qwidget - display_name: Example QWidget - - command: napari-segment.make_magic_widget - display_name: Example Magic Widget - - command: napari-segment.make_func_widget - autogenerate: true - display_name: Example Function Widget \ No newline at end of file + # - command: napari-segment.make_qwidget + # display_name: Example QWidget + # - command: napari-segment.make_magic_widget + # display_name: Example Magic Widget + - command: napari-segment.segment_organoid + # autogenerate: true + display_name: Segment organoid