Skip to content

Commit

Permalink
Merge branch 'main' into set_3d_checkbox
Browse files Browse the repository at this point in the history
  • Loading branch information
psobolewskiPhD authored Sep 22, 2024
2 parents e1375a9 + 2749581 commit e69ec3f
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 42 deletions.
53 changes: 29 additions & 24 deletions .github/workflows/test_and_deploy.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# This workflows will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries

### test workflow from Talley Lambert's napari-omero! thanks!
# https://github.com/tlambert03/napari-omero/blob/main/.github/workflows/ci.yml

name: tests

on:
on:
push:
branches:
- main
Expand All @@ -16,22 +19,25 @@ on:

jobs:
test:
name: ${{ matrix.platform }} py${{ matrix.python-version }}
name: ${{ matrix.platform }} ${{ matrix.python-version }}
runs-on: ${{ matrix.platform }}
defaults:
run:
shell: bash -l {0}
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.8', '3.9', '3.10']
exclude: # torch doesn't support windows python 3.10 https://pytorch.org/get-started/locally/#windows-python
- platform: windows-latest
python-version: '3.10'
steps:
- uses: actions/checkout@v3
platform: [ubuntu-latest]#, macos-latest, windows-latest]
python-version: ["3.9", "3.10", "3.11"]
backend: [pyqt5]

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
steps:
- uses: actions/checkout@v4
- uses: conda-incubator/setup-miniconda@v2
with:
miniconda-version: "latest"
channels: conda-forge
channel-priority: strict
python-version: ${{ matrix.python-version }}

- uses: tlambert03/setup-qt-libs@v1
Expand All @@ -41,27 +47,25 @@ jobs:
run: |
git clone --depth 1 https://github.com/pyvista/gl-ci-helpers.git
powershell gl-ci-helpers/appveyor/install_opengl.ps1
if (Test-Path -Path "C:\Windows\system32\opengl32.dll" -PathType Leaf) {Exit 0} else {Exit 1}
# note: if you need dependencies from conda, considering using
# setup-miniconda: https://github.com/conda-incubator/setup-miniconda
# and
# tox-conda: https://github.com/tox-dev/tox-conda
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install setuptools tox tox-gh-actions
# this runs the platform-specific tests declared in tox.ini
python -m pip install --upgrade setuptools tox tox-conda tox-gh-actions
- name: Test with tox
uses: GabrielBB/xvfb-action@v1
uses: aganders3/headless-gui@v1.2
with:
shell: bash -el {0}
run: python -m tox
env:
PLATFORM: ${{ matrix.platform }}
PYTHON: ${{ matrix.python-version }}
PYVISTA_OFF_SCREEN: True
BACKEND: ${{ matrix.backend }}

- name: Coverage
uses: codecov/codecov-action@v2
- name: Codecov
uses: codecov/codecov-action@v3

deploy:
# this will run when you have tagged a commit, starting with "v*"
Expand All @@ -71,9 +75,9 @@ jobs:
runs-on: ubuntu-latest
if: contains(github.ref, 'tags')
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: "3.x"
- name: Install dependencies
Expand All @@ -87,4 +91,5 @@ jobs:
run: |
git tag
python -m build
twine check dist/*
twine upload dist/*
34 changes: 17 additions & 17 deletions cellpose_napari/_dock_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
from magicgui import magicgui
import sys

CP_models = ["cyto3", "cyto2", "cyto", "nuclei", "tissuenet_cp3",
"livecell_cp3", "yeast_PhC_cp3", "yeast_BF_cp3", "bact_phase_cp3",
"bact_fluor_cp3", "deepbacs_cp3", "cyto2_cp3"]

# initialize logger
# use -v or --verbose when starting napari to increase verbosity
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -57,12 +61,11 @@ def _deco(func):
@thread_worker
@no_grad()
def run_cellpose(image, model_type, custom_model, channels, channel_axis, diameter,
net_avg, resample, cellprob_threshold,
model_match_threshold, do_3D, stitch_threshold):
resample, cellprob_threshold,
flow_threshold, do_3D, stitch_threshold):
from cellpose import models

flow_threshold = (31.0 - model_match_threshold) / 10.
if model_match_threshold==0.0:
if flow_threshold==0.0:
flow_threshold = 0.0
logger.debug('flow_threshold=0 => no masks thrown out due to model mismatch')
logger.debug(f'computing masks with cellprob_threshold={cellprob_threshold}, flow_threshold={flow_threshold}')
Expand All @@ -74,7 +77,6 @@ def run_cellpose(image, model_type, custom_model, channels, channel_axis, diamet
channels=channels,
channel_axis=channel_axis,
diameter=diameter,
net_avg=net_avg,
resample=resample,
cellprob_threshold=cellprob_threshold,
flow_threshold=flow_threshold,
Expand All @@ -93,23 +95,24 @@ def run_cellpose(image, model_type, custom_model, channels, channel_axis, diamet
@thread_worker
def compute_diameter(image, channels, model_type):
from cellpose import models
model_type0 = model_type if model_type in CP_models[:4] else "cyto3"

CP = models.Cellpose(model_type = model_type, gpu=True)
CP = models.Cellpose(model_type = model_type0, gpu=True)
diam = CP.sz.eval(image, channels=channels, channel_axis=-1)[0]
diam = np.around(diam, 2)
del CP
return diam

@thread_worker
def compute_masks(masks_orig, flows_orig, cellprob_threshold, model_match_threshold):
def compute_masks(masks_orig, flows_orig, cellprob_threshold, flow_threshold):
import cv2
from cellpose.utils import fill_holes_and_remove_small_masks
from cellpose.dynamics import get_masks
from cellpose.transforms import resize_image

#print(flows_orig[3].shape, flows_orig[2].shape, masks_orig.shape)
flow_threshold = (31.0 - model_match_threshold) / 10.
if model_match_threshold==0.0:
flow_threshold = (31.0 - flow_threshold) / 10.
if flow_threshold==0.0:
flow_threshold = 0.0
logger.debug('flow_threshold=0 => no masks thrown out due to model mismatch')
logger.debug(f'computing masks with cellprob_threshold={cellprob_threshold}, flow_threshold={flow_threshold}')
Expand All @@ -123,17 +126,16 @@ def compute_masks(masks_orig, flows_orig, cellprob_threshold, model_match_thresh
@magicgui(
call_button='run segmentation',
layout='vertical',
model_type = dict(widget_type='ComboBox', label='model type', choices=['cyto', 'nuclei', 'cyto2', 'custom'], value='cyto', tooltip='there is a <em>cyto</em> model, a new <em>cyto2</em> model from user submissions, and a <em>nuclei</em> model'),
model_type = dict(widget_type='ComboBox', label='model type', choices=[*CP_models, 'custom'], value='cyto3', tooltip='there is a <em>cyto</em> model, a new <em>cyto2</em> model from user submissions, and a <em>nuclei</em> model'),
custom_model = dict(widget_type='FileEdit', label='custom model path: ', tooltip='if model type is custom, specify file path to it here'),
main_channel = dict(widget_type='ComboBox', label='channel to segment', choices=main_channel_choices, value=0, tooltip='choose channel with cells'),
optional_nuclear_channel = dict(widget_type='ComboBox', label='optional nuclear channel', choices=optional_nuclear_channel_choices, value=0, tooltip='optional, if available, choose channel with nuclei of cells'),
diameter = dict(widget_type='LineEdit', label='diameter', value=30, tooltip='approximate diameter of cells to be segmented'),
compute_diameter_shape = dict(widget_type='PushButton', text='compute diameter from shape layer', tooltip='create shape layer with circles and/or squares, select above, and diameter will be estimated from it'),
compute_diameter_button = dict(widget_type='PushButton', text='compute diameter from image', tooltip='cellpose model will estimate diameter from image using specified channels'),
cellprob_threshold = dict(widget_type='FloatSlider', name='cellprob_threshold', value=0.0, min=-8.0, max=8.0, step=0.2, tooltip='cell probability threshold (set lower to get more cells and larger cells)'),
model_match_threshold = dict(widget_type='FloatSlider', name='model_match_threshold', value=27.0, min=0.0, max=30.0, step=0.2, tooltip='threshold on gradient match to accept a mask (set lower to get more cells)'),
flow_threshold = dict(widget_type='FloatSlider', name='flow_threshold', value=0.4, min=0.0, max=3.0, step=0.05, tooltip='threshold on gradient match to accept a mask (set higher to get more cells, or to zero to turn off)'),
compute_masks_button = dict(widget_type='PushButton', text='recompute last masks with new cellprob + model match', enabled=False),
net_average = dict(widget_type='CheckBox', text='average 4 nets', value=True, tooltip='average 4 different fit networks (default) or if not checked run only 1 network (fast)'),
resample_dynamics = dict(widget_type='CheckBox', text='resample dynamics', value=False, tooltip='if False, mask estimation with dynamics run on resized image with diameter=30; if True, flows are resized to original image size before dynamics and mask estimation (turn on for more smooth masks)'),
process_3D = dict(widget_type='CheckBox', text='process stack as 3D', value=False, tooltip='use default 3D processing where flows in X, Y, and Z are computed and dynamics run in 3D to create masks'),
stitch_threshold_3D = dict(widget_type='LineEdit', label='stitch threshold slices', value=0, tooltip='across time or Z, stitch together masks with IoU threshold of "stitch threshold" to create 3D segmentation'),
Expand All @@ -153,9 +155,8 @@ def widget(#label_logo,
compute_diameter_shape,
compute_diameter_button,
cellprob_threshold,
model_match_threshold,
flow_threshold,
compute_masks_button,
net_average,
resample_dynamics,
process_3D,
stitch_threshold_3D,
Expand Down Expand Up @@ -256,10 +257,9 @@ def _new_segmentation(segmentation):
max(0, optional_nuclear_channel)],
channel_axis=widget.channel_axis,
diameter=float(diameter),
net_avg=net_average,
resample=resample_dynamics,
cellprob_threshold=cellprob_threshold,
model_match_threshold=model_match_threshold,
flow_threshold=flow_threshold,
do_3D=(process_3D and float(stitch_threshold_3D)==0 and image_layer.ndim>2),
stitch_threshold=float(stitch_threshold_3D) if image_layer.ndim>2 else 0.0)
cp_worker.returned.connect(_new_segmentation)
Expand Down Expand Up @@ -298,7 +298,7 @@ def _compute_masks(e: Any):
mask_worker = compute_masks(widget.masks_orig,
widget.flows_orig,
widget.cellprob_threshold.value,
widget.model_match_threshold.value)
widget.flow_threshold.value)
mask_worker.returned.connect(update_masks)
mask_worker.start()

Expand Down
2 changes: 1 addition & 1 deletion tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def check_widget():
assert "cp_masks" in viewer.layers[-1].name

# check that the segmentation was proper, should yield 11 cells
assert viewer.layers[-1].data.max() == 11
assert viewer.layers[-1].data.max() == 10

@pytest.mark.skipif(sys.platform.startswith('linux'), reason="ubuntu stalls with two cellpose tests")
def test_compute_diameter(qtbot, viewer_widget):
Expand Down

0 comments on commit e69ec3f

Please sign in to comment.