slide2vec 6.0.1__tar.gz → 6.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 (174) hide show
  1. {slide2vec-6.0.1 → slide2vec-6.1.0}/PKG-INFO +3 -3
  2. {slide2vec-6.0.1 → slide2vec-6.1.0}/pyproject.toml +4 -4
  3. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/__init__.py +1 -1
  4. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/api.py +8 -3
  5. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/configs/default.yaml +1 -1
  6. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec.egg-info/PKG-INFO +3 -3
  7. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec.egg-info/SOURCES.txt +1 -0
  8. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec.egg-info/requires.txt +2 -2
  9. slide2vec-6.1.0/tests/test_hs2p5_integration.py +207 -0
  10. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_hs2p_package_cutover.py +3 -3
  11. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_regression_core.py +16 -0
  12. {slide2vec-6.0.1 → slide2vec-6.1.0}/LICENSE +0 -0
  13. {slide2vec-6.0.1 → slide2vec-6.1.0}/README.md +0 -0
  14. {slide2vec-6.0.1 → slide2vec-6.1.0}/setup.cfg +0 -0
  15. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/__main__.py +0 -0
  16. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/_cufile.py +0 -0
  17. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/artifacts.py +0 -0
  18. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/cli.py +0 -0
  19. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/configs/__init__.py +0 -0
  20. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/configs/resources.py +0 -0
  21. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/cufile.json +0 -0
  22. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/data/__init__.py +0 -0
  23. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/data/dataset.py +0 -0
  24. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/data/tile_reader.py +0 -0
  25. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/data/tile_store.py +0 -0
  26. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/distributed/__init__.py +0 -0
  27. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/distributed/dense_image_worker.py +0 -0
  28. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/distributed/dense_worker.py +0 -0
  29. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/distributed/direct_embed_worker.py +0 -0
  30. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/distributed/image_worker.py +0 -0
  31. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/distributed/pin_gpu.py +0 -0
  32. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/distributed/pipeline_worker.py +0 -0
  33. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/distributed/worker_entry.py +0 -0
  34. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/__init__.py +0 -0
  35. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/base.py +0 -0
  36. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/__init__.py +0 -0
  37. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/conch.py +0 -0
  38. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/dinov2.py +0 -0
  39. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/dinov3.py +0 -0
  40. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/genbio.py +0 -0
  41. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/gigapath.py +0 -0
  42. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/gpfm.py +0 -0
  43. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/hibou.py +0 -0
  44. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/hoptimus.py +0 -0
  45. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/isight.py +0 -0
  46. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/lunit.py +0 -0
  47. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/midnight.py +0 -0
  48. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/moozy/__init__.py +0 -0
  49. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/moozy/blocks.py +0 -0
  50. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/moozy/case.py +0 -0
  51. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/moozy/loading.py +0 -0
  52. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/moozy/slide.py +0 -0
  53. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/moozy/types.py +0 -0
  54. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/mstar.py +0 -0
  55. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/musk.py +0 -0
  56. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/phikon.py +0 -0
  57. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/prism.py +0 -0
  58. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/prism2.py +0 -0
  59. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/prost40m.py +0 -0
  60. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/rudolfv2.py +0 -0
  61. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/titan.py +0 -0
  62. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/uni.py +0 -0
  63. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/virchow.py +0 -0
  64. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/models/waiv.py +0 -0
  65. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/registry.py +0 -0
  66. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/encoders/validation.py +0 -0
  67. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/inference.py +0 -0
  68. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/progress.py +0 -0
  69. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/__init__.py +0 -0
  70. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/artifacts_collect.py +0 -0
  71. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/batching.py +0 -0
  72. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/cpu_budget.py +0 -0
  73. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/dense_encode.py +0 -0
  74. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/dense_encoder_input.py +0 -0
  75. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/dense_image_reading.py +0 -0
  76. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/dense_image_recipe.py +0 -0
  77. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/dense_image_shard.py +0 -0
  78. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/dense_image_stage.py +0 -0
  79. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/dense_regions.py +0 -0
  80. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/dense_shard.py +0 -0
  81. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/dense_sliding.py +0 -0
  82. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/dense_stage.py +0 -0
  83. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/distributed.py +0 -0
  84. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/distributed_stage.py +0 -0
  85. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/effective_encoder_input.py +0 -0
  86. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/embedding.py +0 -0
  87. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/embedding_persist.py +0 -0
  88. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/embedding_pipeline.py +0 -0
  89. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/encoder_input_contract.py +0 -0
  90. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/hierarchical.py +0 -0
  91. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/image_shard.py +0 -0
  92. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/image_specs.py +0 -0
  93. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/image_stage.py +0 -0
  94. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/manifest.py +0 -0
  95. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/model_settings.py +0 -0
  96. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/patient_pipeline.py +0 -0
  97. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/persist_callbacks.py +0 -0
  98. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/persistence.py +0 -0
  99. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/pooled_encoder_input.py +0 -0
  100. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/preprocessing.py +0 -0
  101. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/process_list.py +0 -0
  102. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/progress_bridge.py +0 -0
  103. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/registry.py +0 -0
  104. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/serialization.py +0 -0
  105. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/sharding.py +0 -0
  106. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/slide_encode.py +0 -0
  107. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/tiling.py +0 -0
  108. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/tiling_pipeline.py +0 -0
  109. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/types.py +0 -0
  110. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/runtime/worker_io.py +0 -0
  111. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/utils/__init__.py +0 -0
  112. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/utils/config.py +0 -0
  113. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/utils/coordinates.py +0 -0
  114. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/utils/log_utils.py +0 -0
  115. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/utils/tiling_io.py +0 -0
  116. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec/utils/utils.py +0 -0
  117. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec.egg-info/dependency_links.txt +0 -0
  118. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec.egg-info/entry_points.txt +0 -0
  119. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec.egg-info/not-zip-safe +0 -0
  120. {slide2vec-6.0.1 → slide2vec-6.1.0}/slide2vec.egg-info/top_level.txt +0 -0
  121. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_architecture_runtime_split.py +0 -0
  122. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_attention_extraction.py +0 -0
  123. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_benchmark_tooling.py +0 -0
  124. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_cufile_compat_mode.py +0 -0
  125. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_encode_kit.py +0 -0
  126. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_encoder_input.py +0 -0
  127. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_extraction.py +0 -0
  128. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_image_reading.py +0 -0
  129. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_image_resume.py +0 -0
  130. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_image_shard.py +0 -0
  131. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_image_stage.py +0 -0
  132. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_regions.py +0 -0
  133. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_shard.py +0 -0
  134. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_sliding.py +0 -0
  135. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_source_spacing.py +0 -0
  136. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_stage.py +0 -0
  137. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dense_worker.py +0 -0
  138. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dinov2_natimage.py +0 -0
  139. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_dinov3_natimage.py +0 -0
  140. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_encoder_capabilities.py +0 -0
  141. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_encoder_input_contract.py +0 -0
  142. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_encoder_plugins.py +0 -0
  143. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_encoder_preprocessing.py +0 -0
  144. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_encoder_provider_failures.py +0 -0
  145. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_encoder_registry.py +0 -0
  146. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_gpfm_genbio_heavy.py +0 -0
  147. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_image_shard.py +0 -0
  148. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_image_stage.py +0 -0
  149. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_inference_benchmark.py +0 -0
  150. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_isight.py +0 -0
  151. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_load_model_hf_auth.py +0 -0
  152. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_mascaret.py +0 -0
  153. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_on_slide_persisted.py +0 -0
  154. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_output_consistency.py +0 -0
  155. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_patch_size_metadata.py +0 -0
  156. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_patient_manifest.py +0 -0
  157. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_phaet.py +0 -0
  158. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_pooled_encoder_input.py +0 -0
  159. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_pooled_geometry.py +0 -0
  160. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_prism.py +0 -0
  161. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_prism2.py +0 -0
  162. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_process_list_performance.py +0 -0
  163. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_progress.py +0 -0
  164. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_regression_inference.py +0 -0
  165. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_regression_models.py +0 -0
  166. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_rudolfv2.py +0 -0
  167. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_runtime_batching.py +0 -0
  168. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_sharding.py +0 -0
  169. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_slide_coordinate_preparation.py +0 -0
  170. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_soma_migration.py +0 -0
  171. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_tile_store.py +0 -0
  172. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_tiling_pipeline.py +0 -0
  173. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_titan.py +0 -0
  174. {slide2vec-6.0.1 → slide2vec-6.1.0}/tests/test_worker_gpu_pinning.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slide2vec
3
- Version: 6.0.1
3
+ Version: 6.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
@@ -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.4.3
18
+ Requires-Dist: hs2p[asap,cucim,openslide,sam2,vips]>=5.0.0
19
19
  Requires-Dist: omegaconf
20
20
  Requires-Dist: matplotlib
21
21
  Requires-Dist: numpy<2
@@ -75,7 +75,7 @@ Requires-Dist: numpy<2; extra == "fm"
75
75
  Requires-Dist: pandas; extra == "fm"
76
76
  Requires-Dist: pillow; extra == "fm"
77
77
  Requires-Dist: rich; extra == "fm"
78
- Requires-Dist: hs2p[asap,cucim,openslide,sam2,vips]>=4.4.3; extra == "fm"
78
+ Requires-Dist: hs2p[asap,cucim,openslide,sam2,vips]>=5.0.0; extra == "fm"
79
79
  Requires-Dist: wandb; extra == "fm"
80
80
  Requires-Dist: torch<2.8,>=2.3; extra == "fm"
81
81
  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 = "6.0.1"
7
+ version = "6.1.0"
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.4.3",
24
+ "hs2p[asap,cucim,openslide,sam2,vips]>=5.0.0",
25
25
  "omegaconf",
26
26
  "matplotlib",
27
27
  "numpy<2",
@@ -100,7 +100,7 @@ fm = [
100
100
  "pandas",
101
101
  "pillow",
102
102
  "rich",
103
- "hs2p[asap,cucim,openslide,sam2,vips]>=4.4.3",
103
+ "hs2p[asap,cucim,openslide,sam2,vips]>=5.0.0",
104
104
  "wandb",
105
105
  "torch>=2.3,<2.8",
106
106
  "torchvision>=0.18.0",
@@ -180,7 +180,7 @@ no_implicit_reexport = true
180
180
  max-line-length = 160
181
181
 
182
182
  [tool.bumpver]
183
- current_version = "6.0.1"
183
+ current_version = "6.1.0"
184
184
  version_pattern = "MAJOR.MINOR.PATCH"
185
185
  commit = false
186
186
  tag = false
@@ -31,7 +31,7 @@ from slide2vec.encoders import (
31
31
  )
32
32
 
33
33
 
34
- __version__ = "6.0.1"
34
+ __version__ = "6.1.0"
35
35
 
36
36
  __all__ = [
37
37
  "Model",
@@ -145,13 +145,18 @@ class PreprocessingConfig:
145
145
 
146
146
  #: Slide reading backend. ``"auto"`` tries cucim → vips → openslide → asap.
147
147
  #: Explicit choices: ``"cucim"``, ``"openslide"``, ``"vips"``, ``"asap"``.
148
+ #: Slides without spacing metadata require ``spacing_at_level_0``, including
149
+ #: untagged TIFFs read with VIPS.
148
150
  backend: str = "auto"
149
- #: Source-mask reading backend, resolved independently from the *mask* path
150
- #: (hs2p ≥ 4.3.0). ``"auto"`` probes openability just like :attr:`backend`. Set this
151
+ #: Source-mask reading backend, resolved independently from the *mask* path.
152
+ #: ``"auto"`` probes openability just like :attr:`backend`. Set this
151
153
  #: explicitly (e.g. ``"openslide"``) when a precomputed tissue or annotation mask needs
152
154
  #: a different decoder than its slide — hs2p no longer silently falls back to another
153
155
  #: reader, so a mask the slide backend cannot decode fails unless overridden here.
154
- #: Accepts the same values as :attr:`backend`; ignored for slides with no source mask.
156
+ #: Accepts the same values as :attr:`backend` plus ``"pil"`` for PNG/JPEG masks;
157
+ #: ``"auto"`` selects PIL for these masks. Ignored for slides with no source mask.
158
+ #: Masks need no spacing metadata, but must align to the slide's field of view and
159
+ #: contain only declared label IDs. See :doc:`preprocessing` for hs2p 5 validation.
155
160
  mask_backend: str = "auto"
156
161
  #: Target spacing in µm/px. Resolved from the model preset when ``None``.
157
162
  requested_spacing_um: float | None = None
@@ -36,7 +36,7 @@ tiling:
36
36
  # where `mask_path` is read as a multi-label raster. The `tissue` min_coverage entry below is
37
37
  # the single source of truth for the tissue threshold.
38
38
  output_mode: per_annotation # how sampled tiles are grouped into artifacts. per_annotation: one flat artifact set per sampled class, namespaced under a `<class>/` subdir (the `tissue` class collapses to the flat root). merged: a single flat artifact set per slide over the union of tiles passing any active class threshold — it carries no class label, so it lands at the flat output root (no `<class>/` subdir).
39
- pixel_mapping: # {class_name: integer pixel value in the mask raster}
39
+ pixel_mapping: # {class_name: integer pixel value in the mask raster}; a list (e.g. tumor: [2, 3]) merges several values into one class; every mask value must be declared
40
40
  background: 0
41
41
  tissue: 1
42
42
  colors: # {class_name: [r, g, b] | null} used when rendering previews
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slide2vec
3
- Version: 6.0.1
3
+ Version: 6.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
@@ -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.4.3
18
+ Requires-Dist: hs2p[asap,cucim,openslide,sam2,vips]>=5.0.0
19
19
  Requires-Dist: omegaconf
20
20
  Requires-Dist: matplotlib
21
21
  Requires-Dist: numpy<2
@@ -75,7 +75,7 @@ Requires-Dist: numpy<2; extra == "fm"
75
75
  Requires-Dist: pandas; extra == "fm"
76
76
  Requires-Dist: pillow; extra == "fm"
77
77
  Requires-Dist: rich; extra == "fm"
78
- Requires-Dist: hs2p[asap,cucim,openslide,sam2,vips]>=4.4.3; extra == "fm"
78
+ Requires-Dist: hs2p[asap,cucim,openslide,sam2,vips]>=5.0.0; extra == "fm"
79
79
  Requires-Dist: wandb; extra == "fm"
80
80
  Requires-Dist: torch<2.8,>=2.3; extra == "fm"
81
81
  Requires-Dist: torchvision>=0.18.0; extra == "fm"
@@ -139,6 +139,7 @@ tests/test_encoder_preprocessing.py
139
139
  tests/test_encoder_provider_failures.py
140
140
  tests/test_encoder_registry.py
141
141
  tests/test_gpfm_genbio_heavy.py
142
+ tests/test_hs2p5_integration.py
142
143
  tests/test_hs2p_package_cutover.py
143
144
  tests/test_image_shard.py
144
145
  tests/test_image_stage.py
@@ -1,4 +1,4 @@
1
- hs2p[asap,cucim,openslide,sam2,vips]>=4.4.3
1
+ hs2p[asap,cucim,openslide,sam2,vips]>=5.0.0
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.4.3
30
+ hs2p[asap,cucim,openslide,sam2,vips]>=5.0.0
31
31
  wandb
32
32
  torch<2.8,>=2.3
33
33
  torchvision>=0.18.0
@@ -0,0 +1,207 @@
1
+ """Exercise the slide2vec/hs2p boundary with real readers and persisted artifacts."""
2
+
3
+ from pathlib import Path
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
+ import pytest
8
+ from hs2p import BatchPartialFailureWarning, SlideSpec
9
+ from PIL import Image
10
+
11
+ from slide2vec.api import PreprocessingConfig
12
+ from slide2vec.runtime.embedding import tiling_result_annotation
13
+ from slide2vec.runtime.tiling_pipeline import prepare_tiled_slides
14
+
15
+
16
+ def _flat_slide(tmp_path: Path, labels: np.ndarray) -> SlideSpec:
17
+ image_path = tmp_path / "slide.png"
18
+ mask_path = tmp_path / "mask.png"
19
+ Image.new("RGB", (128, 128), (160, 70, 110)).save(image_path)
20
+ Image.fromarray(labels).save(mask_path)
21
+ return SlideSpec(
22
+ sample_id="slide",
23
+ image_path=image_path,
24
+ mask_path=mask_path,
25
+ spacing_at_level_0=0.5,
26
+ )
27
+
28
+
29
+ def _preprocessing(**kwargs) -> PreprocessingConfig:
30
+ return PreprocessingConfig(
31
+ backend="auto",
32
+ requested_spacing_um=0.5,
33
+ requested_tile_size_px=32,
34
+ segmentation={"downsample": 1},
35
+ filtering={"a_t": 0, "a_h": 0},
36
+ preview={"save_mask_preview": False, "save_tiling_preview": False},
37
+ **kwargs,
38
+ )
39
+
40
+
41
+ def test_spacingless_tissue_mask_aligns_and_round_trips(tmp_path):
42
+ slide = _flat_slide(tmp_path, np.ones((32, 32), dtype=np.uint8))
43
+
44
+ slides, results, process_list = prepare_tiled_slides(
45
+ [slide], _preprocessing(), output_dir=tmp_path / "out", num_workers=1
46
+ )
47
+
48
+ assert slides == [slide]
49
+ result, = results
50
+ expected = np.array([(x, y) for x in range(0, 128, 32) for y in range(0, 128, 32)])
51
+ np.testing.assert_array_equal(np.column_stack((result.x, result.y)), expected)
52
+ assert result.spacing_at_level_0 == 0.5
53
+ assert result.mask_spacing_um == 2.0
54
+ assert result.mask_level == 0
55
+ assert result.mask_backend == "pil"
56
+ row = pd.read_csv(process_list).iloc[0]
57
+ assert row["tiling_status"] == "success"
58
+ assert row["requested_mask_backend"] == "auto"
59
+ assert row["mask_backend"] == "pil"
60
+ assert row["num_tiles"] == 16
61
+
62
+
63
+ @pytest.mark.parametrize("output_mode", ["per_annotation", "merged"])
64
+ def test_spacingless_annotation_mask_groups_values_and_preserves_bag_identity(tmp_path, output_mode):
65
+ labels = np.full((32, 32), 2, dtype=np.uint8)
66
+ labels[:, 16:] = 3
67
+ slide = _flat_slide(tmp_path, labels)
68
+ preprocessing = _preprocessing(masks={
69
+ "pixel_mapping": {"tumor": [2, 3]},
70
+ "min_coverage": {"tissue": None, "tumor": 0.5},
71
+ "colors": {"tumor": [255, 0, 0]},
72
+ "output_mode": output_mode,
73
+ })
74
+
75
+ slides, results, process_list = prepare_tiled_slides(
76
+ [slide], preprocessing, output_dir=tmp_path / "out", num_workers=1
77
+ )
78
+
79
+ assert slides == [slide]
80
+ result, = results
81
+ annotation = "tumor" if output_mode == "per_annotation" else "merged"
82
+ assert tiling_result_annotation(result) == annotation
83
+ assert result.output_mode == output_mode
84
+ assert result.mask_spacing_um == 2.0
85
+ assert len(result.x) == 16
86
+ row = pd.read_csv(process_list).iloc[0]
87
+ assert row["annotation"] == annotation
88
+ assert row["mask_backend"] == "pil"
89
+ assert row["tiling_status"] == "success"
90
+
91
+
92
+ @pytest.mark.parametrize(
93
+ "labels, error",
94
+ [
95
+ (np.ones((32, 16), dtype=np.uint8), "Mask alignment failed"),
96
+ (np.full((32, 32), 255, dtype=np.uint8), "undeclared label IDs"),
97
+ ],
98
+ ids=["misaligned-mask", "undeclared-tissue-value"],
99
+ )
100
+ def test_invalid_source_masks_surface_hs2p_errors(tmp_path, labels, error):
101
+ slide = _flat_slide(tmp_path, labels)
102
+ output_dir = tmp_path / "out"
103
+
104
+ with pytest.warns(BatchPartialFailureWarning, match=error):
105
+ with pytest.raises(RuntimeError, match=error):
106
+ prepare_tiled_slides(
107
+ [slide], _preprocessing(), output_dir=output_dir, num_workers=1
108
+ )
109
+
110
+ row = pd.read_csv(output_dir / "process_list.csv").iloc[0]
111
+ assert row["tiling_status"] == "failed"
112
+ assert error in row["error"]
113
+ assert pd.isna(row["coordinates_npz_path"])
114
+
115
+
116
+ def test_wsi_mask_coordinates_and_previews_remain_compatible(tmp_path):
117
+ pytest.importorskip("openslide")
118
+ from tests.output_consistency_config import (
119
+ TILING_FILTER_PARAMS,
120
+ TILING_MASKS,
121
+ TILING_PARAMS,
122
+ TILING_SEG_PARAMS,
123
+ )
124
+
125
+ fixtures = Path(__file__).parent / "fixtures"
126
+ slide = SlideSpec(
127
+ sample_id="test-wsi",
128
+ image_path=fixtures / "input" / "test-wsi.tif",
129
+ mask_path=fixtures / "input" / "test-mask.tif",
130
+ )
131
+ preprocessing = PreprocessingConfig(
132
+ backend="openslide",
133
+ mask_backend="openslide",
134
+ masks=TILING_MASKS,
135
+ segmentation=TILING_SEG_PARAMS,
136
+ filtering=TILING_FILTER_PARAMS,
137
+ **TILING_PARAMS,
138
+ )
139
+
140
+ slides, results, process_list = prepare_tiled_slides(
141
+ [slide], preprocessing, output_dir=tmp_path / "out", num_workers=1
142
+ )
143
+
144
+ assert slides == [slide]
145
+ result, = results
146
+ with np.load(fixtures / "gt" / "test-wsi.coordinates.npz") as expected:
147
+ np.testing.assert_array_equal(result.x, expected["x"])
148
+ np.testing.assert_array_equal(result.y, expected["y"])
149
+ # hs2p 5 records dimension-derived mask spacing, not the TIFF's nominal tag.
150
+ assert result.mask_level == 1
151
+ assert result.mask_spacing_um == pytest.approx(result.base_spacing_um * 32)
152
+ row = pd.read_csv(process_list).iloc[0]
153
+ for field in ("mask_preview_path", "tiling_preview_path"):
154
+ path = getattr(result, field)
155
+ assert path.is_file()
156
+ assert Path(row[field]) == path
157
+ with Image.open(path) as preview:
158
+ preview.verify()
159
+
160
+
161
+ @pytest.mark.parametrize("output_mode", ["per_annotation", "merged"])
162
+ def test_wsi_annotation_previews_accept_spacingless_masks(tmp_path, output_mode):
163
+ pytest.importorskip("openslide")
164
+ labels = np.full((200, 216), 2, dtype=np.uint8)
165
+ labels[:, 108:] = 3
166
+ mask_path = tmp_path / "annotations.png"
167
+ Image.fromarray(labels).save(mask_path)
168
+ slide = SlideSpec(
169
+ sample_id="test-wsi",
170
+ image_path=Path(__file__).parent / "fixtures" / "input" / "test-wsi.tif",
171
+ mask_path=mask_path,
172
+ )
173
+ preprocessing = PreprocessingConfig(
174
+ backend="openslide",
175
+ requested_spacing_um=0.5,
176
+ requested_tile_size_px=512,
177
+ filtering={"a_t": 0, "a_h": 0},
178
+ masks={
179
+ "pixel_mapping": {"tumor": [2, 3]},
180
+ "min_coverage": {"tissue": None, "tumor": 0.5},
181
+ "colors": {"tumor": [255, 0, 0]},
182
+ "output_mode": output_mode,
183
+ },
184
+ )
185
+
186
+ _, results, process_list = prepare_tiled_slides(
187
+ [slide], preprocessing, output_dir=tmp_path / "out", num_workers=1
188
+ )
189
+
190
+ result, = results
191
+ assert len(result.x) > 0
192
+ assert result.backend == "openslide"
193
+ assert result.mask_backend == "pil"
194
+ assert result.mask_spacing_um == pytest.approx(result.base_spacing_um * 64)
195
+ annotation = "tumor" if output_mode == "per_annotation" else "merged"
196
+ assert tiling_result_annotation(result) == annotation
197
+ preview_dir = tmp_path / "out" / "preview" / "tiling"
198
+ if output_mode == "per_annotation":
199
+ preview_dir /= "tumor"
200
+ assert result.tiling_preview_path == preview_dir / "test-wsi.jpg"
201
+ row = pd.read_csv(process_list).iloc[0]
202
+ for field in ("mask_preview_path", "tiling_preview_path"):
203
+ path = getattr(result, field)
204
+ assert path.is_file()
205
+ assert Path(row[field]) == path
206
+ with Image.open(path) as preview:
207
+ preview.verify()
@@ -23,7 +23,7 @@ def test_package_root_exports_api():
23
23
  assert hasattr(package, "DenseRegionArtifact")
24
24
 
25
25
 
26
- def test_dependency_declarations_require_hs2p_4_4_3_with_consistent_extras():
26
+ def test_dependency_declarations_require_hs2p_5_0_0_with_consistent_extras():
27
27
  project = tomllib.loads(
28
28
  (Path(__file__).resolve().parents[1] / "pyproject.toml").read_text(
29
29
  encoding="utf-8"
@@ -39,8 +39,8 @@ def test_dependency_declarations_require_hs2p_4_4_3_with_consistent_extras():
39
39
  ]
40
40
 
41
41
  assert hs2p_dependencies == [
42
- "hs2p[asap,cucim,openslide,sam2,vips]>=4.4.3",
43
- "hs2p[asap,cucim,openslide,sam2,vips]>=4.4.3",
42
+ "hs2p[asap,cucim,openslide,sam2,vips]>=5.0.0",
43
+ "hs2p[asap,cucim,openslide,sam2,vips]>=5.0.0",
44
44
  ]
45
45
 
46
46
 
@@ -1535,6 +1535,22 @@ def test_masks_boundary_accepts_distinct_integer_value_255():
1535
1535
  assert sampling.pixel_mapping["tumor"] == 255
1536
1536
 
1537
1537
 
1538
+ def test_masks_boundary_forwards_list_valued_pixel_mapping():
1539
+ from slide2vec.runtime.tiling import build_hs2p_configs
1540
+
1541
+ preprocessing = _masks_preprocessing(
1542
+ {
1543
+ "pixel_mapping": {"tumor": [2, 3]},
1544
+ "colors": {"tumor": [255, 0, 0]},
1545
+ "min_coverage": {"tumor": 0.5},
1546
+ }
1547
+ )
1548
+
1549
+ sampling = build_hs2p_configs(preprocessing)[-3]
1550
+
1551
+ assert list(sampling.pixel_mapping["tumor"]) == [2, 3]
1552
+
1553
+
1538
1554
  @pytest.mark.parametrize(
1539
1555
  ("pixel_mapping", "message"),
1540
1556
  [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes