kaiko-eva 0.1.8__py3-none-any.whl → 0.2.0__py3-none-any.whl
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.
Potentially problematic release.
This version of kaiko-eva might be problematic. Click here for more details.
- eva/core/data/datasets/classification/embeddings.py +2 -2
- eva/core/data/datasets/classification/multi_embeddings.py +2 -2
- eva/core/data/datasets/embeddings.py +4 -4
- eva/core/data/samplers/classification/balanced.py +19 -18
- eva/core/loggers/utils/wandb.py +33 -0
- eva/core/models/modules/head.py +1 -1
- eva/core/trainers/functional.py +8 -5
- eva/core/trainers/trainer.py +32 -17
- eva/vision/data/datasets/__init__.py +8 -0
- eva/vision/data/datasets/classification/__init__.py +9 -0
- eva/vision/data/datasets/classification/bracs.py +112 -0
- eva/vision/data/datasets/classification/breakhis.py +210 -0
- eva/vision/data/datasets/classification/gleason_arvaniti.py +172 -0
- eva/vision/data/datasets/classification/unitopatho.py +159 -0
- eva/vision/data/datasets/segmentation/embeddings.py +2 -2
- eva/vision/models/networks/backbones/pathology/mahmood.py +46 -19
- {kaiko_eva-0.1.8.dist-info → kaiko_eva-0.2.0.dist-info}/METADATA +1 -1
- {kaiko_eva-0.1.8.dist-info → kaiko_eva-0.2.0.dist-info}/RECORD +21 -16
- {kaiko_eva-0.1.8.dist-info → kaiko_eva-0.2.0.dist-info}/WHEEL +0 -0
- {kaiko_eva-0.1.8.dist-info → kaiko_eva-0.2.0.dist-info}/entry_points.txt +0 -0
- {kaiko_eva-0.1.8.dist-info → kaiko_eva-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -12,7 +12,7 @@ class EmbeddingsClassificationDataset(embeddings_base.EmbeddingsDataset[torch.Te
|
|
|
12
12
|
"""Embeddings dataset class for classification tasks."""
|
|
13
13
|
|
|
14
14
|
@override
|
|
15
|
-
def
|
|
15
|
+
def load_embeddings(self, index: int) -> torch.Tensor:
|
|
16
16
|
filename = self.filename(index)
|
|
17
17
|
embeddings_path = os.path.join(self._root, filename)
|
|
18
18
|
tensor = torch.load(embeddings_path, map_location="cpu")
|
|
@@ -25,7 +25,7 @@ class EmbeddingsClassificationDataset(embeddings_base.EmbeddingsDataset[torch.Te
|
|
|
25
25
|
return tensor.squeeze(0)
|
|
26
26
|
|
|
27
27
|
@override
|
|
28
|
-
def
|
|
28
|
+
def load_target(self, index: int) -> torch.Tensor:
|
|
29
29
|
target = self._data.at[index, self._column_mapping["target"]]
|
|
30
30
|
return torch.tensor(target, dtype=torch.int64)
|
|
31
31
|
|
|
@@ -66,7 +66,7 @@ class MultiEmbeddingsClassificationDataset(embeddings_base.EmbeddingsDataset[tor
|
|
|
66
66
|
self._multi_ids = list(self._data[self._column_mapping["multi_id"]].unique())
|
|
67
67
|
|
|
68
68
|
@override
|
|
69
|
-
def
|
|
69
|
+
def load_embeddings(self, index: int) -> torch.Tensor:
|
|
70
70
|
"""Loads and stacks all embedding corresponding to the `index`'th multi_id."""
|
|
71
71
|
# Get all embeddings for the given index (multi_id)
|
|
72
72
|
multi_id = self._multi_ids[index]
|
|
@@ -89,7 +89,7 @@ class MultiEmbeddingsClassificationDataset(embeddings_base.EmbeddingsDataset[tor
|
|
|
89
89
|
return embeddings
|
|
90
90
|
|
|
91
91
|
@override
|
|
92
|
-
def
|
|
92
|
+
def load_target(self, index: int) -> np.ndarray:
|
|
93
93
|
"""Returns the target corresponding to the `index`'th multi_id.
|
|
94
94
|
|
|
95
95
|
This method assumes that all the embeddings corresponding to the same `multi_id`
|
|
@@ -98,12 +98,12 @@ class EmbeddingsDataset(base.Dataset, Generic[TargetType]):
|
|
|
98
98
|
Returns:
|
|
99
99
|
A data sample and its target.
|
|
100
100
|
"""
|
|
101
|
-
embeddings = self.
|
|
102
|
-
target = self.
|
|
101
|
+
embeddings = self.load_embeddings(index)
|
|
102
|
+
target = self.load_target(index)
|
|
103
103
|
return self._apply_transforms(embeddings, target)
|
|
104
104
|
|
|
105
105
|
@abc.abstractmethod
|
|
106
|
-
def
|
|
106
|
+
def load_embeddings(self, index: int) -> torch.Tensor:
|
|
107
107
|
"""Returns the `index`'th embedding sample.
|
|
108
108
|
|
|
109
109
|
Args:
|
|
@@ -114,7 +114,7 @@ class EmbeddingsDataset(base.Dataset, Generic[TargetType]):
|
|
|
114
114
|
"""
|
|
115
115
|
|
|
116
116
|
@abc.abstractmethod
|
|
117
|
-
def
|
|
117
|
+
def load_target(self, index: int) -> TargetType:
|
|
118
118
|
"""Returns the `index`'th target sample.
|
|
119
119
|
|
|
120
120
|
Args:
|
|
@@ -4,6 +4,7 @@ from collections import defaultdict
|
|
|
4
4
|
from typing import Dict, Iterator, List
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
|
+
from loguru import logger
|
|
7
8
|
from typing_extensions import override
|
|
8
9
|
|
|
9
10
|
from eva.core.data import datasets
|
|
@@ -33,6 +34,7 @@ class BalancedSampler(SamplerWithDataSource[int]):
|
|
|
33
34
|
self._replacement = replacement
|
|
34
35
|
self._class_indices: Dict[int, List[int]] = defaultdict(list)
|
|
35
36
|
self._random_generator = np.random.default_rng(seed)
|
|
37
|
+
self._indices: List[int] = []
|
|
36
38
|
|
|
37
39
|
def __len__(self) -> int:
|
|
38
40
|
"""Returns the total number of samples."""
|
|
@@ -44,18 +46,7 @@ class BalancedSampler(SamplerWithDataSource[int]):
|
|
|
44
46
|
Returns:
|
|
45
47
|
Iterator yielding dataset indices.
|
|
46
48
|
"""
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
for class_idx in self._class_indices:
|
|
50
|
-
class_indices = self._class_indices[class_idx]
|
|
51
|
-
sampled_indices = self._random_generator.choice(
|
|
52
|
-
class_indices, size=self._num_samples, replace=self._replacement
|
|
53
|
-
).tolist()
|
|
54
|
-
indices.extend(sampled_indices)
|
|
55
|
-
|
|
56
|
-
self._random_generator.shuffle(indices)
|
|
57
|
-
|
|
58
|
-
return iter(indices)
|
|
49
|
+
return iter(self._indices)
|
|
59
50
|
|
|
60
51
|
@override
|
|
61
52
|
def set_dataset(self, data_source: datasets.MapDataset):
|
|
@@ -72,13 +63,13 @@ class BalancedSampler(SamplerWithDataSource[int]):
|
|
|
72
63
|
self._make_indices()
|
|
73
64
|
|
|
74
65
|
def _make_indices(self):
|
|
75
|
-
"""
|
|
66
|
+
"""Samples the indices for each class in the dataset."""
|
|
76
67
|
self._class_indices.clear()
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
68
|
+
for idx in tqdm(range(len(self.data_source)), desc="Fetching class indices for sampler"):
|
|
69
|
+
if hasattr(self.data_source, "load_target"):
|
|
70
|
+
target = self.data_source.load_target(idx) # type: ignore
|
|
71
|
+
else:
|
|
72
|
+
_, target, _ = DataSample(*self.data_source[idx])
|
|
82
73
|
if target is None:
|
|
83
74
|
raise ValueError("The dataset must return non-empty targets.")
|
|
84
75
|
if target.numel() != 1:
|
|
@@ -94,3 +85,13 @@ class BalancedSampler(SamplerWithDataSource[int]):
|
|
|
94
85
|
f"Class {class_idx} has only {len(indices)} samples, "
|
|
95
86
|
f"which is less than the required {self._num_samples} samples."
|
|
96
87
|
)
|
|
88
|
+
|
|
89
|
+
self._indices = []
|
|
90
|
+
for class_idx in self._class_indices:
|
|
91
|
+
class_indices = self._class_indices[class_idx]
|
|
92
|
+
sampled_indices = self._random_generator.choice(
|
|
93
|
+
class_indices, size=self._num_samples, replace=self._replacement
|
|
94
|
+
).tolist()
|
|
95
|
+
self._indices.extend(sampled_indices)
|
|
96
|
+
self._random_generator.shuffle(self._indices)
|
|
97
|
+
logger.debug(f"Sampled indices: {self._indices}")
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# type: ignore
|
|
2
|
+
"""Utility functions for logging with Weights & Biases."""
|
|
3
|
+
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
|
|
6
|
+
from loguru import logger
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def rename_active_run(name: str) -> None:
|
|
10
|
+
"""Renames the current run."""
|
|
11
|
+
import wandb
|
|
12
|
+
|
|
13
|
+
if wandb.run:
|
|
14
|
+
wandb.run.name = name
|
|
15
|
+
wandb.run.save()
|
|
16
|
+
else:
|
|
17
|
+
logger.warning("No active wandb run found that could be renamed.")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def init_run(name: str, init_kwargs: Dict[str, Any]) -> None:
|
|
21
|
+
"""Initializes a new run. If there is an active run, it will be renamed and reused."""
|
|
22
|
+
import wandb
|
|
23
|
+
|
|
24
|
+
init_kwargs["name"] = name
|
|
25
|
+
rename_active_run(name)
|
|
26
|
+
wandb.init(**init_kwargs)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def finish_run() -> None:
|
|
30
|
+
"""Finish the current run."""
|
|
31
|
+
import wandb
|
|
32
|
+
|
|
33
|
+
wandb.finish()
|
eva/core/models/modules/head.py
CHANGED
eva/core/trainers/functional.py
CHANGED
|
@@ -39,7 +39,7 @@ def run_evaluation_session(
|
|
|
39
39
|
base_trainer,
|
|
40
40
|
base_model,
|
|
41
41
|
datamodule,
|
|
42
|
-
run_id=
|
|
42
|
+
run_id=run_index,
|
|
43
43
|
verbose=not verbose,
|
|
44
44
|
)
|
|
45
45
|
recorder.update(validation_scores, test_scores)
|
|
@@ -51,7 +51,7 @@ def run_evaluation(
|
|
|
51
51
|
base_model: modules.ModelModule,
|
|
52
52
|
datamodule: datamodules.DataModule,
|
|
53
53
|
*,
|
|
54
|
-
run_id:
|
|
54
|
+
run_id: int | None = None,
|
|
55
55
|
verbose: bool = True,
|
|
56
56
|
) -> Tuple[_EVALUATE_OUTPUT, _EVALUATE_OUTPUT | None]:
|
|
57
57
|
"""Fits and evaluates a model out-of-place.
|
|
@@ -61,7 +61,6 @@ def run_evaluation(
|
|
|
61
61
|
base_model: The model module to use but not modify.
|
|
62
62
|
datamodule: The data module.
|
|
63
63
|
run_id: The run id to be appended to the output log directory.
|
|
64
|
-
If `None`, it will use the log directory of the trainer as is.
|
|
65
64
|
verbose: Whether to print the validation and test metrics
|
|
66
65
|
in the end of the training.
|
|
67
66
|
|
|
@@ -70,8 +69,12 @@ def run_evaluation(
|
|
|
70
69
|
"""
|
|
71
70
|
trainer, model = _utils.clone(base_trainer, base_model)
|
|
72
71
|
model.configure_model()
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
|
|
73
|
+
trainer.init_logger_run(run_id)
|
|
74
|
+
results = fit_and_validate(trainer, model, datamodule, verbose=verbose)
|
|
75
|
+
trainer.finish_logger_run(run_id)
|
|
76
|
+
|
|
77
|
+
return results
|
|
75
78
|
|
|
76
79
|
|
|
77
80
|
def fit_and_validate(
|
eva/core/trainers/trainer.py
CHANGED
|
@@ -12,6 +12,7 @@ from typing_extensions import override
|
|
|
12
12
|
|
|
13
13
|
from eva.core import loggers as eva_loggers
|
|
14
14
|
from eva.core.data import datamodules
|
|
15
|
+
from eva.core.loggers.utils import wandb as wandb_utils
|
|
15
16
|
from eva.core.models import modules
|
|
16
17
|
from eva.core.trainers import _logging, functional
|
|
17
18
|
|
|
@@ -53,7 +54,7 @@ class Trainer(pl_trainer.Trainer):
|
|
|
53
54
|
self._session_id: str = _logging.generate_session_id()
|
|
54
55
|
self._log_dir: str = self.default_log_dir
|
|
55
56
|
|
|
56
|
-
self.
|
|
57
|
+
self.init_logger_run(0)
|
|
57
58
|
|
|
58
59
|
@property
|
|
59
60
|
def default_log_dir(self) -> str:
|
|
@@ -65,31 +66,45 @@ class Trainer(pl_trainer.Trainer):
|
|
|
65
66
|
def log_dir(self) -> str | None:
|
|
66
67
|
return self.strategy.broadcast(self._log_dir)
|
|
67
68
|
|
|
68
|
-
def
|
|
69
|
-
"""
|
|
69
|
+
def init_logger_run(self, run_id: int | None) -> None:
|
|
70
|
+
"""Setup the loggers & log directories when starting a new run.
|
|
70
71
|
|
|
71
72
|
Args:
|
|
72
|
-
|
|
73
|
+
run_id: The id of the current run.
|
|
73
74
|
"""
|
|
75
|
+
subdirectory = f"run_{run_id}" if run_id is not None else ""
|
|
74
76
|
self._log_dir = os.path.join(self.default_root_dir, self._session_id, subdirectory)
|
|
75
77
|
|
|
76
78
|
enabled_loggers = []
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
79
|
+
for logger in self.loggers or []:
|
|
80
|
+
if isinstance(logger, (pl_loggers.CSVLogger, pl_loggers.TensorBoardLogger)):
|
|
81
|
+
if not cloud_io._is_local_file_protocol(self.default_root_dir):
|
|
82
|
+
loguru.logger.warning(
|
|
83
|
+
f"Skipped {type(logger).__name__} as remote storage is not supported."
|
|
84
|
+
)
|
|
85
|
+
continue
|
|
86
|
+
else:
|
|
87
|
+
logger._root_dir = self.default_root_dir
|
|
88
|
+
logger._name = self._session_id
|
|
89
|
+
logger._version = subdirectory
|
|
90
|
+
elif isinstance(logger, pl_loggers.WandbLogger):
|
|
91
|
+
task_name = self.default_root_dir.split("/")[-1]
|
|
92
|
+
run_name = os.getenv("WANDB_RUN_NAME", f"{task_name}_{self._session_id}")
|
|
93
|
+
wandb_utils.init_run(f"{run_name}_{run_id}", logger._wandb_init)
|
|
94
|
+
enabled_loggers.append(logger)
|
|
90
95
|
|
|
91
96
|
self._loggers = enabled_loggers or [eva_loggers.DummyLogger(self._log_dir)]
|
|
92
97
|
|
|
98
|
+
def finish_logger_run(self, run_id: int | None) -> None:
|
|
99
|
+
"""Finish the current run in the enabled loggers.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
run_id: The id of the current run.
|
|
103
|
+
"""
|
|
104
|
+
for logger in self.loggers or []:
|
|
105
|
+
if isinstance(logger, pl_loggers.WandbLogger):
|
|
106
|
+
wandb_utils.finish_run()
|
|
107
|
+
|
|
93
108
|
def run_evaluation_session(
|
|
94
109
|
self,
|
|
95
110
|
model: modules.ModelModule,
|
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
from eva.vision.data.datasets.classification import (
|
|
4
4
|
BACH,
|
|
5
|
+
BRACS,
|
|
5
6
|
CRC,
|
|
6
7
|
MHIST,
|
|
7
8
|
PANDA,
|
|
9
|
+
BreaKHis,
|
|
8
10
|
Camelyon16,
|
|
11
|
+
GleasonArvaniti,
|
|
9
12
|
PANDASmall,
|
|
10
13
|
PatchCamelyon,
|
|
14
|
+
UniToPatho,
|
|
11
15
|
WsiClassificationDataset,
|
|
12
16
|
)
|
|
13
17
|
from eva.vision.data.datasets.segmentation import (
|
|
@@ -26,12 +30,16 @@ from eva.vision.data.datasets.wsi import MultiWsiDataset, WsiDataset
|
|
|
26
30
|
__all__ = [
|
|
27
31
|
"BACH",
|
|
28
32
|
"BCSS",
|
|
33
|
+
"BreaKHis",
|
|
34
|
+
"BRACS",
|
|
29
35
|
"CRC",
|
|
36
|
+
"GleasonArvaniti",
|
|
30
37
|
"MHIST",
|
|
31
38
|
"PANDA",
|
|
32
39
|
"PANDASmall",
|
|
33
40
|
"Camelyon16",
|
|
34
41
|
"PatchCamelyon",
|
|
42
|
+
"UniToPatho",
|
|
35
43
|
"WsiClassificationDataset",
|
|
36
44
|
"CoNSeP",
|
|
37
45
|
"EmbeddingsSegmentationDataset",
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
"""Image classification datasets API."""
|
|
2
2
|
|
|
3
3
|
from eva.vision.data.datasets.classification.bach import BACH
|
|
4
|
+
from eva.vision.data.datasets.classification.bracs import BRACS
|
|
5
|
+
from eva.vision.data.datasets.classification.breakhis import BreaKHis
|
|
4
6
|
from eva.vision.data.datasets.classification.camelyon16 import Camelyon16
|
|
5
7
|
from eva.vision.data.datasets.classification.crc import CRC
|
|
8
|
+
from eva.vision.data.datasets.classification.gleason_arvaniti import GleasonArvaniti
|
|
6
9
|
from eva.vision.data.datasets.classification.mhist import MHIST
|
|
7
10
|
from eva.vision.data.datasets.classification.panda import PANDA, PANDASmall
|
|
8
11
|
from eva.vision.data.datasets.classification.patch_camelyon import PatchCamelyon
|
|
12
|
+
from eva.vision.data.datasets.classification.unitopatho import UniToPatho
|
|
9
13
|
from eva.vision.data.datasets.classification.wsi import WsiClassificationDataset
|
|
10
14
|
|
|
11
15
|
__all__ = [
|
|
12
16
|
"BACH",
|
|
17
|
+
"BreaKHis",
|
|
18
|
+
"BRACS",
|
|
19
|
+
"Camelyon16",
|
|
13
20
|
"CRC",
|
|
21
|
+
"GleasonArvaniti",
|
|
14
22
|
"MHIST",
|
|
15
23
|
"PatchCamelyon",
|
|
24
|
+
"UniToPatho",
|
|
16
25
|
"WsiClassificationDataset",
|
|
17
26
|
"PANDA",
|
|
18
27
|
"PANDASmall",
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""BRACS dataset class."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Callable, Dict, List, Literal, Tuple
|
|
5
|
+
|
|
6
|
+
import torch
|
|
7
|
+
from torchvision import tv_tensors
|
|
8
|
+
from torchvision.datasets import folder
|
|
9
|
+
from typing_extensions import override
|
|
10
|
+
|
|
11
|
+
from eva.vision.data.datasets import _validators
|
|
12
|
+
from eva.vision.data.datasets.classification import base
|
|
13
|
+
from eva.vision.utils import io
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BRACS(base.ImageClassification):
|
|
17
|
+
"""Dataset class for BRACS images and corresponding targets."""
|
|
18
|
+
|
|
19
|
+
_expected_dataset_lengths: Dict[str, int] = {
|
|
20
|
+
"train": 3657,
|
|
21
|
+
"val": 312,
|
|
22
|
+
"test": 570,
|
|
23
|
+
}
|
|
24
|
+
"""Expected dataset lengths for the splits and complete dataset."""
|
|
25
|
+
|
|
26
|
+
_license: str = "CC BY-NC 4.0 (https://creativecommons.org/licenses/by-nc/4.0/)"
|
|
27
|
+
"""Dataset license."""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
root: str,
|
|
32
|
+
split: Literal["train", "val", "test"],
|
|
33
|
+
transforms: Callable | None = None,
|
|
34
|
+
) -> None:
|
|
35
|
+
"""Initializes the dataset.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
root: Path to the root directory of the dataset.
|
|
39
|
+
split: Dataset split to use.
|
|
40
|
+
transforms: A function/transform which returns a transformed
|
|
41
|
+
version of the raw data samples.
|
|
42
|
+
"""
|
|
43
|
+
super().__init__(transforms=transforms)
|
|
44
|
+
|
|
45
|
+
self._root = root
|
|
46
|
+
self._split = split
|
|
47
|
+
|
|
48
|
+
self._samples: List[Tuple[str, int]] = []
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
@override
|
|
52
|
+
def classes(self) -> List[str]:
|
|
53
|
+
return ["0_N", "1_PB", "2_UDH", "3_FEA", "4_ADH", "5_DCIS", "6_IC"]
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
@override
|
|
57
|
+
def class_to_idx(self) -> Dict[str, int]:
|
|
58
|
+
return {name: index for index, name in enumerate(self.classes)}
|
|
59
|
+
|
|
60
|
+
@override
|
|
61
|
+
def filename(self, index: int) -> str:
|
|
62
|
+
image_path, *_ = self._samples[index]
|
|
63
|
+
return os.path.relpath(image_path, self._dataset_path)
|
|
64
|
+
|
|
65
|
+
@override
|
|
66
|
+
def prepare_data(self) -> None:
|
|
67
|
+
_validators.check_dataset_exists(self._root, True)
|
|
68
|
+
|
|
69
|
+
@override
|
|
70
|
+
def configure(self) -> None:
|
|
71
|
+
self._samples = self._make_dataset()
|
|
72
|
+
|
|
73
|
+
@override
|
|
74
|
+
def validate(self) -> None:
|
|
75
|
+
_validators.check_dataset_integrity(
|
|
76
|
+
self,
|
|
77
|
+
length=self._expected_dataset_lengths[self._split],
|
|
78
|
+
n_classes=7,
|
|
79
|
+
first_and_last_labels=("0_N", "6_IC"),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@override
|
|
83
|
+
def load_image(self, index: int) -> tv_tensors.Image:
|
|
84
|
+
image_path, _ = self._samples[index]
|
|
85
|
+
return io.read_image_as_tensor(image_path)
|
|
86
|
+
|
|
87
|
+
@override
|
|
88
|
+
def load_target(self, index: int) -> torch.Tensor:
|
|
89
|
+
_, target = self._samples[index]
|
|
90
|
+
return torch.tensor(target, dtype=torch.long)
|
|
91
|
+
|
|
92
|
+
@override
|
|
93
|
+
def __len__(self) -> int:
|
|
94
|
+
return len(self._samples)
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def _dataset_path(self) -> str:
|
|
98
|
+
"""Returns the full path of dataset directory."""
|
|
99
|
+
return os.path.join(self._root, "BRACS_RoI/latest_version")
|
|
100
|
+
|
|
101
|
+
def _make_dataset(self) -> List[Tuple[str, int]]:
|
|
102
|
+
"""Builds the dataset for the specified split."""
|
|
103
|
+
dataset = folder.make_dataset(
|
|
104
|
+
directory=os.path.join(self._dataset_path, self._split),
|
|
105
|
+
class_to_idx=self.class_to_idx,
|
|
106
|
+
extensions=(".png"),
|
|
107
|
+
)
|
|
108
|
+
return dataset
|
|
109
|
+
|
|
110
|
+
def _print_license(self) -> None:
|
|
111
|
+
"""Prints the dataset license."""
|
|
112
|
+
print(f"Dataset license: {self._license}")
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""BreaKHis dataset class."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import glob
|
|
5
|
+
import os
|
|
6
|
+
from typing import Any, Callable, Dict, List, Literal, Set
|
|
7
|
+
|
|
8
|
+
import torch
|
|
9
|
+
from torchvision import tv_tensors
|
|
10
|
+
from torchvision.datasets import utils
|
|
11
|
+
from typing_extensions import override
|
|
12
|
+
|
|
13
|
+
from eva.vision.data.datasets import _validators, structs
|
|
14
|
+
from eva.vision.data.datasets.classification import base
|
|
15
|
+
from eva.vision.utils import io
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BreaKHis(base.ImageClassification):
|
|
19
|
+
"""Dataset class for BreaKHis images and corresponding targets."""
|
|
20
|
+
|
|
21
|
+
_resources: List[structs.DownloadResource] = [
|
|
22
|
+
structs.DownloadResource(
|
|
23
|
+
filename="BreaKHis_v1.tar.gz",
|
|
24
|
+
url="http://www.inf.ufpr.br/vri/databases/BreaKHis_v1.tar.gz",
|
|
25
|
+
),
|
|
26
|
+
]
|
|
27
|
+
"""Dataset resources."""
|
|
28
|
+
|
|
29
|
+
_val_patient_ids: Set[str] = {
|
|
30
|
+
"18842D",
|
|
31
|
+
"19979",
|
|
32
|
+
"15275",
|
|
33
|
+
"15792",
|
|
34
|
+
"16875",
|
|
35
|
+
"3909",
|
|
36
|
+
"5287",
|
|
37
|
+
"16716",
|
|
38
|
+
"2773",
|
|
39
|
+
"5695",
|
|
40
|
+
"16184CD",
|
|
41
|
+
"23060CD",
|
|
42
|
+
"21998CD",
|
|
43
|
+
"21998EF",
|
|
44
|
+
}
|
|
45
|
+
"""Patient IDs to use for dataset splits."""
|
|
46
|
+
|
|
47
|
+
_expected_dataset_lengths: Dict[str | None, int] = {
|
|
48
|
+
"train": 1132,
|
|
49
|
+
"val": 339,
|
|
50
|
+
None: 1471,
|
|
51
|
+
}
|
|
52
|
+
"""Expected dataset lengths for the splits and complete dataset."""
|
|
53
|
+
|
|
54
|
+
_default_magnifications = ["40X"]
|
|
55
|
+
"""Default magnification to use for images in train/val datasets."""
|
|
56
|
+
|
|
57
|
+
_license: str = "CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)"
|
|
58
|
+
"""Dataset license."""
|
|
59
|
+
|
|
60
|
+
def __init__(
|
|
61
|
+
self,
|
|
62
|
+
root: str,
|
|
63
|
+
split: Literal["train", "val"] | None = None,
|
|
64
|
+
magnifications: List[Literal["40X", "100X", "200X", "400X"]] | None = None,
|
|
65
|
+
download: bool = False,
|
|
66
|
+
transforms: Callable | None = None,
|
|
67
|
+
) -> None:
|
|
68
|
+
"""Initialize the dataset.
|
|
69
|
+
|
|
70
|
+
The dataset is split into train and validation by taking into account
|
|
71
|
+
the patient IDs to avoid any data leakage.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
root: Path to the root directory of the dataset. The dataset will
|
|
75
|
+
be downloaded and extracted here, if it does not already exist.
|
|
76
|
+
split: Dataset split to use. If `None`, the entire dataset is used.
|
|
77
|
+
magnifications: A list of the WSI magnifications to select. By default
|
|
78
|
+
only 40X images are used.
|
|
79
|
+
download: Whether to download the data for the specified split.
|
|
80
|
+
Note that the download will be executed only by additionally
|
|
81
|
+
calling the :meth:`prepare_data` method and if the data does
|
|
82
|
+
not yet exist on disk.
|
|
83
|
+
transforms: A function/transform which returns a transformed
|
|
84
|
+
version of the raw data samples.
|
|
85
|
+
"""
|
|
86
|
+
super().__init__(transforms=transforms)
|
|
87
|
+
|
|
88
|
+
self._root = root
|
|
89
|
+
self._split = split
|
|
90
|
+
self._download = download
|
|
91
|
+
|
|
92
|
+
self._magnifications = magnifications or self._default_magnifications
|
|
93
|
+
self._indices: List[int] = []
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
@override
|
|
97
|
+
def classes(self) -> List[str]:
|
|
98
|
+
return ["TA", "MC", "F", "DC"]
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
@override
|
|
102
|
+
def class_to_idx(self) -> Dict[str, int]:
|
|
103
|
+
return {label: index for index, label in enumerate(self.classes)}
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def _dataset_path(self) -> str:
|
|
107
|
+
"""Returns the path of the image data of the dataset."""
|
|
108
|
+
return os.path.join(self._root, "BreaKHis_v1", "histology_slides")
|
|
109
|
+
|
|
110
|
+
@functools.cached_property
|
|
111
|
+
def _image_files(self) -> List[str]:
|
|
112
|
+
"""Return the list of image files in the dataset.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
List of image file paths.
|
|
116
|
+
"""
|
|
117
|
+
image_files = []
|
|
118
|
+
for magnification in self._magnifications:
|
|
119
|
+
files_pattern = os.path.join(self._dataset_path, f"**/{magnification}", "*.png")
|
|
120
|
+
image_files.extend(list(glob.glob(files_pattern, recursive=True)))
|
|
121
|
+
return sorted(image_files)
|
|
122
|
+
|
|
123
|
+
@override
|
|
124
|
+
def filename(self, index: int) -> str:
|
|
125
|
+
image_path = self._image_files[self._indices[index]]
|
|
126
|
+
return os.path.relpath(image_path, self._dataset_path)
|
|
127
|
+
|
|
128
|
+
@override
|
|
129
|
+
def prepare_data(self) -> None:
|
|
130
|
+
if self._download:
|
|
131
|
+
self._download_dataset()
|
|
132
|
+
_validators.check_dataset_exists(self._root, True)
|
|
133
|
+
|
|
134
|
+
@override
|
|
135
|
+
def configure(self) -> None:
|
|
136
|
+
self._indices = self._make_indices()
|
|
137
|
+
|
|
138
|
+
@override
|
|
139
|
+
def validate(self) -> None:
|
|
140
|
+
_validators.check_dataset_integrity(
|
|
141
|
+
self,
|
|
142
|
+
length=self._expected_dataset_lengths[self._split],
|
|
143
|
+
n_classes=4,
|
|
144
|
+
first_and_last_labels=("TA", "DC"),
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
@override
|
|
148
|
+
def load_image(self, index: int) -> tv_tensors.Image:
|
|
149
|
+
image_path = self._image_files[self._indices[index]]
|
|
150
|
+
return io.read_image_as_tensor(image_path)
|
|
151
|
+
|
|
152
|
+
@override
|
|
153
|
+
def load_target(self, index: int) -> torch.Tensor:
|
|
154
|
+
class_name = self._extract_class(self._image_files[self._indices[index]])
|
|
155
|
+
return torch.tensor(self.class_to_idx[class_name], dtype=torch.long)
|
|
156
|
+
|
|
157
|
+
@override
|
|
158
|
+
def load_metadata(self, index: int) -> Dict[str, Any]:
|
|
159
|
+
return {"patient_id": self._extract_patient_id(self._image_files[self._indices[index]])}
|
|
160
|
+
|
|
161
|
+
@override
|
|
162
|
+
def __len__(self) -> int:
|
|
163
|
+
return len(self._indices)
|
|
164
|
+
|
|
165
|
+
def _download_dataset(self) -> None:
|
|
166
|
+
"""Downloads the dataset."""
|
|
167
|
+
for resource in self._resources:
|
|
168
|
+
if os.path.isdir(self._dataset_path):
|
|
169
|
+
continue
|
|
170
|
+
|
|
171
|
+
self._print_license()
|
|
172
|
+
utils.download_and_extract_archive(
|
|
173
|
+
resource.url,
|
|
174
|
+
download_root=self._root,
|
|
175
|
+
filename=resource.filename,
|
|
176
|
+
remove_finished=True,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
def _print_license(self) -> None:
|
|
180
|
+
"""Prints the dataset license."""
|
|
181
|
+
print(f"Dataset license: {self._license}")
|
|
182
|
+
|
|
183
|
+
def _extract_patient_id(self, image_file: str) -> str:
|
|
184
|
+
"""Extracts the patient ID from the image file name."""
|
|
185
|
+
return os.path.basename(image_file).split("-")[2]
|
|
186
|
+
|
|
187
|
+
def _extract_class(self, file: str) -> str:
|
|
188
|
+
return os.path.basename(file).split("-")[0].split("_")[-1]
|
|
189
|
+
|
|
190
|
+
def _make_indices(self) -> List[int]:
|
|
191
|
+
"""Builds the dataset indices for the specified split."""
|
|
192
|
+
train_indices = []
|
|
193
|
+
val_indices = []
|
|
194
|
+
|
|
195
|
+
for index, image_file in enumerate(self._image_files):
|
|
196
|
+
if self._extract_class(image_file) not in self.classes:
|
|
197
|
+
continue
|
|
198
|
+
patient_id = self._extract_patient_id(image_file)
|
|
199
|
+
if patient_id in self._val_patient_ids:
|
|
200
|
+
val_indices.append(index)
|
|
201
|
+
else:
|
|
202
|
+
train_indices.append(index)
|
|
203
|
+
|
|
204
|
+
split_indices = {
|
|
205
|
+
"train": train_indices,
|
|
206
|
+
"val": val_indices,
|
|
207
|
+
None: train_indices + val_indices,
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return split_indices[self._split]
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""GleasonArvaniti dataset class."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import glob
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Callable, Dict, List, Literal
|
|
8
|
+
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import torch
|
|
11
|
+
from loguru import logger
|
|
12
|
+
from torchvision import tv_tensors
|
|
13
|
+
from typing_extensions import override
|
|
14
|
+
|
|
15
|
+
from eva.vision.data.datasets import _validators
|
|
16
|
+
from eva.vision.data.datasets.classification import base
|
|
17
|
+
from eva.vision.utils import io
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class GleasonArvaniti(base.ImageClassification):
|
|
21
|
+
"""Dataset class for GleasonArvaniti images and corresponding targets."""
|
|
22
|
+
|
|
23
|
+
_expected_dataset_lengths: Dict[str | None, int] = {
|
|
24
|
+
"train": 15303,
|
|
25
|
+
"val": 2482,
|
|
26
|
+
"test": 4967,
|
|
27
|
+
None: 22752,
|
|
28
|
+
}
|
|
29
|
+
"""Expected dataset lengths for the splits and complete dataset."""
|
|
30
|
+
|
|
31
|
+
_license: str = "CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/)"
|
|
32
|
+
"""Dataset license."""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
root: str,
|
|
37
|
+
split: Literal["train", "val", "test"] | None = None,
|
|
38
|
+
transforms: Callable | None = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Initialize the dataset.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
root: Path to the root directory of the dataset.
|
|
44
|
+
split: Dataset split to use. If `None`, the entire dataset is used.
|
|
45
|
+
transforms: A function/transform which returns a transformed
|
|
46
|
+
version of the raw data samples.
|
|
47
|
+
"""
|
|
48
|
+
super().__init__(transforms=transforms)
|
|
49
|
+
|
|
50
|
+
self._root = root
|
|
51
|
+
self._split = split
|
|
52
|
+
|
|
53
|
+
self._indices: List[int] = []
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
@override
|
|
57
|
+
def classes(self) -> List[str]:
|
|
58
|
+
return ["benign", "gleason_3", "gleason_4", "gleason_5"]
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
@override
|
|
62
|
+
def class_to_idx(self) -> Dict[str, int]:
|
|
63
|
+
return {name: index for index, name in enumerate(self.classes)}
|
|
64
|
+
|
|
65
|
+
@functools.cached_property
|
|
66
|
+
def _image_files(self) -> List[str]:
|
|
67
|
+
"""Return the list of image files in the dataset.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
List of image file paths.
|
|
71
|
+
"""
|
|
72
|
+
subdirs = ["train_validation_patches_750", "test_patches_750/patho_1"]
|
|
73
|
+
|
|
74
|
+
image_files = []
|
|
75
|
+
for subdir in subdirs:
|
|
76
|
+
files_pattern = os.path.join(self._root, subdir, "**/*.jpg")
|
|
77
|
+
image_files += list(glob.glob(files_pattern, recursive=True))
|
|
78
|
+
|
|
79
|
+
return sorted(image_files)
|
|
80
|
+
|
|
81
|
+
@functools.cached_property
|
|
82
|
+
def _manifest(self) -> pd.DataFrame:
|
|
83
|
+
"""Returns the train.csv & test.csv files as dataframe."""
|
|
84
|
+
df_train = pd.read_csv(os.path.join(self._root, "train.csv"))
|
|
85
|
+
df_val = pd.read_csv(os.path.join(self._root, "test.csv"))
|
|
86
|
+
df_train["split"], df_val["split"] = "train", "val"
|
|
87
|
+
return pd.concat([df_train, df_val], axis=0).set_index("image_id")
|
|
88
|
+
|
|
89
|
+
@override
|
|
90
|
+
def filename(self, index: int) -> str:
|
|
91
|
+
image_path = self._image_files[self._indices[index]]
|
|
92
|
+
return os.path.relpath(image_path, self._root)
|
|
93
|
+
|
|
94
|
+
@override
|
|
95
|
+
def prepare_data(self) -> None:
|
|
96
|
+
_validators.check_dataset_exists(self._root, download_available=False)
|
|
97
|
+
if not os.path.isdir(os.path.join(self._root, "train_validation_patches_750")):
|
|
98
|
+
raise FileNotFoundError(
|
|
99
|
+
f"`train_validation_patches_750` directory not found in {self._root}"
|
|
100
|
+
)
|
|
101
|
+
if not os.path.isdir(os.path.join(self._root, "test_patches_750")):
|
|
102
|
+
raise FileNotFoundError(f"`test_patches_750` directory not found in {self._root}")
|
|
103
|
+
|
|
104
|
+
if self._split == "test":
|
|
105
|
+
logger.warning(
|
|
106
|
+
"The test split currently leads to unstable evaluation results. "
|
|
107
|
+
"We recommend using the validation split instead."
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
@override
|
|
111
|
+
def configure(self) -> None:
|
|
112
|
+
self._indices = self._make_indices()
|
|
113
|
+
|
|
114
|
+
@override
|
|
115
|
+
def validate(self) -> None:
|
|
116
|
+
_validators.check_dataset_integrity(
|
|
117
|
+
self,
|
|
118
|
+
length=self._expected_dataset_lengths[self._split],
|
|
119
|
+
n_classes=4,
|
|
120
|
+
first_and_last_labels=("benign", "gleason_5"),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@override
|
|
124
|
+
def load_image(self, index: int) -> tv_tensors.Image:
|
|
125
|
+
image_path = self._image_files[self._indices[index]]
|
|
126
|
+
return io.read_image_as_tensor(image_path)
|
|
127
|
+
|
|
128
|
+
@override
|
|
129
|
+
def load_target(self, index: int) -> torch.Tensor:
|
|
130
|
+
target = self._extract_class(self._image_files[self._indices[index]])
|
|
131
|
+
return torch.tensor(target, dtype=torch.long)
|
|
132
|
+
|
|
133
|
+
@override
|
|
134
|
+
def __len__(self) -> int:
|
|
135
|
+
return len(self._indices)
|
|
136
|
+
|
|
137
|
+
def _print_license(self) -> None:
|
|
138
|
+
"""Prints the dataset license."""
|
|
139
|
+
print(f"Dataset license: {self._license}")
|
|
140
|
+
|
|
141
|
+
def _extract_micro_array_id(self, file: str) -> str:
|
|
142
|
+
"""Extracts the ID of the tissue micro array from the file name."""
|
|
143
|
+
return Path(file).stem.split("_")[0]
|
|
144
|
+
|
|
145
|
+
def _extract_class(self, file: str) -> int:
|
|
146
|
+
"""Extracts the class label from the file name."""
|
|
147
|
+
return int(Path(file).stem.split("_")[-1])
|
|
148
|
+
|
|
149
|
+
def _make_indices(self) -> List[int]:
|
|
150
|
+
"""Builds the dataset indices for the specified split."""
|
|
151
|
+
train_indices, val_indices, test_indices = [], [], []
|
|
152
|
+
|
|
153
|
+
for index, image_file in enumerate(self._image_files):
|
|
154
|
+
array_id = self._extract_micro_array_id(image_file)
|
|
155
|
+
|
|
156
|
+
if array_id == "ZT76":
|
|
157
|
+
val_indices.append(index)
|
|
158
|
+
elif array_id in {"ZT111", "ZT199", "ZT204"}:
|
|
159
|
+
train_indices.append(index)
|
|
160
|
+
elif "test_patches_750" in image_file:
|
|
161
|
+
test_indices.append(index)
|
|
162
|
+
else:
|
|
163
|
+
raise ValueError(f"Invalid microarray value found for file {image_file}")
|
|
164
|
+
|
|
165
|
+
split_indices = {
|
|
166
|
+
"train": train_indices,
|
|
167
|
+
"val": val_indices,
|
|
168
|
+
"test": test_indices,
|
|
169
|
+
None: train_indices + val_indices + test_indices,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return split_indices[self._split]
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""UniToPatho dataset class."""
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import glob
|
|
5
|
+
import os
|
|
6
|
+
from typing import Callable, Dict, List, Literal
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import torch
|
|
10
|
+
from torchvision import tv_tensors
|
|
11
|
+
from typing_extensions import override
|
|
12
|
+
|
|
13
|
+
from eva.vision.data.datasets import _validators
|
|
14
|
+
from eva.vision.data.datasets.classification import base
|
|
15
|
+
from eva.vision.utils import io
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class UniToPatho(base.ImageClassification):
|
|
19
|
+
"""Dataset class for UniToPatho images and corresponding targets."""
|
|
20
|
+
|
|
21
|
+
_expected_dataset_lengths: Dict[str | None, int] = {
|
|
22
|
+
"train": 6270,
|
|
23
|
+
"val": 2399,
|
|
24
|
+
None: 8669,
|
|
25
|
+
}
|
|
26
|
+
"""Expected dataset lengths for the splits and complete dataset."""
|
|
27
|
+
|
|
28
|
+
_license: str = "CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)"
|
|
29
|
+
"""Dataset license."""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
root: str,
|
|
34
|
+
split: Literal["train", "val"] | None = None,
|
|
35
|
+
transforms: Callable | None = None,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Initialize the dataset.
|
|
38
|
+
|
|
39
|
+
The dataset is split into train and validation by taking into account
|
|
40
|
+
the patient IDs to avoid any data leakage.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
root: Path to the root directory of the dataset.
|
|
44
|
+
split: Dataset split to use. If `None`, the entire dataset is used.
|
|
45
|
+
transforms: A function/transform which returns a transformed
|
|
46
|
+
version of the raw data samples.
|
|
47
|
+
"""
|
|
48
|
+
super().__init__(transforms=transforms)
|
|
49
|
+
|
|
50
|
+
self._root = root
|
|
51
|
+
self._split = split
|
|
52
|
+
|
|
53
|
+
self._indices: List[int] = []
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
@override
|
|
57
|
+
def classes(self) -> List[str]:
|
|
58
|
+
return ["HP", "NORM", "TA.HG", "TA.LG", "TVA.HG", "TVA.LG"]
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
@override
|
|
62
|
+
def class_to_idx(self) -> Dict[str, int]:
|
|
63
|
+
return {"HP": 0, "NORM": 1, "TA.HG": 2, "TA.LG": 3, "TVA.HG": 4, "TVA.LG": 5}
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def _dataset_path(self) -> str:
|
|
67
|
+
"""Returns the path of the image data of the dataset."""
|
|
68
|
+
return os.path.join(self._root, "800")
|
|
69
|
+
|
|
70
|
+
@functools.cached_property
|
|
71
|
+
def _image_files(self) -> List[str]:
|
|
72
|
+
"""Return the list of image files in the dataset.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
List of image file paths.
|
|
76
|
+
"""
|
|
77
|
+
files_pattern = os.path.join(self._dataset_path, "**/*.png")
|
|
78
|
+
image_files = list(glob.glob(files_pattern, recursive=True))
|
|
79
|
+
return sorted(image_files)
|
|
80
|
+
|
|
81
|
+
@functools.cached_property
|
|
82
|
+
def _manifest(self) -> pd.DataFrame:
|
|
83
|
+
"""Returns the train.csv & test.csv files as dataframe."""
|
|
84
|
+
df_train = pd.read_csv(os.path.join(self._dataset_path, "train.csv"))
|
|
85
|
+
df_val = pd.read_csv(os.path.join(self._dataset_path, "test.csv"))
|
|
86
|
+
df_train["split"], df_val["split"] = "train", "val"
|
|
87
|
+
return pd.concat([df_train, df_val], axis=0).set_index("image_id")
|
|
88
|
+
|
|
89
|
+
@override
|
|
90
|
+
def filename(self, index: int) -> str:
|
|
91
|
+
image_path = self._image_files[self._indices[index]]
|
|
92
|
+
return os.path.relpath(image_path, self._dataset_path)
|
|
93
|
+
|
|
94
|
+
@override
|
|
95
|
+
def prepare_data(self) -> None:
|
|
96
|
+
_validators.check_dataset_exists(self._root, True)
|
|
97
|
+
|
|
98
|
+
@override
|
|
99
|
+
def configure(self) -> None:
|
|
100
|
+
self._indices = self._make_indices()
|
|
101
|
+
|
|
102
|
+
@override
|
|
103
|
+
def validate(self) -> None:
|
|
104
|
+
_validators.check_dataset_integrity(
|
|
105
|
+
self,
|
|
106
|
+
length=self._expected_dataset_lengths[self._split],
|
|
107
|
+
n_classes=6,
|
|
108
|
+
first_and_last_labels=("HP", "TVA.LG"),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
@override
|
|
112
|
+
def load_image(self, index: int) -> tv_tensors.Image:
|
|
113
|
+
image_path = self._image_files[self._indices[index]]
|
|
114
|
+
return io.read_image_as_tensor(image_path)
|
|
115
|
+
|
|
116
|
+
@override
|
|
117
|
+
def load_target(self, index: int) -> torch.Tensor:
|
|
118
|
+
target = self._extract_class(self._image_files[self._indices[index]])
|
|
119
|
+
return torch.tensor(target, dtype=torch.long)
|
|
120
|
+
|
|
121
|
+
@override
|
|
122
|
+
def __len__(self) -> int:
|
|
123
|
+
return len(self._indices)
|
|
124
|
+
|
|
125
|
+
def _print_license(self) -> None:
|
|
126
|
+
"""Prints the dataset license."""
|
|
127
|
+
print(f"Dataset license: {self._license}")
|
|
128
|
+
|
|
129
|
+
def _extract_image_id(self, image_file: str) -> str:
|
|
130
|
+
"""Extracts the image_id from the file name."""
|
|
131
|
+
return os.path.basename(image_file)
|
|
132
|
+
|
|
133
|
+
def _extract_class(self, file: str) -> int:
|
|
134
|
+
image_id = self._extract_image_id(file)
|
|
135
|
+
return int(self._manifest.at[image_id, "top_label"])
|
|
136
|
+
|
|
137
|
+
def _make_indices(self) -> List[int]:
|
|
138
|
+
"""Builds the dataset indices for the specified split."""
|
|
139
|
+
train_indices = []
|
|
140
|
+
val_indices = []
|
|
141
|
+
|
|
142
|
+
for index, image_file in enumerate(self._image_files):
|
|
143
|
+
image_id = self._extract_image_id(image_file)
|
|
144
|
+
split = self._manifest.at[image_id, "split"]
|
|
145
|
+
|
|
146
|
+
if split == "train":
|
|
147
|
+
train_indices.append(index)
|
|
148
|
+
elif split == "val":
|
|
149
|
+
val_indices.append(index)
|
|
150
|
+
else:
|
|
151
|
+
raise ValueError(f"Invalid split value found: {split}")
|
|
152
|
+
|
|
153
|
+
split_indices = {
|
|
154
|
+
"train": train_indices,
|
|
155
|
+
"val": val_indices,
|
|
156
|
+
None: train_indices + val_indices,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return split_indices[self._split]
|
|
@@ -14,7 +14,7 @@ class EmbeddingsSegmentationDataset(embeddings_base.EmbeddingsDataset[tv_tensors
|
|
|
14
14
|
"""Embeddings segmentation dataset."""
|
|
15
15
|
|
|
16
16
|
@override
|
|
17
|
-
def
|
|
17
|
+
def load_embeddings(self, index: int) -> List[torch.Tensor]:
|
|
18
18
|
filename = self.filename(index)
|
|
19
19
|
embeddings_path = os.path.join(self._root, filename)
|
|
20
20
|
embeddings = torch.load(embeddings_path, map_location="cpu")
|
|
@@ -23,7 +23,7 @@ class EmbeddingsSegmentationDataset(embeddings_base.EmbeddingsDataset[tv_tensors
|
|
|
23
23
|
return [tensor.squeeze(0) for tensor in embeddings]
|
|
24
24
|
|
|
25
25
|
@override
|
|
26
|
-
def
|
|
26
|
+
def load_target(self, index: int) -> tv_tensors.Mask:
|
|
27
27
|
filename = self._data.at[index, self._column_mapping["target"]]
|
|
28
28
|
mask_path = os.path.join(self._root, filename)
|
|
29
29
|
semantic_labels = torch.load(mask_path, map_location="cpu")
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"""Pathology FMs from MahmoodLab."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
|
-
from pathlib import Path
|
|
5
3
|
from typing import Tuple
|
|
6
4
|
|
|
7
|
-
import
|
|
8
|
-
|
|
5
|
+
import timm
|
|
6
|
+
import torch
|
|
9
7
|
from torch import nn
|
|
10
8
|
|
|
11
9
|
from eva.vision.models import wrappers
|
|
@@ -18,7 +16,6 @@ def mahmood_uni(
|
|
|
18
16
|
dynamic_img_size: bool = True,
|
|
19
17
|
out_indices: int | Tuple[int, ...] | None = None,
|
|
20
18
|
hf_token: str | None = None,
|
|
21
|
-
download_dir: str = os.path.join(str(Path.home()), ".cache/eva"),
|
|
22
19
|
) -> nn.Module:
|
|
23
20
|
"""Initializes UNI model from MahmoodLab.
|
|
24
21
|
|
|
@@ -27,29 +24,59 @@ def mahmood_uni(
|
|
|
27
24
|
the grid size (interpolate abs and/or ROPE pos) in the forward pass.
|
|
28
25
|
out_indices: Whether and which multi-level patch embeddings to return.
|
|
29
26
|
hf_token: HuggingFace token to download the model.
|
|
30
|
-
download_dir: Directory to download the model checkpoint.
|
|
31
27
|
|
|
32
28
|
Returns:
|
|
33
29
|
The model instance.
|
|
34
30
|
"""
|
|
35
|
-
|
|
36
|
-
if not os.path.exists(checkpoint_path):
|
|
37
|
-
logger.info(f"Downloading the model checkpoint to {download_dir} ...")
|
|
38
|
-
os.makedirs(download_dir, exist_ok=True)
|
|
39
|
-
_utils.huggingface_login(hf_token)
|
|
40
|
-
huggingface_hub.hf_hub_download(
|
|
41
|
-
"MahmoodLab/UNI",
|
|
42
|
-
filename="pytorch_model.bin",
|
|
43
|
-
local_dir=download_dir,
|
|
44
|
-
force_download=True,
|
|
45
|
-
)
|
|
31
|
+
_utils.huggingface_login(hf_token)
|
|
46
32
|
|
|
47
33
|
return wrappers.TimmModel(
|
|
48
|
-
model_name="
|
|
34
|
+
model_name="hf-hub:MahmoodLab/uni",
|
|
35
|
+
pretrained=True,
|
|
49
36
|
out_indices=out_indices,
|
|
50
37
|
model_kwargs={
|
|
51
38
|
"init_values": 1e-5,
|
|
52
39
|
"dynamic_img_size": dynamic_img_size,
|
|
53
40
|
},
|
|
54
|
-
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@register_model("pathology/mahmood_uni2_h")
|
|
45
|
+
def mahmood_uni2_h(
|
|
46
|
+
dynamic_img_size: bool = True,
|
|
47
|
+
out_indices: int | Tuple[int, ...] | None = None,
|
|
48
|
+
hf_token: str | None = None,
|
|
49
|
+
) -> nn.Module:
|
|
50
|
+
"""Initializes UNI model from MahmoodLab.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
dynamic_img_size: Support different input image sizes by allowing to change
|
|
54
|
+
the grid size (interpolate abs and/or ROPE pos) in the forward pass.
|
|
55
|
+
out_indices: Whether and which multi-level patch embeddings to return.
|
|
56
|
+
hf_token: HuggingFace token to download the model.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
The model instance.
|
|
60
|
+
"""
|
|
61
|
+
_utils.huggingface_login(hf_token)
|
|
62
|
+
|
|
63
|
+
return wrappers.TimmModel(
|
|
64
|
+
model_name="hf-hub:MahmoodLab/UNI2-h",
|
|
65
|
+
pretrained=True,
|
|
66
|
+
out_indices=out_indices,
|
|
67
|
+
model_kwargs={
|
|
68
|
+
"img_size": 224,
|
|
69
|
+
"patch_size": 14,
|
|
70
|
+
"depth": 24,
|
|
71
|
+
"num_heads": 24,
|
|
72
|
+
"init_values": 1e-5,
|
|
73
|
+
"embed_dim": 1536,
|
|
74
|
+
"mlp_ratio": 2.66667 * 2,
|
|
75
|
+
"num_classes": 0,
|
|
76
|
+
"no_embed_class": True,
|
|
77
|
+
"mlp_layer": timm.layers.SwiGLUPacked,
|
|
78
|
+
"act_layer": torch.nn.SiLU,
|
|
79
|
+
"reg_tokens": 8,
|
|
80
|
+
"dynamic_img_size": dynamic_img_size,
|
|
81
|
+
},
|
|
55
82
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kaiko-eva
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Evaluation Framework for oncology foundation models.
|
|
5
5
|
Keywords: machine-learning,evaluation-framework,oncology,foundation-models
|
|
6
6
|
Author-Email: Ioannis Gatopoulos <ioannis@kaiko.ai>, =?utf-8?q?Nicolas_K=C3=A4nzig?= <nicolas@kaiko.ai>, Roman Moser <roman@kaiko.ai>
|
|
@@ -25,14 +25,14 @@ eva/core/data/datamodules/schemas.py,sha256=rzcf3uow6T6slVSwxEGDVmpi3QUvkiDoT_gC
|
|
|
25
25
|
eva/core/data/datasets/__init__.py,sha256=jWPxT3gjQjwS6HqVZAb7KhMgzgklPgHeH51iPxDh_Tg,493
|
|
26
26
|
eva/core/data/datasets/base.py,sha256=BLzlRFuByhrGmI7NFwn7-Tw0vpSYSRhl2Y65iX4KaMw,2526
|
|
27
27
|
eva/core/data/datasets/classification/__init__.py,sha256=wJ2jD9YODftt-dMcMf0TbCjJt47qXYBKkD4-XXajvRQ,340
|
|
28
|
-
eva/core/data/datasets/classification/embeddings.py,sha256=
|
|
29
|
-
eva/core/data/datasets/classification/multi_embeddings.py,sha256=
|
|
28
|
+
eva/core/data/datasets/classification/embeddings.py,sha256=hBO6dIRHAhoCaYb3ANc9fgvdBjyQNKPTrIhjc9y8-Ys,1108
|
|
29
|
+
eva/core/data/datasets/classification/multi_embeddings.py,sha256=4hQy4741NDKqWCpm3kGq7aC28DF5gcwUuIpYhnbTyeM,4601
|
|
30
30
|
eva/core/data/datasets/dataset.py,sha256=tA6Wd_7vqOE9GsukSWrgN9zaZKtKCHaE58SqIfWxWdg,124
|
|
31
|
-
eva/core/data/datasets/embeddings.py,sha256=
|
|
31
|
+
eva/core/data/datasets/embeddings.py,sha256=0y7Fa4zHr4Y0mcB9pyP26YaeTPtetwVf_n6fnkTcgp0,5541
|
|
32
32
|
eva/core/data/datasets/typings.py,sha256=KSmckjsU64pGV-8uSLkD1HmvPKYlyypngiRx9yy4RDs,383
|
|
33
33
|
eva/core/data/samplers/__init__.py,sha256=rRrKtg4l6YoziD3M0MkctQvX1NdRxaQa5sm6RHH_jXc,315
|
|
34
34
|
eva/core/data/samplers/classification/__init__.py,sha256=gvv7BH4lG9JlkMaTOnaL0f4k1ghiVBgrH64bh1-rreQ,147
|
|
35
|
-
eva/core/data/samplers/classification/balanced.py,sha256=
|
|
35
|
+
eva/core/data/samplers/classification/balanced.py,sha256=MGTHt-WQaQKiJ5A1D_P6HJ6YzPTD-ERhc0R7rNMFqfg,3788
|
|
36
36
|
eva/core/data/samplers/random.py,sha256=znl0Z9a-X-3attP-EH9jwwo83n40FXW_JzOLNZAml_c,1252
|
|
37
37
|
eva/core/data/samplers/sampler.py,sha256=0DOLUzFoweqEubuO1A4bZBRU0AWFoWGWrO3pawRT-eI,877
|
|
38
38
|
eva/core/data/splitting/__init__.py,sha256=VQJ8lfakbv6y2kAk3VDtITAvh7kcZo3H1JwJBc5jT08,198
|
|
@@ -55,6 +55,7 @@ eva/core/loggers/log/image.py,sha256=iUwntQCdRNLtkSdqu8CvV34l06zPYVo4NAW2gUeiJIM
|
|
|
55
55
|
eva/core/loggers/log/parameters.py,sha256=7Xi-I5gQvEVv71d58bwdZ-Hb4287NXxaUyMfriq_KDU,1634
|
|
56
56
|
eva/core/loggers/log/utils.py,sha256=k4Q7uKpAQctfDv0EEYPnPv6wt9LnckEeqGvbYSLfKO0,415
|
|
57
57
|
eva/core/loggers/loggers.py,sha256=igHxdxJSotWSg6nEOKnfFuBszzblHgi8T7sBrE00FEs,166
|
|
58
|
+
eva/core/loggers/utils/wandb.py,sha256=GdwzEeFTAng5kl_kIVRxKL7rvwqyicQHSaZS8VSMXvU,747
|
|
58
59
|
eva/core/losses/__init__.py,sha256=D-Mp9fUFFFoH9YYWntVH3B839zHS3GjFJzkbQThzj6Y,118
|
|
59
60
|
eva/core/losses/cross_entropy.py,sha256=Sunz7ogDAJpGvZtuk9cAxKZJBO08CKIEvbCoewEvees,862
|
|
60
61
|
eva/core/metrics/__init__.py,sha256=-9Qch4npEQpy3oF6NUhh9WinCmFBFe0D2eEYCR0S0xU,558
|
|
@@ -72,7 +73,7 @@ eva/core/metrics/structs/schemas.py,sha256=ZaSrx0j_NfIwT7joMUD1LyrKdAXTLaeSzWYTH
|
|
|
72
73
|
eva/core/metrics/structs/typings.py,sha256=qJd-FiD2IhJgBeo8FyP0vpVUIH4RKb1k6zYvHtjUA04,388
|
|
73
74
|
eva/core/models/__init__.py,sha256=T6Fo886LxMj-Y58_ylzkPkFSnFR2aISiMIbuO_weC4s,430
|
|
74
75
|
eva/core/models/modules/__init__.py,sha256=QJWJ42BceXZBzDGgk5FHBcCaRrB9egTFKVF6gDsBYfM,255
|
|
75
|
-
eva/core/models/modules/head.py,sha256=
|
|
76
|
+
eva/core/models/modules/head.py,sha256=2rPlo2Osuq77gjrJmvQKCvNTaawvQRirK2CM2o24_xs,5184
|
|
76
77
|
eva/core/models/modules/inference.py,sha256=ih-0Rr2oNf2N6maiXPOW7XH5KVwUT1_MOxnJKOhJ1uQ,978
|
|
77
78
|
eva/core/models/modules/module.py,sha256=LtjYxTZb7UY0owonmt_yQ5EySw3sX-xD9HLN2io8EK4,6697
|
|
78
79
|
eva/core/models/modules/typings.py,sha256=yFMJCE4Nrfd8VEXU1zk8p6Sz5M7UslwitYPVC2OPLSY,776
|
|
@@ -96,8 +97,8 @@ eva/core/trainers/__init__.py,sha256=jhsKJF7HAae7EOiG3gKIAHH_h3dZlTE2JRcCHJmOzJc
|
|
|
96
97
|
eva/core/trainers/_logging.py,sha256=gi4FqPy2GuVmh0WZY6mYwF7zMPvnoFA050B0XdCP6PU,2571
|
|
97
98
|
eva/core/trainers/_recorder.py,sha256=y6i5hfXftWjeV3eQHmMjUOkWumnZ2QNv_u275LLmvPA,7702
|
|
98
99
|
eva/core/trainers/_utils.py,sha256=M3h8lVhUmkeSiEXpX9hRdMvThGFCnTP15gv-hd1CZkc,321
|
|
99
|
-
eva/core/trainers/functional.py,sha256=
|
|
100
|
-
eva/core/trainers/trainer.py,sha256=
|
|
100
|
+
eva/core/trainers/functional.py,sha256=rLtQZw8TcAa4NYIf901TmoQiJDNm4RGVLN-64nku3Jo,4445
|
|
101
|
+
eva/core/trainers/trainer.py,sha256=a3OwLWOZKDqxayrd0ugUmxJKyQx6XDb4GHtdL8-AEV0,4826
|
|
101
102
|
eva/core/utils/__init__.py,sha256=cndVBvtYxEW7hykH39GCNVI86zkXNn8Lw2A0sUJHS04,237
|
|
102
103
|
eva/core/utils/clone.py,sha256=qcThZOuAs1cs0uV3BL5eKeM2VIBjuRPBe1t-NiUFM5Y,569
|
|
103
104
|
eva/core/utils/io/__init__.py,sha256=Py03AmoxhmTHkro6CzNps27uXKkXPzdA18mG97xHhWI,172
|
|
@@ -116,17 +117,21 @@ eva/vision/callbacks/loggers/batch/__init__.py,sha256=DVYP7Aonbi4wg_ERHRj_8kb87E
|
|
|
116
117
|
eva/vision/callbacks/loggers/batch/base.py,sha256=hcAd5iiHvjZ0DIf4Qt4ENT54D6ky_1OO4rKQZqeo-1k,3628
|
|
117
118
|
eva/vision/callbacks/loggers/batch/segmentation.py,sha256=GYh2kfexW5pUZ0BdApYJI3e8xsuNkjIzkj5jnuKtHR4,6886
|
|
118
119
|
eva/vision/data/__init__.py,sha256=aoKPmX8P2Q2k2W3nlq8vFU41FV6Sze-0SDuWtU-ETh4,111
|
|
119
|
-
eva/vision/data/datasets/__init__.py,sha256=
|
|
120
|
+
eva/vision/data/datasets/__init__.py,sha256=wvbkhBv_yS7hHMdMR-QpNHMkGoGzSL0L33XaXUwXTpM,1040
|
|
120
121
|
eva/vision/data/datasets/_utils.py,sha256=epPcaYE4w2_LtUKLLQJh6qQxUNVBe22JA06k4WUerYQ,1430
|
|
121
122
|
eva/vision/data/datasets/_validators.py,sha256=77WZj8ewsuxUjW5WegJ-7zDuR6WdF5JbaOYdywhKIK4,2594
|
|
122
|
-
eva/vision/data/datasets/classification/__init__.py,sha256=
|
|
123
|
+
eva/vision/data/datasets/classification/__init__.py,sha256=5fOGZxKGPeMCf3Jd9qAOYADPrkZnYg97_QE4DC79AMI,1074
|
|
123
124
|
eva/vision/data/datasets/classification/bach.py,sha256=kZba1dQlJWZAmA03akJ4fVUU-y9W8ezOwlgs2zL-QrE,5432
|
|
124
125
|
eva/vision/data/datasets/classification/base.py,sha256=Ci0HoOhOuHwICTi1TUGA1PwZe642RywolTVfMhKrFHk,2772
|
|
126
|
+
eva/vision/data/datasets/classification/bracs.py,sha256=e9SqnQ_HVm9ypQiwsFi5tbngqs0yEZsfVBk3pt91W80,3347
|
|
127
|
+
eva/vision/data/datasets/classification/breakhis.py,sha256=_rzGx5IgJSW73es7Gusr_oOzI1jHCPhRH8yRvqcmuqw,6905
|
|
125
128
|
eva/vision/data/datasets/classification/camelyon16.py,sha256=sChvRo0jbOVUMJvfpsFxgFOsYgci3v9wjeMBEjUysJU,8287
|
|
126
129
|
eva/vision/data/datasets/classification/crc.py,sha256=8qjz9OklLg1gAr46RKZdlClmlO9awwfp0dkTs8v5jTE,5670
|
|
130
|
+
eva/vision/data/datasets/classification/gleason_arvaniti.py,sha256=CCXeBA3dlic7ZRiarf4_f76qkct8PMNM_tCfz3IRUPA,5893
|
|
127
131
|
eva/vision/data/datasets/classification/mhist.py,sha256=xzShPncSfAV6Q5ojfimeq748MfA0n77fGWa9EpdRzYU,3055
|
|
128
132
|
eva/vision/data/datasets/classification/panda.py,sha256=BU_gDoX3ZSDUugwaO2n0XSZhzseK1rkPoHMRoJLGL84,7303
|
|
129
133
|
eva/vision/data/datasets/classification/patch_camelyon.py,sha256=fElKteZKx4M6AjylnhhgNH1jewHegWc1K8h4FFKp0gE,7171
|
|
134
|
+
eva/vision/data/datasets/classification/unitopatho.py,sha256=vC-dFbhETfDD9paTeQ73Dg1vLPWsK12AfpiBFznESaM,5151
|
|
130
135
|
eva/vision/data/datasets/classification/wsi.py,sha256=x3mQ8iwyiSdfQOjJuV7_cd8-LRjjhY9tjtzuD8O87Lg,4099
|
|
131
136
|
eva/vision/data/datasets/segmentation/__init__.py,sha256=hGNr7BM_StxvmlOKWWfHp615qgsrB6BB3qMOiYhE0Og,791
|
|
132
137
|
eva/vision/data/datasets/segmentation/_total_segmentator.py,sha256=DTaQaAisY7j1h0-zYk1_81Sr4b3D9PTMieYX0PMPtIc,3127
|
|
@@ -134,7 +139,7 @@ eva/vision/data/datasets/segmentation/_utils.py,sha256=ps1qpuEkPgvwUw6H-KKaLaYqD
|
|
|
134
139
|
eva/vision/data/datasets/segmentation/base.py,sha256=11IMODMB7KJ8Bs5p7MyOsBXCyPFJXfYcDLAIMitUwEk,3023
|
|
135
140
|
eva/vision/data/datasets/segmentation/bcss.py,sha256=NHjHd1tgIfIw6TxsZTGb63iMEwXFbWX_JAwRT5WVsj4,8274
|
|
136
141
|
eva/vision/data/datasets/segmentation/consep.py,sha256=Pw3LvVIK2scj_ys7rVNRb9B8snP8HlDIAbaI3v6ObQk,6056
|
|
137
|
-
eva/vision/data/datasets/segmentation/embeddings.py,sha256=
|
|
142
|
+
eva/vision/data/datasets/segmentation/embeddings.py,sha256=RsTuAwGEJPnWPY7q3pwcjmqtEj0wtRBNRBD4a0RcGtA,1218
|
|
138
143
|
eva/vision/data/datasets/segmentation/lits.py,sha256=cBRU5lkiTMAi_ZwyDQUN3ODyXUlLtuMWFLPDajcZnOo,7194
|
|
139
144
|
eva/vision/data/datasets/segmentation/lits_balanced.py,sha256=s5kPfqB41Vkcm5Jh34mLAO0NweMSIlV2fMXJsRjJsF8,3384
|
|
140
145
|
eva/vision/data/datasets/segmentation/monusac.py,sha256=OTWHAD1b48WeT6phVf466w_nJUOGdBCGKWiWw68PAdw,8423
|
|
@@ -194,7 +199,7 @@ eva/vision/models/networks/backbones/pathology/gigapath.py,sha256=mfGXtKhY7XLpKQ
|
|
|
194
199
|
eva/vision/models/networks/backbones/pathology/histai.py,sha256=X_we3U7GK91RrXyOX2PJB-YFDF2ozdL2fzZhNxm9SVU,1914
|
|
195
200
|
eva/vision/models/networks/backbones/pathology/kaiko.py,sha256=GSdBG4WXrs1PWB2hr-sy_dFe2riwpPKwHx71esDoVfE,3952
|
|
196
201
|
eva/vision/models/networks/backbones/pathology/lunit.py,sha256=ku4lr9pWeeHatHN4x4OVgwlve9sVqiRqIbgI0PXLiqg,2160
|
|
197
|
-
eva/vision/models/networks/backbones/pathology/mahmood.py,sha256=
|
|
202
|
+
eva/vision/models/networks/backbones/pathology/mahmood.py,sha256=VYoVWrMNkoaEqa0och-GbwGd0VISQmbtzk1dSBZ1M0I,2464
|
|
198
203
|
eva/vision/models/networks/backbones/pathology/owkin.py,sha256=uWJV5fgY7UZX6ilgGzkPY9fnlOiF03W7E8rc9TmlHGg,1231
|
|
199
204
|
eva/vision/models/networks/backbones/pathology/paige.py,sha256=MjOLgdEKk8tdAIpCiHelasGwPE7xgzaooW6EE7IsuEE,1642
|
|
200
205
|
eva/vision/models/networks/backbones/registry.py,sha256=anjILtEHHB6Ltwiw22h1bsgWtIjh_l5_fkPh87K7-d0,1631
|
|
@@ -225,8 +230,8 @@ eva/vision/utils/io/image.py,sha256=IdOkr5MYqhYHz8U9drZ7wULTM3YHwCWSjZlu_Qdl4GQ,
|
|
|
225
230
|
eva/vision/utils/io/mat.py,sha256=qpGifyjmpE0Xhv567Si7-zxKrgkgE0sywP70cHiLFGU,808
|
|
226
231
|
eva/vision/utils/io/nifti.py,sha256=4YoKjKuoNdE0qY7tYB_WlnSsYAx2oBzZRZXczc_8HAU,2555
|
|
227
232
|
eva/vision/utils/io/text.py,sha256=qYgfo_ZaDZWfG02NkVVYzo5QFySqdCCz5uLA9d-zXtI,701
|
|
228
|
-
kaiko_eva-0.
|
|
229
|
-
kaiko_eva-0.
|
|
230
|
-
kaiko_eva-0.
|
|
231
|
-
kaiko_eva-0.
|
|
232
|
-
kaiko_eva-0.
|
|
233
|
+
kaiko_eva-0.2.0.dist-info/METADATA,sha256=CTFbtAErERl1SU0--56Y5-d1tXrr1vBNtNVkaV3orrA,24899
|
|
234
|
+
kaiko_eva-0.2.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
|
235
|
+
kaiko_eva-0.2.0.dist-info/entry_points.txt,sha256=6CSLu9bmQYJSXEg8gbOzRhxH0AGs75BB-vPm3VvfcNE,88
|
|
236
|
+
kaiko_eva-0.2.0.dist-info/licenses/LICENSE,sha256=e6AEzr7j_R-PYr2qLO-JwLn8y70jbVD3U2mxbRmwcI4,11338
|
|
237
|
+
kaiko_eva-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|