slide2vec 4.6.0__tar.gz → 4.6.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.
Files changed (96) hide show
  1. {slide2vec-4.6.0 → slide2vec-4.6.1}/PKG-INFO +3 -3
  2. {slide2vec-4.6.0 → slide2vec-4.6.1}/pyproject.toml +4 -4
  3. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/__init__.py +1 -1
  4. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/hierarchical.py +25 -7
  5. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec.egg-info/PKG-INFO +3 -3
  6. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec.egg-info/requires.txt +2 -2
  7. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_regression_inference.py +41 -0
  8. {slide2vec-4.6.0 → slide2vec-4.6.1}/LICENSE +0 -0
  9. {slide2vec-4.6.0 → slide2vec-4.6.1}/README.md +0 -0
  10. {slide2vec-4.6.0 → slide2vec-4.6.1}/setup.cfg +0 -0
  11. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/__main__.py +0 -0
  12. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/api.py +0 -0
  13. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/artifacts.py +0 -0
  14. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/cli.py +0 -0
  15. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/configs/__init__.py +0 -0
  16. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/configs/default.yaml +0 -0
  17. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/configs/resources.py +0 -0
  18. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/data/__init__.py +0 -0
  19. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/data/dataset.py +0 -0
  20. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/data/tile_reader.py +0 -0
  21. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/data/tile_store.py +0 -0
  22. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/distributed/__init__.py +0 -0
  23. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/distributed/direct_embed_worker.py +0 -0
  24. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/distributed/pipeline_worker.py +0 -0
  25. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/__init__.py +0 -0
  26. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/base.py +0 -0
  27. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/__init__.py +0 -0
  28. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/conch.py +0 -0
  29. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/gigapath.py +0 -0
  30. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/hibou.py +0 -0
  31. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/hoptimus.py +0 -0
  32. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/lunit.py +0 -0
  33. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/midnight.py +0 -0
  34. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/moozy/__init__.py +0 -0
  35. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/moozy/blocks.py +0 -0
  36. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/moozy/case.py +0 -0
  37. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/moozy/loading.py +0 -0
  38. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/moozy/slide.py +0 -0
  39. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/moozy/types.py +0 -0
  40. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/musk.py +0 -0
  41. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/phikon.py +0 -0
  42. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/prism.py +0 -0
  43. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/prost40m.py +0 -0
  44. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/titan.py +0 -0
  45. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/uni.py +0 -0
  46. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/models/virchow.py +0 -0
  47. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/registry.py +0 -0
  48. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/encoders/validation.py +0 -0
  49. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/inference.py +0 -0
  50. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/progress.py +0 -0
  51. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/__init__.py +0 -0
  52. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/artifacts_collect.py +0 -0
  53. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/batching.py +0 -0
  54. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/cpu_budget.py +0 -0
  55. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/distributed.py +0 -0
  56. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/distributed_stage.py +0 -0
  57. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/embedding.py +0 -0
  58. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/embedding_persist.py +0 -0
  59. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/embedding_pipeline.py +0 -0
  60. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/manifest.py +0 -0
  61. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/model_settings.py +0 -0
  62. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/patient_pipeline.py +0 -0
  63. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/persist_callbacks.py +0 -0
  64. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/persistence.py +0 -0
  65. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/process_list.py +0 -0
  66. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/progress_bridge.py +0 -0
  67. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/registry.py +0 -0
  68. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/serialization.py +0 -0
  69. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/slide_encode.py +0 -0
  70. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/tiling.py +0 -0
  71. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/tiling_pipeline.py +0 -0
  72. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/types.py +0 -0
  73. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/runtime/worker_io.py +0 -0
  74. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/utils/__init__.py +0 -0
  75. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/utils/config.py +0 -0
  76. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/utils/coordinates.py +0 -0
  77. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/utils/log_utils.py +0 -0
  78. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/utils/tiling_io.py +0 -0
  79. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec/utils/utils.py +0 -0
  80. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec.egg-info/SOURCES.txt +0 -0
  81. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec.egg-info/dependency_links.txt +0 -0
  82. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec.egg-info/entry_points.txt +0 -0
  83. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec.egg-info/not-zip-safe +0 -0
  84. {slide2vec-4.6.0 → slide2vec-4.6.1}/slide2vec.egg-info/top_level.txt +0 -0
  85. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_architecture_runtime_split.py +0 -0
  86. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_dense_extraction.py +0 -0
  87. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_dense_locality_gated.py +0 -0
  88. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_encoder_registry.py +0 -0
  89. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_hs2p_package_cutover.py +0 -0
  90. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_output_consistency.py +0 -0
  91. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_progress.py +0 -0
  92. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_regression_core.py +0 -0
  93. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_regression_models.py +0 -0
  94. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_runtime_batching.py +0 -0
  95. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_tile_store.py +0 -0
  96. {slide2vec-4.6.0 → slide2vec-4.6.1}/tests/test_tiling_pipeline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slide2vec
3
- Version: 4.6.0
3
+ Version: 4.6.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,sam2,vips]>=4.0.7
18
+ Requires-Dist: hs2p[asap,cucim,openslide,sam2,vips]>=4.0.8
19
19
  Requires-Dist: omegaconf
20
20
  Requires-Dist: matplotlib
21
21
  Requires-Dist: numpy<2
@@ -65,7 +65,7 @@ Requires-Dist: numpy<2; extra == "fm"
65
65
  Requires-Dist: pandas; extra == "fm"
66
66
  Requires-Dist: pillow; extra == "fm"
67
67
  Requires-Dist: rich; extra == "fm"
68
- Requires-Dist: hs2p[asap,cucim,openslide,sam2,vips]>=4.0.7; extra == "fm"
68
+ Requires-Dist: hs2p[asap,cucim,openslide,sam2,vips]>=4.0.8; extra == "fm"
69
69
  Requires-Dist: wandb; extra == "fm"
70
70
  Requires-Dist: torch<2.8,>=2.3; extra == "fm"
71
71
  Requires-Dist: torchvision>=0.18.0; extra == "fm"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "slide2vec"
7
- version = "4.6.0"
7
+ version = "4.6.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,sam2,vips]>=4.0.7",
24
+ "hs2p[asap,cucim,openslide,sam2,vips]>=4.0.8",
25
25
  "omegaconf",
26
26
  "matplotlib",
27
27
  "numpy<2",
@@ -88,7 +88,7 @@ fm = [
88
88
  "pandas",
89
89
  "pillow",
90
90
  "rich",
91
- "hs2p[asap,cucim,openslide,sam2,vips]>=4.0.7",
91
+ "hs2p[asap,cucim,openslide,sam2,vips]>=4.0.8",
92
92
  "wandb",
93
93
  "torch>=2.3,<2.8",
94
94
  "torchvision>=0.18.0",
@@ -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.0"
167
+ current_version = "4.6.1"
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
@@ -11,7 +11,7 @@ from slide2vec.api import (
11
11
  from slide2vec.artifacts import HierarchicalEmbeddingArtifact, SlideEmbeddingArtifact, TileEmbeddingArtifact
12
12
 
13
13
 
14
- __version__ = "4.6.0"
14
+ __version__ = "4.6.1"
15
15
 
16
16
  __all__ = [
17
17
  "Model",
@@ -1,4 +1,5 @@
1
1
  import numpy as np
2
+ from hs2p.wsi.geometry import plan_spacing_read
2
3
 
3
4
  from slide2vec.api import PreprocessingConfig
4
5
  from slide2vec.utils.coordinates import coordinate_arrays
@@ -17,6 +18,21 @@ def is_hierarchical_preprocessing(preprocessing: PreprocessingConfig | None) ->
17
18
  return preprocessing.region_tile_multiple is not None or preprocessing.requested_region_size_px is not None
18
19
 
19
20
 
21
+ def _level_downsample_pairs(level_downsamples) -> list[tuple[float, float]]:
22
+ pairs: list[tuple[float, float]] = []
23
+ for downsample in level_downsamples:
24
+ if isinstance(downsample, (tuple, list, np.ndarray)):
25
+ if len(downsample) == 0:
26
+ raise ValueError("level_downsamples entries must not be empty")
27
+ x_value = float(downsample[0])
28
+ y_value = float(downsample[1]) if len(downsample) > 1 else x_value
29
+ pairs.append((x_value, y_value))
30
+ else:
31
+ value = float(downsample)
32
+ pairs.append((value, value))
33
+ return pairs
34
+
35
+
20
36
  def resolve_hierarchical_geometry(preprocessing: PreprocessingConfig, tiling_result) -> dict[str, int]:
21
37
  if preprocessing.region_tile_multiple is None:
22
38
  raise ValueError("Hierarchical preprocessing requires region_tile_multiple")
@@ -28,14 +44,16 @@ def resolve_hierarchical_geometry(preprocessing: PreprocessingConfig, tiling_res
28
44
  multiple = int(preprocessing.region_tile_multiple)
29
45
  if requested_region_size_px % multiple != 0:
30
46
  raise ValueError("requested_region_size_px must be divisible by region_tile_multiple")
31
- read_spacing_um = float(getattr(tiling_result, "read_spacing_um"))
32
47
  base_spacing_um = float(getattr(tiling_result, "base_spacing_um"))
33
- if abs(read_spacing_um - requested_spacing_um) / requested_spacing_um <= float(preprocessing.tolerance):
34
- read_tile_size_px = requested_tile_size_px
35
- else:
36
- read_tile_size_px = int(
37
- round(requested_tile_size_px * requested_spacing_um / read_spacing_um)
38
- )
48
+ read_plan = plan_spacing_read(
49
+ requested_spacing_um=requested_spacing_um,
50
+ level0_spacing_um=base_spacing_um,
51
+ level_downsamples=_level_downsample_pairs(getattr(tiling_result, "level_downsamples")),
52
+ target_size_px=(requested_tile_size_px, requested_tile_size_px),
53
+ tolerance=float(preprocessing.tolerance),
54
+ )
55
+ read_spacing_um = float(read_plan.read_spacing_um)
56
+ read_tile_size_px = int(read_plan.read_size_px[0])
39
57
  read_region_size_px = read_tile_size_px * multiple
40
58
  # Use the actual read geometry that produced the tile crop. When the
41
59
  # resolved spacing is considered equivalent to the requested spacing,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slide2vec
3
- Version: 4.6.0
3
+ Version: 4.6.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,sam2,vips]>=4.0.7
18
+ Requires-Dist: hs2p[asap,cucim,openslide,sam2,vips]>=4.0.8
19
19
  Requires-Dist: omegaconf
20
20
  Requires-Dist: matplotlib
21
21
  Requires-Dist: numpy<2
@@ -65,7 +65,7 @@ Requires-Dist: numpy<2; extra == "fm"
65
65
  Requires-Dist: pandas; extra == "fm"
66
66
  Requires-Dist: pillow; extra == "fm"
67
67
  Requires-Dist: rich; extra == "fm"
68
- Requires-Dist: hs2p[asap,cucim,openslide,sam2,vips]>=4.0.7; extra == "fm"
68
+ Requires-Dist: hs2p[asap,cucim,openslide,sam2,vips]>=4.0.8; extra == "fm"
69
69
  Requires-Dist: wandb; extra == "fm"
70
70
  Requires-Dist: torch<2.8,>=2.3; extra == "fm"
71
71
  Requires-Dist: torchvision>=0.18.0; extra == "fm"
@@ -1,4 +1,4 @@
1
- hs2p[asap,cucim,openslide,sam2,vips]>=4.0.7
1
+ hs2p[asap,cucim,openslide,sam2,vips]>=4.0.8
2
2
  omegaconf
3
3
  matplotlib
4
4
  numpy<2
@@ -27,7 +27,7 @@ numpy<2
27
27
  pandas
28
28
  pillow
29
29
  rich
30
- hs2p[asap,cucim,openslide,sam2,vips]>=4.0.7
30
+ hs2p[asap,cucim,openslide,sam2,vips]>=4.0.8
31
31
  wandb
32
32
  torch<2.8,>=2.3
33
33
  torchvision>=0.18.0
@@ -3892,6 +3892,7 @@ def test_resolve_hierarchical_geometry_scales_tile_first_under_spacing_mismatch(
3892
3892
  read_spacing_um=0.27,
3893
3893
  tile_size_lv0=3319,
3894
3894
  base_spacing_um=0.27,
3895
+ level_downsamples=[1.0],
3895
3896
  )
3896
3897
 
3897
3898
  geometry = hierarchical.resolve_hierarchical_geometry(preprocessing, tiling_result)
@@ -3902,6 +3903,44 @@ def test_resolve_hierarchical_geometry_scales_tile_first_under_spacing_mismatch(
3902
3903
  assert geometry["tiles_per_region"] == 64
3903
3904
 
3904
3905
 
3906
+ def test_resolve_hierarchical_geometry_uses_hs2p_spacing_plan_for_tile_size(monkeypatch):
3907
+ calls = []
3908
+
3909
+ def fake_plan_spacing_read(**kwargs):
3910
+ calls.append(kwargs)
3911
+ return SimpleNamespace(read_size_px=(415, 415), read_spacing_um=0.27)
3912
+
3913
+ monkeypatch.setattr(hierarchical, "plan_spacing_read", fake_plan_spacing_read)
3914
+ preprocessing = PreprocessingConfig(
3915
+ requested_spacing_um=0.5,
3916
+ requested_tile_size_px=224,
3917
+ requested_region_size_px=1792,
3918
+ region_tile_multiple=8,
3919
+ )
3920
+ tiling_result = SimpleNamespace(
3921
+ read_tile_size_px=3319,
3922
+ read_spacing_um=0.27,
3923
+ tile_size_lv0=3319,
3924
+ base_spacing_um=0.27,
3925
+ level_downsamples=[1.0, 2.0, 4.0],
3926
+ )
3927
+
3928
+ geometry = hierarchical.resolve_hierarchical_geometry(preprocessing, tiling_result)
3929
+
3930
+ assert calls == [
3931
+ {
3932
+ "requested_spacing_um": 0.5,
3933
+ "level0_spacing_um": 0.27,
3934
+ "level_downsamples": [(1.0, 1.0), (2.0, 2.0), (4.0, 4.0)],
3935
+ "target_size_px": (224, 224),
3936
+ "tolerance": 0.05,
3937
+ }
3938
+ ]
3939
+ assert geometry["read_tile_size_px"] == 415
3940
+ assert geometry["read_region_size_px"] == 3320
3941
+ assert geometry["tile_size_lv0"] == 415
3942
+
3943
+
3905
3944
  def test_resolve_hierarchical_geometry_keeps_level0_footprint_when_spacing_matches_base():
3906
3945
  import slide2vec.inference as inference
3907
3946
 
@@ -3916,6 +3955,7 @@ def test_resolve_hierarchical_geometry_keeps_level0_footprint_when_spacing_match
3916
3955
  read_spacing_um=0.486187607049942,
3917
3956
  tile_size_lv0=224,
3918
3957
  base_spacing_um=0.486187607049942,
3958
+ level_downsamples=[1.0],
3919
3959
  )
3920
3960
 
3921
3961
  geometry = hierarchical.resolve_hierarchical_geometry(preprocessing, tiling_result)
@@ -4067,6 +4107,7 @@ def test_compute_hierarchical_embeddings_for_slide_encodes_flat_tile_batches_and
4067
4107
  requested_spacing_um=0.5,
4068
4108
  read_spacing_um=0.5,
4069
4109
  base_spacing_um=0.5,
4110
+ level_downsamples=[1.0],
4070
4111
  read_level=0,
4071
4112
  )
4072
4113
 
File without changes
File without changes
File without changes
File without changes
File without changes