Skip to content

Commit ebe5bf6

Browse files
authored
Added more anisotropy handling + fix global in review (#14)
* Added more anisotropy handling + fix global in review - Added to inference - Added to crop - Moved all anisotropy functions to AnisoWIdgets class - Fixed logo for helper - Removed global in review, now always opens in new window - Added weights to gitignore (.pth) * Update tox.ini Test fix * lint black * Test fix + napari hub quickstart - Changed review test - Wrote missing quickstart for napari-hub * Trying to fix loose viewer in tests * Test fix
1 parent 1b372ba commit ebe5bf6

24 files changed

+217
-155
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ __pycache__/
99
# unwanted results files
1010
*.tif
1111
napari_cellseg3d/_tests/res/*.csv
12+
*.pth
1213

1314
# Distribution / packaging
1415
.Python

.napari/DESCRIPTION.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,19 @@ make sure to mention this.
6868
If you know of researchers, groups or labs using your plugin, or if it has been cited
6969
anywhere, feel free to also include this information here.
7070
-->
71-
<!--
7271
## Quickstart
7372

73+
Install from pip with `pip install napari-cellseg3d`
74+
75+
OR
76+
77+
- Install napari from pip with `pip install "napari[all]"`,
78+
then from the “Plugins” menu within the napari application, select “Install/Uninstall Package(s)...”
79+
- Copy `napari-cellseg3d` and paste it where it says “Install by name/url…”
80+
- Click “Install”
81+
<!--
82+
83+
7484
This section should go through step-by-step examples of how your plugin should be used.
7585
Where your plugin provides multiple dock widgets or functions, you should split these
7686
out into separate subsections for easy browsing. Include screenshots and videos

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ If you encounter any problems, please [file an issue] along with a detailed desc
6666
To run tests locally:
6767

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

7272
## Contributing

docs/res/logo/logo_alpha.png

-484 KB
Binary file not shown.

docs/res/logo/logo_background.png

-626 KB
Binary file not shown.

napari_cellseg3d/_tests/test_review.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@
55

66
def test_launch_review(make_napari_viewer):
77

8-
view = make_napari_viewer()
9-
widget = rev.Reviewer(view)
8+
view = make_napari_viewer()
9+
widget = rev.Reviewer(view)
1010

11-
# widget.filetype_choice.setCurrentIndex(0)
11+
# widget.filetype_choice.setCurrentIndex(0)
1212

13-
im_path = os.path.dirname(os.path.realpath(__file__)) + "/res/test.tif"
13+
im_path = os.path.dirname(os.path.realpath(__file__)) + "/res/test.tif"
1414

15-
widget.image_path = im_path
16-
widget.label_path = im_path
15+
widget.image_path = im_path
16+
widget.label_path = im_path
17+
18+
print(widget.image_path)
19+
print(widget.label_path)
20+
print(widget.as_folder)
21+
print(widget.filetype)
22+
widget.run_review()
23+
widget._viewer.close()
24+
25+
assert widget._viewer is not None
1726

18-
print(widget.image_path)
19-
print(widget.label_path)
20-
print(widget.as_folder)
21-
print(widget.filetype)
22-
widget.run_review()
2327

24-
assert widget._viewer is not None

napari_cellseg3d/interface.py

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
BOTT_AL = Qt.AlignmentFlag.AlignBottom
4242
"""Alias for Qt.AlignmentFlag.AlignBottom, to use in addWidget"""
4343
###############
44-
44+
# colors
4545
dark_red = "#72071d" # crimson red
4646
default_cyan = "#8dd3c7" # turquoise cyan (default matplotlib line color under dark background context)
4747
napari_grey = "#262930" # napari background color (grey)
@@ -95,7 +95,7 @@ def open_file_dialog(
9595
return filenames
9696

9797

98-
def make_label(name, parent=None):
98+
def make_label(name, parent=None): # TODO update to child class
9999
"""Creates a QLabel
100100
101101
Args:
@@ -113,7 +113,7 @@ def make_label(name, parent=None):
113113

114114
def make_scrollable(
115115
contained_layout, containing_widget, min_wh=None, max_wh=None, base_wh=None
116-
):
116+
): # TODO convert to child class
117117
"""Creates a QScrollArea and sets it up, then adds the contained_widget to it,
118118
and finally adds the scroll area in a layout and sets it to the contaning_widget
119119
@@ -163,7 +163,7 @@ def make_n_spinboxes(
163163
parent=None,
164164
double=False,
165165
fixed=True,
166-
) -> Union[list, QWidget]:
166+
) -> Union[list, QWidget]: # TODO: child class if possible ?
167167
"""
168168
169169
Args:
@@ -222,7 +222,7 @@ def add_to_group(title, widget, layout, L=7, T=20, R=7, B=11):
222222
layout.addWidget(group)
223223

224224

225-
def make_group(title, L=7, T=20, R=7, B=11, parent=None):
225+
def make_group(title, L=7, T=20, R=7, B=11, parent=None): # TODO : child class
226226
"""Creates a group widget and layout, with a header (`title`) and content margins for top/left/right/bottom `L, T, R, B` (in pixels)
227227
Group widget and layout returned will have a Fixed size policy.
228228
@@ -246,7 +246,9 @@ def make_group(title, L=7, T=20, R=7, B=11, parent=None):
246246
return group, layout
247247

248248

249-
def make_container(L=0, T=0, R=1, B=11, vertical=True, parent=None):
249+
def make_container(
250+
L=0, T=0, R=1, B=11, vertical=True, parent=None
251+
): # TODO child class?
250252
"""Creates a QWidget and a layout for the purpose of containing other modules, with a Fixed layout.
251253
252254
Args:
@@ -276,7 +278,7 @@ def make_container(L=0, T=0, R=1, B=11, vertical=True, parent=None):
276278
return container_widget, container_layout
277279

278280

279-
def make_button(
281+
def make_button( # TODO child class
280282
title: str = None,
281283
func: callable = None,
282284
parent: QWidget = None,
@@ -339,7 +341,7 @@ def __init__(
339341
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
340342

341343

342-
def make_combobox(
344+
def make_combobox( # TODO child class
343345
entries=None,
344346
parent: QWidget = None,
345347
label: str = None,
@@ -405,7 +407,7 @@ def __init__(
405407
self.toggled.connect(func)
406408

407409

408-
def make_checkbox(
410+
def make_checkbox( # TODO update calls to class
409411
title: str = None,
410412
func: callable = None,
411413
parent: QWidget = None,
@@ -508,9 +510,9 @@ def __init__(self, parent, default_x=1, default_y=1, default_z=1):
508510
self.box_widgets = make_n_spinboxes(
509511
n=3, min=1.0, max=1000, default=1, step=0.5, double=True
510512
)
511-
self.box_widgets[0].setValue(default_x) # TODO change default
512-
self.box_widgets[1].setValue(default_y) # TODO change default
513-
self.box_widgets[2].setValue(default_z) # TODO change default
513+
self.box_widgets[0].setValue(default_x)
514+
self.box_widgets[1].setValue(default_y)
515+
self.box_widgets[2].setValue(default_z)
514516

515517
for w in self.box_widgets:
516518
w.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
@@ -547,9 +549,48 @@ def build(self):
547549
add_widgets(self._layout, [self.checkbox, self.container])
548550
self.setLayout(self._layout)
549551

550-
def get_anisotropy_factors(self):
551-
"""Returns : the resolution in microns for each of the three dimensions"""
552-
return [w.value() for w in self.box_widgets]
552+
def get_anisotropy_resolution_xyz(self, as_factors=True):
553+
"""
554+
Args :
555+
as_factors: if True, returns zoom factors, otherwise returns the input resolution
556+
557+
Returns : the resolution in microns for each of the three dimensions. ZYX order suitable for napari scale"""
558+
559+
resolution = [w.value() for w in self.box_widgets]
560+
if as_factors:
561+
return self.anisotropy_zoom_factor(resolution)
562+
563+
return resolution
564+
565+
def get_anisotropy_resolution_zyx(self, as_factors=True):
566+
"""
567+
Args :
568+
as_factors: if True, returns zoom factors, otherwise returns the input resolution
569+
570+
Returns : the resolution in microns for each of the three dimensions. XYZ order suitable for MONAI"""
571+
resolution = [w.value() for w in self.box_widgets]
572+
if as_factors:
573+
resolution = self.anisotropy_zoom_factor(resolution)
574+
575+
return [resolution[2], resolution[1], resolution[0]]
576+
577+
def anisotropy_zoom_factor(self, aniso_res):
578+
"""Computes a zoom factor to correct anisotropy, based on anisotropy resolutions
579+
580+
Args:
581+
resolutions: array for resolution (float) in microns for each axis
582+
583+
Returns: an array with the corresponding zoom factors for each axis (all values divided by min)
584+
585+
"""
586+
587+
base = min(aniso_res)
588+
zoom_factors = [base / res for res in aniso_res]
589+
return zoom_factors
590+
591+
def is_enabled(self):
592+
"""Returns : whether anisotropy correction has been enabled or not"""
593+
return self.checkbox.isChecked()
553594

554595

555596
def open_url(url):

napari_cellseg3d/launch_review.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
from pathlib import Path
33

44
import matplotlib.pyplot as plt
5+
import napari
56
import numpy as np
67
from magicgui import magicgui
7-
from matplotlib.backends.backend_qt5agg import \
8-
FigureCanvasQTAgg as FigureCanvas
8+
from matplotlib.backends.backend_qt5agg import (
9+
FigureCanvasQTAgg as FigureCanvas,
10+
)
911
from matplotlib.figure import Figure
1012
from monai.transforms import Zoom
1113
from qtpy.QtWidgets import QSizePolicy
@@ -17,7 +19,6 @@
1719

1820

1921
def launch_review(
20-
viewer,
2122
original,
2223
base,
2324
raw,
@@ -48,7 +49,6 @@ def launch_review(
4849
and determine whether it should be labeled or not.
4950
5051
Args:
51-
viewer (napari.viewer.Viewer): The viewer the widgets are to be displayed in
5252
5353
original (dask.array.Array): The original images/volumes that have been labeled
5454
@@ -68,12 +68,13 @@ def launch_review(
6868
6969
zoom_factor (array(int)): zoom factors for each axis
7070
71-
71+
Returns : list of all docked widgets
7272
"""
7373
images_original = original
7474
base_label = base
7575

76-
view1 = viewer
76+
view1 = napari.Viewer()
77+
viewer = view1 #TODO fix duplicate name
7778

7879
view1.scale_bar.visible = True
7980

@@ -144,10 +145,6 @@ def launch_review(
144145
# return labeled_c, labeled_sorted, nums
145146
#
146147
# worker = create_label()
147-
148-
layer = view1.layers[0]
149-
layer1 = view1.layers[1]
150-
151148
# if not as_folder:
152149
# r_path = os.path.dirname(r_path)
153150

@@ -324,3 +321,5 @@ def crop_volume_around_point(points, layer):
324321
-inferior_bound[2] : 100 - superior_bound[2],
325322
] = crop_temp
326323
return cropped_volume
324+
325+
return view1, [file_widget, canvas, dmg]

napari_cellseg3d/model_framework.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import napari
55
import torch
6+
67
# Qt
78
from qtpy.QtWidgets import QLineEdit
89
from qtpy.QtWidgets import QProgressBar

napari_cellseg3d/model_instance_seg.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from __future__ import print_function
33

44
import numpy as np
5+
56
# from skimage.measure import marching_cubes
67
# from skimage.measure import mesh_surface_area
78
from skimage.measure import label

0 commit comments

Comments
 (0)