slide2vec 4.1.0__tar.gz → 4.1.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.
- {slide2vec-4.1.0 → slide2vec-4.1.1}/PKG-INFO +7 -7
- {slide2vec-4.1.0 → slide2vec-4.1.1}/README.md +4 -4
- {slide2vec-4.1.0 → slide2vec-4.1.1}/pyproject.toml +4 -4
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/__init__.py +1 -1
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/api.py +35 -35
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/configs/default.yaml +6 -6
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/data/tile_reader.py +6 -6
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/base.py +1 -1
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/gigapath.py +2 -2
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/registry.py +2 -2
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/validation.py +8 -8
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/inference.py +83 -71
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/utils/config.py +8 -8
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/utils/log_utils.py +7 -1
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec.egg-info/PKG-INFO +7 -7
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec.egg-info/requires.txt +2 -2
- {slide2vec-4.1.0 → slide2vec-4.1.1}/tests/test_batch_collator_timing.py +5 -5
- {slide2vec-4.1.0 → slide2vec-4.1.1}/tests/test_output_consistency.py +2 -2
- {slide2vec-4.1.0 → slide2vec-4.1.1}/tests/test_progress.py +2 -2
- {slide2vec-4.1.0 → slide2vec-4.1.1}/tests/test_regression_core.py +46 -46
- {slide2vec-4.1.0 → slide2vec-4.1.1}/tests/test_regression_inference.py +62 -41
- {slide2vec-4.1.0 → slide2vec-4.1.1}/tests/test_regression_models.py +7 -7
- {slide2vec-4.1.0 → slide2vec-4.1.1}/LICENSE +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/setup.cfg +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/__main__.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/artifacts.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/cli.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/configs/__init__.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/data/__init__.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/data/dataset.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/data/tile_store.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/distributed/__init__.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/distributed/direct_embed_worker.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/distributed/pipeline_worker.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/__init__.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/__init__.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/conch.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/hibou.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/hoptimus.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/midnight.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/musk.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/phikon.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/prism.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/prost40m.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/titan.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/uni.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/encoders/models/virchow.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/main.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/model_settings.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/progress.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/registry.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/resources.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/runtime_types.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/utils/__init__.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/utils/coordinates.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/utils/tiling_io.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec/utils/utils.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec.egg-info/SOURCES.txt +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec.egg-info/dependency_links.txt +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec.egg-info/entry_points.txt +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec.egg-info/not-zip-safe +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/slide2vec.egg-info/top_level.txt +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/tests/test_encoder_registry.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/tests/test_hs2p_package_cutover.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/tests/test_packaging_metadata.py +0 -0
- {slide2vec-4.1.0 → slide2vec-4.1.1}/tests/test_tile_store.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: slide2vec
|
|
3
|
-
Version: 4.1.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: Embedding of whole slide images with Foundation Models
|
|
5
5
|
Author-email: Clément Grisi <clement.grisi@radboudumc.nl>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -15,7 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: hs2p[asap,cucim,openslide,vips]>=3.
|
|
18
|
+
Requires-Dist: hs2p[asap,cucim,openslide,vips]>=3.2.0
|
|
19
19
|
Requires-Dist: omegaconf
|
|
20
20
|
Requires-Dist: matplotlib
|
|
21
21
|
Requires-Dist: numpy<2
|
|
@@ -63,7 +63,7 @@ Requires-Dist: numpy<2; extra == "fm"
|
|
|
63
63
|
Requires-Dist: pandas; extra == "fm"
|
|
64
64
|
Requires-Dist: pillow; extra == "fm"
|
|
65
65
|
Requires-Dist: rich; extra == "fm"
|
|
66
|
-
Requires-Dist: hs2p[asap,cucim,openslide,vips]>=3.
|
|
66
|
+
Requires-Dist: hs2p[asap,cucim,openslide,vips]>=3.2.0; extra == "fm"
|
|
67
67
|
Requires-Dist: wandb; extra == "fm"
|
|
68
68
|
Requires-Dist: torch<2.8,>=2.3; extra == "fm"
|
|
69
69
|
Requires-Dist: torchvision>=0.18.0; extra == "fm"
|
|
@@ -143,8 +143,8 @@ from slide2vec import ExecutionOptions, Pipeline, PreprocessingConfig
|
|
|
143
143
|
pipeline = Pipeline(
|
|
144
144
|
model=model,
|
|
145
145
|
preprocessing=PreprocessingConfig(
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
requested_spacing_um=0.5,
|
|
147
|
+
requested_tile_size_px=224,
|
|
148
148
|
tissue_threshold=0.1,
|
|
149
149
|
),
|
|
150
150
|
execution=ExecutionOptions(output_dir="outputs/demo"),
|
|
@@ -160,8 +160,8 @@ Tile embeddings can be spatially grouped into regions for downstream models that
|
|
|
160
160
|
|
|
161
161
|
```python
|
|
162
162
|
preprocessing = PreprocessingConfig(
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
requested_spacing_um=0.5,
|
|
164
|
+
requested_tile_size_px=224,
|
|
165
165
|
region_tile_multiple=6, # 6x6 tiles per region
|
|
166
166
|
)
|
|
167
167
|
embedded = model.embed_slide("/path/to/slide.svs", preprocessing=preprocessing)
|
|
@@ -45,8 +45,8 @@ from slide2vec import ExecutionOptions, Pipeline, PreprocessingConfig
|
|
|
45
45
|
pipeline = Pipeline(
|
|
46
46
|
model=model,
|
|
47
47
|
preprocessing=PreprocessingConfig(
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
requested_spacing_um=0.5,
|
|
49
|
+
requested_tile_size_px=224,
|
|
50
50
|
tissue_threshold=0.1,
|
|
51
51
|
),
|
|
52
52
|
execution=ExecutionOptions(output_dir="outputs/demo"),
|
|
@@ -62,8 +62,8 @@ Tile embeddings can be spatially grouped into regions for downstream models that
|
|
|
62
62
|
|
|
63
63
|
```python
|
|
64
64
|
preprocessing = PreprocessingConfig(
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
requested_spacing_um=0.5,
|
|
66
|
+
requested_tile_size_px=224,
|
|
67
67
|
region_tile_multiple=6, # 6x6 tiles per region
|
|
68
68
|
)
|
|
69
69
|
embedded = model.embed_slide("/path/to/slide.svs", preprocessing=preprocessing)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "slide2vec"
|
|
7
|
-
version = "4.1.
|
|
7
|
+
version = "4.1.1"
|
|
8
8
|
description = "Embedding of whole slide images with Foundation Models"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -21,7 +21,7 @@ classifiers = [
|
|
|
21
21
|
"Programming Language :: Python :: 3.13",
|
|
22
22
|
]
|
|
23
23
|
dependencies = [
|
|
24
|
-
"hs2p[asap,cucim,openslide,vips]>=3.
|
|
24
|
+
"hs2p[asap,cucim,openslide,vips]>=3.2.0",
|
|
25
25
|
"omegaconf",
|
|
26
26
|
"matplotlib",
|
|
27
27
|
"numpy<2",
|
|
@@ -85,7 +85,7 @@ fm = [
|
|
|
85
85
|
"pandas",
|
|
86
86
|
"pillow",
|
|
87
87
|
"rich",
|
|
88
|
-
"hs2p[asap,cucim,openslide,vips]>=3.
|
|
88
|
+
"hs2p[asap,cucim,openslide,vips]>=3.2.0",
|
|
89
89
|
"wandb",
|
|
90
90
|
"torch>=2.3,<2.8",
|
|
91
91
|
"torchvision>=0.18.0",
|
|
@@ -154,7 +154,7 @@ no_implicit_reexport = true
|
|
|
154
154
|
max-line-length = 160
|
|
155
155
|
|
|
156
156
|
[tool.bumpver]
|
|
157
|
-
current_version = "4.1.
|
|
157
|
+
current_version = "4.1.1"
|
|
158
158
|
version_pattern = "MAJOR.MINOR.PATCH"
|
|
159
159
|
commit = false # We do version bumping in CI, not as a commit
|
|
160
160
|
tag = false # Git tag already exists — we don't auto-tag
|
|
@@ -2,7 +2,7 @@ from slide2vec.api import EmbeddedSlide, ExecutionOptions, Model, Pipeline, Prep
|
|
|
2
2
|
from slide2vec.artifacts import HierarchicalEmbeddingArtifact, SlideEmbeddingArtifact, TileEmbeddingArtifact
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
__version__ = "4.1.
|
|
5
|
+
__version__ = "4.1.1"
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"Model",
|
|
@@ -42,9 +42,9 @@ TilingResultsInput = Sequence[Any] | Mapping[str, Any]
|
|
|
42
42
|
@dataclass(frozen=True, kw_only=True)
|
|
43
43
|
class PreprocessingConfig:
|
|
44
44
|
backend: str = "auto"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
requested_spacing_um: float | None = None
|
|
46
|
+
requested_tile_size_px: int | None = None
|
|
47
|
+
requested_region_size_px: int | None = None
|
|
48
48
|
region_tile_multiple: int | None = None
|
|
49
49
|
tolerance: float = 0.05
|
|
50
50
|
overlap: float = 0.0
|
|
@@ -75,11 +75,11 @@ class PreprocessingConfig:
|
|
|
75
75
|
preview_downsample = int(preview_cfg.downsample)
|
|
76
76
|
return cls(
|
|
77
77
|
backend=tiling.backend,
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
requested_spacing_um=float(tiling.params.requested_spacing_um),
|
|
79
|
+
requested_tile_size_px=int(tiling.params.requested_tile_size_px),
|
|
80
|
+
requested_region_size_px=(
|
|
81
81
|
int(v)
|
|
82
|
-
if (v := getattr(tiling.params, "
|
|
82
|
+
if (v := getattr(tiling.params, "requested_region_size_px", None)) is not None
|
|
83
83
|
else None
|
|
84
84
|
),
|
|
85
85
|
region_tile_multiple=(
|
|
@@ -454,28 +454,28 @@ def _resolve_direct_api_preprocessing(
|
|
|
454
454
|
return defaults
|
|
455
455
|
|
|
456
456
|
if preprocessing is None:
|
|
457
|
-
|
|
457
|
+
requested_tile_size_px, requested_spacing_um = ensure_defaults()
|
|
458
458
|
return _resolve_hierarchical_preprocessing(
|
|
459
459
|
PreprocessingConfig(
|
|
460
460
|
backend="auto",
|
|
461
|
-
|
|
462
|
-
|
|
461
|
+
requested_spacing_um=requested_spacing_um,
|
|
462
|
+
requested_tile_size_px=requested_tile_size_px,
|
|
463
463
|
)
|
|
464
464
|
)
|
|
465
465
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
if
|
|
466
|
+
requested_spacing_um = preprocessing.requested_spacing_um
|
|
467
|
+
requested_tile_size_px = preprocessing.requested_tile_size_px
|
|
468
|
+
if requested_spacing_um is None or requested_tile_size_px is None:
|
|
469
469
|
default_tile_size_px, default_spacing_um = ensure_defaults()
|
|
470
|
-
if
|
|
471
|
-
|
|
472
|
-
if
|
|
473
|
-
|
|
470
|
+
if requested_spacing_um is None:
|
|
471
|
+
requested_spacing_um = default_spacing_um
|
|
472
|
+
if requested_tile_size_px is None:
|
|
473
|
+
requested_tile_size_px = default_tile_size_px
|
|
474
474
|
return _resolve_hierarchical_preprocessing(
|
|
475
475
|
replace(
|
|
476
476
|
preprocessing,
|
|
477
|
-
|
|
478
|
-
|
|
477
|
+
requested_spacing_um=requested_spacing_um,
|
|
478
|
+
requested_tile_size_px=requested_tile_size_px,
|
|
479
479
|
)
|
|
480
480
|
)
|
|
481
481
|
|
|
@@ -484,7 +484,7 @@ def _default_preprocessing_from_registry(name: str | None) -> tuple[int, float]:
|
|
|
484
484
|
if not name or name not in encoder_registry:
|
|
485
485
|
raise ValueError(
|
|
486
486
|
"Cannot infer preprocessing defaults without a registered model. "
|
|
487
|
-
"Pass preprocessing.
|
|
487
|
+
"Pass preprocessing.requested_spacing_um and preprocessing.requested_tile_size_px explicitly."
|
|
488
488
|
)
|
|
489
489
|
|
|
490
490
|
defaults = resolve_preprocessing_defaults(name)
|
|
@@ -499,7 +499,7 @@ def _validate_model_config(
|
|
|
499
499
|
name = model.name
|
|
500
500
|
if name not in encoder_registry:
|
|
501
501
|
return
|
|
502
|
-
if preprocessing.region_tile_multiple is not None or preprocessing.
|
|
502
|
+
if preprocessing.region_tile_multiple is not None or preprocessing.requested_region_size_px is not None:
|
|
503
503
|
info = encoder_registry.info(name)
|
|
504
504
|
if info["level"] != "tile":
|
|
505
505
|
raise ValueError("Hierarchical preprocessing is only supported for tile encoders")
|
|
@@ -508,8 +508,8 @@ def _validate_model_config(
|
|
|
508
508
|
precision = None if on_cpu or execution is None else execution.precision
|
|
509
509
|
validate_encoder_config(
|
|
510
510
|
name,
|
|
511
|
-
|
|
512
|
-
|
|
511
|
+
requested_tile_size_px=preprocessing.requested_tile_size_px,
|
|
512
|
+
requested_spacing_um=preprocessing.requested_spacing_um,
|
|
513
513
|
precision=precision,
|
|
514
514
|
output_variant=model._output_variant,
|
|
515
515
|
allow_non_recommended=bool(model.allow_non_recommended_settings),
|
|
@@ -518,32 +518,32 @@ def _validate_model_config(
|
|
|
518
518
|
|
|
519
519
|
def _resolve_hierarchical_preprocessing(preprocessing: PreprocessingConfig) -> PreprocessingConfig:
|
|
520
520
|
multiple = preprocessing.region_tile_multiple
|
|
521
|
-
|
|
521
|
+
requested_region_size_px = preprocessing.requested_region_size_px
|
|
522
522
|
if multiple is not None:
|
|
523
523
|
multiple = int(multiple)
|
|
524
524
|
if multiple < 2:
|
|
525
525
|
raise ValueError("region_tile_multiple must be at least 2")
|
|
526
|
-
if multiple is None and
|
|
526
|
+
if multiple is None and requested_region_size_px is None:
|
|
527
527
|
return preprocessing
|
|
528
|
-
if preprocessing.
|
|
528
|
+
if preprocessing.requested_tile_size_px is None:
|
|
529
529
|
raise ValueError(
|
|
530
|
-
"
|
|
530
|
+
"requested_tile_size_px must be resolved before deriving hierarchical region geometry"
|
|
531
531
|
)
|
|
532
|
-
if
|
|
533
|
-
|
|
532
|
+
if requested_region_size_px is None:
|
|
533
|
+
requested_region_size_px = int(preprocessing.requested_tile_size_px) * int(multiple)
|
|
534
534
|
elif multiple is None:
|
|
535
|
-
if int(
|
|
535
|
+
if int(requested_region_size_px) % int(preprocessing.requested_tile_size_px) != 0:
|
|
536
536
|
raise ValueError(
|
|
537
|
-
"
|
|
537
|
+
"requested_region_size_px must be an exact multiple of requested_tile_size_px"
|
|
538
538
|
)
|
|
539
|
-
multiple = int(
|
|
540
|
-
elif int(
|
|
539
|
+
multiple = int(requested_region_size_px) // int(preprocessing.requested_tile_size_px)
|
|
540
|
+
elif int(requested_region_size_px) != int(preprocessing.requested_tile_size_px) * int(multiple):
|
|
541
541
|
raise ValueError(
|
|
542
|
-
"
|
|
542
|
+
"requested_region_size_px must match requested_tile_size_px * region_tile_multiple"
|
|
543
543
|
)
|
|
544
544
|
return replace(
|
|
545
545
|
preprocessing,
|
|
546
|
-
|
|
546
|
+
requested_region_size_px=int(requested_region_size_px),
|
|
547
547
|
region_tile_multiple=int(multiple),
|
|
548
548
|
)
|
|
549
549
|
|
|
@@ -26,10 +26,10 @@ tiling:
|
|
|
26
26
|
read_tiles_from: # path to an existing directory containing pre-extracted `.tiles.tar` tile stores to reuse instead of starting tiling from scratch
|
|
27
27
|
backend: "auto" # backend to use for slide reading; "auto" lets hs2p resolve the best backend per slide, preferring cuCIM when available
|
|
28
28
|
params:
|
|
29
|
-
|
|
29
|
+
requested_spacing_um: # spacing at which to tile the slide, in microns per pixel; filled from a preset model when available
|
|
30
30
|
tolerance: 0.05 # tolerance for matching the spacing (float between 0 and 1, deciding how much the spacing can deviate from the one specified in the slide metadata)
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
requested_tile_size_px: # size of the tiles to extract, in pixels; filled from a preset model when available
|
|
32
|
+
requested_region_size_px: # size of hierarchical parent regions in pixels; when unset and region_tile_multiple is set, derived from requested_tile_size_px * region_tile_multiple
|
|
33
33
|
region_tile_multiple: # hierarchical region grid width/height in tiles; e.g. 6 means 6x6 tiles per region
|
|
34
34
|
overlap: 0.0 # percentage of overlap between two consecutive tiles (float between 0 and 1)
|
|
35
35
|
tissue_threshold: 0.1 # minimum fraction of pixels that must be tissue to keep a tile (float between 0 and 1)
|
|
@@ -44,8 +44,8 @@ tiling:
|
|
|
44
44
|
use_otsu: false # use otsu's method instead of simple binary thresholding
|
|
45
45
|
use_hsv: true # use HSV thresholding instead of simple binary thresholding
|
|
46
46
|
filter_params:
|
|
47
|
-
ref_tile_size: ${tiling.params.
|
|
48
|
-
a_t: 4 # area filter threshold for tissue (positive integer, the minimum size of detected foreground contours to consider, relative to the reference tile size ref_tile_size, e.g. a value 10 means only detected foreground contours of size greater than 10 [ref_tile_size, ref_tile_size] tiles at spacing tiling.params.
|
|
47
|
+
ref_tile_size: ${tiling.params.requested_tile_size_px} # reference tile size at the target spacing
|
|
48
|
+
a_t: 4 # area filter threshold for tissue (positive integer, the minimum size of detected foreground contours to consider, relative to the reference tile size ref_tile_size, e.g. a value 10 means only detected foreground contours of size greater than 10 [ref_tile_size, ref_tile_size] tiles at spacing tiling.params.requested_spacing_um will be kept)
|
|
49
49
|
a_h: 2 # area filter threshold for holes (positive integer, the minimum size of detected holes/cavities in foreground contours to avoid, once again relative to the reference tile size ref_tile_size)
|
|
50
50
|
filter_white: false # whether to filter out mostly white tiles
|
|
51
51
|
filter_black: false # whether to filter out mostly black tiles
|
|
@@ -78,7 +78,7 @@ wandb:
|
|
|
78
78
|
project: "" # wandb project name
|
|
79
79
|
username: "" # wandb username
|
|
80
80
|
exp_name: "" # wandb experiment name
|
|
81
|
-
tags: ["features", "${model.name}", "${tiling.params.
|
|
81
|
+
tags: ["features", "${model.name}", "${tiling.params.requested_tile_size_px}"] # wandb tags
|
|
82
82
|
dir: "/home/user/"
|
|
83
83
|
group:
|
|
84
84
|
resume_id: "${resume_dirname}"
|
|
@@ -89,7 +89,7 @@ class WSITileReader:
|
|
|
89
89
|
self._num_cucim_workers = num_cucim_workers
|
|
90
90
|
self._gpu_decode = gpu_decode
|
|
91
91
|
self._read_level = int(tiling_result.read_level)
|
|
92
|
-
self._tile_size_px = int(tiling_result.
|
|
92
|
+
self._tile_size_px = int(tiling_result.read_tile_size_px)
|
|
93
93
|
self._x = tiling_result.x
|
|
94
94
|
self._y = tiling_result.y
|
|
95
95
|
self._reader = None
|
|
@@ -215,7 +215,7 @@ class OnTheFlyBatchTileCollator:
|
|
|
215
215
|
gpu_decode: bool = False,
|
|
216
216
|
use_supertiles: bool = True,
|
|
217
217
|
):
|
|
218
|
-
self.tile_size = int(tiling_result.
|
|
218
|
+
self.tile_size = int(tiling_result.read_tile_size_px)
|
|
219
219
|
self._reader = WSITileReader(
|
|
220
220
|
image_path,
|
|
221
221
|
tiling_result,
|
|
@@ -354,8 +354,8 @@ class OnTheFlyHierarchicalBatchCollator:
|
|
|
354
354
|
tiling_result: TilingResult,
|
|
355
355
|
region_index: np.ndarray,
|
|
356
356
|
subtile_index_within_region: np.ndarray,
|
|
357
|
-
|
|
358
|
-
|
|
357
|
+
read_region_size_px: int,
|
|
358
|
+
read_tile_size_px: int,
|
|
359
359
|
backend: str = "cucim",
|
|
360
360
|
num_cucim_workers: int = 4,
|
|
361
361
|
gpu_decode: bool = False,
|
|
@@ -363,11 +363,11 @@ class OnTheFlyHierarchicalBatchCollator:
|
|
|
363
363
|
self._region_index = np.asarray(region_index, dtype=np.int32)
|
|
364
364
|
self._subtile_index_within_region = np.asarray(subtile_index_within_region, dtype=np.int32)
|
|
365
365
|
self._tiles_per_region = int(self._subtile_index_within_region.max()) + 1 if len(self._subtile_index_within_region) else 0
|
|
366
|
-
self._tile_size = int(
|
|
366
|
+
self._tile_size = int(read_tile_size_px)
|
|
367
367
|
self._reader = WSIRegionReader(
|
|
368
368
|
image_path,
|
|
369
369
|
read_level=int(tiling_result.read_level),
|
|
370
|
-
region_size_px=int(
|
|
370
|
+
region_size_px=int(read_region_size_px),
|
|
371
371
|
backend=backend,
|
|
372
372
|
num_cucim_workers=num_cucim_workers,
|
|
373
373
|
gpu_decode=gpu_decode,
|
|
@@ -69,9 +69,9 @@ class GigaPathSlideEncoder(SlideEncoder):
|
|
|
69
69
|
coordinates: torch.Tensor,
|
|
70
70
|
*,
|
|
71
71
|
base_spacing_um: float,
|
|
72
|
-
|
|
72
|
+
requested_spacing_um: float,
|
|
73
73
|
) -> torch.Tensor:
|
|
74
|
-
scale = float(base_spacing_um) / float(
|
|
74
|
+
scale = float(base_spacing_um) / float(requested_spacing_um)
|
|
75
75
|
return torch.floor(coordinates.to(torch.float32) * scale).to(torch.long)
|
|
76
76
|
|
|
77
77
|
def encode_slide(
|
|
@@ -131,8 +131,8 @@ def resolve_preprocessing_defaults(
|
|
|
131
131
|
supported_text = ", ".join(f"{s:g}" for s in unique_spacings)
|
|
132
132
|
raise ValueError(
|
|
133
133
|
f"Encoder '{encoder_name}' supports multiple spacings [{supported_text}]; "
|
|
134
|
-
"cannot infer a default
|
|
135
|
-
"Pass preprocessing.
|
|
134
|
+
"cannot infer a default requested_spacing_um. "
|
|
135
|
+
"Pass preprocessing.requested_spacing_um explicitly."
|
|
136
136
|
)
|
|
137
137
|
spacing_um = unique_spacings[0]
|
|
138
138
|
return {
|
|
@@ -17,8 +17,8 @@ def validate_encoder_config(
|
|
|
17
17
|
encoder_name: str,
|
|
18
18
|
*,
|
|
19
19
|
info: dict[str, Any] | None = None,
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
requested_tile_size_px: int | None = None,
|
|
21
|
+
requested_spacing_um: float | None = None,
|
|
22
22
|
precision: str | None = None,
|
|
23
23
|
output_variant: str | None = None,
|
|
24
24
|
allow_non_recommended: bool = False,
|
|
@@ -44,20 +44,20 @@ def validate_encoder_config(
|
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
rec_spacing = info["supported_spacing_um"] if "supported_spacing_um" in info else None
|
|
47
|
-
if
|
|
47
|
+
if requested_spacing_um is not None and rec_spacing is not None:
|
|
48
48
|
valid_spacings = rec_spacing if isinstance(rec_spacing, list) else [rec_spacing]
|
|
49
|
-
if not any(abs(float(
|
|
49
|
+
if not any(abs(float(requested_spacing_um) - float(s)) <= 1e-8 for s in valid_spacings):
|
|
50
50
|
supported_text = ", ".join(f"{s:g}" for s in valid_spacings)
|
|
51
51
|
mismatches.append(
|
|
52
|
-
f"
|
|
52
|
+
f"requested_spacing_um={float(requested_spacing_um):g} (recommended: [{supported_text}])"
|
|
53
53
|
)
|
|
54
54
|
|
|
55
|
-
if
|
|
55
|
+
if requested_tile_size_px is not None:
|
|
56
56
|
reqs = resolve_preprocessing_requirements(encoder_name, info)
|
|
57
57
|
rec_tile_size = reqs["tile_size_px"]
|
|
58
|
-
if rec_tile_size is not None and int(
|
|
58
|
+
if rec_tile_size is not None and int(requested_tile_size_px) != int(rec_tile_size):
|
|
59
59
|
mismatches.append(
|
|
60
|
-
f"
|
|
60
|
+
f"requested_tile_size_px={requested_tile_size_px} (recommended: {rec_tile_size})"
|
|
61
61
|
)
|
|
62
62
|
|
|
63
63
|
if not mismatches:
|