slide2vec 7.1.0__tar.gz → 7.1.2__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-7.1.0 → slide2vec-7.1.2}/PKG-INFO +29 -68
- {slide2vec-7.1.0 → slide2vec-7.1.2}/README.md +4 -0
- slide2vec-7.1.2/pyproject.toml +264 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/__init__.py +1 -1
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/api.py +70 -37
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/dinov3.py +1 -3
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/moozy/loading.py +1 -1
- slide2vec-7.1.2/slide2vec/encoders/models/prism2.py +172 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/inference.py +14 -14
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/cpu_budget.py +2 -2
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/dense_image_stage.py +4 -4
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/dense_stage.py +4 -4
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/distributed_stage.py +7 -7
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/embedding_pipeline.py +2 -2
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/image_stage.py +4 -4
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/serialization.py +4 -2
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/slide_encode.py +3 -2
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec.egg-info/PKG-INFO +29 -68
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec.egg-info/SOURCES.txt +1 -0
- slide2vec-7.1.2/slide2vec.egg-info/requires.txt +66 -0
- slide2vec-7.1.2/tests/test_execution_options.py +116 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_isight.py +1 -1
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_mascaret.py +5 -3
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_phaet.py +5 -3
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_prism2.py +95 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_regression_core.py +4 -6
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_regression_inference.py +65 -0
- slide2vec-7.1.0/pyproject.toml +0 -204
- slide2vec-7.1.0/slide2vec/encoders/models/prism2.py +0 -94
- slide2vec-7.1.0/slide2vec.egg-info/requires.txt +0 -109
- {slide2vec-7.1.0 → slide2vec-7.1.2}/LICENSE +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/setup.cfg +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/__main__.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/_cufile.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/artifacts.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/cli.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/configs/__init__.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/configs/default.yaml +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/configs/resources.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/cufile.json +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/data/__init__.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/data/dataset.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/data/tile_reader.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/data/tile_store.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/distributed/__init__.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/distributed/dense_image_worker.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/distributed/dense_worker.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/distributed/direct_embed_worker.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/distributed/image_worker.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/distributed/pin_gpu.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/distributed/pipeline_worker.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/distributed/worker_entry.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/__init__.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/base.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/__init__.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/conch.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/dinov2.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/genbio.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/gigapath.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/gpfm.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/hibou.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/hoptimus.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/isight.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/lunit.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/mettle.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/midnight.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/moozy/__init__.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/moozy/blocks.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/moozy/case.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/moozy/slide.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/moozy/types.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/mstar.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/musk.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/phikon.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/prism.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/prost40m.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/rudolfv2.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/titan.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/uni.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/virchow.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/models/waiv.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/registry.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/encoders/validation.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/progress.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/__init__.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/artifacts_collect.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/batching.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/dense_encode.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/dense_encoder_input.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/dense_image_reading.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/dense_image_recipe.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/dense_image_shard.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/dense_regions.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/dense_shard.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/dense_sliding.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/distributed.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/effective_encoder_input.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/embedding.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/embedding_persist.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/encoder_input_contract.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/feature_identity.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/hierarchical.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/image_shard.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/image_specs.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/manifest.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/model_settings.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/patient_pipeline.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/persist_callbacks.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/persistence.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/pooled_encoder_input.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/preprocessing.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/process_list.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/progress_bridge.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/registry.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/sharding.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/tiling.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/tiling_pipeline.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/types.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/runtime/worker_io.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/utils/__init__.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/utils/config.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/utils/coordinates.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/utils/log_utils.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/utils/tiling_io.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec/utils/utils.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec.egg-info/dependency_links.txt +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec.egg-info/entry_points.txt +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec.egg-info/not-zip-safe +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/slide2vec.egg-info/top_level.txt +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_attention_extraction.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_benchmark_tooling.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_cli_noninteractive.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_cufile_compat_mode.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_encode_kit.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_encoder_input.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_extraction.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_image_reading.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_image_resume.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_image_shard.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_image_stage.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_regions.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_shard.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_sliding.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_source_spacing.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_stage.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dense_worker.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dinov2_natimage.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_dinov3_natimage.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_drop_in_encoders.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_encoder_capabilities.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_encoder_input_contract.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_encoder_plugins.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_encoder_preprocessing.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_encoder_provider_failures.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_encoder_registry.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_feature_identity.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_gigapath.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_gpfm_genbio_heavy.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_hierarchical_encoding.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_hs2p5_integration.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_hs2p_package_cutover.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_image_shard.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_image_stage.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_inference_benchmark.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_load_model_hf_auth.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_mettle.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_on_slide_persisted.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_output_consistency.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_patch_size_metadata.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_patient_annotation_sampling.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_patient_manifest.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_patient_tiling_only.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_pooled_encoder_input.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_pooled_geometry.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_pooled_identity_api.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_prism.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_process_list_performance.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_progress.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_regression_models.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_rudolfv2.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_runtime_batching.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_sharding.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_slide_coordinate_preparation.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_slide_latents.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_soma_migration.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_tile_store.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_tiling_pipeline.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_titan.py +0 -0
- {slide2vec-7.1.0 → slide2vec-7.1.2}/tests/test_worker_gpu_pinning.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: slide2vec
|
|
3
|
-
Version: 7.1.
|
|
3
|
+
Version: 7.1.2
|
|
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
|
|
@@ -11,94 +11,51 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
15
14
|
Requires-Python: >=3.10
|
|
16
15
|
Description-Content-Type: text/markdown
|
|
17
16
|
License-File: LICENSE
|
|
18
17
|
Requires-Dist: hs2p[asap,cucim,openslide,sam2,tifffile,vips]>=6.1.0
|
|
19
18
|
Requires-Dist: omegaconf
|
|
20
19
|
Requires-Dist: matplotlib
|
|
21
|
-
Requires-Dist: numpy<2
|
|
22
20
|
Requires-Dist: pandas
|
|
23
21
|
Requires-Dist: pillow
|
|
24
22
|
Requires-Dist: rich
|
|
25
23
|
Requires-Dist: torch
|
|
26
24
|
Requires-Dist: torchvision
|
|
27
|
-
Requires-Dist: transformers
|
|
25
|
+
Requires-Dist: transformers<5,>=4.47
|
|
28
26
|
Requires-Dist: wandb
|
|
29
27
|
Requires-Dist: einops
|
|
30
|
-
Requires-Dist:
|
|
28
|
+
Requires-Dist: einops-exts
|
|
29
|
+
Requires-Dist: timm>=1.0.20
|
|
31
30
|
Requires-Dist: huggingface_hub
|
|
32
|
-
|
|
33
|
-
Requires-Dist: torch>=2.0; extra == "hoptimus"
|
|
34
|
-
Requires-Dist: torchvision>=0.15.0; extra == "hoptimus"
|
|
35
|
-
Requires-Dist: xformers>=0.0.18; extra == "hoptimus"
|
|
36
|
-
Provides-Extra: virchow
|
|
37
|
-
Requires-Dist: timm>=1.0.3; extra == "virchow"
|
|
38
|
-
Requires-Dist: torch>=2.0; extra == "virchow"
|
|
39
|
-
Provides-Extra: uni
|
|
40
|
-
Requires-Dist: torch>=2.0; extra == "uni"
|
|
41
|
-
Requires-Dist: timm>=1.0.3; extra == "uni"
|
|
42
|
-
Requires-Dist: xformers>=0.0.18; extra == "uni"
|
|
31
|
+
Requires-Dist: packaging
|
|
43
32
|
Provides-Extra: prism
|
|
44
|
-
Requires-Dist:
|
|
45
|
-
Requires-Dist:
|
|
46
|
-
Requires-Dist:
|
|
47
|
-
Requires-Dist: environs==11.0.0; extra == "prism"
|
|
48
|
-
Requires-Dist: sacremoses==0.1.1; extra == "prism"
|
|
49
|
-
Requires-Dist: xformers==0.0.31; extra == "prism"
|
|
33
|
+
Requires-Dist: torch>=2.3; extra == "prism"
|
|
34
|
+
Requires-Dist: environs; extra == "prism"
|
|
35
|
+
Requires-Dist: sacremoses; extra == "prism"
|
|
50
36
|
Provides-Extra: prism2
|
|
51
37
|
Requires-Dist: torch>=2.3; extra == "prism2"
|
|
52
|
-
Requires-Dist: transformers
|
|
53
|
-
Requires-Dist: safetensors; extra == "prism2"
|
|
54
|
-
Requires-Dist: einops; extra == "prism2"
|
|
55
|
-
Requires-Dist: flash-attn>=2.6.3; extra == "prism2"
|
|
56
|
-
Provides-Extra: hibou
|
|
57
|
-
Requires-Dist: scipy~=1.8.1; extra == "hibou"
|
|
58
|
-
Requires-Dist: scikit-image~=0.19.3; extra == "hibou"
|
|
59
|
-
Provides-Extra: moozy
|
|
60
|
-
Requires-Dist: huggingface_hub<1.0,>=0.30.0; extra == "moozy"
|
|
38
|
+
Requires-Dist: transformers<5,>=4.51; extra == "prism2"
|
|
61
39
|
Provides-Extra: titan
|
|
62
|
-
Requires-Dist:
|
|
63
|
-
Requires-Dist:
|
|
64
|
-
Requires-Dist:
|
|
65
|
-
Requires-Dist:
|
|
66
|
-
Requires-Dist: transformers==4.46.0; extra == "titan"
|
|
40
|
+
Requires-Dist: einops; extra == "titan"
|
|
41
|
+
Requires-Dist: einops-exts; extra == "titan"
|
|
42
|
+
Requires-Dist: timm>=1.0.3; extra == "titan"
|
|
43
|
+
Requires-Dist: transformers<5,>=4.46; extra == "titan"
|
|
67
44
|
Provides-Extra: waiv
|
|
68
|
-
Requires-Dist: transformers
|
|
69
|
-
|
|
70
|
-
Requires-Dist:
|
|
45
|
+
Requires-Dist: transformers>=4.57; extra == "waiv"
|
|
46
|
+
Provides-Extra: flash-attn
|
|
47
|
+
Requires-Dist: flash-attn>=2.6.3; extra == "flash-attn"
|
|
48
|
+
Provides-Extra: xformers
|
|
49
|
+
Requires-Dist: xformers; extra == "xformers"
|
|
71
50
|
Provides-Extra: fm
|
|
72
|
-
Requires-Dist:
|
|
73
|
-
Requires-Dist: matplotlib; extra == "fm"
|
|
74
|
-
Requires-Dist: numpy<2; extra == "fm"
|
|
75
|
-
Requires-Dist: pandas; extra == "fm"
|
|
76
|
-
Requires-Dist: pillow; extra == "fm"
|
|
77
|
-
Requires-Dist: rich; extra == "fm"
|
|
78
|
-
Requires-Dist: hs2p[asap,cucim,openslide,sam2,tifffile,vips]>=6.1.0; extra == "fm"
|
|
79
|
-
Requires-Dist: wandb; extra == "fm"
|
|
80
|
-
Requires-Dist: torch<2.8,>=2.3; extra == "fm"
|
|
81
|
-
Requires-Dist: torchvision>=0.18.0; extra == "fm"
|
|
82
|
-
Requires-Dist: einops>=0.8.0; extra == "fm"
|
|
83
|
-
Requires-Dist: timm>=1.0.20; extra == "fm"
|
|
84
|
-
Requires-Dist: huggingface_hub<1.0,>=0.30.0; extra == "fm"
|
|
85
|
-
Requires-Dist: environs; extra == "fm"
|
|
86
|
-
Requires-Dist: einops-exts>=0.0.4; extra == "fm"
|
|
87
|
-
Requires-Dist: transformers>=4.53; extra == "fm"
|
|
88
|
-
Requires-Dist: sacremoses; extra == "fm"
|
|
89
|
-
Requires-Dist: xformers>=0.0.31; extra == "fm"
|
|
90
|
-
Requires-Dist: scipy>=1.8.1; extra == "fm"
|
|
91
|
-
Requires-Dist: scikit-image>=0.19.3; extra == "fm"
|
|
92
|
-
Requires-Dist: torchmetrics>=0.10.3; extra == "fm"
|
|
93
|
-
Requires-Dist: fvcore; extra == "fm"
|
|
94
|
-
Requires-Dist: iopath; extra == "fm"
|
|
95
|
-
Requires-Dist: webdataset; extra == "fm"
|
|
96
|
-
Requires-Dist: scikit-survival; extra == "fm"
|
|
97
|
-
Requires-Dist: scikit-learn; extra == "fm"
|
|
51
|
+
Requires-Dist: slide2vec[prism,prism2,titan,waiv]; extra == "fm"
|
|
98
52
|
Requires-Dist: fairscale; extra == "fm"
|
|
99
|
-
|
|
100
|
-
Requires-Dist:
|
|
101
|
-
Requires-Dist:
|
|
53
|
+
Provides-Extra: cpu
|
|
54
|
+
Requires-Dist: torch; extra == "cpu"
|
|
55
|
+
Requires-Dist: torchvision; extra == "cpu"
|
|
56
|
+
Provides-Extra: cu128
|
|
57
|
+
Requires-Dist: torch; extra == "cu128"
|
|
58
|
+
Requires-Dist: torchvision; extra == "cu128"
|
|
102
59
|
Provides-Extra: docs
|
|
103
60
|
Requires-Dist: sphinx>=8.1; extra == "docs"
|
|
104
61
|
Requires-Dist: furo; extra == "docs"
|
|
@@ -131,6 +88,10 @@ pip install slide2vec
|
|
|
131
88
|
|
|
132
89
|
Many models need additional dependencies available through `pip install "slide2vec[fm]"`. See the [model installation guide](https://clemsgrs.github.io/slide2vec/models.html#model-installation) for model-specific extras, separate environments, and upstream packages.
|
|
133
90
|
|
|
91
|
+
### Reproducible model environments
|
|
92
|
+
|
|
93
|
+
The repository ships a `uv.lock` file that pins every package for each extra. From a checkout, `uv sync --extra cu128 --extra fm` installs the exact locked stack (use `--extra cpu` for CPU-only PyTorch). Add `--extra flash-attn` for PRISM2 and `gigapath-slide`. The Docker image is built from the same lock; its `SLIDE2VEC_EXTRAS` build argument selects the extras, for example `--build-arg SLIDE2VEC_EXTRAS="cu128"` for the base install only.
|
|
94
|
+
|
|
134
95
|
For gated models such as Virchow2, request access on the model's Hugging Face page and authenticate with `hf auth login` or an `HF_TOKEN` environment variable.
|
|
135
96
|
|
|
136
97
|
## Embed a slide
|
|
@@ -15,6 +15,10 @@ pip install slide2vec
|
|
|
15
15
|
|
|
16
16
|
Many models need additional dependencies available through `pip install "slide2vec[fm]"`. See the [model installation guide](https://clemsgrs.github.io/slide2vec/models.html#model-installation) for model-specific extras, separate environments, and upstream packages.
|
|
17
17
|
|
|
18
|
+
### Reproducible model environments
|
|
19
|
+
|
|
20
|
+
The repository ships a `uv.lock` file that pins every package for each extra. From a checkout, `uv sync --extra cu128 --extra fm` installs the exact locked stack (use `--extra cpu` for CPU-only PyTorch). Add `--extra flash-attn` for PRISM2 and `gigapath-slide`. The Docker image is built from the same lock; its `SLIDE2VEC_EXTRAS` build argument selects the extras, for example `--build-arg SLIDE2VEC_EXTRAS="cu128"` for the base install only.
|
|
21
|
+
|
|
18
22
|
For gated models such as Virchow2, request access on the model's Hugging Face page and authenticate with `hf auth login` or an `HF_TOKEN` environment variable.
|
|
19
23
|
|
|
20
24
|
## Embed a slide
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "slide2vec"
|
|
7
|
+
version = "7.1.2"
|
|
8
|
+
description = "Embedding of whole slide images with Foundation Models"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "Apache-2.0"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Clément Grisi", email = "clement.grisi@radboudumc.nl" },
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
]
|
|
22
|
+
dependencies = [
|
|
23
|
+
"hs2p[asap,cucim,openslide,sam2,tifffile,vips]>=6.1.0",
|
|
24
|
+
"omegaconf",
|
|
25
|
+
"matplotlib",
|
|
26
|
+
"pandas",
|
|
27
|
+
"pillow",
|
|
28
|
+
"rich",
|
|
29
|
+
"torch",
|
|
30
|
+
"torchvision",
|
|
31
|
+
# <5: Hibou's remote code imports transformers.onnx and
|
|
32
|
+
# pytorch_utils.find_pruneable_heads_and_indices, both removed in v5.
|
|
33
|
+
# >=4.47: PRISM's configuring_prism.py needs sub_configs / base_config_key.
|
|
34
|
+
"transformers>=4.47,<5",
|
|
35
|
+
"wandb",
|
|
36
|
+
"einops",
|
|
37
|
+
# TITAN remote code (used by the built-in conchv15 preset) imports it.
|
|
38
|
+
"einops-exts",
|
|
39
|
+
# first release that registers vit_base_patch16_dinov3
|
|
40
|
+
"timm>=1.0.20",
|
|
41
|
+
"huggingface_hub",
|
|
42
|
+
"packaging",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.urls]
|
|
46
|
+
Homepage = "https://github.com/clemsgrs/slide2vec"
|
|
47
|
+
"Bug Tracker" = "https://github.com/clemsgrs/slide2vec/issues"
|
|
48
|
+
|
|
49
|
+
[project.scripts]
|
|
50
|
+
slide2vec = "slide2vec.cli:entrypoint"
|
|
51
|
+
|
|
52
|
+
# Rule for extras: lower bounds only. An exact or upper pin is kept only when it
|
|
53
|
+
# is structural (the model's code breaks outside it); the reason sits next to
|
|
54
|
+
# the pin. A model whose stack conflicts with the shared stack gets its own
|
|
55
|
+
# extra and a uv conflict below. numpy<2 comes from hs2p[asap].
|
|
56
|
+
[project.optional-dependencies]
|
|
57
|
+
prism = [
|
|
58
|
+
# perceiver.py calls torch.nn.attention.sdpa_kernel with a list (absent in 2.2)
|
|
59
|
+
"torch>=2.3",
|
|
60
|
+
"environs",
|
|
61
|
+
"sacremoses",
|
|
62
|
+
]
|
|
63
|
+
prism2 = [
|
|
64
|
+
"torch>=2.3",
|
|
65
|
+
# the text (diagnostic) variant calls a Phi3Model helper whose signature
|
|
66
|
+
# changed in 4.52 and which 4.53 removed; slide2vec.encoders.models.prism2
|
|
67
|
+
# vendors it, so no upper bound. PRISM2 also imports flash_attn: add the
|
|
68
|
+
# flash-attn extra (CUDA only, so not pulled in here)
|
|
69
|
+
"transformers>=4.51,<5",
|
|
70
|
+
]
|
|
71
|
+
titan = [
|
|
72
|
+
"einops",
|
|
73
|
+
"einops-exts",
|
|
74
|
+
"timm>=1.0.3",
|
|
75
|
+
"transformers>=4.46,<5",
|
|
76
|
+
]
|
|
77
|
+
waiv = [
|
|
78
|
+
# phaet and mascaret load with zero missing keys at the pinned revisions
|
|
79
|
+
"transformers>=4.57",
|
|
80
|
+
]
|
|
81
|
+
# gigapath-slide needs flash-attn on GPUs with compute capability above 7
|
|
82
|
+
flash-attn = [
|
|
83
|
+
"flash-attn>=2.6.3",
|
|
84
|
+
]
|
|
85
|
+
xformers = [
|
|
86
|
+
# optional: PRISM's PERCEIVER_MEM_EFF_ATTN path; gigapath-slide needs it
|
|
87
|
+
# on GPUs with compute capability 7 or below. Unpinned: each xformers
|
|
88
|
+
# build pins its torch.
|
|
89
|
+
"xformers",
|
|
90
|
+
]
|
|
91
|
+
fm = [
|
|
92
|
+
"slide2vec[prism,prism2,titan,waiv]",
|
|
93
|
+
# hard import in MUSK and gigapath-slide
|
|
94
|
+
"fairscale",
|
|
95
|
+
]
|
|
96
|
+
# torch wheel flavour for uv; when neither is selected torch comes from PyPI
|
|
97
|
+
cpu = [
|
|
98
|
+
"torch",
|
|
99
|
+
"torchvision",
|
|
100
|
+
]
|
|
101
|
+
cu128 = [
|
|
102
|
+
"torch",
|
|
103
|
+
"torchvision",
|
|
104
|
+
]
|
|
105
|
+
docs = [
|
|
106
|
+
"sphinx>=8.1",
|
|
107
|
+
"furo",
|
|
108
|
+
"myst-parser",
|
|
109
|
+
"sphinx-copybutton",
|
|
110
|
+
"sphinx-autodoc-typehints",
|
|
111
|
+
]
|
|
112
|
+
testing = [
|
|
113
|
+
"pytest>=6.0",
|
|
114
|
+
"pytest-cov>=2.0",
|
|
115
|
+
"mypy>=0.910",
|
|
116
|
+
"flake8>=3.9",
|
|
117
|
+
"flake8-pyproject>=1.2.3",
|
|
118
|
+
"tox>=3.24",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
[dependency-groups]
|
|
122
|
+
dev = [
|
|
123
|
+
"pytest>=6.0",
|
|
124
|
+
"pytest-cov>=2.0",
|
|
125
|
+
"mypy>=0.910",
|
|
126
|
+
"flake8>=3.9",
|
|
127
|
+
"flake8-pyproject>=1.2.3",
|
|
128
|
+
"tox>=3.24",
|
|
129
|
+
# tests/test_encoder_plugins.py installs a plugin with `python -m pip`;
|
|
130
|
+
# uv environments ship without pip
|
|
131
|
+
"pip",
|
|
132
|
+
]
|
|
133
|
+
docs = [
|
|
134
|
+
"sphinx>=8.1",
|
|
135
|
+
"furo",
|
|
136
|
+
"myst-parser",
|
|
137
|
+
"sphinx-copybutton",
|
|
138
|
+
"sphinx-autodoc-typehints",
|
|
139
|
+
]
|
|
140
|
+
# encoder packages that are not on PyPI (musk, conch, gigapath-slide, sam2)
|
|
141
|
+
models-git = [
|
|
142
|
+
"musk",
|
|
143
|
+
"conch",
|
|
144
|
+
"gigapath",
|
|
145
|
+
"sam-2",
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
[tool.uv]
|
|
149
|
+
# ASAP and cucim are Linux-only. numpy<2 (from hs2p[asap]) has no wheels for
|
|
150
|
+
# Python 3.13+, so the lock covers Python 3.10 to 3.12.
|
|
151
|
+
environments = ["sys_platform == 'linux' and python_full_version < '3.13'"]
|
|
152
|
+
# Lock-only: the cu128 index stops at torch 2.11. Hold the CPU and PyPI forks at
|
|
153
|
+
# the same release so the CPU CI image tests the torch the CUDA image ships.
|
|
154
|
+
# This is not part of the published metadata.
|
|
155
|
+
constraint-dependencies = ["torch<2.12"]
|
|
156
|
+
conflicts = [
|
|
157
|
+
[
|
|
158
|
+
{ extra = "cpu" },
|
|
159
|
+
{ extra = "cu128" },
|
|
160
|
+
],
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
# flash-attn ships only an sdist whose build imports torch; declare its metadata
|
|
164
|
+
# so uv can lock it without building it
|
|
165
|
+
[[tool.uv.dependency-metadata]]
|
|
166
|
+
name = "flash-attn"
|
|
167
|
+
requires-dist = ["torch", "einops"]
|
|
168
|
+
|
|
169
|
+
# flash-attn's setup.py imports torch, packaging, wheel and psutil. Build it in
|
|
170
|
+
# an isolated environment that holds the same torch as the runtime environment.
|
|
171
|
+
[tool.uv.extra-build-dependencies]
|
|
172
|
+
flash-attn = [
|
|
173
|
+
{ requirement = "torch", match-runtime = true },
|
|
174
|
+
"packaging",
|
|
175
|
+
"psutil",
|
|
176
|
+
"wheel",
|
|
177
|
+
"ninja",
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
[tool.uv.sources]
|
|
181
|
+
torch = [
|
|
182
|
+
{ index = "pytorch-cpu", extra = "cpu" },
|
|
183
|
+
{ index = "pytorch-cu128", extra = "cu128" },
|
|
184
|
+
]
|
|
185
|
+
torchvision = [
|
|
186
|
+
{ index = "pytorch-cpu", extra = "cpu" },
|
|
187
|
+
{ index = "pytorch-cu128", extra = "cu128" },
|
|
188
|
+
]
|
|
189
|
+
musk = { git = "https://github.com/lilab-stanford/MUSK.git" }
|
|
190
|
+
conch = { git = "https://github.com/Mahmoodlab/CONCH.git" }
|
|
191
|
+
gigapath = { git = "https://github.com/prov-gigapath/prov-gigapath.git" }
|
|
192
|
+
sam-2 = { git = "https://github.com/facebookresearch/sam2.git" }
|
|
193
|
+
|
|
194
|
+
[[tool.uv.index]]
|
|
195
|
+
name = "pytorch-cpu"
|
|
196
|
+
url = "https://download.pytorch.org/whl/cpu"
|
|
197
|
+
explicit = true
|
|
198
|
+
|
|
199
|
+
[[tool.uv.index]]
|
|
200
|
+
name = "pytorch-cu128"
|
|
201
|
+
url = "https://download.pytorch.org/whl/cu128"
|
|
202
|
+
explicit = true
|
|
203
|
+
|
|
204
|
+
[tool.setuptools]
|
|
205
|
+
include-package-data = true
|
|
206
|
+
zip-safe = false
|
|
207
|
+
|
|
208
|
+
[tool.setuptools.packages.find]
|
|
209
|
+
include = ["slide2vec*"]
|
|
210
|
+
|
|
211
|
+
[tool.setuptools.package-data]
|
|
212
|
+
slide2vec = ["py.typed", "cufile.json"]
|
|
213
|
+
"slide2vec.configs" = ["*.yaml"]
|
|
214
|
+
|
|
215
|
+
[tool.pytest.ini_options]
|
|
216
|
+
addopts = "--cov=slide2vec -m 'not gpu_integration'"
|
|
217
|
+
testpaths = [
|
|
218
|
+
"tests",
|
|
219
|
+
]
|
|
220
|
+
markers = [
|
|
221
|
+
"heavy: real-weight foundation-model inference on CPU; minutes per test. Included in the PR suite and available separately through the manual heavy workflow (.github/workflows/nightly-heavy.yaml).",
|
|
222
|
+
"gpu_integration: real one-GPU versus multi-GPU parity; requires at least two visible CUDA devices. Run explicitly with `CUDA_VISIBLE_DEVICES=0,1 python -m pytest -m gpu_integration --no-cov`.",
|
|
223
|
+
]
|
|
224
|
+
|
|
225
|
+
[tool.mypy]
|
|
226
|
+
mypy_path = "."
|
|
227
|
+
check_untyped_defs = true
|
|
228
|
+
disallow_any_generics = true
|
|
229
|
+
ignore_missing_imports = true
|
|
230
|
+
no_implicit_optional = true
|
|
231
|
+
show_error_codes = true
|
|
232
|
+
strict_equality = true
|
|
233
|
+
warn_redundant_casts = true
|
|
234
|
+
warn_return_any = true
|
|
235
|
+
warn_unreachable = true
|
|
236
|
+
warn_unused_configs = true
|
|
237
|
+
no_implicit_reexport = true
|
|
238
|
+
|
|
239
|
+
[tool.flake8]
|
|
240
|
+
max-line-length = 160
|
|
241
|
+
|
|
242
|
+
[tool.bumpver]
|
|
243
|
+
current_version = "7.1.2"
|
|
244
|
+
version_pattern = "MAJOR.MINOR.PATCH"
|
|
245
|
+
commit = false
|
|
246
|
+
tag = false
|
|
247
|
+
push = false
|
|
248
|
+
files = [
|
|
249
|
+
"pyproject.toml",
|
|
250
|
+
"slide2vec/__init__.py",
|
|
251
|
+
"docs/conf.py",
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
[tool.bumpver.file_patterns]
|
|
255
|
+
"pyproject.toml" = [
|
|
256
|
+
'^current_version = "{version}"$',
|
|
257
|
+
'^version = "{version}"$',
|
|
258
|
+
]
|
|
259
|
+
"slide2vec/__init__.py" = [
|
|
260
|
+
'^__version__ = "{version}"$',
|
|
261
|
+
]
|
|
262
|
+
"docs/conf.py" = [
|
|
263
|
+
'^release = "{version}"$',
|
|
264
|
+
]
|
|
@@ -7,7 +7,7 @@ import warnings
|
|
|
7
7
|
from dataclasses import dataclass, field, replace
|
|
8
8
|
from contextlib import contextmanager
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Any, Callable, Mapping, Protocol, Sequence
|
|
10
|
+
from typing import Any, Callable, Mapping, NamedTuple, Protocol, Sequence
|
|
11
11
|
|
|
12
12
|
import torch
|
|
13
13
|
from hs2p import (
|
|
@@ -261,9 +261,12 @@ class ExecutionOptions:
|
|
|
261
261
|
#: (capped by CPU / SLURM limit, then split across the resolved GPU count).
|
|
262
262
|
#: Image-only routes safely use zero when auto selection happens after model loading.
|
|
263
263
|
num_workers_per_gpu: int | None = None
|
|
264
|
-
#: Tiling worker count. ``None`` means auto
|
|
264
|
+
#: Tiling worker count. ``None`` means auto. The field keeps the requested value;
|
|
265
|
+
#: :meth:`resolved_num_preprocessing_workers` gives the count a run uses, capped by the
|
|
266
|
+
#: CPU / SLURM limit.
|
|
265
267
|
num_preprocessing_workers: int | None = None
|
|
266
|
-
#: Number of GPUs to use. ``None``
|
|
268
|
+
#: Number of GPUs to use. ``None`` means all available GPUs. The field keeps the
|
|
269
|
+
#: requested value; :meth:`resolved_num_gpus` gives the count a run uses.
|
|
267
270
|
num_gpus: int | None = None
|
|
268
271
|
#: Forward-pass dtype — ``"fp16"``, ``"bf16"``, ``"fp32"``,
|
|
269
272
|
#: or ``None`` (auto-determined from the model preset).
|
|
@@ -313,11 +316,9 @@ class ExecutionOptions:
|
|
|
313
316
|
)
|
|
314
317
|
|
|
315
318
|
def __post_init__(self) -> None:
|
|
316
|
-
resolved_num_gpus = _default_num_gpus() if self.num_gpus is None else self.num_gpus
|
|
317
|
-
object.__setattr__(self, "num_gpus", resolved_num_gpus)
|
|
318
319
|
object.__setattr__(self, "precision", normalize_precision_name(self.precision))
|
|
319
320
|
object.__setattr__(self, "output_dtype", normalize_output_dtype(self.output_dtype))
|
|
320
|
-
if
|
|
321
|
+
if self.num_gpus is not None and self.num_gpus < 1:
|
|
321
322
|
raise ValueError("ExecutionOptions.num_gpus must be at least 1")
|
|
322
323
|
if self.batch_size < 1:
|
|
323
324
|
raise ValueError("ExecutionOptions.batch_size must be at least 1")
|
|
@@ -325,40 +326,62 @@ class ExecutionOptions:
|
|
|
325
326
|
raise ValueError("ExecutionOptions.prefetch_factor must be at least 1")
|
|
326
327
|
if self.num_workers_per_gpu is not None and self.num_workers_per_gpu < 0:
|
|
327
328
|
raise ValueError("ExecutionOptions.num_workers_per_gpu must be non-negative")
|
|
329
|
+
if self.num_preprocessing_workers is not None and self.num_preprocessing_workers < 0:
|
|
330
|
+
raise ValueError("ExecutionOptions.num_preprocessing_workers must be non-negative")
|
|
328
331
|
if self.on_image_mismatch not in IMAGE_MISMATCH_POLICIES:
|
|
329
332
|
raise ValueError(
|
|
330
333
|
"ExecutionOptions.on_image_mismatch must be one of "
|
|
331
334
|
f"{', '.join(repr(policy) for policy in IMAGE_MISMATCH_POLICIES)}; "
|
|
332
335
|
f"got {self.on_image_mismatch!r}"
|
|
333
336
|
)
|
|
337
|
+
|
|
338
|
+
def resolved_num_gpus(self) -> int:
|
|
339
|
+
"""GPU count a run uses: :attr:`num_gpus`, or every visible GPU when it is ``None``."""
|
|
340
|
+
return self._machine_resolution.num_gpus
|
|
341
|
+
|
|
342
|
+
def resolved_num_preprocessing_workers(self) -> int:
|
|
343
|
+
"""Tiling worker count a run uses: :attr:`num_preprocessing_workers`, or the CPU /
|
|
344
|
+
SLURM limit when it is ``None``, and never more than that limit."""
|
|
345
|
+
return self._machine_resolution.num_preprocessing_workers
|
|
346
|
+
|
|
347
|
+
def resolved_num_workers_per_gpu(self) -> int:
|
|
348
|
+
if self.num_workers_per_gpu is not None:
|
|
349
|
+
return self.num_workers_per_gpu
|
|
350
|
+
return max(1, cpu_worker_limit() // self.resolved_num_gpus())
|
|
351
|
+
|
|
352
|
+
@functools.cached_property
|
|
353
|
+
def _machine_resolution(self) -> "_MachineResolution":
|
|
354
|
+
# Resolved once per instance, on first use, so every reader in a run agrees and the
|
|
355
|
+
# log line is written once. The cache sits outside the dataclass fields: equality,
|
|
356
|
+
# hashing and repr see only what the caller asked for.
|
|
357
|
+
num_gpus = _default_num_gpus() if self.num_gpus is None else self.num_gpus
|
|
334
358
|
cap = cpu_worker_limit()
|
|
335
|
-
|
|
336
|
-
slurm_limit = slurm_cpu_limit()
|
|
337
|
-
capped_num_preprocessing_workers = (
|
|
359
|
+
num_preprocessing_workers = (
|
|
338
360
|
cap if self.num_preprocessing_workers is None else min(self.num_preprocessing_workers, cap)
|
|
339
361
|
)
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
f"{
|
|
346
|
-
if self.num_workers_per_gpu is None
|
|
347
|
-
else str(resolved_num_workers)
|
|
362
|
+
num_workers_per_gpu = (
|
|
363
|
+
max(1, cap // num_gpus) if self.num_workers_per_gpu is None else self.num_workers_per_gpu
|
|
364
|
+
)
|
|
365
|
+
slurm_limit = slurm_cpu_limit()
|
|
366
|
+
cap_source = (
|
|
367
|
+
f"slurm_cpu_limit={slurm_limit}" if slurm_limit is not None else f"cpu_count={os.cpu_count() or 1}"
|
|
348
368
|
)
|
|
349
|
-
|
|
350
|
-
"ExecutionOptions: num_workers_per_gpu=%s, num_preprocessing_workers=%d "
|
|
369
|
+
logging.getLogger(__name__).info(
|
|
370
|
+
"ExecutionOptions: num_gpus=%s, num_workers_per_gpu=%s, num_preprocessing_workers=%d "
|
|
351
371
|
"(preprocessing cap=%d via %s)",
|
|
352
|
-
|
|
353
|
-
|
|
372
|
+
_auto_label(num_gpus, requested=self.num_gpus),
|
|
373
|
+
_auto_label(num_workers_per_gpu, requested=self.num_workers_per_gpu),
|
|
374
|
+
num_preprocessing_workers,
|
|
354
375
|
cap,
|
|
355
376
|
cap_source,
|
|
356
377
|
)
|
|
378
|
+
return _MachineResolution(num_gpus=num_gpus, num_preprocessing_workers=num_preprocessing_workers)
|
|
357
379
|
|
|
358
|
-
def
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
380
|
+
def __getstate__(self) -> dict[str, Any]:
|
|
381
|
+
# A copy or an unpickled instance resolves again on the machine that uses it.
|
|
382
|
+
state = dict(self.__dict__)
|
|
383
|
+
state.pop("_machine_resolution", None)
|
|
384
|
+
return state
|
|
362
385
|
|
|
363
386
|
def resolved_image_num_workers_per_gpu(self) -> int:
|
|
364
387
|
"""Resolve safe post-model-load image-transform workers for this rank."""
|
|
@@ -898,8 +921,9 @@ class Model:
|
|
|
898
921
|
The dense counterpart of the pooled coordinate path: each ``SlideRegions`` names a
|
|
899
922
|
slide + a set of level-0 ROI coordinates, and every ROI is read, encoded through the
|
|
900
923
|
dense transform, and written to ``dense_embeddings/[<class>/]<sample_id>/<x>_<y>.pt``
|
|
901
|
-
plus a geometry sidecar. The run splits its ROIs across
|
|
902
|
-
|
|
924
|
+
plus a geometry sidecar. The run splits its ROIs across
|
|
925
|
+
``execution.resolved_num_gpus()`` GPUs (all visible GPUs by default);
|
|
926
|
+
``num_gpus=1`` encodes fully in-process. Resume is
|
|
903
927
|
automatic — only ROIs whose sidecar has the same source-spacing declaration,
|
|
904
928
|
resolved hs2p read plan and feature identity are skipped. Returns one
|
|
905
929
|
:class:`~slide2vec.artifacts.DenseRegionArtifact` per input ROI.
|
|
@@ -932,10 +956,10 @@ class Model:
|
|
|
932
956
|
**normalization-only** transform, padded up to the encoder's patch multiple, encoded
|
|
933
957
|
— whole-image, or by sliding the encoder's native field and blending the token grids
|
|
934
958
|
— and written to ``dense_image_embeddings/<sample_id>.pt`` plus a geometry sidecar.
|
|
935
|
-
The run splits its images across
|
|
936
|
-
``num_gpus=1`` encodes fully in-process. Resume is
|
|
937
|
-
only when its payload exists and its sidecar records
|
|
938
|
-
identity and complete extraction recipe. Returns one
|
|
959
|
+
The run splits its images across ``execution.resolved_num_gpus()`` GPUs (all
|
|
960
|
+
visible GPUs by default); ``num_gpus=1`` encodes fully in-process. Resume is
|
|
961
|
+
automatic: an image is skipped only when its payload exists and its sidecar records
|
|
962
|
+
the same normalized source identity and complete extraction recipe. Returns one
|
|
939
963
|
:class:`~slide2vec.artifacts.DenseImageArtifact` per input image, in input order.
|
|
940
964
|
|
|
941
965
|
Every source is opened through hs2p. PNG/JPEG inputs use hs2p's one-level PIL reader
|
|
@@ -977,12 +1001,12 @@ class Model:
|
|
|
977
1001
|
set — and slide2vec neither tiles nor reads a slide. Each :class:`ImageSpec` is
|
|
978
1002
|
decoded, preprocessed with the encoder's **shipped** transform, encoded, and written
|
|
979
1003
|
to ``image_embeddings/<sample_id>.pt`` plus a provenance sidecar. The run splits its
|
|
980
|
-
images across all visible GPUs
|
|
981
|
-
fully in-process. Resume is automatic: an image is
|
|
982
|
-
records this ``output_format``, the requested
|
|
983
|
-
identity. A different recorded feature identity
|
|
984
|
-
``image_path`` raises unless
|
|
985
|
-
provenance re-encodes. Returns
|
|
1004
|
+
images across ``execution.resolved_num_gpus()`` GPUs (all visible GPUs by
|
|
1005
|
+
default); ``num_gpus=1`` encodes fully in-process. Resume is automatic: an image is
|
|
1006
|
+
skipped only when its sidecar records this ``output_format``, the requested
|
|
1007
|
+
``image_path`` and the full feature identity. A different recorded feature identity
|
|
1008
|
+
raises; a different recorded ``image_path`` raises unless
|
|
1009
|
+
``execution.on_image_mismatch="reencode"``; missing provenance re-encodes. Returns
|
|
986
1010
|
one :class:`~slide2vec.artifacts.ImageEmbeddingArtifact` per input image, in input
|
|
987
1011
|
order.
|
|
988
1012
|
|
|
@@ -1342,6 +1366,15 @@ def _default_num_gpus() -> int:
|
|
|
1342
1366
|
return max(1, torch.cuda.device_count()) if torch.cuda.is_available() else 1
|
|
1343
1367
|
|
|
1344
1368
|
|
|
1369
|
+
class _MachineResolution(NamedTuple):
|
|
1370
|
+
num_gpus: int
|
|
1371
|
+
num_preprocessing_workers: int
|
|
1372
|
+
|
|
1373
|
+
|
|
1374
|
+
def _auto_label(resolved: int, *, requested: int | None) -> str:
|
|
1375
|
+
return f"{resolved} (requested=auto)" if requested is None else str(resolved)
|
|
1376
|
+
|
|
1377
|
+
|
|
1345
1378
|
def _require_output_dir_for_persistence(execution: ExecutionOptions, *, method_name: str) -> None:
|
|
1346
1379
|
if execution.output_dir is None:
|
|
1347
1380
|
raise ValueError(f"ExecutionOptions.output_dir is required for {method_name}")
|
|
@@ -36,9 +36,7 @@ def require_timm_for_dinov3() -> None:
|
|
|
36
36
|
if Version(timm.__version__) < Version(_MIN_TIMM_VERSION):
|
|
37
37
|
raise ImportError(
|
|
38
38
|
f"dinov3-vitb16 requires timm>={_MIN_TIMM_VERSION} (the DINOv3 "
|
|
39
|
-
f"architectures were added in that release); found timm=={timm.__version__}.
|
|
40
|
-
"Note that the 'titan' extra pins timm==1.0.3 and cannot be installed "
|
|
41
|
-
"alongside dinov3-vitb16."
|
|
39
|
+
f"architectures were added in that release); found timm=={timm.__version__}."
|
|
42
40
|
)
|
|
43
41
|
|
|
44
42
|
|
|
@@ -79,7 +79,7 @@ def load_moozy_inference_components(device: torch.device) -> MoozyInferenceCompo
|
|
|
79
79
|
except Exception as exc:
|
|
80
80
|
raise RuntimeError(
|
|
81
81
|
"huggingface_hub is required for bundled MOOZY inference support. "
|
|
82
|
-
"Install it with: pip install
|
|
82
|
+
"Install it with: pip install huggingface_hub"
|
|
83
83
|
) from exc
|
|
84
84
|
|
|
85
85
|
checkpoint_path = hf_hub_download(repo_id=_HF_REPO_ID, filename=_HF_CHECKPOINT)
|