Skip to content

Commit 7d0461f

Browse files
authored
Merge pull request #59 from C-Achard/cy/improvements
Cy/improvements
2 parents 0ca3cd4 + f83b74f commit 7d0461f

File tree

13 files changed

+428
-363
lines changed

13 files changed

+428
-363
lines changed

.github/workflows/test_and_deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
# strategy:
2424
# matrix:
2525
# platform: [windows-latest, macos-latest, ubuntu-latest]
26-
# python-version: [3.9]
26+
# python-version: [3.8, 3.9]
2727
#
2828
# steps:
2929
# - uses: actions/checkout@v2

README.md

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,89 @@
1-
# napari-cellseg3d
1+
# napari-cellseg3d: a napari plug-in for 3d deep learning models for cell segmentation
22

3-
[![License](https://img.shields.io/pypi/l/napari-cellseg3d.svg?color=green)](https://github.com/C_Achard/napari-cellseg3d/raw/main/LICENSE)
4-
[![PyPI](https://img.shields.io/pypi/v/napari-cellseg3d.svg?color=green)](https://pypi.org/project/napari-cellseg3d)
5-
[![Python Version](https://img.shields.io/pypi/pyversions/napari-cellseg3d.svg?color=green)](https://python.org)
6-
[![tests](https://github.com/C_Achard/napari-cellseg3d/workflows/tests/badge.svg)](https://github.com/C_Achard/napari-cellseg3d/actions)
7-
[![codecov](https://codecov.io/gh/C_Achard/napari-cellseg3d/branch/main/graph/badge.svg)](https://codecov.io/gh/C_Achard/napari-cellseg3d)
8-
[![napari hub](https://img.shields.io/endpoint?url=https://api.napari-hub.org/shields/napari-cellseg3d)](https://napari-hub.org/plugins/napari-cellseg3d)
93

10-
plugin for cell segmentation
4+
<img src="https://images.squarespace-cdn.com/content/v1/57f6d51c9f74566f55ecf271/04991e21-9cee-4b21-bdfc-d465fd73247d/CELLSEGGIT.png?format=2500w" width="250" title="cellseg3d" alt="cellseg3d" align="right" vspace = "80">
115

12-
----------------------------------
6+
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://www.gnu.org/licenses/mit)
7+
[![PyPI](https://img.shields.io/pypi/v/napari-cellseg-3d.svg?color=green)](https://pypi.org/project/napari-cellseg-3d)
8+
[![Python Version](https://img.shields.io/pypi/pyversions/napari-cellseg-3d.svg?color=green)](https://python.org)
9+
![Tests](https://github.com/AdaptiveMotorControlLab/CellSeg3d/workflows/Python%20package/badge.svg)
10+
[![codecov](https://codecov.io/gh/AdaptiveMotorControlLab/CellSeg3d/branch/main/graph/badge.svg)](https://codecov.io/gh/AdaptiveMotorControlLab/CellSeg3d)
11+
[![napari hub](https://img.shields.io/endpoint?url=https://api.napari-hub.org/shields/napari-cellseg-3d)](https://napari-hub.org/plugins/napari-cellseg-3d)
1312

14-
This [napari] plugin was generated with [Cookiecutter] using [@napari]'s [cookiecutter-napari-plugin] template.
1513

16-
<!--
17-
Don't miss the full getting started guide to set up your new package:
18-
https://github.com/napari/cookiecutter-napari-plugin#getting-started
1914

20-
and review the napari docs for plugin developers:
21-
https://napari.org/plugins/stable/index.html
22-
-->
15+
A napari plugin for 3D cell segmentation: training, inference, and data review. In particular, this project was developed for analysis of mesoSPIM-acquired (cleared tissue + lightsheet) datasets.
2316

24-
## Requirements
25-
26-
Requires manual installation of pytorch and MONAI.
27-
For Pytorch, please see [PyTorch]'s website for installation instructions.
28-
A CUDA-capable GPU is not needed but very strongly recommended, especially for training.
29-
30-
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.
3117

18+
----------------------------------
3219

3320
## Installation
3421

35-
You can install `napari-cellseg3d` via [pip]:
22+
You can install `napari-cellseg-3d` via [pip]:
3623

37-
pip install napari-cellseg3d
24+
pip install napari-cellseg-3d
3825

39-
For local installation, please run:
26+
## Documentation
4027

41-
```
42-
pip install -e .
43-
```
28+
Available on the [Github pages website](https://adaptivemotorcontrollab.github.io/cellseg3d-docs/)
4429

45-
## Documentation
30+
Source files can be found at https://AdaptiveMotorControlLab.github.io/cellseg3d-docs
4631

47-
You can generate docs by running ``make html`` in the docs folder
32+
You can also generate docs by running ``make html`` in the docs folder.
4833

4934
## Usage
5035

5136
To use the plugin, please run:
5237
```
5338
napari
5439
```
55-
Then go into Plugins > napari-cellseg3d, and choose which tool to use.
40+
Then go into Plugins > napari-cellseg-3d, and choose which tool to use.
5641

5742
- **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.
5843
- **Infer**: This module allows you to use pre-trained segmentation algorithms on volumes to automatically label cells.
5944
- **Train**: This module allows you to train segmentation algorithms from labeled volumes.
60-
- **Crop utility**: This module allows you to crop your volumes and labels dynamically, by selecting a fixed size volume and moving it around the image.
45+
- **Utilities**: This module allows you to perform several actions like cropping your volumes and labels dynamically, by selecting a fixed size volume and moving it around the image; computing prediction scores from ground truth and predicition labels; or converting labels from instance to segmentation and the opposite.
6146

6247
## Testing
6348

6449
To run tests locally:
6550

6651
- Locally : run ``pytest`` in the plugin folder
67-
- Locally with coverage : In the plugin folder, run ``coverage run --source=napari_cellseg3d -m pytest`` then ``coverage.xml`` to generate a .xml coverage file.
52+
- Locally with coverage : In the plugin folder, run ``coverage run --source=src -m pytest`` then ``coverage.xml`` to generate a .xml coverage file.
6853
- With tox : run ``tox`` in the plugin folder (will simulate tests with several python and OS configs, requires substantial storage space)
6954

7055
## Contributing
7156

72-
Contributions are very welcome. Tests can be run with [tox], please ensure
73-
the coverage at least stays the same before you submit a pull request.
57+
Contributions are very welcome.
58+
Please ensure the coverage at least stays the same before you submit a pull request.
59+
60+
For local installation, please run:
61+
62+
```
63+
pip install -e .
64+
```
65+
7466

7567
## License
7668

7769
Distributed under the terms of the [MIT] license,
78-
"napari-cellseg3d" is free and open source software
70+
"napari-cellseg-3d" is free and open source software
7971

8072
## Issues
8173

8274
If you encounter any problems, please [file an issue] along with a detailed description.
8375

76+
## Requirements
77+
**Python >= 3.8 required**
78+
79+
Requires manual installation of **pytorch** and **MONAI**.
80+
81+
For Pytorch, please see [PyTorch's website for installation instructions].
82+
A CUDA-capable GPU is not needed but very strongly recommended, especially for training.
83+
84+
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.
85+
86+
[[file an issue]: https://github.com/AdaptiveMotorControlLab/CellSeg3d/issues
8487
[napari]: https://github.com/napari/napari
8588
[Cookiecutter]: https://github.com/audreyr/cookiecutter
8689
[@napari]: https://github.com/napari
@@ -97,5 +100,18 @@ If you encounter any problems, please [file an issue] along with a detailed desc
97100
[pip]: https://pypi.org/project/pip/
98101
[PyPI]: https://pypi.org/
99102

100-
[PyTorch]: https://pytorch.org/get-started/locally/
101-
[MONAI's optional dependencies]: https://docs.monai.io/en/stable/installation.html#installing-the-recommended-dependencies
103+
[PyTorch's website for installation instructions]: https://pytorch.org/get-started/locally/
104+
[MONAI's optional dependencies]: https://docs.monai.io/en/stable/installation.html#installing-the-recommended-dependencies
105+
106+
## Acknowledgements
107+
108+
This plugin was developed by Cyril Achard & Maxime Vidal.
109+
This [napari] plugin was generated with [Cookiecutter] using [@napari]'s [cookiecutter-napari-plugin] template. This work was funded, in part, from the Wyss Center to the Adaptive Motor Control Lab.
110+
111+
<!--
112+
Don't miss the full getting started guide to set up your new package:
113+
https://github.com/napari/cookiecutter-napari-plugin#getting-started
114+
115+
and review the napari docs for plugin developers:
116+
https://napari.org/plugins/stable/index.html
117+
-->

docs/res/guides/inference_module_guide.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,7 @@ Interface and functionalities
4040

4141
| You can then choose one of the provided **models** above, which will be used for inference.
4242
| You may also choose to **load custom weights** rather than the pre-trained ones, simply ensure they are **compatible** (e.g. produced from the training module for the same model)
43-
44-
.. note::
45-
Currently the SegResNet model requires you to provide the size of the images the model was trained with due to the VAE module.
46-
Provided weights use a size of 128, please leave it as is if you're not using custom weights.
43+
| If you choose to use a SegResNet with custom weights, you will have to provide the size of images it was trained on to ensure compatibility. (See note below)
4744
4845
* **Inference parameters** :
4946

@@ -106,6 +103,10 @@ Once it has finished, results will be saved then displayed in napari; each outpu
106103
On the left side, a progress bar and a log will keep you informed on the process.
107104

108105

106+
.. note::
107+
Currently the SegResNet model requires you to provide the size of the images the model was trained with due to the VAE module.
108+
Provided weights use a size of 128, please leave it as is if you're not using custom weights.
109+
109110

110111
.. note::
111112
| The files will be saved using the following format :

napari_cellseg3d/model_instance_seg.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import numpy as np
55
from skimage.measure import label
6+
67
# from skimage.measure import marching_cubes
78
# from skimage.measure import mesh_surface_area
89
from skimage.measure import regionprops
@@ -13,6 +14,7 @@
1314

1415
from napari_cellseg3d.utils import fill_list_in_between
1516
from napari_cellseg3d.utils import sphericity_axis
17+
1618
# from napari_cellseg3d.utils import sphericity_volume_area
1719

1820

@@ -189,7 +191,7 @@ def volume_stats(volume_image):
189191
190192
Returns:
191193
dict: Statistics described above
192-
"""
194+
"""
193195

194196
properties = regionprops(volume_image)
195197
number_objects = np.amax(volume_image)
@@ -218,7 +220,9 @@ def fill(lst, n=len(properties) - 1):
218220

219221
return {
220222
"Volume": volume,
221-
"Centroid": [region.centroid for region in properties],
223+
"Centroid x": [region.centroid[0] for region in properties],
224+
"Centroid y": [region.centroid[1] for region in properties],
225+
"Centroid z": [region.centroid[2] for region in properties],
222226
# "Sphericity (volume/area)": sphericity_va,
223227
"Sphericity (axes)": sphericity_ax,
224228
"Image size": fill([volume_image.shape]),

napari_cellseg3d/model_workers.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
# local
4545
from napari_cellseg3d.model_instance_seg import binary_connected
4646
from napari_cellseg3d.model_instance_seg import binary_watershed
47+
from napari_cellseg3d.model_instance_seg import volume_stats
4748

4849
"""
4950
Writing something to log messages from outside the main thread is rather problematic (plenty of silent crashes...)
@@ -92,6 +93,7 @@ def __init__(
9293
use_window,
9394
window_infer_size,
9495
keep_on_cpu,
96+
stats_csv,
9597
):
9698
"""Initializes a worker for inference with the arguments needed by the :py:func:`~inference` function.
9799
@@ -118,6 +120,8 @@ def __init__(
118120
119121
* keep_on_cpu: keep images on CPU or no
120122
123+
* stats_csv: compute stats on cells and save them to a csv file
124+
121125
Note: See :py:func:`~self.inference`
122126
"""
123127

@@ -137,6 +141,7 @@ def __init__(
137141
self.use_window = use_window
138142
self.window_infer_size = window_infer_size
139143
self.keep_on_cpu = keep_on_cpu
144+
self.stats_to_csv = stats_csv
140145

141146
"""These attributes are all arguments of :py:func:~inference, please see that for reference"""
142147

@@ -215,6 +220,8 @@ def inference(self):
215220
216221
* keep_on_cpu: keep images on CPU or no
217222
223+
* stats_csv: compute stats on cells and save them to a csv file
224+
218225
Yields:
219226
dict: contains :
220227
* "image_id" : index of the returned image
@@ -447,6 +454,14 @@ def method(image):
447454
f"Instance segmentation results for image n°{image_id} have been saved as:"
448455
)
449456
self.log(os.path.split(instance_filepath)[1])
457+
458+
if self.stats_to_csv:
459+
data_dict = volume_stats(
460+
instance_labels
461+
) # TODO test with area mesh function
462+
else:
463+
data_dict = None
464+
450465
else:
451466
instance_labels = None
452467

@@ -457,6 +472,7 @@ def method(image):
457472
"image_id": i + 1,
458473
"original": original,
459474
"instance_labels": instance_labels,
475+
"object stats": data_dict,
460476
"result": out,
461477
"model_name": self.model_dict["name"],
462478
}

napari_cellseg3d/plugin_base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ def __init__(self, viewer: "napari.viewer.Viewer", parent=None):
209209

210210
def make_close_button(self):
211211
btn = ui.make_button("Close", self.remove_from_viewer)
212+
btn.setToolTip(
213+
"Close the window and all docked widgets. Make sure to save your work !"
214+
)
212215
return btn
213216

214217
def make_prev_button(self):

napari_cellseg3d/plugin_model_inference.py

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from napari_cellseg3d import interface as ui
1313
from napari_cellseg3d import utils
1414
from napari_cellseg3d.model_framework import ModelFramework
15-
from napari_cellseg3d.model_instance_seg import volume_stats
1615
from napari_cellseg3d.model_workers import InferenceWorker
1716

1817

@@ -119,9 +118,6 @@ def __init__(self, viewer: "napari.viewer.Viewer"):
119118
######################
120119
# TODO : better way ?
121120
self.segres_size = ui.make_n_spinboxes(min=1, max=1024, default=128)
122-
self.segres_size.setToolTip(
123-
"Image size on which the SegResNet has been trained (default : 128)"
124-
)
125121
self.model_choice.currentIndexChanged.connect(
126122
self.toggle_display_segres_size
127123
)
@@ -235,6 +231,36 @@ def __init__(self, viewer: "napari.viewer.Viewer"):
235231
self.btn_model_path.setVisible(False)
236232
self.lbl_model_path.setVisible(False)
237233

234+
##################
235+
##################
236+
#tooltips
237+
self.view_checkbox.setToolTip("Show results in the napari viewer")
238+
self.display_number_choice.setToolTip("Choose how many results to display once the work is done.\n"
239+
"Maximum is 10 for clarity")
240+
self.show_original_checkbox.setToolTip("Displays the image used for inference in the viewer")
241+
self.segres_size.setToolTip(
242+
"Image size on which the SegResNet has been trained (default : 128)"
243+
)
244+
self.aniso_checkbox.setToolTip("If you have anisotropic data, you can scale data using your resolution in microns")
245+
[w.setToolTip("Resolution in microns") for w in self.aniso_box_widgets]
246+
thresh_desc = "Thresholding : all values in the image below the chosen probability threshold will be set to 0, and all others to 1."
247+
self.thresholding_checkbox.setToolTip(thresh_desc)
248+
self.thresholding_count.setToolTip(thresh_desc)
249+
self.window_infer_box.setToolTip("Sliding window inference runs the model on parts of the image"
250+
"\nrather than the whole image, to reduce memory requirements."
251+
"\nUse this if you have large images.")
252+
self.window_size_choice.setToolTip("Size of the window to run inference with (in pixels)")
253+
self.keep_data_on_cpu_box.setToolTip("If enabled, data will be kept on the RAM rather than the VRAM.\nCan avoid out of memory issues with CUDA")
254+
self.instance_box.setToolTip("Instance segmentation will convert instance (0/1) labels to labels that attempt to assign an unique ID to each cell.")
255+
self.instance_method_choice.setToolTip("Choose which method to use for instance segmentation"
256+
"\nConnected components : all separated objects will be assigned an unique ID. Robust but will not work correctly with adjacent/touching objects\n"
257+
"Watershed : assigns objects ID based on the probability gradient surrounding an object. Requires the model to surround objects in a gradient; can possibly correctly separate unique but touching/adjacent objects.")
258+
self.instance_prob_thresh.setToolTip("All objects below this probability will be ignored (set to 0)")
259+
self.instance_small_object_thresh.setToolTip("Will remove all objects smaller (in volume) than the specified number of pixels")
260+
self.save_stats_to_csv_box.setToolTip("Will save several statistics for each object to a csv in the results folder. Stats include : volume, centroid coordinates, sphericity")
261+
##################
262+
##################
263+
238264
self.build()
239265

240266
def check_ready(self):
@@ -587,6 +613,7 @@ def start(self): # TODO update
587613
use_window=self.use_window_inference,
588614
window_infer_size=self.window_inference_size,
589615
keep_on_cpu=self.keep_on_cpu,
616+
stats_csv=self.stats_to_csv,
590617
)
591618

592619
yield_connect_show_res = lambda data: self.on_yield(
@@ -703,21 +730,12 @@ def on_yield(data, widget):
703730

704731
instance_layer = viewer.add_labels(labels, name=name)
705732

706-
if widget.stats_to_csv: # TODO move to worker
707-
708-
cell_data = volume_stats(
709-
labels
710-
) # TODO test with area mesh function
711-
# count = np.tile("", len(cell_data["Volume"])-1)
712-
# cell_data["Cell count"] = np.insert(count, 0, number_cells)
713-
# cell_data["Total cell volume"] = np.insert(
714-
# count, 0, tot_cell_volume
715-
# )
716-
# cell_data["Cell volume ratio"] = np.insert(
717-
# count, 0, tot_cell_volume / len(labels.flatten())
718-
# )
733+
data_dict = data["object stats"]
734+
if (
735+
widget.stats_to_csv and data_dict is not None
736+
): # TODO move to worker
719737

720-
numeric_data = pd.DataFrame(cell_data)
738+
numeric_data = pd.DataFrame(data_dict)
721739

722740
csv_name = f"/{method}_seg_results_{image_id}_{utils.get_date_time()}.csv"
723741
numeric_data.to_csv(

0 commit comments

Comments
 (0)