careamics 0.1.0rc1__py3-none-any.whl → 0.1.0rc2__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 careamics might be problematic. Click here for more details.

@@ -1,7 +1,7 @@
1
1
  """Provide utilities for exporting models to BioImage model zoo."""
2
2
 
3
3
  __all__ = [
4
- "build_zip_model",
4
+ "save_bioimage_model",
5
5
  "import_bioimage_model",
6
6
  "get_default_model_specs",
7
7
  "PYTORCH_STATE_DICT",
@@ -9,7 +9,7 @@ __all__ = [
9
9
 
10
10
  from .io import (
11
11
  PYTORCH_STATE_DICT,
12
- build_zip_model,
13
- get_default_model_specs,
14
12
  import_bioimage_model,
13
+ save_bioimage_model,
15
14
  )
15
+ from .rdf import get_default_model_specs
careamics/bioimage/io.py CHANGED
@@ -5,125 +5,20 @@ from typing import Union
5
5
  import torch
6
6
  from bioimageio.core import load_resource_description
7
7
  from bioimageio.core.build_spec import build_model
8
- from bioimageio.spec.model.raw_nodes import Model
9
8
 
10
- from careamics.config.config import (
11
- Configuration,
12
- )
9
+ from careamics.config.config import Configuration
10
+ from careamics.utils.context import cwd
13
11
 
14
12
  PYTORCH_STATE_DICT = "pytorch_state_dict"
15
13
 
16
14
 
17
- def _get_model_doc(name: str) -> str:
18
- """
19
- Return markdown documentation path for the provided model.
20
-
21
- Parameters
22
- ----------
23
- name : str
24
- Model's name.
25
-
26
- Returns
27
- -------
28
- str
29
- Path to the model's markdown documentation.
30
-
31
- Raises
32
- ------
33
- FileNotFoundError
34
- If the documentation file was not found.
35
- """
36
- doc = Path(__file__).parent.joinpath("docs").joinpath(f"{name}.md")
37
- if doc.exists():
38
- return str(doc.absolute())
39
- else:
40
- raise FileNotFoundError(f"Documentation for {name} was not found.")
41
-
42
-
43
- def get_default_model_specs(
44
- name: str, mean: float, std: float, is_3D: bool = False
45
- ) -> dict:
46
- """
47
- Return the default bioimage.io specs for the provided model's name.
48
-
49
- Currently only supports `n2v` model.
50
-
51
- Parameters
52
- ----------
53
- name : str
54
- Algorithm's name.
55
- mean : float
56
- Mean of the dataset.
57
- std : float
58
- Std of the dataset.
59
- is_3D : bool, optional
60
- Whether the model is 3D or not, by default False.
61
-
62
- Returns
63
- -------
64
- dict
65
- Model specs compatible with bioimage.io export.
66
- """
67
- rdf = {
68
- "name": "Noise2Void",
69
- "description": "Self-supervised denoising.",
70
- "license": "BSD-3-Clause",
71
- "authors": [
72
- {"name": "Alexander Krull"},
73
- {"name": "Tim-Oliver Buchholz"},
74
- {"name": "Florian Jug"},
75
- ],
76
- "cite": [
77
- {
78
- "doi": "10.48550/arXiv.1811.10980",
79
- "text": 'A. Krull, T.-O. Buchholz and F. Jug, "Noise2Void - Learning '
80
- 'Denoising From Single Noisy Images," 2019 IEEE/CVF '
81
- "Conference on Computer Vision and Pattern Recognition "
82
- "(CVPR), 2019, pp. 2124-2132",
83
- }
84
- ],
85
- # "input_axes": ["bcyx"], <- overriden in save_as_bioimage
86
- "preprocessing": [ # for multiple inputs
87
- [ # multiple processes per input
88
- {
89
- "kwargs": {
90
- "axes": "zyx" if is_3D else "yx",
91
- "mean": [mean],
92
- "mode": "fixed",
93
- "std": [std],
94
- },
95
- "name": "zero_mean_unit_variance",
96
- }
97
- ]
98
- ],
99
- # "output_axes": ["bcyx"], <- overriden in save_as_bioimage
100
- "postprocessing": [ # for multiple outputs
101
- [ # multiple processes per input
102
- {
103
- "kwargs": {
104
- "axes": "zyx" if is_3D else "yx",
105
- "gain": [std],
106
- "offset": [mean],
107
- },
108
- "name": "scale_linear",
109
- }
110
- ]
111
- ],
112
- "tags": ["unet", "denoising", "Noise2Void", "tensorflow", "napari"],
113
- }
114
-
115
- rdf["documentation"] = _get_model_doc(name)
116
-
117
- return rdf
118
-
119
-
120
- def build_zip_model(
15
+ def save_bioimage_model(
121
16
  path: Union[str, Path],
122
17
  config: Configuration,
123
- model_specs: dict,
124
- ) -> Model:
18
+ specs: dict,
19
+ ) -> None:
125
20
  """
126
- Build bioimage model zip file from model specification data.
21
+ Build bioimage model zip file from model RDF data.
127
22
 
128
23
  Parameters
129
24
  ----------
@@ -131,71 +26,86 @@ def build_zip_model(
131
26
  Path to the model zip file.
132
27
  config : Configuration
133
28
  Configuration object.
134
- model_specs : dict
135
- Model specification data.
136
-
137
- Returns
138
- -------
139
- Model
140
- Bioimage model object.
29
+ specs : dict
30
+ Model RDF dict.
141
31
  """
142
32
  workdir = config.working_directory
143
33
 
144
- # load best checkpoint
145
- checkpoint_path = workdir.joinpath(f"{config.experiment_name}_best.pth").absolute()
146
- checkpoint = torch.load(checkpoint_path, map_location="cpu")
147
-
148
- # save chekpoint entries in separate files
149
- weight_path = workdir.joinpath("model_weights.pth")
150
- torch.save(checkpoint["model_state_dict"], weight_path)
151
-
152
- optim_path = workdir.joinpath("optim.pth")
153
- torch.save(checkpoint["optimizer_state_dict"], optim_path)
154
-
155
- scheduler_path = workdir.joinpath("scheduler.pth")
156
- torch.save(checkpoint["scheduler_state_dict"], scheduler_path)
157
-
158
- grad_path = workdir.joinpath("grad.pth")
159
- torch.save(checkpoint["grad_scaler_state_dict"], grad_path)
160
-
161
- config_path = workdir.joinpath("config.pth")
162
- torch.save(config.model_dump(), config_path)
163
-
164
- # Create attachments
165
- attachments = [
166
- str(optim_path),
167
- str(scheduler_path),
168
- str(grad_path),
169
- str(config_path),
170
- ]
171
-
172
- model_specs.update(
173
- {
174
- "weight_type": PYTORCH_STATE_DICT,
175
- "weight_uri": str(weight_path),
176
- "attachments": {"files": attachments},
177
- }
178
- )
34
+ # temporary folder
35
+ temp_folder = Path.home().joinpath(".careamics", "bmz_tmp")
36
+ temp_folder.mkdir(exist_ok=True, parents=True)
37
+
38
+ # change working directory to the temp folder
39
+ with cwd(temp_folder):
40
+ # load best checkpoint
41
+ checkpoint_path = workdir.joinpath(
42
+ f"{config.experiment_name}_best.pth"
43
+ ).absolute()
44
+ checkpoint = torch.load(checkpoint_path, map_location="cpu")
45
+
46
+ # save chekpoint entries in separate files
47
+ weight_path = Path("model_weights.pth")
48
+ torch.save(checkpoint["model_state_dict"], weight_path)
49
+
50
+ optim_path = Path("optim.pth")
51
+ torch.save(checkpoint["optimizer_state_dict"], optim_path)
52
+
53
+ scheduler_path = Path("scheduler.pth")
54
+ torch.save(checkpoint["scheduler_state_dict"], scheduler_path)
55
+
56
+ grad_path = Path("grad.pth")
57
+ torch.save(checkpoint["grad_scaler_state_dict"], grad_path)
58
+
59
+ config_path = Path("config.pth")
60
+ torch.save(config.model_dump(), config_path)
61
+
62
+ # create attachments
63
+ attachments = [
64
+ str(optim_path),
65
+ str(scheduler_path),
66
+ str(grad_path),
67
+ str(config_path),
68
+ ]
69
+
70
+ # create requirements file
71
+ requirements = Path("requirements.txt")
72
+ with open(requirements, "w") as f:
73
+ f.write("git+https://github.com/CAREamics/careamics.git")
74
+
75
+ algo_config = config.algorithm
76
+ specs.update(
77
+ {
78
+ "weight_type": PYTORCH_STATE_DICT,
79
+ "weight_uri": str(weight_path),
80
+ "architecture": "careamics.models.unet.UNet",
81
+ "pytorch_version": torch.__version__,
82
+ "model_kwargs": {
83
+ "conv_dim": algo_config.get_conv_dim(),
84
+ "depth": algo_config.model_parameters.depth,
85
+ "num_channels_init": algo_config.model_parameters.num_channels_init,
86
+ },
87
+ "dependencies": "pip:" + str(requirements),
88
+ "attachments": {"files": attachments},
89
+ }
90
+ )
179
91
 
180
- if config.algorithm.is_3D:
181
- model_specs["tags"].append("3D")
182
- else:
183
- model_specs["tags"].append("2D")
92
+ if config.algorithm.is_3D:
93
+ specs["tags"].append("3D")
94
+ else:
95
+ specs["tags"].append("2D")
184
96
 
185
- # build model zip
186
- raw_model = build_model(
187
- output_path=Path(path).absolute(),
188
- **model_specs,
189
- )
97
+ # build model zip
98
+ build_model(
99
+ output_path=Path(path).absolute(),
100
+ **specs,
101
+ )
190
102
 
191
- # remove the temporary files
192
- weight_path.unlink()
193
- optim_path.unlink()
194
- scheduler_path.unlink()
195
- grad_path.unlink()
196
- config_path.unlink()
103
+ # remove temporary files
104
+ for file in temp_folder.glob("*"):
105
+ file.unlink()
197
106
 
198
- return raw_model
107
+ # delete temporary folder
108
+ temp_folder.rmdir()
199
109
 
200
110
 
201
111
  def import_bioimage_model(model_path: Union[str, Path]) -> Path:
@@ -219,11 +129,12 @@ def import_bioimage_model(model_path: Union[str, Path]) -> Path:
219
129
  FileNotFoundError
220
130
  If the checkpoint file was not found.
221
131
  """
222
- if isinstance(model_path, str):
223
- model_path = Path(model_path)
132
+ model_path = Path(model_path)
133
+
224
134
  # check the model extension (should be a zip file).
225
135
  if model_path.suffix != ".zip":
226
136
  raise ValueError("Invalid model format. Expected bioimage model zip file.")
137
+
227
138
  # load the model
228
139
  rdf = load_resource_description(model_path)
229
140
 
@@ -0,0 +1,105 @@
1
+ """RDF related methods."""
2
+ from pathlib import Path
3
+
4
+
5
+ def _get_model_doc(name: str) -> str:
6
+ """
7
+ Return markdown documentation path for the provided model.
8
+
9
+ Parameters
10
+ ----------
11
+ name : str
12
+ Model's name.
13
+
14
+ Returns
15
+ -------
16
+ str
17
+ Path to the model's markdown documentation.
18
+
19
+ Raises
20
+ ------
21
+ FileNotFoundError
22
+ If the documentation file was not found.
23
+ """
24
+ doc = Path(__file__).parent.joinpath("docs").joinpath(f"{name}.md")
25
+ if doc.exists():
26
+ return str(doc.absolute())
27
+ else:
28
+ raise FileNotFoundError(f"Documentation for {name} was not found.")
29
+
30
+
31
+ def get_default_model_specs(
32
+ name: str, mean: float, std: float, is_3D: bool = False
33
+ ) -> dict:
34
+ """
35
+ Return the default bioimage.io specs for the provided model's name.
36
+
37
+ Currently only supports `Noise2Void` model.
38
+
39
+ Parameters
40
+ ----------
41
+ name : str
42
+ Algorithm's name.
43
+ mean : float
44
+ Mean of the dataset.
45
+ std : float
46
+ Std of the dataset.
47
+ is_3D : bool, optional
48
+ Whether the model is 3D or not, by default False.
49
+
50
+ Returns
51
+ -------
52
+ dict
53
+ Model specs compatible with bioimage.io export.
54
+ """
55
+ rdf = {
56
+ "name": "Noise2Void",
57
+ "description": "Self-supervised denoising.",
58
+ "license": "BSD-3-Clause",
59
+ "authors": [
60
+ {"name": "Alexander Krull"},
61
+ {"name": "Tim-Oliver Buchholz"},
62
+ {"name": "Florian Jug"},
63
+ ],
64
+ "cite": [
65
+ {
66
+ "doi": "10.48550/arXiv.1811.10980",
67
+ "text": 'A. Krull, T.-O. Buchholz and F. Jug, "Noise2Void - Learning '
68
+ 'Denoising From Single Noisy Images," 2019 IEEE/CVF '
69
+ "Conference on Computer Vision and Pattern Recognition "
70
+ "(CVPR), 2019, pp. 2124-2132",
71
+ }
72
+ ],
73
+ # "input_axes": ["bcyx"], <- overriden in save_as_bioimage
74
+ "preprocessing": [ # for multiple inputs
75
+ [ # multiple processes per input
76
+ {
77
+ "kwargs": {
78
+ "axes": "zyx" if is_3D else "yx",
79
+ "mean": [mean],
80
+ "mode": "fixed",
81
+ "std": [std],
82
+ },
83
+ "name": "zero_mean_unit_variance",
84
+ }
85
+ ]
86
+ ],
87
+ # "output_axes": ["bcyx"], <- overriden in save_as_bioimage
88
+ "postprocessing": [ # for multiple outputs
89
+ [ # multiple processes per input
90
+ {
91
+ "kwargs": {
92
+ "axes": "zyx" if is_3D else "yx",
93
+ "gain": [std],
94
+ "offset": [mean],
95
+ },
96
+ "name": "scale_linear",
97
+ }
98
+ ]
99
+ ],
100
+ "tags": ["unet", "denoising", "Noise2Void", "tensorflow", "napari"],
101
+ }
102
+
103
+ rdf["documentation"] = _get_model_doc(name)
104
+
105
+ return rdf
@@ -13,10 +13,11 @@ from pydantic import (
13
13
  model_validator,
14
14
  )
15
15
 
16
- from .algorithm import Algorithm
16
+ # ignore typing-only-first-party-import in this file (flake8)
17
+ from .algorithm import Algorithm # noqa: TCH001
17
18
  from .config_filter import paths_to_str
18
- from .data import Data
19
- from .training import Training
19
+ from .data import Data # noqa: TCH001
20
+ from .training import Training # noqa: TCH001
20
21
 
21
22
 
22
23
  class Configuration(BaseModel):
careamics/config/data.py CHANGED
@@ -12,7 +12,7 @@ from pydantic import (
12
12
  model_validator,
13
13
  )
14
14
 
15
- from ..utils import check_axes_validity
15
+ from careamics.utils import check_axes_validity
16
16
 
17
17
 
18
18
  class SupportedExtension(str, Enum):
@@ -23,7 +23,7 @@ class SupportedExtension(str, Enum):
23
23
  - tif/tiff: .tiff files.
24
24
  """
25
25
 
26
- TIFF = "tiff"
26
+ TIFF = "tiff" # TODO these should be a single one
27
27
  TIF = "tif"
28
28
 
29
29
  @classmethod
@@ -46,15 +46,11 @@ def _update_axes(array: np.ndarray, axes: str) -> np.ndarray:
46
46
  Updated array.
47
47
  """
48
48
  # concatenate ST axes to N, return NCZYX
49
- if ("S" in axes or "T" in axes) and array.dtype != "O":
49
+ if "S" in axes or "T" in axes:
50
50
  new_axes_len = len(axes.replace("Z", "").replace("YX", ""))
51
51
  # TODO test reshape as it can scramble data, moveaxis is probably better
52
52
  array = array.reshape(-1, *array.shape[new_axes_len:]).astype(np.float32)
53
53
 
54
- elif array.dtype == "O":
55
- for i in range(len(array)):
56
- array[i] = np.expand_dims(array[i], axis=0).astype(np.float32)
57
-
58
54
  else:
59
55
  array = np.expand_dims(array, axis=0).astype(np.float32)
60
56
 
@@ -5,8 +5,9 @@ from typing import Callable, Dict, List, Optional, Tuple, Union
5
5
  import numpy as np
6
6
  import torch
7
7
 
8
- from ..utils import normalize
9
- from ..utils.logging import get_logger
8
+ from careamics.utils import normalize
9
+ from careamics.utils.logging import get_logger
10
+
10
11
  from .dataset_utils import (
11
12
  list_files,
12
13
  read_tiff,
@@ -9,7 +9,8 @@ from typing import Generator, List, Optional, Tuple, Union
9
9
  import numpy as np
10
10
  from skimage.util import view_as_windows
11
11
 
12
- from ..utils.logging import get_logger
12
+ from careamics.utils.logging import get_logger
13
+
13
14
  from .extraction_strategy import ExtractionStrategy
14
15
 
15
16
  logger = get_logger(__name__)
@@ -481,13 +482,11 @@ def generate_patches(
481
482
  elif patch_extraction_method == ExtractionStrategy.SEQUENTIAL:
482
483
  patches = _extract_patches_sequential(sample, patch_size=patch_size)
483
484
 
484
- elif patch_extraction_method == ExtractionStrategy.RANDOM:
485
+ else:
486
+ # random patching
485
487
  patches = _extract_patches_random(sample, patch_size=patch_size)
486
488
 
487
- if patches is None:
488
- raise ValueError("No patch generated")
489
-
490
489
  return patches
491
490
  else:
492
- # no patching
491
+ # no patching, return a generator for the sample
493
492
  return (sample for _ in range(1))
@@ -6,9 +6,10 @@ Methods to set up the datasets for training, validation and prediction.
6
6
  from pathlib import Path
7
7
  from typing import List, Optional, Union
8
8
 
9
- from ..config import Configuration
10
- from ..manipulation import default_manipulate
11
- from ..utils import check_tiling_validity
9
+ from careamics.config import Configuration
10
+ from careamics.manipulation import default_manipulate
11
+ from careamics.utils import check_tiling_validity
12
+
12
13
  from .extraction_strategy import ExtractionStrategy
13
14
  from .in_memory_dataset import InMemoryDataset
14
15
  from .tiff_dataset import TiffDataset
@@ -10,8 +10,9 @@ from typing import Callable, Dict, Generator, List, Optional, Tuple, Union
10
10
  import numpy as np
11
11
  import torch
12
12
 
13
- from ..utils import normalize
14
- from ..utils.logging import get_logger
13
+ from careamics.utils import normalize
14
+ from careamics.utils.logging import get_logger
15
+
15
16
  from .dataset_utils import (
16
17
  list_files,
17
18
  read_tiff,
@@ -53,7 +54,7 @@ class TiffDataset(torch.utils.data.IterableDataset):
53
54
  def __init__(
54
55
  self,
55
56
  data_path: Union[str, Path],
56
- data_format: str,
57
+ data_format: str, # TODO: TiffDataset should not know that they are tiff
57
58
  axes: str,
58
59
  patch_extraction_method: Union[ExtractionStrategy, None],
59
60
  patch_size: Optional[Union[List[int], Tuple[int]]] = None,