Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@ pip install slide2vec

2. Create a configuration file

A good starting point is the default configuration file `slide2vec/configs/default.yaml` where parameters are documented.<br>
A good starting point are the default configuration files where parameters are documented:<br>
- for preprocessing options: `slide2vec/configs/default_tiling.yaml`
- for model options: `slide2vec/configs/default_model_.yaml`

We've also added default configuration files for each of the foundation models currently supported:
- tile-level: `uni`, `uni2`, `virchow`, `virchow2`, `prov-gigapath`, `h-optimus-0`, `h-optimus-1`, `h0-mini`, `conch`, `musk`, `phikonv2`, `hibou-b`, `hibou-L`, [`kaiko`](https://github.com/kaiko-ai/towards_large_pathology_fms)
- tile-level: `uni`, `uni2`, `virchow`, `virchow2`, `prov-gigapath`, `h-optimus-0`, `h-optimus-1`, `h0-mini`, `conch`, `musk`, `phikonv2`, `hibou-b`, `hibou-L`, `MidNight12k`, [`kaiko`](https://github.com/kaiko-ai/towards_large_pathology_fms)
- slide-level: `prov-gigapath`, `titan`, `prism`


Expand Down
9 changes: 6 additions & 3 deletions slide2vec/configs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ def load_config(config_name: str):
return OmegaConf.load(pathlib.Path(__file__).parent.resolve() / config_filename)


default_config = load_config("default")
default_tiling_config = load_config("default_tiling")
default_model_config = load_config("default_model")


def load_and_merge_config(config_name: str):
default_config = OmegaConf.create(default_config)
default_tiling_config = OmegaConf.create(default_tiling_config)
default_model_config = OmegaConf.create(default_model_config)
default_config = OmegaConf.merge(default_tiling_config, default_model_config)
loaded_config = load_config(config_name)
return OmegaConf.merge(default_config, loaded_config)
return OmegaConf.merge(default_config, loaded_config)
64 changes: 0 additions & 64 deletions slide2vec/configs/default.yaml

This file was deleted.

34 changes: 34 additions & 0 deletions slide2vec/configs/default_model.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
csv: # path to csv containing slide paths

output_dir: "output" # output directory
resume: false # resume from a previous run
resume_dirname: # directory name to resume from

seed: 0 # seed for reproducibility

model:
level: "tile" # level at which to extract the features ("tile", "region" or "slide")
name: # foundation model name ["uni", "uni2", "virchow", "virchow2", "prov-gigapath", "h-optimus-0", "h-optimus-1", "titan", "prism"] (leave empty when using a custom model)
mode: "cls" # embedding mode ["cls", "full"]
arch: # architecture of custom model
pretrained_weights: # path to the pretrained weights when using a custom model
batch_size: 256
tile_size: ${tiling.params.tile_size}
restrict_to_tissue: false # whether to restrict tile content to tissue pixels only when feeding tile through encoder
patch_size: 256 # if level is "region", size used to unroll the region into patches
save_tile_embeddings: false # whether to save tile embeddings alongside the pooled slide embedding when level is "slide"
save_latents: false # whether to save the latent representations from the model alongside the slide embedding (only supported for 'prism')

speed:
fp16: false # use mixed precision during model inference
num_workers_embedding: 8 # number of workers for data loading when embedding slides

wandb:
enable: false
project: "" # wandb project name
username: "" # wandb username
exp_name: "" # wandb experiment name
tags: ["features", "${model.level}", "${tiling.params.tile_size}"] # wandb tags
dir: "/home/user/"
group:
resume_id: "${resume_dirname}"
1 change: 1 addition & 0 deletions slide2vec/configs/default_tiling.yaml
1 change: 1 addition & 0 deletions slide2vec/data/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def __init__(
path=self.path,
mask_path=self.mask_path,
backend=self.backend,
segment=self.mask_path is None,
segment_params=segment_params,
sampling_params=sampling_params,
)
Expand Down
9 changes: 6 additions & 3 deletions slide2vec/embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,13 @@ def main(args):
process_list.is_file()
), "Process list CSV not found. Ensure tiling has been run."
process_df = pd.read_csv(process_list)
cols = ["wsi_name", "wsi_path", "tiling_status", "error", "traceback"]
if "feature_status" not in process_df.columns:
process_df["feature_status"] = ["tbp"] * len(process_df)
cols = ["wsi_name", "wsi_path", "mask_path", "tiling_status", "feature_status", "error", "traceback"]
process_df = process_df[cols]
if "mask_path" not in process_df.columns:
process_df["mask_path"] = [None] * len(process_df)
cols = ["wsi_name", "wsi_path", "mask_path", "tiling_status", "feature_status", "error", "traceback"]
process_df = process_df[cols]

skip_feature_extraction = process_df["feature_status"].str.contains("success").all()

Expand Down Expand Up @@ -217,7 +220,7 @@ def main(args):
total = len(process_stack)

wsi_paths_to_process = [Path(x) for x in process_stack.wsi_path.values.tolist()]
mask_paths_to_process = [Path(x) for x in process_stack.mask_path.values.tolist()]
mask_paths_to_process = [Path(x) if x is not None and not pd.isna(x) else None for x in process_stack.mask_path.values.tolist()]
combined_paths = zip(wsi_paths_to_process, mask_paths_to_process)

features_dir = Path(cfg.output_dir, "features")
Expand Down
2 changes: 1 addition & 1 deletion slide2vec/hs2p
10 changes: 7 additions & 3 deletions slide2vec/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import slide2vec.distributed as distributed
from slide2vec.utils import initialize_wandb, fix_random_seeds, get_sha, setup_logging
from slide2vec.configs import default_config
from slide2vec.configs import default_tiling_config, default_model_config

logger = logging.getLogger("slide2vec")

Expand All @@ -25,7 +25,9 @@ def write_config(cfg, output_dir, name="config.yaml"):


def get_cfg_from_file(config_file):
default_cfg = OmegaConf.create(default_config)
default_tiling_cfg = OmegaConf.create(default_tiling_config)
default_embedding_cfg = OmegaConf.create(default_model_config)
default_cfg = OmegaConf.merge(default_tiling_cfg, default_embedding_cfg)
cfg = OmegaConf.load(config_file)
cfg = OmegaConf.merge(default_cfg, cfg)
OmegaConf.resolve(cfg)
Expand All @@ -36,7 +38,9 @@ def get_cfg_from_args(args):
if args.output_dir is not None:
args.output_dir = os.path.abspath(args.output_dir)
args.opts += [f"output_dir={args.output_dir}"]
default_cfg = OmegaConf.create(default_config)
default_tiling_cfg = OmegaConf.create(default_tiling_config)
default_embedding_cfg = OmegaConf.create(default_model_config)
default_cfg = OmegaConf.merge(default_tiling_cfg, default_embedding_cfg)
cfg = OmegaConf.load(args.config_file)
cfg = OmegaConf.merge(default_cfg, cfg, OmegaConf.from_cli(args.opts))
OmegaConf.resolve(cfg)
Expand Down
2 changes: 1 addition & 1 deletion test/input/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ model:

speed:
fp16: true
num_workers_tiling: 4
num_workers: 4
num_workers_embedding: 4

wandb:
Expand Down
Loading