slide2vec 4.6.2__tar.gz → 4.6.4__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.6.2 → slide2vec-4.6.4}/PKG-INFO +1 -1
- {slide2vec-4.6.2 → slide2vec-4.6.4}/pyproject.toml +2 -2
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/__init__.py +1 -1
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/conch.py +12 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/hibou.py +6 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/midnight.py +6 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/musk.py +5 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/utils/config.py +25 -7
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec.egg-info/PKG-INFO +1 -1
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_regression_core.py +28 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/LICENSE +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/README.md +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/setup.cfg +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/__main__.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/api.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/artifacts.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/cli.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/configs/__init__.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/configs/default.yaml +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/configs/resources.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/data/__init__.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/data/dataset.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/data/tile_reader.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/data/tile_store.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/distributed/__init__.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/distributed/direct_embed_worker.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/distributed/pipeline_worker.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/__init__.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/base.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/__init__.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/gigapath.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/hoptimus.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/lunit.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/moozy/__init__.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/moozy/blocks.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/moozy/case.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/moozy/loading.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/moozy/slide.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/moozy/types.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/phikon.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/prism.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/prost40m.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/titan.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/uni.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/models/virchow.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/registry.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/encoders/validation.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/inference.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/progress.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/__init__.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/artifacts_collect.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/batching.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/cpu_budget.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/distributed.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/distributed_stage.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/embedding.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/embedding_persist.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/embedding_pipeline.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/hierarchical.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/manifest.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/model_settings.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/patient_pipeline.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/persist_callbacks.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/persistence.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/process_list.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/progress_bridge.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/registry.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/serialization.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/slide_encode.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/tiling.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/tiling_pipeline.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/types.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/runtime/worker_io.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/utils/__init__.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/utils/coordinates.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/utils/log_utils.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/utils/tiling_io.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec/utils/utils.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec.egg-info/SOURCES.txt +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec.egg-info/dependency_links.txt +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec.egg-info/entry_points.txt +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec.egg-info/not-zip-safe +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec.egg-info/requires.txt +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/slide2vec.egg-info/top_level.txt +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_architecture_runtime_split.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_attention_extraction.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_dense_extraction.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_dense_locality_gated.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_encoder_registry.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_hs2p_package_cutover.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_output_consistency.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_progress.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_regression_inference.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_regression_models.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_runtime_batching.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_tile_store.py +0 -0
- {slide2vec-4.6.2 → slide2vec-4.6.4}/tests/test_tiling_pipeline.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "slide2vec"
|
|
7
|
-
version = "4.6.
|
|
7
|
+
version = "4.6.4"
|
|
8
8
|
description = "Embedding of whole slide images with Foundation Models"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -164,7 +164,7 @@ no_implicit_reexport = true
|
|
|
164
164
|
max-line-length = 160
|
|
165
165
|
|
|
166
166
|
[tool.bumpver]
|
|
167
|
-
current_version = "4.6.
|
|
167
|
+
current_version = "4.6.4"
|
|
168
168
|
version_pattern = "MAJOR.MINOR.PATCH"
|
|
169
169
|
commit = false # We do version bumping in CI, not as a commit
|
|
170
170
|
tag = false # Git tag already exists — we don't auto-tag
|
|
@@ -141,6 +141,13 @@ class CONCH(TileEncoder):
|
|
|
141
141
|
def encode_dim(self) -> int:
|
|
142
142
|
return 512
|
|
143
143
|
|
|
144
|
+
@property
|
|
145
|
+
def patch_size(self) -> tuple[int, int]:
|
|
146
|
+
# The CONCH vision trunk is a timm ViT-B/16; expose its patch size so the
|
|
147
|
+
# dense path can resolve the token grid. open_clip builds the trunk without
|
|
148
|
+
# dynamic_img_size, so dense extraction must use the native 448 window.
|
|
149
|
+
return _patch_size_from_trunk(self._model.visual.trunk)
|
|
150
|
+
|
|
144
151
|
@property
|
|
145
152
|
def device(self) -> torch.device:
|
|
146
153
|
return self._device
|
|
@@ -204,6 +211,11 @@ class CONCHv15(TileEncoder):
|
|
|
204
211
|
def encode_dim(self) -> int:
|
|
205
212
|
return 768
|
|
206
213
|
|
|
214
|
+
@property
|
|
215
|
+
def patch_size(self) -> tuple[int, int]:
|
|
216
|
+
# CONCHv15's TITAN trunk is a timm ViT; expose its patch size for the dense path.
|
|
217
|
+
return _patch_size_from_trunk(self._model.trunk)
|
|
218
|
+
|
|
207
219
|
@property
|
|
208
220
|
def device(self) -> torch.device:
|
|
209
221
|
return self._device
|
|
@@ -123,6 +123,12 @@ class _HibouBase(TileEncoder):
|
|
|
123
123
|
def encode_dim(self) -> int:
|
|
124
124
|
return self._encode_dim
|
|
125
125
|
|
|
126
|
+
@property
|
|
127
|
+
def patch_size(self) -> tuple[int, int]:
|
|
128
|
+
# HF Dinov2-style config carries the patch size; expose it for the dense path.
|
|
129
|
+
patch = int(self._model.config.patch_size)
|
|
130
|
+
return patch, patch
|
|
131
|
+
|
|
126
132
|
@property
|
|
127
133
|
def device(self) -> torch.device:
|
|
128
134
|
return self._device
|
|
@@ -124,6 +124,12 @@ class Midnight(TileEncoder):
|
|
|
124
124
|
def encode_dim(self) -> int:
|
|
125
125
|
return 3072
|
|
126
126
|
|
|
127
|
+
@property
|
|
128
|
+
def patch_size(self) -> tuple[int, int]:
|
|
129
|
+
# HF Dinov2-style config carries the patch size; expose it for the dense path.
|
|
130
|
+
patch = int(self._model.config.patch_size)
|
|
131
|
+
return patch, patch
|
|
132
|
+
|
|
127
133
|
@property
|
|
128
134
|
def device(self) -> torch.device:
|
|
129
135
|
return self._device
|
|
@@ -114,6 +114,11 @@ class MUSK(TileEncoder):
|
|
|
114
114
|
def encode_dim(self) -> int:
|
|
115
115
|
return 2048 if self._output_variant == "ms_aug" else 1024 # cls
|
|
116
116
|
|
|
117
|
+
@property
|
|
118
|
+
def patch_size(self) -> tuple[int, int]:
|
|
119
|
+
# BEiT3 vision embedding carries the patch size; expose it for the dense path.
|
|
120
|
+
return _as_hw(self._model.beit3.vision_embed.patch_size)
|
|
121
|
+
|
|
117
122
|
@property
|
|
118
123
|
def device(self) -> torch.device:
|
|
119
124
|
return self._device
|
|
@@ -41,6 +41,21 @@ def _encoder_derived_cfg(model_name: str) -> dict:
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
def _fill_null_encoder_defaults(cfg, encoder_defaults: dict) -> None:
|
|
45
|
+
"""Fill null leaves with encoder defaults after user/CLI config merging."""
|
|
46
|
+
defaults_cfg = OmegaConf.create(encoder_defaults)
|
|
47
|
+
for path in (
|
|
48
|
+
"tiling.params.requested_tile_size_px",
|
|
49
|
+
"tiling.params.requested_spacing_um",
|
|
50
|
+
"speed.precision",
|
|
51
|
+
):
|
|
52
|
+
if OmegaConf.select(cfg, path) is not None:
|
|
53
|
+
continue
|
|
54
|
+
default_value = OmegaConf.select(defaults_cfg, path)
|
|
55
|
+
if default_value is not None:
|
|
56
|
+
OmegaConf.update(cfg, path, default_value, merge=False)
|
|
57
|
+
|
|
58
|
+
|
|
44
59
|
def validate_model_recommended_settings(cfg, *, run_on_cpu: bool = False) -> None:
|
|
45
60
|
from slide2vec.encoders.registry import encoder_registry
|
|
46
61
|
from slide2vec.encoders.validation import validate_encoder_config
|
|
@@ -88,14 +103,17 @@ def get_cfg_from_args(args):
|
|
|
88
103
|
|
|
89
104
|
default_cfg = OmegaConf.create(default_config)
|
|
90
105
|
model_name = OmegaConf.select(requested_cfg, "model.name")
|
|
91
|
-
spacing = OmegaConf.select(requested_cfg, "tiling.params.requested_spacing_um")
|
|
92
|
-
tile_size = OmegaConf.select(requested_cfg, "tiling.params.requested_tile_size_px")
|
|
93
|
-
if model_name and (spacing is None or tile_size is None):
|
|
94
|
-
encoder_defaults = _encoder_derived_cfg(model_name)
|
|
95
|
-
if encoder_defaults:
|
|
96
|
-
default_cfg = OmegaConf.merge(default_cfg, OmegaConf.create(encoder_defaults))
|
|
97
|
-
|
|
98
106
|
cfg = OmegaConf.merge(default_cfg, user_cfg, cli_cfg)
|
|
107
|
+
if model_name and any(
|
|
108
|
+
OmegaConf.select(cfg, path) is None
|
|
109
|
+
for path in (
|
|
110
|
+
"tiling.params.requested_tile_size_px",
|
|
111
|
+
"tiling.params.requested_spacing_um",
|
|
112
|
+
"speed.precision",
|
|
113
|
+
)
|
|
114
|
+
):
|
|
115
|
+
encoder_defaults = _encoder_derived_cfg(model_name)
|
|
116
|
+
_fill_null_encoder_defaults(cfg, encoder_defaults)
|
|
99
117
|
OmegaConf.resolve(cfg)
|
|
100
118
|
validate_model_recommended_settings(cfg, run_on_cpu=bool(getattr(args, "run_on_cpu", False)))
|
|
101
119
|
return cfg
|
|
@@ -79,6 +79,34 @@ def test_get_cfg_from_args_fills_missing_preprocessing_from_single_spacing_model
|
|
|
79
79
|
assert cfg.tiling.params.requested_tile_size_px == 448
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
def test_get_cfg_from_args_fills_null_spacing_from_model_default(tmp_path: Path):
|
|
83
|
+
pytest.importorskip("omegaconf")
|
|
84
|
+
|
|
85
|
+
from slide2vec.utils.config import get_cfg_from_args
|
|
86
|
+
|
|
87
|
+
cfg_path = tmp_path / "config.yaml"
|
|
88
|
+
cfg_path.write_text(
|
|
89
|
+
"\n".join(
|
|
90
|
+
[
|
|
91
|
+
"csv: /tmp/slides.csv",
|
|
92
|
+
"output_dir: /tmp/output",
|
|
93
|
+
"model:",
|
|
94
|
+
" name: conchv15",
|
|
95
|
+
"tiling:",
|
|
96
|
+
" params:",
|
|
97
|
+
" requested_spacing_um:",
|
|
98
|
+
" requested_tile_size_px: 448",
|
|
99
|
+
]
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
args = SimpleNamespace(config_file=str(cfg_path), output_dir=None, opts=[], run_on_cpu=False)
|
|
103
|
+
|
|
104
|
+
cfg = get_cfg_from_args(args)
|
|
105
|
+
|
|
106
|
+
assert cfg.tiling.params.requested_spacing_um == pytest.approx(0.5)
|
|
107
|
+
assert cfg.tiling.params.requested_tile_size_px == 448
|
|
108
|
+
|
|
109
|
+
|
|
82
110
|
def test_get_cfg_from_args_rejects_models_with_ambiguous_spacing_defaults(tmp_path: Path):
|
|
83
111
|
pytest.importorskip("omegaconf")
|
|
84
112
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|