slide2vec 7.1.1__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.1 → slide2vec-7.1.2}/PKG-INFO +29 -68
- {slide2vec-7.1.1 → slide2vec-7.1.2}/README.md +4 -0
- slide2vec-7.1.2/pyproject.toml +264 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/__init__.py +1 -1
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/dinov3.py +1 -3
- {slide2vec-7.1.1 → 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.1 → slide2vec-7.1.2}/slide2vec.egg-info/PKG-INFO +29 -68
- slide2vec-7.1.2/slide2vec.egg-info/requires.txt +66 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_isight.py +1 -1
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_mascaret.py +5 -3
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_phaet.py +5 -3
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_prism2.py +95 -0
- slide2vec-7.1.1/pyproject.toml +0 -204
- slide2vec-7.1.1/slide2vec/encoders/models/prism2.py +0 -94
- slide2vec-7.1.1/slide2vec.egg-info/requires.txt +0 -109
- {slide2vec-7.1.1 → slide2vec-7.1.2}/LICENSE +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/setup.cfg +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/__main__.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/_cufile.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/api.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/artifacts.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/cli.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/configs/__init__.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/configs/default.yaml +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/configs/resources.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/cufile.json +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/data/__init__.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/data/dataset.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/data/tile_reader.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/data/tile_store.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/distributed/__init__.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/distributed/dense_image_worker.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/distributed/dense_worker.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/distributed/direct_embed_worker.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/distributed/image_worker.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/distributed/pin_gpu.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/distributed/pipeline_worker.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/distributed/worker_entry.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/__init__.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/base.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/__init__.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/conch.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/dinov2.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/genbio.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/gigapath.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/gpfm.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/hibou.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/hoptimus.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/isight.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/lunit.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/mettle.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/midnight.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/moozy/__init__.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/moozy/blocks.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/moozy/case.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/moozy/slide.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/moozy/types.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/mstar.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/musk.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/phikon.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/prism.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/prost40m.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/rudolfv2.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/titan.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/uni.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/virchow.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/models/waiv.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/registry.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/encoders/validation.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/inference.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/progress.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/__init__.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/artifacts_collect.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/batching.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/cpu_budget.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/dense_encode.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/dense_encoder_input.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/dense_image_reading.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/dense_image_recipe.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/dense_image_shard.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/dense_image_stage.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/dense_regions.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/dense_shard.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/dense_sliding.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/dense_stage.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/distributed.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/distributed_stage.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/effective_encoder_input.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/embedding.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/embedding_persist.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/embedding_pipeline.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/encoder_input_contract.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/feature_identity.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/hierarchical.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/image_shard.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/image_specs.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/image_stage.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/manifest.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/model_settings.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/patient_pipeline.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/persist_callbacks.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/persistence.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/pooled_encoder_input.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/preprocessing.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/process_list.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/progress_bridge.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/registry.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/serialization.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/sharding.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/slide_encode.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/tiling.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/tiling_pipeline.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/types.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/runtime/worker_io.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/utils/__init__.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/utils/config.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/utils/coordinates.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/utils/log_utils.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/utils/tiling_io.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec/utils/utils.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec.egg-info/SOURCES.txt +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec.egg-info/dependency_links.txt +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec.egg-info/entry_points.txt +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec.egg-info/not-zip-safe +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/slide2vec.egg-info/top_level.txt +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_attention_extraction.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_benchmark_tooling.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_cli_noninteractive.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_cufile_compat_mode.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_encode_kit.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_encoder_input.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_extraction.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_image_reading.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_image_resume.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_image_shard.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_image_stage.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_regions.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_shard.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_sliding.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_source_spacing.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_stage.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dense_worker.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dinov2_natimage.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_dinov3_natimage.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_drop_in_encoders.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_encoder_capabilities.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_encoder_input_contract.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_encoder_plugins.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_encoder_preprocessing.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_encoder_provider_failures.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_encoder_registry.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_execution_options.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_feature_identity.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_gigapath.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_gpfm_genbio_heavy.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_hierarchical_encoding.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_hs2p5_integration.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_hs2p_package_cutover.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_image_shard.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_image_stage.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_inference_benchmark.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_load_model_hf_auth.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_mettle.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_on_slide_persisted.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_output_consistency.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_patch_size_metadata.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_patient_annotation_sampling.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_patient_manifest.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_patient_tiling_only.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_pooled_encoder_input.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_pooled_geometry.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_pooled_identity_api.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_prism.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_process_list_performance.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_progress.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_regression_core.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_regression_inference.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_regression_models.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_rudolfv2.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_runtime_batching.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_sharding.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_slide_coordinate_preparation.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_slide_latents.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_soma_migration.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_tile_store.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_tiling_pipeline.py +0 -0
- {slide2vec-7.1.1 → slide2vec-7.1.2}/tests/test_titan.py +0 -0
- {slide2vec-7.1.1 → 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
|
+
]
|
|
@@ -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)
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""PRISM2 slide encoder implementation."""
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
from transformers import AutoModel, AutoProcessor
|
|
5
|
+
from transformers.models.phi3.modeling_phi3 import Phi3Model
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from transformers.cache_utils import SlidingWindowCache
|
|
9
|
+
except ImportError: # pragma: no cover - older transformers
|
|
10
|
+
SlidingWindowCache = () # type: ignore[assignment,misc]
|
|
11
|
+
|
|
12
|
+
from slide2vec.encoders.base import (
|
|
13
|
+
SlideEncoder,
|
|
14
|
+
preferred_default_device,
|
|
15
|
+
resolve_requested_output_variant,
|
|
16
|
+
)
|
|
17
|
+
from slide2vec.encoders.registry import register_encoder
|
|
18
|
+
|
|
19
|
+
PRISM2_MODEL_ID = "paige-ai/Prism2"
|
|
20
|
+
PRISM2_REVISION = "450352d0ddc6b42b21ce20794ce0fbefe6b5a47a"
|
|
21
|
+
_PHI3_MASK_HELPER = "_prepare_4d_causal_attention_mask_with_cache_position"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _prepare_4d_causal_attention_mask_with_cache_position(
|
|
25
|
+
attention_mask: torch.Tensor | None,
|
|
26
|
+
sequence_length: int,
|
|
27
|
+
target_length: int,
|
|
28
|
+
dtype: torch.dtype,
|
|
29
|
+
cache_position: torch.Tensor,
|
|
30
|
+
batch_size: int,
|
|
31
|
+
device: torch.device | None = None,
|
|
32
|
+
config=None,
|
|
33
|
+
past_key_values=None,
|
|
34
|
+
**_: object,
|
|
35
|
+
) -> torch.Tensor | None:
|
|
36
|
+
"""``Phi3Model._prepare_4d_causal_attention_mask_with_cache_position`` as
|
|
37
|
+
shipped by transformers 4.52.4, accepting the ``device`` keyword of 4.51.
|
|
38
|
+
|
|
39
|
+
The pinned PRISM2 remote code (written against 4.51.3) calls it with
|
|
40
|
+
``device=`` from the text decoder, which only the ``diagnostic`` output
|
|
41
|
+
variant runs; the ``base`` variant never reaches it. 4.52 dropped the
|
|
42
|
+
``device`` parameter and 4.53 removed the helper, so this one is installed
|
|
43
|
+
on ``Phi3Model`` for every transformers version. Returns a
|
|
44
|
+
``(batch_size, 1, sequence_length, target_length)`` mask in ``dtype``: 0
|
|
45
|
+
where a query may attend, the dtype minimum elsewhere. A 4D
|
|
46
|
+
``attention_mask`` is returned as is.
|
|
47
|
+
"""
|
|
48
|
+
if attention_mask is not None and attention_mask.dim() == 4:
|
|
49
|
+
return attention_mask
|
|
50
|
+
if device is None:
|
|
51
|
+
device = cache_position.device
|
|
52
|
+
min_dtype = torch.finfo(dtype).min
|
|
53
|
+
causal_mask = torch.full(
|
|
54
|
+
(sequence_length, target_length), fill_value=min_dtype, dtype=dtype, device=device
|
|
55
|
+
)
|
|
56
|
+
positions = torch.arange(target_length, device=device)
|
|
57
|
+
diagonal_attend_mask = positions > cache_position.reshape(-1, 1)
|
|
58
|
+
text_config = config.get_text_config() if config is not None else None
|
|
59
|
+
sliding_window = getattr(text_config, "sliding_window", None)
|
|
60
|
+
if getattr(text_config, "use_sliding_window", True) and sliding_window is not None:
|
|
61
|
+
if not isinstance(past_key_values, SlidingWindowCache) or sequence_length > target_length:
|
|
62
|
+
sliding_attend_mask = positions <= (cache_position.reshape(-1, 1) - sliding_window)
|
|
63
|
+
diagonal_attend_mask.bitwise_or_(sliding_attend_mask)
|
|
64
|
+
causal_mask *= diagonal_attend_mask
|
|
65
|
+
causal_mask = causal_mask[None, None, :, :].expand(batch_size, 1, -1, -1)
|
|
66
|
+
if attention_mask is not None:
|
|
67
|
+
causal_mask = causal_mask.clone()
|
|
68
|
+
if attention_mask.shape[-1] > target_length:
|
|
69
|
+
attention_mask = attention_mask[:, :target_length]
|
|
70
|
+
mask_length = attention_mask.shape[-1]
|
|
71
|
+
padding_mask = causal_mask[:, :, :, :mask_length] + attention_mask[:, None, None, :].to(device)
|
|
72
|
+
padding_mask = padding_mask == 0
|
|
73
|
+
causal_mask[:, :, :, :mask_length] = causal_mask[:, :, :, :mask_length].masked_fill(
|
|
74
|
+
padding_mask, min_dtype
|
|
75
|
+
)
|
|
76
|
+
return causal_mask
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def install_phi3_mask_helper() -> None:
|
|
80
|
+
"""Give ``Phi3Model`` the mask helper the PRISM2 remote code calls.
|
|
81
|
+
|
|
82
|
+
Installed unconditionally: 4.51 ships a compatible helper, 4.52 ships one
|
|
83
|
+
that rejects the ``device`` keyword PRISM2 passes, and 4.53 ships none.
|
|
84
|
+
``Phi3Model`` itself calls the helper with keywords only, so the vendored
|
|
85
|
+
one serves every version.
|
|
86
|
+
"""
|
|
87
|
+
setattr(
|
|
88
|
+
Phi3Model,
|
|
89
|
+
_PHI3_MASK_HELPER,
|
|
90
|
+
staticmethod(_prepare_4d_causal_attention_mask_with_cache_position),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@register_encoder(
|
|
95
|
+
"prism2",
|
|
96
|
+
level="slide",
|
|
97
|
+
tile_encoder="virchow2",
|
|
98
|
+
tile_encoder_output_variant="cls",
|
|
99
|
+
output_variants={
|
|
100
|
+
"base": {"encode_dim": 2560},
|
|
101
|
+
"diagnostic": {"encode_dim": 3072},
|
|
102
|
+
},
|
|
103
|
+
default_output_variant="base",
|
|
104
|
+
supported_spacing_um=0.5,
|
|
105
|
+
precision="bf16",
|
|
106
|
+
source="paige-ai/Prism2",
|
|
107
|
+
)
|
|
108
|
+
class Prism2SlideEncoder(SlideEncoder):
|
|
109
|
+
def __init__(self, *, output_variant: str | None = None):
|
|
110
|
+
self._output_variant = resolve_requested_output_variant(
|
|
111
|
+
output_variant,
|
|
112
|
+
default="base",
|
|
113
|
+
allowed=("base", "diagnostic"),
|
|
114
|
+
)
|
|
115
|
+
install_phi3_mask_helper()
|
|
116
|
+
shared_load_kwargs = {
|
|
117
|
+
"revision": PRISM2_REVISION,
|
|
118
|
+
"trust_remote_code": True,
|
|
119
|
+
}
|
|
120
|
+
self._model = AutoModel.from_pretrained(
|
|
121
|
+
PRISM2_MODEL_ID,
|
|
122
|
+
**shared_load_kwargs,
|
|
123
|
+
torch_dtype="auto",
|
|
124
|
+
).eval()
|
|
125
|
+
self._processor = AutoProcessor.from_pretrained(
|
|
126
|
+
PRISM2_MODEL_ID,
|
|
127
|
+
**shared_load_kwargs,
|
|
128
|
+
)
|
|
129
|
+
self._device = preferred_default_device()
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def encode_dim(self) -> int:
|
|
133
|
+
return 2560 if self._output_variant == "base" else 3072
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def device(self) -> torch.device:
|
|
137
|
+
return self._device
|
|
138
|
+
|
|
139
|
+
def to(self, device: torch.device | str) -> "Prism2SlideEncoder":
|
|
140
|
+
self._device = torch.device(device)
|
|
141
|
+
if self._output_variant == "base":
|
|
142
|
+
# The pinned upstream get_base_embedding path uses image_resampler only.
|
|
143
|
+
# Keep the excluded Phi-3 decoder on CPU instead of spending GPU memory
|
|
144
|
+
# on text-generation parameters this base-only preset can never call.
|
|
145
|
+
self._model.image_resampler.to(self._device)
|
|
146
|
+
else:
|
|
147
|
+
diagnostic_modules = (
|
|
148
|
+
self._model.image_resampler,
|
|
149
|
+
self._model.img_projection,
|
|
150
|
+
self._model.text_decoder,
|
|
151
|
+
)
|
|
152
|
+
for module in diagnostic_modules:
|
|
153
|
+
if self._device.type == "cuda":
|
|
154
|
+
module.to(self._device, dtype=torch.bfloat16)
|
|
155
|
+
else:
|
|
156
|
+
module.to(self._device)
|
|
157
|
+
return self
|
|
158
|
+
|
|
159
|
+
def encode_slide(
|
|
160
|
+
self,
|
|
161
|
+
tile_features: torch.Tensor,
|
|
162
|
+
coordinates: torch.Tensor | None = None,
|
|
163
|
+
*,
|
|
164
|
+
tile_size_lv0: int | None = None,
|
|
165
|
+
) -> torch.Tensor:
|
|
166
|
+
batch = self._processor(tile_embeddings=[tile_features]).to(self._device)
|
|
167
|
+
embedding_method = (
|
|
168
|
+
self._model.get_base_embedding
|
|
169
|
+
if self._output_variant == "base"
|
|
170
|
+
else self._model.get_diagnostic_embedding
|
|
171
|
+
)
|
|
172
|
+
return embedding_method(**batch).squeeze(0)
|