Skip to content

Commit

Permalink
Voronoi-Otsu labeling + instance segmentation code overhaul (#34)
Browse files Browse the repository at this point in the history
* Instance segmentation refactor + Voronoi-Otsu

- Improved code for instance segmentation
- Added Voronoi-Otsu labeling from pyclesperanto
TODO : credits for labeling

* Disabled small removal in Voronoi-Otsu

* Added new docs for instance seg

* Docs + UI update

- Updated welcome/README
- Changed step for DoubleCounter

* Update requirements.txt

Fix typo

* isort

* Fix tests

* Fixed parental issues and instance seg widget init

- Fixed widgets parents that were incorrectly init
- Improve use of instance seg. method classes and init

* Fix inference

* Added labeling tools + UI tweaks

- Added tools from MLCourse to evaluate labels and auto-correct them
- Instance seg benchmark notebook
- Tweaked utils UI to scale according to Viewer size

Co-Authored-By: gityves <[email protected]>

* Testing instance methods

Co-Authored-By: gityves <[email protected]>

* Many fixes

- Fixed monai reqs
- Added custom functions for label checking
- Fixed return type of voronoi_otsu and utils.resize
- black

* black

* Complete instance method evaluation

* Added pre-commit hooks

* Enfore pre-commit style

* Update .gitignore

* Version bump

* Updated project files

* Fixed missing parent error

* Fixed wrong value in instance sliders

* Removing dask-image

* Fixed erroneous dtype conversion

* Update test_plugin_utils.py

* Temporary test action patch

* Update plugin_convert.py

* Update tox.ini

Added pocl for testing on GH Actions

* Update tox.ini

* Found existing pocl

* Updated utils test to avoid Voronoi-Otsu

VO is missing CL runtime

* Relabeling tests

* Run full suite of pre-commit hooks

* Enforce style

* Instance segmentation refactor + Voronoi-Otsu

- Improved code for instance segmentation
- Added Voronoi-Otsu labeling from pyclesperanto
TODO : credits for labeling

* Disabled small removal in Voronoi-Otsu

* Added new docs for instance seg

* isort

* Fix tests

* Fixed parental issues and instance seg widget init

- Fixed widgets parents that were incorrectly init
- Improve use of instance seg. method classes and init

* Fix inference

* Added labeling tools + UI tweaks

- Added tools from MLCourse to evaluate labels and auto-correct them
- Instance seg benchmark notebook
- Tweaked utils UI to scale according to Viewer size

Co-Authored-By: gityves <[email protected]>

* Testing instance methods

Co-Authored-By: gityves <[email protected]>

* Many fixes

- Fixed monai reqs
- Added custom functions for label checking
- Fixed return type of voronoi_otsu and utils.resize
- black

* black

* Complete instance method evaluation

* Enfore pre-commit style

* Removing dask-image

* Fixed erroneous dtype conversion

* Update test_plugin_utils.py

* Update tox.ini

* Added new pre-commit hooks

* Run full suite of pre-commit hooks

* Enforce style

* Documentation update, crop contrast fix

* Update plugin_model_inference.py

* Updated hooks

---------

Co-authored-by: gityves <[email protected]>
  • Loading branch information
C-Achard and gityves authored Jun 11, 2023
1 parent 89e71dc commit e00806c
Show file tree
Hide file tree
Showing 43 changed files with 2,857 additions and 618 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test_and_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ on:
branches:
- main
- npe2
- cy/voronoi-otsu
tags:
- "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
pull_request:
branches:
- main
- npe2
- cy/voronoi-otsu
workflow_dispatch:

jobs:
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,8 @@ notebooks/full_plot.html
*.csv
*.png
*.prof

#include test data
!napari_cellseg3d/_tests/res/test.tif
!napari_cellseg3d/_tests/res/test.png
!napari_cellseg3d/_tests/res/test_labels.tif
13 changes: 8 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ repos:
# - id: check-docstring-first
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
args: ["--profile", "black", --line-length=79]
- id: check-yaml
- id: check-added-large-files
- id: check-toml
# - repo: https://github.com/pycqa/isort
# rev: 5.12.0
# hooks:
# - id: isort
# args: ["--profile", "black", --line-length=79]
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.0.262'
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,9 @@ Distributed under the terms of the [MIT] license.

## Acknowledgements

This plugin was developed by Cyril Achard, Maxime Vidal, Mackenzie Mathis. This work was funded, in part, from the Wyss Center to the [Mathis Laboratory of Adaptive Motor Control](https://www.mackenziemathislab.org/).

This plugin was developed by Cyril Achard, Maxime Vidal, Mackenzie Mathis.
This work was funded, in part, from the Wyss Center to the [Mathis Laboratory of Adaptive Motor Control](https://www.mackenziemathislab.org/).
Please refer to the documentation for full acknowledgements.

## Plugin base
This [napari] plugin was generated with [Cookiecutter] using [@napari]'s [cookiecutter-napari-plugin] template.
Expand Down
23 changes: 23 additions & 0 deletions docs/res/code/model_instance_seg.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,29 @@
model_instance_seg.py
===========================================

Classes
-------------

InstanceMethod
**************************************
.. autoclass:: napari_cellseg3d.code_models.model_instance_seg::InstanceMethod
:members: __init__

ConnectedComponents
**************************************
.. autoclass:: napari_cellseg3d.code_models.model_instance_seg::ConnectedComponents
:members: __init__

Watershed
**************************************
.. autoclass:: napari_cellseg3d.code_models.model_instance_seg::Watershed
:members: __init__

VoronoiOtsu
**************************************
.. autoclass:: napari_cellseg3d.code_models.model_instance_seg::VoronoiOtsu
:members: __init__


Functions
-------------
Expand Down
5 changes: 0 additions & 5 deletions docs/res/code/plugin_convert.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ ToSemanticUtils
.. autoclass:: napari_cellseg3d.code_plugins.plugin_convert::ToSemanticUtils
:members: __init__

InstanceWidgets
**********************************
.. autoclass:: napari_cellseg3d.code_plugins.plugin_convert::InstanceWidgets
:members: __init__, run_method

ToInstanceUtils
**********************************
.. autoclass:: napari_cellseg3d.code_plugins.plugin_convert::ToInstanceUtils
Expand Down
6 changes: 3 additions & 3 deletions docs/res/guides/cropping_module_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ If you'd like to change the size of the volume, change the parameters as previou

Creating new layers
---------------------------------
To "zoom in" your volume, you can use the "Create new layers" checkbox to make a new layer not controlled by the plugin next
time you hit Start. This way, you can first select your region of interest by using the tool as described above,
the enable the option, select the cropped layer, and define a smaller crop size to have easier access to your region of interest.
To "zoom in" your volume, you can use the "Create new layers" checkbox to make a new cropping layer controlled by the sliders
next time you hit Start. This way, you can first select your region of interest by using the tool as described above,
then enable the option, select the cropped region produced before as the input layer, and define a smaller crop size in order to crop within your region of interest.

Interface & functionalities
---------------------------------------------------------------
Expand Down
10 changes: 9 additions & 1 deletion docs/res/guides/utils_module_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@ Label conversion utility guide
==================================

This utility will let you convert labels to various different formats.

You will have to specify the results directory for saving; afterwards you can run each action on a folder or on the currently selected layer.

You can :

* Crop 3D volumes :
Please refer to :ref:`cropping_module_guide` for a guide on using the cropping utility.

* Convert to instance labels :
This will convert 0/1 semantic labels to instance label, with a unique ID for each object using the watershed method.
This will convert 0/1 semantic labels to instance label, with a unique ID for each object.
The available methods for this are :

* Connected components : simple method that will assign a unique ID to each connected component. Does not work well for touching objects (objects will often be fused), works for anisotropic volumes.
* Watershed : method based on topographic maps. Works well for touching objects and anisotropic volumes; touching objects may be fused.
* Voronoi-Otsu : method based on Voronoi diagrams. Works well for touching objects but only for isotropic volumes.
* Convert to semantic labels :
This will convert instance labels with unique IDs per object into 0/1 semantic labels, for example for training.

Expand Down
36 changes: 23 additions & 13 deletions docs/res/welcome.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,28 @@ You can install `napari-cellseg3d` via [pip]:

``pip install napari-cellseg3d``

For local installation, please run:
For local installation after cloning, please run in the CellSeg3D folder:

``pip install -e .``

Requirements
--------------------------------------------

.. note::
A **CUDA-capable GPU** is not needed but **very strongly recommended**, especially for training and possibly inference.

.. important::
A **CUDA-capable GPU** is not needed but **very strongly recommended**, especially for training.
This package requires you have napari installed with PyQt5 or PySide2 first.
If you do not have a Qt backend you can use :

This package requires you have napari installed first.
``pip install napari-cellseg3d[all]``
to install PyQt5 by default.

It also depends on PyTorch and some optional dependencies of MONAI. These come in the pip package above, but if
It also depends on PyTorch and some optional dependencies of MONAI. These come in the pip package as requirements, but if
you need further assistance see below.

* For help with PyTorch, please see `PyTorch's website`_ for installation instructions, with or without CUDA depending on your hardware.
Depending on your setup, you might wish to install torch first.

* If you get errors from MONAI regarding missing readers, please see `MONAI's optional dependencies`_ page for instructions on getting the readers required by your images.

Expand All @@ -70,14 +76,13 @@ To use the plugin, please run:

Then go into Plugins > napari-cellseg3d, and choose which tool to use:


- **Review**: This module allows you to review your labels, from predictions or manual labeling, and correct them if needed. It then saves the status of each file in a csv, for easier monitoring
- **Inference**: This module allows you to use pre-trained segmentation algorithms on volumes to automatically label cells
- **Training**: This module allows you to train segmentation algorithms from labeled volumes
- **Utilities**: This module allows you to use several utilities, e.g. to crop your volumes and labels, compute prediction scores or convert labels
- **Help/About...** : Quick access to version info, Github page and docs

See above for links to detailed guides regarding the usage of the modules.
See the documentation for links to detailed guides regarding the usage of the modules.

Acknowledgments & References
---------------------------------------------
Expand All @@ -90,24 +95,29 @@ We also provide a model that was trained in-house on mesoSPIM nuclei data in col

This plugin mainly uses the following libraries and software:

* `napari website`_
* `napari`_

* `PyTorch website`_
* `PyTorch`_

* `MONAI project website`_ (various models used here are credited `on their website`_)
* `MONAI project`_ (various models used here are credited `on their website`_)

* `pyclEsperanto`_ (for the Voronoi Otsu labeling) by Robert Haase

* A custom re-implementation of the `WNet model`_ by Xia and Kulis [#]_

.. _Mathis Laboratory of Adaptive Motor Control: http://www.mackenziemathislab.org/
.. _Wyss Center: https://wysscenter.ch/
.. _TRAILMAP project on GitHub: https://github.com/AlbertPun/TRAILMAP
.. _napari website: https://napari.org/
.. _PyTorch website: https://pytorch.org/
.. _MONAI project website: https://monai.io/
.. _napari: https://napari.org/
.. _PyTorch: https://pytorch.org/
.. _MONAI project: https://monai.io/
.. _on their website: https://docs.monai.io/en/stable/networks.html#nets

.. _pyclEsperanto: https://github.com/clEsperanto/pyclesperanto_prototype
.. _WNet model: https://arxiv.org/abs/1711.08506

.. rubric:: References

.. [#] Mapping mesoscale axonal projections in the mouse brain using a 3D convolutional network, Friedmann et al., 2020 ( https://pnas.org/cgi/doi/10.1073/pnas.1918465117 )
.. [#] The mesoSPIM initiative: open-source light-sheet microscopes for imaging cleared tissue, Voigt et al., 2019 ( https://doi.org/10.1038/s41592-019-0554-0 )
.. [#] MONAI Project website ( https://monai.io/ )
.. [#] W-Net: A Deep Model for Fully Unsupervised Image Segmentation, Xia and Kulis, 2018 ( https://arxiv.org/abs/1711.08506 )
2 changes: 2 additions & 0 deletions napari_cellseg3d/_tests/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
qt_api=pyqt5
Binary file added napari_cellseg3d/_tests/res/test_labels.tif
Binary file not shown.
52 changes: 52 additions & 0 deletions napari_cellseg3d/_tests/test_labels_correction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from pathlib import Path

import numpy as np
from tifffile import imread

from napari_cellseg3d.dev_scripts import artefact_labeling as al
from napari_cellseg3d.dev_scripts import correct_labels as cl
from napari_cellseg3d.dev_scripts import evaluate_labels as el

res_folder = Path(__file__).resolve().parent / "res"
image_path = res_folder / "test.tif"
image = imread(str(image_path))

labels_path = res_folder / "test_labels.tif"
labels = imread(str(labels_path)) # .astype(np.int32)


def test_artefact_labeling():
output_path = str(res_folder / "test_artifacts.tif")
al.create_artefact_labels(image, labels, output_path=output_path)
assert Path(output_path).is_file()


def test_artefact_labeling_utils():
crop_test = al.crop_image(image)
assert isinstance(crop_test, np.ndarray)
output_path = str(res_folder / "test_cropped.tif")
al.crop_image_path(image, path_image_out=output_path)
assert Path(output_path).is_file()


def test_correct_labels():
output_path = res_folder / "test_correct"
output_path.mkdir(exist_ok=True, parents=True)
cl.relabel_non_unique_i(
labels, str(output_path / "corrected.tif"), go_fast=True
)


def test_relabel(make_napari_viewer):
viewer = make_napari_viewer()
cl.relabel(
str(image_path),
str(labels_path),
go_fast=True,
viewer=viewer,
test=True,
)


def test_evaluate_model_performance():
el.evaluate_model_performance(labels, labels, print_details=True)
18 changes: 17 additions & 1 deletion napari_cellseg3d/_tests/test_plugin_utils.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
from napari_cellseg3d.code_plugins.plugin_utilities import Utilities
from pathlib import Path

import numpy as np
from tifffile import imread

from napari_cellseg3d.code_plugins.plugin_utilities import (
UTILITIES_WIDGETS,
Utilities,
)


def test_utils_plugin(make_napari_viewer):
view = make_napari_viewer()
widget = Utilities(view)

im_path = str(Path(__file__).resolve().parent / "res/test.tif")
image = imread(im_path)
view.add_image(image)
view.add_labels(image.astype(np.uint8))

view.window.add_dock_widget(widget)
for i, utils_name in enumerate(UTILITIES_WIDGETS.keys()):
widget.utils_choice.setCurrentIndex(i)
assert isinstance(
widget.utils_widgets[i], UTILITIES_WIDGETS[utils_name]
)
if utils_name == "Convert to instance labels":
# to avoid issues with Voronoi-Otsu missing runtime
menu = widget.utils_widgets[i].instance_widgets.method_choice
menu.setCurrentIndex(menu.currentIndex() + 1)

widget.utils_widgets[i]._start()
6 changes: 4 additions & 2 deletions napari_cellseg3d/_tests/test_weight_download.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from napari_cellseg3d.code_models.model_workers import WEIGHTS_DIR
from napari_cellseg3d.code_models.model_workers import WeightsDownloader
from napari_cellseg3d.code_models.model_workers import (
WEIGHTS_DIR,
WeightsDownloader,
)


# DISABLED, causes GitHub actions to freeze
Expand Down
15 changes: 6 additions & 9 deletions napari_cellseg3d/code_models/model_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
import torch

# Qt
from qtpy.QtWidgets import QProgressBar
from qtpy.QtWidgets import QSizePolicy
from qtpy.QtWidgets import QProgressBar, QSizePolicy

# local
from napari_cellseg3d import config
from napari_cellseg3d import config, utils
from napari_cellseg3d import interface as ui
from napari_cellseg3d import utils
from napari_cellseg3d.code_plugins.plugin_base import BasePluginFolder

warnings.formatwarning = utils.format_Warning
Expand Down Expand Up @@ -286,11 +284,10 @@ def _load_weights_path(self):
)
if file[0] == self._default_weights_folder:
return
if file is not None:
if file[0] != "":
self.weights_config.path = file[0]
self.weights_filewidget.text_field.setText(file[0])
self._default_weights_folder = str(Path(file[0]).parent)
if file is not None and file[0] != "":
self.weights_config.path = file[0]
self.weights_filewidget.text_field.setText(file[0])
self._default_weights_folder = str(Path(file[0]).parent)

@staticmethod
def get_device(show=True):
Expand Down
Loading

0 comments on commit e00806c

Please sign in to comment.