slide2vec 4.0.4__tar.gz → 4.1.0__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.0.4 → slide2vec-4.1.0}/PKG-INFO +1 -1
- {slide2vec-4.0.4 → slide2vec-4.1.0}/pyproject.toml +2 -2
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/__init__.py +1 -1
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/distributed/direct_embed_worker.py +3 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/inference.py +16 -6
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/PKG-INFO +1 -1
- {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_regression_inference.py +44 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/LICENSE +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/README.md +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/setup.cfg +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/__main__.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/api.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/artifacts.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/cli.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/configs/__init__.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/configs/default.yaml +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/data/__init__.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/data/dataset.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/data/tile_reader.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/data/tile_store.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/distributed/__init__.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/distributed/pipeline_worker.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/__init__.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/base.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/__init__.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/conch.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/gigapath.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/hibou.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/hoptimus.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/midnight.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/musk.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/phikon.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/prism.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/prost40m.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/titan.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/uni.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/virchow.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/registry.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/validation.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/main.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/model_settings.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/progress.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/registry.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/resources.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/runtime_types.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/utils/__init__.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/utils/config.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/utils/coordinates.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/utils/log_utils.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/utils/tiling_io.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/utils/utils.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/SOURCES.txt +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/dependency_links.txt +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/entry_points.txt +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/not-zip-safe +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/requires.txt +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/top_level.txt +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_batch_collator_timing.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_encoder_registry.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_hs2p_package_cutover.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_output_consistency.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_packaging_metadata.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_progress.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_regression_core.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_regression_models.py +0 -0
- {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_tile_store.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "slide2vec"
|
|
7
|
-
version = "4.0
|
|
7
|
+
version = "4.1.0"
|
|
8
8
|
description = "Embedding of whole slide images with Foundation Models"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -154,7 +154,7 @@ no_implicit_reexport = true
|
|
|
154
154
|
max-line-length = 160
|
|
155
155
|
|
|
156
156
|
[tool.bumpver]
|
|
157
|
-
current_version = "4.0
|
|
157
|
+
current_version = "4.1.0"
|
|
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.0
|
|
5
|
+
__version__ = "4.1.0"
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"Model",
|
|
@@ -25,6 +25,7 @@ def main(argv=None) -> int:
|
|
|
25
25
|
_compute_hierarchical_embedding_shard_for_slide,
|
|
26
26
|
_compute_tile_embeddings_for_slide,
|
|
27
27
|
_is_hierarchical_preprocessing,
|
|
28
|
+
_resolve_hierarchical_geometry,
|
|
28
29
|
deserialize_execution,
|
|
29
30
|
deserialize_preprocessing,
|
|
30
31
|
load_successful_tiled_slides,
|
|
@@ -74,9 +75,11 @@ def main(argv=None) -> int:
|
|
|
74
75
|
slide, tiling_result = paired_by_sample[sample_id]
|
|
75
76
|
loaded = model._load_backend()
|
|
76
77
|
if _is_hierarchical_preprocessing(preprocessing):
|
|
78
|
+
geometry = _resolve_hierarchical_geometry(preprocessing, tiling_result)
|
|
77
79
|
index = _build_hierarchical_index(
|
|
78
80
|
tiling_result,
|
|
79
81
|
region_tile_multiple=int(preprocessing.region_tile_multiple),
|
|
82
|
+
tile_size_lv0=int(geometry["tile_size_lv0"]),
|
|
80
83
|
)
|
|
81
84
|
flat_indices = np.array_split(index.flat_index, world_size)[global_rank]
|
|
82
85
|
shard_indices, tile_embeddings = _compute_hierarchical_embedding_shard_for_slide(
|
|
@@ -111,19 +111,23 @@ def _resolve_hierarchical_geometry(preprocessing: PreprocessingConfig, tiling_re
|
|
|
111
111
|
raise ValueError("Hierarchical preprocessing requires target_region_size_px")
|
|
112
112
|
target_tile_size_px = int(preprocessing.target_tile_size_px)
|
|
113
113
|
target_region_size_px = int(preprocessing.target_region_size_px)
|
|
114
|
-
|
|
115
|
-
tile_size_lv0 = int(getattr(tiling_result, "tile_size_lv0"))
|
|
114
|
+
target_spacing_um = float(preprocessing.target_spacing_um)
|
|
116
115
|
multiple = int(preprocessing.region_tile_multiple)
|
|
117
116
|
if target_region_size_px % multiple != 0:
|
|
118
117
|
raise ValueError("target_region_size_px must be divisible by region_tile_multiple")
|
|
118
|
+
effective_spacing_um = float(getattr(tiling_result, "effective_spacing_um"))
|
|
119
|
+
base_spacing_um = float(getattr(tiling_result, "base_spacing_um"))
|
|
120
|
+
effective_tile_size_px = int(round(target_tile_size_px * target_spacing_um / effective_spacing_um))
|
|
121
|
+
effective_region_size_px = effective_tile_size_px * multiple
|
|
122
|
+
tile_size_lv0 = int(round(target_tile_size_px * target_spacing_um / base_spacing_um))
|
|
119
123
|
return {
|
|
120
124
|
"region_tile_multiple": multiple,
|
|
121
125
|
"tiles_per_region": multiple * multiple,
|
|
122
126
|
"target_tile_size_px": target_tile_size_px,
|
|
123
|
-
"effective_tile_size_px":
|
|
127
|
+
"effective_tile_size_px": effective_tile_size_px,
|
|
124
128
|
"target_region_size_px": target_region_size_px,
|
|
125
129
|
"effective_region_size_px": effective_region_size_px,
|
|
126
|
-
"tile_size_lv0": tile_size_lv0
|
|
130
|
+
"tile_size_lv0": tile_size_lv0,
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
|
|
@@ -131,14 +135,18 @@ def _build_hierarchical_index(
|
|
|
131
135
|
tiling_result,
|
|
132
136
|
*,
|
|
133
137
|
region_tile_multiple: int,
|
|
138
|
+
tile_size_lv0: int | None = None,
|
|
134
139
|
) -> HierarchicalIndex:
|
|
135
140
|
x_values, y_values = coordinate_arrays(tiling_result)
|
|
136
141
|
num_regions = int(len(x_values))
|
|
137
142
|
multiple = int(region_tile_multiple)
|
|
138
143
|
if multiple < 2:
|
|
139
144
|
raise ValueError("region_tile_multiple must be at least 2")
|
|
140
|
-
|
|
141
|
-
|
|
145
|
+
subtile_size_lv0 = (
|
|
146
|
+
int(tile_size_lv0)
|
|
147
|
+
if tile_size_lv0 is not None
|
|
148
|
+
else int(getattr(tiling_result, "tile_size_lv0")) // multiple
|
|
149
|
+
)
|
|
142
150
|
tiles_per_region = multiple * multiple
|
|
143
151
|
if num_regions == 0:
|
|
144
152
|
empty = np.empty(0, dtype=np.int64)
|
|
@@ -1275,6 +1283,7 @@ def _compute_hierarchical_embeddings_for_slide(
|
|
|
1275
1283
|
index = _build_hierarchical_index(
|
|
1276
1284
|
tiling_result,
|
|
1277
1285
|
region_tile_multiple=int(geometry["region_tile_multiple"]),
|
|
1286
|
+
tile_size_lv0=int(geometry["tile_size_lv0"]),
|
|
1278
1287
|
)
|
|
1279
1288
|
resolved_indices = index.flat_index
|
|
1280
1289
|
if flat_indices is not None:
|
|
@@ -1370,6 +1379,7 @@ def _compute_hierarchical_embedding_shard_for_slide(
|
|
|
1370
1379
|
index = _build_hierarchical_index(
|
|
1371
1380
|
tiling_result,
|
|
1372
1381
|
region_tile_multiple=int(geometry["region_tile_multiple"]),
|
|
1382
|
+
tile_size_lv0=int(geometry["tile_size_lv0"]),
|
|
1373
1383
|
)
|
|
1374
1384
|
resolved_indices = np.asarray(flat_indices, dtype=np.int64)
|
|
1375
1385
|
collate_fn = OnTheFlyHierarchicalBatchCollator(
|
|
@@ -2808,6 +2808,49 @@ def test_build_hierarchical_index_is_region_major_and_row_major_within_region():
|
|
|
2808
2808
|
)
|
|
2809
2809
|
|
|
2810
2810
|
|
|
2811
|
+
def test_resolve_hierarchical_geometry_scales_tile_first_under_spacing_mismatch():
|
|
2812
|
+
import slide2vec.inference as inference
|
|
2813
|
+
|
|
2814
|
+
preprocessing = PreprocessingConfig(
|
|
2815
|
+
target_spacing_um=0.5,
|
|
2816
|
+
target_tile_size_px=224,
|
|
2817
|
+
target_region_size_px=1792,
|
|
2818
|
+
region_tile_multiple=8,
|
|
2819
|
+
)
|
|
2820
|
+
tiling_result = SimpleNamespace(
|
|
2821
|
+
effective_tile_size_px=3319,
|
|
2822
|
+
effective_spacing_um=0.27,
|
|
2823
|
+
tile_size_lv0=3319,
|
|
2824
|
+
base_spacing_um=0.27,
|
|
2825
|
+
)
|
|
2826
|
+
|
|
2827
|
+
geometry = inference._resolve_hierarchical_geometry(preprocessing, tiling_result)
|
|
2828
|
+
|
|
2829
|
+
assert geometry["effective_tile_size_px"] == 415
|
|
2830
|
+
assert geometry["effective_region_size_px"] == 3320
|
|
2831
|
+
assert geometry["tile_size_lv0"] == 415
|
|
2832
|
+
assert geometry["tiles_per_region"] == 64
|
|
2833
|
+
|
|
2834
|
+
|
|
2835
|
+
def test_build_hierarchical_index_uses_tile_first_level0_offsets_under_spacing_mismatch():
|
|
2836
|
+
import slide2vec.inference as inference
|
|
2837
|
+
|
|
2838
|
+
tiling_result = SimpleNamespace(
|
|
2839
|
+
x=np.array([100], dtype=np.int64),
|
|
2840
|
+
y=np.array([200], dtype=np.int64),
|
|
2841
|
+
)
|
|
2842
|
+
|
|
2843
|
+
index = inference._build_hierarchical_index(
|
|
2844
|
+
tiling_result,
|
|
2845
|
+
region_tile_multiple=8,
|
|
2846
|
+
tile_size_lv0=415,
|
|
2847
|
+
)
|
|
2848
|
+
|
|
2849
|
+
assert index.tiles_per_region == 64
|
|
2850
|
+
np.testing.assert_array_equal(index.subtile_x[:8], np.array([100, 515, 930, 1345, 1760, 2175, 2590, 3005], dtype=np.int64))
|
|
2851
|
+
np.testing.assert_array_equal(index.subtile_y[::8], np.array([200, 615, 1030, 1445, 1860, 2275, 2690, 3105], dtype=np.int64))
|
|
2852
|
+
|
|
2853
|
+
|
|
2811
2854
|
def test_merge_hierarchical_embedding_shards_restores_original_region_shape():
|
|
2812
2855
|
import slide2vec.inference as inference
|
|
2813
2856
|
|
|
@@ -2932,6 +2975,7 @@ def test_compute_hierarchical_embeddings_for_slide_encodes_flat_tile_batches_and
|
|
|
2932
2975
|
tile_size_lv0=448,
|
|
2933
2976
|
target_spacing_um=0.5,
|
|
2934
2977
|
effective_spacing_um=0.5,
|
|
2978
|
+
base_spacing_um=0.5,
|
|
2935
2979
|
read_level=0,
|
|
2936
2980
|
)
|
|
2937
2981
|
|
|
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
|