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.
Files changed (66) hide show
  1. {slide2vec-4.0.4 → slide2vec-4.1.0}/PKG-INFO +1 -1
  2. {slide2vec-4.0.4 → slide2vec-4.1.0}/pyproject.toml +2 -2
  3. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/__init__.py +1 -1
  4. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/distributed/direct_embed_worker.py +3 -0
  5. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/inference.py +16 -6
  6. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/PKG-INFO +1 -1
  7. {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_regression_inference.py +44 -0
  8. {slide2vec-4.0.4 → slide2vec-4.1.0}/LICENSE +0 -0
  9. {slide2vec-4.0.4 → slide2vec-4.1.0}/README.md +0 -0
  10. {slide2vec-4.0.4 → slide2vec-4.1.0}/setup.cfg +0 -0
  11. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/__main__.py +0 -0
  12. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/api.py +0 -0
  13. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/artifacts.py +0 -0
  14. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/cli.py +0 -0
  15. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/configs/__init__.py +0 -0
  16. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/configs/default.yaml +0 -0
  17. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/data/__init__.py +0 -0
  18. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/data/dataset.py +0 -0
  19. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/data/tile_reader.py +0 -0
  20. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/data/tile_store.py +0 -0
  21. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/distributed/__init__.py +0 -0
  22. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/distributed/pipeline_worker.py +0 -0
  23. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/__init__.py +0 -0
  24. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/base.py +0 -0
  25. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/__init__.py +0 -0
  26. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/conch.py +0 -0
  27. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/gigapath.py +0 -0
  28. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/hibou.py +0 -0
  29. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/hoptimus.py +0 -0
  30. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/midnight.py +0 -0
  31. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/musk.py +0 -0
  32. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/phikon.py +0 -0
  33. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/prism.py +0 -0
  34. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/prost40m.py +0 -0
  35. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/titan.py +0 -0
  36. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/uni.py +0 -0
  37. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/models/virchow.py +0 -0
  38. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/registry.py +0 -0
  39. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/encoders/validation.py +0 -0
  40. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/main.py +0 -0
  41. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/model_settings.py +0 -0
  42. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/progress.py +0 -0
  43. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/registry.py +0 -0
  44. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/resources.py +0 -0
  45. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/runtime_types.py +0 -0
  46. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/utils/__init__.py +0 -0
  47. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/utils/config.py +0 -0
  48. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/utils/coordinates.py +0 -0
  49. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/utils/log_utils.py +0 -0
  50. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/utils/tiling_io.py +0 -0
  51. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec/utils/utils.py +0 -0
  52. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/SOURCES.txt +0 -0
  53. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/dependency_links.txt +0 -0
  54. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/entry_points.txt +0 -0
  55. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/not-zip-safe +0 -0
  56. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/requires.txt +0 -0
  57. {slide2vec-4.0.4 → slide2vec-4.1.0}/slide2vec.egg-info/top_level.txt +0 -0
  58. {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_batch_collator_timing.py +0 -0
  59. {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_encoder_registry.py +0 -0
  60. {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_hs2p_package_cutover.py +0 -0
  61. {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_output_consistency.py +0 -0
  62. {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_packaging_metadata.py +0 -0
  63. {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_progress.py +0 -0
  64. {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_regression_core.py +0 -0
  65. {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_regression_models.py +0 -0
  66. {slide2vec-4.0.4 → slide2vec-4.1.0}/tests/test_tile_store.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slide2vec
3
- Version: 4.0.4
3
+ Version: 4.1.0
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "slide2vec"
7
- version = "4.0.4"
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.4"
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.4"
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
- effective_region_size_px = int(getattr(tiling_result, "effective_tile_size_px"))
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": effective_region_size_px // multiple,
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 // multiple,
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
- tile_size_lv0 = int(getattr(tiling_result, "tile_size_lv0"))
141
- subtile_size_lv0 = tile_size_lv0 // multiple
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(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slide2vec
3
- Version: 4.0.4
3
+ Version: 4.1.0
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
@@ -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