Skip to content

Commit efcb4b9

Browse files
adding new 3d options to gui and to RTD
1 parent 51535c9 commit efcb4b9

File tree

9 files changed

+287
-157
lines changed

9 files changed

+287
-157
lines changed

README.md

+1-15
Original file line numberDiff line numberDiff line change
@@ -240,18 +240,4 @@ Check out [Omnipose](https://github.com/kevinjohncutler/omnipose), an extension
240240
241241
Pytorch is now the default deep neural network software for cellpose. Mxnet will still be supported. To install mxnet (CPU), run `pip install mxnet-mkl`. To use mxnet in a notebook, declare `torch=False` when creating a model, e.g. `model = models.Cellpose(torch=False)`. To use mxnet on the command line, add the flag `--mxnet`, e.g. `python -m cellpose --dir ~/images/ --mxnet`. The pytorch implementation is 20% faster than the mxnet implementation when running on the GPU and 20% slower when running on the CPU.
242242
243-
Dynamics are computed using bilinear interpolation by default instead of nearest neighbor interpolation. Set `interp=False` in `model.eval` to turn off. The bilinear interpolation will be slightly slower on the CPU, but it is faster than nearest neighbor if using torch and the GPU is enabled.
244-
245-
246-
### Timing (v0.6)
247-
248-
You can check if cellpose is running the MKL version (if you are using the CPU not the GPU) by adding the flag `--check_mkl`. If you are not using MKL cellpose will be much slower. Here are Cellpose run times divided into the time it takes to run the deep neural network (DNN) and the time for postprocessing (gradient tracking, segmentation, quality control etc.). The DNN runtime is shown using either a GPU (Nvidia GTX 1080Ti) or a CPU (Intel 10-core 7900X), with or without network ensembling (4net vs 1net). The postprocessing runtime is similar regardless of ensembling or CPU/GPU version. Runtime is shown for different image sizes, all with a cell diameter of 30 pixels (the average from our training set).
249-
250-
| | 256 pix | 512 pix | 1024 pix |
251-
|----|-------|------|----------|
252-
| DNN (1net, GPU) | 0.054 s | 0.12 s | 0.31 s |
253-
| DNN (1net, CPU) | 0.30 s | 0.65 s | 2.4 s |
254-
| DNN (4net, GPU) | 0.23 s | 0.41 s | 1.3 s |
255-
| DNN (4net, CPU) | 1.3 s | 2.5 s | 9.1 s |
256-
| | | | |
257-
| Postprocessing (CPU) | 0.32 s | 1.2 s | 6.1 s |
243+
Dynamics are computed using bilinear interpolation by default instead of nearest neighbor interpolation. Set `interp=False` in `model.eval` to turn off. The bilinear interpolation will be slightly slower on the CPU, but it is faster than nearest neighbor if using torch and the GPU is enabled.

cellpose/gui/gui.py

+46-10
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@ def __init__(self, image=None, logger=None):
301301

302302
self.load_3D = False
303303
self.stitch_threshold = 0.
304+
self.dP_smooth = 0.
305+
self.anisotropy = 1.
306+
self.min_size = 15
307+
self.resample = True
304308

305309
self.setAcceptDrops(True)
306310
self.win.show()
@@ -2414,6 +2418,15 @@ def compute_segmentation(self, custom=False, model_name=None, load_model=True):
24142418
do_3D = self.load_3D
24152419
stitch_threshold = float(self.stitch_threshold.text()) if not isinstance(
24162420
self.stitch_threshold, float) else self.stitch_threshold
2421+
anisotropy = float(self.anisotropy.text()) if not isinstance(
2422+
self.anisotropy, float) else self.anisotropy
2423+
dP_smooth = float(self.dP_smooth.text()) if not isinstance(
2424+
self.dP_smooth, float) else self.dP_smooth
2425+
min_size = int(self.min_size.text()) if not isinstance(
2426+
self.min_size, int) else self.min_size
2427+
resample = self.resample.isChecked() if not isinstance(
2428+
self.resample, bool) else self.resample
2429+
24172430
do_3D = False if stitch_threshold > 0. else do_3D
24182431

24192432
channels = self.get_channels()
@@ -2433,6 +2446,8 @@ def compute_segmentation(self, custom=False, model_name=None, load_model=True):
24332446
cellprob_threshold=cellprob_threshold,
24342447
flow_threshold=flow_threshold, do_3D=do_3D, niter=niter,
24352448
normalize=normalize_params, stitch_threshold=stitch_threshold,
2449+
anisotropy=anisotropy, resample=resample, dP_smooth=dP_smooth,
2450+
min_size=min_size,
24362451
progress=self.progress, z_axis=0 if self.NZ > 1 else None)[:2]
24372452
except Exception as e:
24382453
print("NET ERROR: %s" % e)
@@ -2452,17 +2467,38 @@ def compute_segmentation(self, custom=False, model_name=None, load_model=True):
24522467
else:
24532468
flows_new.append(np.zeros(flows[1][0].shape, dtype="uint8"))
24542469

2455-
if self.restore and "upsample" in self.restore:
2456-
self.Ly, self.Lx = self.Lyr, self.Lxr
2457-
2458-
if flows_new[0].shape[-3:-1] != (self.Ly, self.Lx):
2459-
self.flows = []
2460-
for j in range(len(flows_new)):
2461-
self.flows.append(
2462-
resize_image(flows_new[j], Ly=self.Ly, Lx=self.Lx,
2463-
interpolation=cv2.INTER_NEAREST))
2470+
if not self.load_3D:
2471+
if self.restore and "upsample" in self.restore:
2472+
self.Ly, self.Lx = self.Lyr, self.Lxr
2473+
2474+
if flows_new[0].shape[-3:-1] != (self.Ly, self.Lx):
2475+
self.flows = []
2476+
for j in range(len(flows_new)):
2477+
self.flows.append(
2478+
resize_image(flows_new[j], Ly=self.Ly, Lx=self.Lx,
2479+
interpolation=cv2.INTER_NEAREST))
2480+
else:
2481+
self.flows = flows_new
24642482
else:
2465-
self.flows = flows_new
2483+
if not resample:
2484+
self.flows = []
2485+
Lz, Ly, Lx = self.NZ, self.Ly, self.Lx
2486+
Lz0, Ly0, Lx0 = flows_new[0].shape[:3]
2487+
print("GUI_INFO: resizing flows to original image size")
2488+
for j in range(len(flows_new)):
2489+
flow0 = flows_new[j]
2490+
if Ly0 != Ly:
2491+
flow0 = resize_image(flow0, Ly=Ly, Lx=Lx,
2492+
no_channels=flow0.ndim==3,
2493+
interpolation=cv2.INTER_NEAREST)
2494+
if Lz0 != Lz:
2495+
flow0 = np.swapaxes(resize_image(np.swapaxes(flow0, 0, 1),
2496+
Ly=Lz, Lx=Lx,
2497+
no_channels=flow0.ndim==3,
2498+
interpolation=cv2.INTER_NEAREST), 0, 1)
2499+
self.flows.append(flow0)
2500+
else:
2501+
self.flows = flows_new
24662502

24672503
# add first axis
24682504
if self.NZ == 1:

cellpose/gui/gui3d.py

+57-5
Original file line numberDiff line numberDiff line change
@@ -150,21 +150,73 @@ def __init__(self, image=None, logger=None):
150150

151151
b = 22
152152

153-
b += 1
154-
label = QLabel("3D stitch threshold:")
153+
label = QLabel("stitch threshold:")
155154
label.setToolTip(
156155
"for 3D volumes, turn on stitch_threshold to stitch masks across planes instead of running cellpose in 3D (see docs for details)"
157156
)
158157
label.setFont(self.medfont)
159-
self.segBoxG.addWidget(label, b, 0, 1, 6)
158+
self.segBoxG.addWidget(label, b, 0, 1, 4)
160159
self.stitch_threshold = QLineEdit()
161160
self.stitch_threshold.setText("0.0")
162-
self.stitch_threshold.setFixedWidth(40)
161+
self.stitch_threshold.setFixedWidth(30)
163162
self.stitch_threshold.setFont(self.medfont)
164163
self.stitch_threshold.setToolTip(
165164
"for 3D volumes, turn on stitch_threshold to stitch masks across planes instead of running cellpose in 3D (see docs for details)"
166165
)
167-
self.segBoxG.addWidget(self.stitch_threshold, b, 7, 1, 2)
166+
self.segBoxG.addWidget(self.stitch_threshold, b, 4, 1, 1)
167+
168+
label = QLabel("dP_smooth:")
169+
label.setToolTip(
170+
"for 3D volumes, smooth flows by a Gaussian with standard deviation dP_smooth (see docs for details)"
171+
)
172+
label.setFont(self.medfont)
173+
self.segBoxG.addWidget(label, b, 5, 1, 3)
174+
self.dP_smooth = QLineEdit()
175+
self.dP_smooth.setText("0.0")
176+
self.dP_smooth.setFixedWidth(30)
177+
self.dP_smooth.setFont(self.medfont)
178+
self.dP_smooth.setToolTip(
179+
"for 3D volumes, smooth flows by a Gaussian with standard deviation dP_smooth (see docs for details)"
180+
)
181+
self.segBoxG.addWidget(self.dP_smooth, b, 8, 1, 1)
182+
183+
b+=1
184+
label = QLabel("anisotropy:")
185+
label.setToolTip(
186+
"for 3D volumes, increase in sampling in Z vs XY as a ratio, e.g. set set to 2.0 if Z is sampled half as dense as X or Y (see docs for details)"
187+
)
188+
label.setFont(self.medfont)
189+
self.segBoxG.addWidget(label, b, 0, 1, 4)
190+
self.anisotropy = QLineEdit()
191+
self.anisotropy.setText("1.0")
192+
self.anisotropy.setFixedWidth(30)
193+
self.anisotropy.setFont(self.medfont)
194+
self.anisotropy.setToolTip(
195+
"for 3D volumes, increase in sampling in Z vs XY as a ratio, e.g. set set to 2.0 if Z is sampled half as dense as X or Y (see docs for details)"
196+
)
197+
self.segBoxG.addWidget(self.anisotropy, b, 4, 1, 1)
198+
199+
self.resample = QCheckBox("resample")
200+
self.resample.setToolTip("reample before creating masks; if diameter > 30 resample will use more CPU+GPU memory (see docs for more details)")
201+
self.resample.setFont(self.medfont)
202+
self.resample.setChecked(True)
203+
self.segBoxG.addWidget(self.resample, b, 5, 1, 4)
204+
205+
b+=1
206+
label = QLabel("min_size:")
207+
label.setToolTip(
208+
"all masks less than this size in pixels (volume) will be removed"
209+
)
210+
label.setFont(self.medfont)
211+
self.segBoxG.addWidget(label, b, 0, 1, 4)
212+
self.min_size = QLineEdit()
213+
self.min_size.setText("15")
214+
self.min_size.setFixedWidth(50)
215+
self.min_size.setFont(self.medfont)
216+
self.min_size.setToolTip(
217+
"all masks less than this size in pixels (volume) will be removed"
218+
)
219+
self.segBoxG.addWidget(self.min_size, b, 4, 1, 3)
168220

169221
b += 1
170222
self.orthobtn = QCheckBox("ortho")

docs/benchmark.rst

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Timing + memory usage
2+
------------------------------------
3+
4+
The algorithm runtime and memory usage increases with the data size. The runtimes
5+
shown below are for a single image run for the first time on an A100 with a batch_size of 32
6+
- this timing includes warm-up of GPU, thus runtimes
7+
will be faster for subsequent images. It will also be faster if you run many images of the same size
8+
input as an array into Cellpose with a large batch_size. The runtimes will also be
9+
slightly faster if you have fewer cells/cell pixels.
10+
11+
.. image:: https://www.cellpose.org/static/images/benchmark_plot.png
12+
:width: 600
13+
14+
Table for 2D:
15+
16+
.. image:: https://www.cellpose.org/static/images/benchmark_2d.png
17+
:width: 400
18+
19+
Table for 3D:
20+
21+
.. image:: https://www.cellpose.org/static/images/benchmark_3d.png
22+
:width: 400
23+
24+
If you are running out of GPU memory for your images, you can reduce the
25+
``batch_size`` parameter in the ``model.eval`` function or in the CLI (default is 8).
26+
27+
If you have even larger images than above, you may want to tile them
28+
before running Cellpose.

docs/do3d.rst

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
.. _do3d:
2+
3+
3D segmentation
4+
------------------------------------
5+
6+
Input format
7+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8+
9+
Tiffs with multiple planes and multiple channels are supported in the GUI (can
10+
drag-and-drop tiffs) and supported when running in a notebook.
11+
To open the GUI with z-stack support, use ``python -m cellpose --Zstack``.
12+
Multiplane images should be of shape nplanes x channels x nY x nX or as
13+
nplanes x nY x nX. You can test this by running in python
14+
15+
::
16+
17+
import tifffile
18+
data = tifffile.imread('img.tif')
19+
print(data.shape)
20+
21+
If drag-and-drop of the tiff into
22+
the GUI does not work correctly, then it's likely that the shape of the tiff is
23+
incorrect. If drag-and-drop works (you can see a tiff with multiple planes),
24+
then the GUI will automatically run 3D segmentation and display it in the GUI. Watch
25+
the command line for progress. It is recommended to use a GPU to speed up processing.
26+
27+
In the CLI/notebook, you can specify the ``channel_axis`` and/or ``z_axis``
28+
parameters to specify the axis (0-based) of the image which corresponds to the image channels and to the z axis.
29+
For example an image with 2 channels of shape (1024,1024,2,105,1) can be
30+
specified with ``channel_axis=2`` and ``z_axis=3``. If ``channel_axis=None``
31+
cellpose will try to automatically determine the channel axis by choosing
32+
the dimension with the minimal size after squeezing. If ``z_axis=None``
33+
cellpose will automatically select the first non-channel axis of the image
34+
to be the Z axis. These parameters can be specified using the command line
35+
with ``--channel_axis`` or ``--z_axis`` or as inputs to ``model.eval`` for
36+
the ``Cellpose`` or ``CellposeModel`` model.
37+
38+
Volumetric stacks do not always have the same sampling in XY as they do in Z.
39+
Therefore you can set an ``anisotropy`` parameter in CLI/notebook to allow for differences in
40+
sampling, e.g. set to 2.0 if Z is sampled half as dense as X or Y, and then in the algorithm
41+
Z is upsampled by 2x.
42+
43+
Segmentation settings
44+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45+
46+
The default segmentation in the GUI is 2.5D segmentation, where the flows are computed
47+
on each YX, ZY and ZX slice and then averaged, and then the dynamics are run in 3D.
48+
Specify this segmentation format in the notebook with ``do_3D=True`` or in the CLI with ``--do_3D``
49+
(with the CLI it will segment all tiffs in the folder as 3D tiffs if possible).
50+
51+
If you see many cells that are fragmented, you can smooth the flows before the dynamics
52+
are run in 3D using the ``dP_smooth`` parameter, which specifies the standard deviation of
53+
a Gaussian for smoothing the flows. The default is 0.0, which means no smoothing. Alternatively/additionally,
54+
you may want to train a model on 2D slices from your 3D data to improve the segmentation (see below).
55+
56+
The network rescales images using the user diameter and the model ``diam_mean`` (usually 30),
57+
so for example if you input a diameter of 90 and the model was trained with a diameter of 30,
58+
then the image will be downsampled by a factor of 3 for computing the flows. If ``resample``
59+
is enabled, then the image will then be upsampled for finding the masks. This will take
60+
additional CPU and GPU memory, so for 3D you may want to set ``resample=False`` or in the CLI ``--no_resample``
61+
(more details here :ref:`resample`).
62+
63+
There may be additional differences in YZ and XZ slices
64+
that make them unable to be used for 3D segmentation.
65+
I'd recommend viewing the volume in those dimensions if
66+
the segmentation is failing, using the orthoviews (activate in the bottom left of the GUI).
67+
In those instances, you may want to turn off
68+
3D segmentation (``do_3D=False``) and run instead with ``stitch_threshold>0``.
69+
Cellpose will create ROIs in 2D on each XY slice and then stitch them across
70+
slices if the IoU between the mask on the current slice and the next slice is
71+
greater than or equal to the ``stitch_threshold``. Alternatively, you can train a separate model for
72+
YX slices vs ZY and ZX slices, and then specify the separate model for ZY/ZX slices
73+
using the ``pretrained_model_ortho`` option in ``CellposeModel``.
74+
75+
3D segmentation ignores the ``flow_threshold`` because we did not find that
76+
it helped to filter out false positives in our test 3D cell volume. Instead,
77+
we found that setting ``min_size`` is a good way to remove false positives.
78+
79+
Training for 3D segmentation
80+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
81+
82+
You can create image crops from z-stacks (in YX, YZ and XZ) using the script ``cellpose/gui/make_train.py``.
83+
If you have anisotropic volumes, then set the ``--anisotropy`` flag to the ratio between pixel size in Z and in YX,
84+
e.g. set ``--anisotropy 5`` for pixel size of 1.0 um in YX and 5.0 um in Z. Now you can
85+
drag-and-drop an image from the folder into the GUI and start to re-train a model
86+
by labeling your crops and using the ``Train`` option in the GUI (see the
87+
Cellpose2 tutorial for more advice). If the model with all crops
88+
isn't working well, you can alternatively separate the crops
89+
into two folders (YX and ZY/ZX) and train separate networks, and use
90+
``pretrained_model_ortho`` when declaring your model.
91+
92+
See the help message for more information:
93+
94+
::
95+
96+
python cellpose\gui\make_train.py --help
97+
usage: make_train.py [-h] [--dir DIR] [--image_path IMAGE_PATH] [--look_one_level_down] [--img_filter IMG_FILTER]
98+
[--channel_axis CHANNEL_AXIS] [--z_axis Z_AXIS] [--chan CHAN] [--chan2 CHAN2] [--invert]
99+
[--all_channels] [--anisotropy ANISOTROPY] [--sharpen_radius SHARPEN_RADIUS]
100+
[--tile_norm TILE_NORM] [--nimg_per_tif NIMG_PER_TIF] [--crop_size CROP_SIZE]
101+
102+
cellpose parameters
103+
104+
options:
105+
-h, --help show this help message and exit
106+
107+
input image arguments:
108+
--dir DIR folder containing data to run or train on.
109+
--image_path IMAGE_PATH
110+
if given and --dir not given, run on single image instead of folder (cannot train with this
111+
option)
112+
--look_one_level_down
113+
run processing on all subdirectories of current folder
114+
--img_filter IMG_FILTER
115+
end string for images to run on
116+
--channel_axis CHANNEL_AXIS
117+
axis of image which corresponds to image channels
118+
--z_axis Z_AXIS axis of image which corresponds to Z dimension
119+
--chan CHAN channel to segment; 0: GRAY, 1: RED, 2: GREEN, 3: BLUE. Default: 0
120+
--chan2 CHAN2 nuclear channel (if cyto, optional); 0: NONE, 1: RED, 2: GREEN, 3: BLUE. Default: 0
121+
--invert invert grayscale channel
122+
--all_channels use all channels in image if using own model and images with special channels
123+
--anisotropy ANISOTROPY
124+
anisotropy of volume in 3D
125+
126+
algorithm arguments:
127+
--sharpen_radius SHARPEN_RADIUS
128+
high-pass filtering radius. Default: 0.0
129+
--tile_norm TILE_NORM
130+
tile normalization block size. Default: 0
131+
--nimg_per_tif NIMG_PER_TIF
132+
number of crops in XY to save per tiff. Default: 10
133+
--crop_size CROP_SIZE
134+
size of random crop to save. Default: 512

docs/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Cellpose: a generalist algorithm for cellular segmentation
5050
inputs
5151
settings
5252
outputs
53+
do3d
5354
models
5455
restore
5556
train

0 commit comments

Comments
 (0)