Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cellpose segmentation #40

Merged
merged 1 commit into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11.0-beta.5 - 3.11']
python-version: ['3.8', '3.9', '3.10', '3.11']

steps:
- uses: actions/checkout@v3
Expand Down
18 changes: 18 additions & 0 deletions config_cellpose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# SPDX-FileCopyrightText: 2023 Friedrich Miescher Institute for Biomedical Research (FMI), Basel (Switzerland)
#
# SPDX-License-Identifier: MIT

# Required
file_selection: # criteria for file selection in case of multiple channels/slices per position
channel: C01
process: # choose method how to segment, filter, and sample the objects
segment: cellpose
filter: []
sample: centers

# Each subsequent section provides arguments to one of the methods defined in 'process'
cellpose:
diameter: 10.0
pretrained_model: cyto2
cellprob_threshold: 0.0
flow_threshold: 0.4
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ build-backend = "hatchling.build"
name = "faim-wako-searchfirst"
description = ''
readme = "README.md"
requires-python = ">=3.7"
requires-python = ">=3.8"
license = "MIT"
keywords = []
authors = [
Expand All @@ -19,7 +19,6 @@ authors = [
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
Expand All @@ -28,6 +27,7 @@ classifiers = [
"Programming Language :: Python :: Implementation :: PyPy",
]
dependencies = [
"cellpose",
"confuse",
"rich",
"scikit-image",
Expand Down Expand Up @@ -55,7 +55,7 @@ cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=faim_w
no-cov = "cov --no-cov {args}"

[[tool.hatch.envs.test.matrix]]
python = ["37", "38", "39", "310", "311"]
python = ["38", "39", "310", "311"]

[tool.coverage.run]
branch = true
Expand Down
36 changes: 32 additions & 4 deletions src/faim_wako_searchfirst/segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
"""

import logging
from pathlib import Path
from typing import Union

import numpy as np
from cellpose import models
from scipy.ndimage import binary_fill_holes
from skimage.measure import label, regionprops

Expand All @@ -26,10 +29,6 @@ def threshold(
:param img: input image
:param threshold: global threshold
:param include_holes: if true, holes will be filled
:param min_size: minimum object size
:param max_size: maximum object size
:param min_eccentricity: minimum eccentricity of object
:param max_eccentricity: maximum eccentricity of object
:param logger:

:return: a label image representing the detected objects
Expand All @@ -41,3 +40,32 @@ def threshold(
regions = regionprops(labeled_image)
logger.info(f"Found {len(regions)} connected components.")
return labeled_image


def cellpose(
img,
diameter: float,
pretrained_model: Union[str, Path] = "cyto2",
logger=logging,
**kwargs,
):
"""Segment a given image by global thresholding.

:param img: input image
:param diameter: expected object diameter
:param pretrained_model: name of cellpose model, or path to pretrained model
:param logger:

:return: a label image representing the detected objects
"""
logger.info(f"Load cellpose model: {pretrained_model}")
model: models.CellposeModel = models.CellposeModel(
pretrained_model=pretrained_model,
)
mask, _, _ = model.eval(
img,
channels=[0, 0],
diameter=diameter,
**kwargs,
)
return mask
36 changes: 36 additions & 0 deletions tests/test_cellpose.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# SPDX-FileCopyrightText: 2023 Friedrich Miescher Institute for Biomedical Research (FMI), Basel (Switzerland)
#
# SPDX-License-Identifier: MIT

"""Test faim_wako_searchfirst module."""
import csv
import shutil
from pathlib import Path

import pytest
from faim_wako_searchfirst.main import run


@pytest.fixture
def _data_path(tmp_path):
# copy test set into tmp_path, return resulting Path
testset_path = Path("tests/resources/TestSet")
assert testset_path.exists()
return shutil.copytree(testset_path, tmp_path / "TestSet")


def test_cellpose(_data_path):
"""Test run with parameters defined in the sample config_cellpose.yml file."""
run(_data_path, configfile="config_cellpose.yml")
csv_path = _data_path / "TestSet_D07_T0001F002L01A02Z01C01.csv"
assert csv_path.exists()
with open(csv_path, "r") as csv_file:
reader = csv.reader(csv_file, quoting=csv.QUOTE_NONNUMERIC)
entries = list(reader)
assert len(entries) == 3, "Incorrect number of objects detected."
assert entries[0] == pytest.approx([1, 40.5, 30.5])
assert entries[1] == pytest.approx([2, 158.5, 58.5])
assert entries[2] == pytest.approx([3, 79.5287, 146.4483])

segmentation_folder = _data_path.parent / (_data_path.name + "_segmentation")
assert sum(1 for _ in segmentation_folder.glob("*")) == 1