lavlab-shell 0.2.0__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lavlab_shell-0.3.1/.github/workflows/build.yml +54 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/.github/workflows/publish.yml +11 -8
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/.github/workflows/pytest.yml +11 -3
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/Dockerfile +4 -2
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/PKG-INFO +2 -1
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/pyproject.toml +11 -12
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/src/shell/__about__.py +1 -1
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/src/shell/cli.py +73 -33
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/src/shell/infer_omero_wsi.py +296 -75
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/src/shell/infer_wsi.py +137 -37
- lavlab_shell-0.3.1/src/shell/inference.py +294 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/src/shell/model.py +3 -3
- lavlab_shell-0.3.1/src/shell/transforms.py +650 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/src/shell/weights/model_v1.pth +0 -0
- lavlab_shell-0.2.0/.github/workflows/build.yml +0 -22
- lavlab_shell-0.2.0/src/shell/inference.py +0 -126
- lavlab_shell-0.2.0/src/shell/preprocessing.py +0 -337
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/.devcontainer/Dockerfile +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/.editorconfig +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/.github/dependabot.yml +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/.github/workflows/lint.yml +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/.gitignore +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/.pre-commit-config.yaml +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/CONTRIBUTING.md +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/LICENSE.txt +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/Makefile +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/README.md +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/docs/api.md +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/docs/index.md +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/mkdocs.yml +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/requirements/requirements-docs.txt +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/requirements/requirements-lint.txt +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/requirements/requirements-test.txt +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/requirements/requirements-types.txt +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/requirements.txt +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/src/shell/__init__.py +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/src/shell/benchmark.py +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/src/shell/py.typed +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/tests/__init__.py +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/tests/conftest.py +0 -0
- {lavlab_shell-0.2.0 → lavlab_shell-0.3.1}/tests/test_shell.py +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Build Wheel using Hatch
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build-wheel:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- name: Set up Python
|
|
15
|
+
uses: actions/setup-python@v4
|
|
16
|
+
with:
|
|
17
|
+
python-version: '3.12'
|
|
18
|
+
|
|
19
|
+
- name: Install build tools
|
|
20
|
+
run: |
|
|
21
|
+
python -m pip install --upgrade pip setuptools wheel
|
|
22
|
+
python -m pip install "virtualenv==20.23.1"
|
|
23
|
+
python -m pip install --upgrade hatch
|
|
24
|
+
|
|
25
|
+
- name: Build wheel with hatch
|
|
26
|
+
run: hatch build
|
|
27
|
+
- name: Upload wheel
|
|
28
|
+
uses: actions/upload-artifact@v4
|
|
29
|
+
with:
|
|
30
|
+
name: wheel
|
|
31
|
+
path: dist/*.whl
|
|
32
|
+
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
|
|
35
|
+
- name: Build Docker image (hatch stage)
|
|
36
|
+
uses: docker/build-push-action@v4
|
|
37
|
+
with:
|
|
38
|
+
context: .
|
|
39
|
+
file: ./Dockerfile
|
|
40
|
+
target: hatch
|
|
41
|
+
tags: shell-ci:latest
|
|
42
|
+
|
|
43
|
+
- name: Run build inside Docker container
|
|
44
|
+
run: |
|
|
45
|
+
docker run --rm \
|
|
46
|
+
-v "${{ github.workspace }}:/app" \
|
|
47
|
+
-w /app \
|
|
48
|
+
shell-ci:latest build
|
|
49
|
+
|
|
50
|
+
- name: Upload wheel
|
|
51
|
+
uses: actions/upload-artifact@v4
|
|
52
|
+
with:
|
|
53
|
+
name: wheel
|
|
54
|
+
path: dist/*.whl
|
|
@@ -17,17 +17,20 @@ jobs:
|
|
|
17
17
|
- name: Checkout
|
|
18
18
|
uses: actions/checkout@v4
|
|
19
19
|
|
|
20
|
-
- name:
|
|
21
|
-
uses:
|
|
20
|
+
- name: Build Docker image (hatch stage)
|
|
21
|
+
uses: docker/build-push-action@v4
|
|
22
22
|
with:
|
|
23
|
-
|
|
23
|
+
context: .
|
|
24
|
+
file: ./Dockerfile
|
|
25
|
+
target: hatch
|
|
26
|
+
tags: shell-ci:latest
|
|
24
27
|
|
|
25
|
-
- name:
|
|
28
|
+
- name: Build wheel and sdist inside Docker
|
|
26
29
|
run: |
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
docker run --rm \
|
|
31
|
+
-v "${{ github.workspace }}:/app" \
|
|
32
|
+
-w /app \
|
|
33
|
+
shell-ci:latest build
|
|
31
34
|
|
|
32
35
|
- name: Publish to PyPI
|
|
33
36
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -10,11 +10,19 @@ jobs:
|
|
|
10
10
|
steps:
|
|
11
11
|
- uses: actions/checkout@v4
|
|
12
12
|
|
|
13
|
-
- name:
|
|
14
|
-
|
|
13
|
+
- name: Set up Python
|
|
14
|
+
uses: actions/setup-python@v4
|
|
15
|
+
with:
|
|
16
|
+
python-version: '3.12'
|
|
17
|
+
|
|
18
|
+
- name: Install test dependencies
|
|
19
|
+
run: |
|
|
20
|
+
python -m pip install --upgrade pip
|
|
21
|
+
python -m pip install pytest pytest-cov coverage[toml]>=6.2
|
|
15
22
|
|
|
16
23
|
- name: Run pytest and generate coverage report
|
|
17
|
-
run:
|
|
24
|
+
run: |
|
|
25
|
+
pytest --cov=src --cov-report=xml:coverage.xml
|
|
18
26
|
|
|
19
27
|
- name: Upload coverage artifact
|
|
20
28
|
uses: actions/upload-artifact@v4
|
|
@@ -22,7 +22,9 @@ COPY src/ src/
|
|
|
22
22
|
RUN chown -R vscode:vscode /app
|
|
23
23
|
|
|
24
24
|
FROM base AS hatch
|
|
25
|
-
RUN pip3 install --no-cache-dir
|
|
25
|
+
RUN pip3 install --no-cache-dir --upgrade pip setuptools wheel
|
|
26
|
+
RUN pip3 install --no-cache-dir 'virtualenv<21'
|
|
27
|
+
RUN pip3 install --no-cache-dir --upgrade hatch hatch-uv
|
|
26
28
|
ENV HATCH_ENV=default
|
|
27
29
|
ENTRYPOINT ["hatch", "run"]
|
|
28
30
|
|
|
@@ -32,7 +34,7 @@ COPY requirements.txt ./
|
|
|
32
34
|
COPY tests/ tests/
|
|
33
35
|
COPY docs/ docs/
|
|
34
36
|
COPY mkdocs.yml ./
|
|
35
|
-
RUN pip3 install --no-cache-dir hatch \
|
|
37
|
+
RUN pip3 install --no-cache-dir hatch hatch-uv \
|
|
36
38
|
&& hatch build \
|
|
37
39
|
&& pip3 install --no-cache-dir $(find /app -name 'requirement*.txt' -exec echo -n '-r {} ' \;)
|
|
38
40
|
USER vscode
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lavlab-shell
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: SHELL Highlights Epithelium and Lumen Locations — whole-slide H&E segmentation
|
|
5
5
|
Project-URL: Documentation, https://github.com/laviolette-lab/shell#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/laviolette-lab/shell/issues
|
|
@@ -21,6 +21,7 @@ Requires-Dist: monai
|
|
|
21
21
|
Requires-Dist: numpy
|
|
22
22
|
Requires-Dist: openslide-python
|
|
23
23
|
Requires-Dist: pyvips
|
|
24
|
+
Requires-Dist: scikit-image
|
|
24
25
|
Requires-Dist: scipy
|
|
25
26
|
Requires-Dist: torch
|
|
26
27
|
Provides-Extra: omero
|
|
@@ -33,6 +33,7 @@ dependencies = [
|
|
|
33
33
|
"pyvips",
|
|
34
34
|
"openslide-python",
|
|
35
35
|
"macenko-pca",
|
|
36
|
+
"scikit-image",
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
[project.optional-dependencies]
|
|
@@ -53,27 +54,25 @@ path = "src/shell/__about__.py"
|
|
|
53
54
|
[tool.hatch.build.targets.wheel]
|
|
54
55
|
packages = ["src/shell"]
|
|
55
56
|
|
|
56
|
-
[tool.hatch.env]
|
|
57
|
-
requires = ["hatch-pip-compile"]
|
|
58
|
-
|
|
59
57
|
[tool.hatch.envs.default]
|
|
60
|
-
|
|
61
|
-
pip-compile-resolver = "uv"
|
|
62
|
-
pip-compile-hashes = false
|
|
63
|
-
pip-compile-args = ["--allow-unsafe", "--universal"]
|
|
64
|
-
dependencies = ["setuptools==82.0.0"]
|
|
58
|
+
dependencies = ["setuptools>=82.0.0"]
|
|
65
59
|
|
|
66
60
|
[tool.hatch.envs.default.scripts]
|
|
67
61
|
build = "hatch build && chmod -R 777 dist/*"
|
|
68
62
|
|
|
69
63
|
[tool.hatch.envs.test]
|
|
70
|
-
dependencies = [
|
|
64
|
+
dependencies = [
|
|
65
|
+
"pytest",
|
|
66
|
+
"pytest-cov",
|
|
67
|
+
"coverage[toml]>=6.2",
|
|
68
|
+
"setuptools>=82.0.0",
|
|
69
|
+
]
|
|
71
70
|
[tool.hatch.envs.test.scripts]
|
|
72
71
|
test = "pytest {args:tests}"
|
|
73
72
|
cov = "pytest --cov=src --cov-report=term-missing --cov-report=xml {args:tests}"
|
|
74
73
|
|
|
75
74
|
[tool.hatch.envs.lint]
|
|
76
|
-
dependencies = ["ruff>=0.4.0", "setuptools
|
|
75
|
+
dependencies = ["ruff>=0.4.0", "setuptools>=82.0.0"]
|
|
77
76
|
[tool.hatch.envs.lint.scripts]
|
|
78
77
|
check = "ruff check src tests"
|
|
79
78
|
format = "ruff format src tests"
|
|
@@ -82,7 +81,7 @@ fix = "ruff check --fix src tests"
|
|
|
82
81
|
all = ["format", "fix", "check"]
|
|
83
82
|
|
|
84
83
|
[tool.hatch.envs.types]
|
|
85
|
-
dependencies = ["mypy>=1.0.0", "setuptools
|
|
84
|
+
dependencies = ["mypy>=1.0.0", "setuptools>=82.0.0"]
|
|
86
85
|
[tool.hatch.envs.types.scripts]
|
|
87
86
|
check = "mypy --install-types --non-interactive {args:src/shell tests}"
|
|
88
87
|
|
|
@@ -95,7 +94,7 @@ dependencies = [
|
|
|
95
94
|
"mkdocs-minify-plugin",
|
|
96
95
|
"mkdocs-material-extensions",
|
|
97
96
|
"mkdocs-git-revision-date-localized-plugin",
|
|
98
|
-
"setuptools
|
|
97
|
+
"setuptools>=82.0.0",
|
|
99
98
|
]
|
|
100
99
|
[tool.hatch.envs.docs.scripts]
|
|
101
100
|
build-docs = "mkdocs build"
|
|
@@ -12,6 +12,30 @@ Provides two subcommands:
|
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
# Ensure PyTorch initialises its runtime and threads before other native
|
|
18
|
+
# libraries (pyvips, Ice/OMERO) to avoid macOS-level races that can lead
|
|
19
|
+
# to segmentation faults when different C extensions compete for thread
|
|
20
|
+
# runtime ownership. Set conservative thread counts before importing torch.
|
|
21
|
+
os.environ.setdefault("OMP_NUM_THREADS", "1")
|
|
22
|
+
os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
|
|
23
|
+
os.environ.setdefault("MKL_NUM_THREADS", "1")
|
|
24
|
+
os.environ.setdefault("VECLIB_MAXIMUM_THREADS", "1")
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
# Import torch early so it initialises its native runtime in the main
|
|
28
|
+
# thread. This is intentional — we only probe __version__ to force
|
|
29
|
+
# initialisation without performing heavy work here.
|
|
30
|
+
import torch # type: ignore
|
|
31
|
+
|
|
32
|
+
_ = torch.__version__ # ensure attribute access triggers initialisation
|
|
33
|
+
TORCH_AVAILABLE = True
|
|
34
|
+
except Exception:
|
|
35
|
+
# If torch is unavailable we still continue — the program will emit
|
|
36
|
+
# a clearer error later when trying to build/load the model.
|
|
37
|
+
TORCH_AVAILABLE = False
|
|
38
|
+
|
|
15
39
|
import argparse
|
|
16
40
|
import getpass
|
|
17
41
|
import logging
|
|
@@ -19,6 +43,9 @@ import sys
|
|
|
19
43
|
|
|
20
44
|
from shell.__about__ import __version__
|
|
21
45
|
|
|
46
|
+
# Module logger — used for diagnostic messages instead of printing directly.
|
|
47
|
+
log = logging.getLogger(__name__)
|
|
48
|
+
|
|
22
49
|
|
|
23
50
|
def build_parser() -> argparse.ArgumentParser:
|
|
24
51
|
"""Build and return the argument parser."""
|
|
@@ -98,12 +125,6 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
98
125
|
default=None,
|
|
99
126
|
help="Optional: also save the intermediate EHO image.",
|
|
100
127
|
)
|
|
101
|
-
infer_p.add_argument(
|
|
102
|
-
"--stain-downsample",
|
|
103
|
-
type=int,
|
|
104
|
-
default=4,
|
|
105
|
-
help="Downsample factor for stain parameter estimation.",
|
|
106
|
-
)
|
|
107
128
|
infer_p.add_argument(
|
|
108
129
|
"--device",
|
|
109
130
|
type=str,
|
|
@@ -138,7 +159,6 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
138
159
|
default=1.0,
|
|
139
160
|
help="Desired output resolution (um/pixel)",
|
|
140
161
|
)
|
|
141
|
-
omero_p.add_argument("--max-dim", type=int, default=None)
|
|
142
162
|
omero_p.add_argument(
|
|
143
163
|
"--model-path",
|
|
144
164
|
type=str,
|
|
@@ -283,7 +303,6 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
283
303
|
target_mpp=args.target_mpp,
|
|
284
304
|
mpp=args.mpp,
|
|
285
305
|
save_eho=args.save_eho,
|
|
286
|
-
stain_downsample=args.stain_downsample,
|
|
287
306
|
device=args.device,
|
|
288
307
|
)
|
|
289
308
|
|
|
@@ -295,36 +314,57 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
295
314
|
return 0
|
|
296
315
|
|
|
297
316
|
if args.command == "infer-omero":
|
|
317
|
+
# Diagnostic prints added to help narrow where a macOS segfault occurs.
|
|
318
|
+
# These prints are intentionally lightweight and flushed so they appear
|
|
319
|
+
# even if the process crashes in a C extension later on.
|
|
320
|
+
log.debug("DIAG: infer-omero selected")
|
|
321
|
+
log.debug("DIAG: about to import infer_omero_wsi")
|
|
298
322
|
from shell.infer_omero_wsi import infer_omero_wsi
|
|
299
323
|
|
|
324
|
+
log.debug("DIAG: imported infer_omero_wsi")
|
|
325
|
+
|
|
300
326
|
if args.password is None:
|
|
301
327
|
args.password = getpass.getpass("OMERO password: ")
|
|
302
328
|
|
|
303
|
-
print(f"Connecting to {args.host}:{args.port} …")
|
|
304
|
-
infer_omero_wsi
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
329
|
+
print(f"Connecting to {args.host}:{args.port} …", flush=True)
|
|
330
|
+
log.debug("DIAG: about to call infer_omero_wsi")
|
|
331
|
+
try:
|
|
332
|
+
infer_omero_wsi(
|
|
333
|
+
host=args.host,
|
|
334
|
+
port=args.port,
|
|
335
|
+
username=args.username,
|
|
336
|
+
password=args.password,
|
|
337
|
+
image_id=args.image_id,
|
|
338
|
+
model_path=args.model_path,
|
|
339
|
+
model_version=args.model_version,
|
|
340
|
+
output_path=args.output,
|
|
341
|
+
target_mpp=args.target_mpp,
|
|
342
|
+
group_id=args.group_id,
|
|
343
|
+
save_eho=args.save_eho,
|
|
344
|
+
no_tissue_crop=args.no_tissue_crop,
|
|
345
|
+
device=args.device,
|
|
346
|
+
inference_tile_size=args.inference_tile_size,
|
|
347
|
+
tile_overlap=args.tile_overlap,
|
|
348
|
+
sw_overlap=args.sw_overlap,
|
|
349
|
+
num_bg_tiles=args.num_bg_tiles,
|
|
350
|
+
min_thumb_size=args.min_thumb_size,
|
|
351
|
+
min_tissue_frac=args.min_tissue_frac,
|
|
352
|
+
prefetch_depth=args.prefetch_depth,
|
|
353
|
+
num_fetch_workers=args.fetch_workers,
|
|
354
|
+
)
|
|
355
|
+
log.debug("DIAG: infer_omero_wsi returned normally")
|
|
356
|
+
except BaseException as e:
|
|
357
|
+
# Catch and print Python-level exceptions; note segfaults (native crashes)
|
|
358
|
+
# will not be caught here, but these diagnostics will show progress up to
|
|
359
|
+
# the crash point.
|
|
360
|
+
import sys
|
|
361
|
+
import traceback
|
|
362
|
+
|
|
363
|
+
log.debug("DIAG: infer_omero_wsi raised an exception:")
|
|
364
|
+
traceback.print_exc()
|
|
365
|
+
# Re-raise so existing behaviour (non-zero exit) remains.
|
|
366
|
+
raise
|
|
367
|
+
log.debug("DIAG: after infer_omero_wsi (should have saved output)")
|
|
328
368
|
print(f"Saved prediction to {args.output}")
|
|
329
369
|
return 0
|
|
330
370
|
|