careamics 0.1.0rc1__py3-none-any.whl → 0.1.0rc3__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.

Files changed (132) hide show
  1. careamics/__init__.py +14 -4
  2. careamics/callbacks/__init__.py +6 -0
  3. careamics/callbacks/hyperparameters_callback.py +42 -0
  4. careamics/callbacks/progress_bar_callback.py +57 -0
  5. careamics/careamist.py +761 -0
  6. careamics/config/__init__.py +27 -3
  7. careamics/config/algorithm_model.py +167 -0
  8. careamics/config/architectures/__init__.py +17 -0
  9. careamics/config/architectures/architecture_model.py +29 -0
  10. careamics/config/architectures/custom_model.py +150 -0
  11. careamics/config/architectures/register_model.py +101 -0
  12. careamics/config/architectures/unet_model.py +96 -0
  13. careamics/config/architectures/vae_model.py +39 -0
  14. careamics/config/callback_model.py +92 -0
  15. careamics/config/configuration_factory.py +460 -0
  16. careamics/config/configuration_model.py +596 -0
  17. careamics/config/data_model.py +555 -0
  18. careamics/config/inference_model.py +283 -0
  19. careamics/config/noise_models.py +162 -0
  20. careamics/config/optimizer_models.py +181 -0
  21. careamics/config/references/__init__.py +45 -0
  22. careamics/config/references/algorithm_descriptions.py +131 -0
  23. careamics/config/references/references.py +38 -0
  24. careamics/config/support/__init__.py +33 -0
  25. careamics/config/support/supported_activations.py +24 -0
  26. careamics/config/support/supported_algorithms.py +18 -0
  27. careamics/config/support/supported_architectures.py +18 -0
  28. careamics/config/support/supported_data.py +82 -0
  29. careamics/{dataset/extraction_strategy.py → config/support/supported_extraction_strategies.py} +5 -2
  30. careamics/config/support/supported_loggers.py +8 -0
  31. careamics/config/support/supported_losses.py +25 -0
  32. careamics/config/support/supported_optimizers.py +55 -0
  33. careamics/config/support/supported_pixel_manipulations.py +15 -0
  34. careamics/config/support/supported_struct_axis.py +19 -0
  35. careamics/config/support/supported_transforms.py +23 -0
  36. careamics/config/tile_information.py +104 -0
  37. careamics/config/training_model.py +65 -0
  38. careamics/config/transformations/__init__.py +14 -0
  39. careamics/config/transformations/n2v_manipulate_model.py +63 -0
  40. careamics/config/transformations/nd_flip_model.py +32 -0
  41. careamics/config/transformations/normalize_model.py +31 -0
  42. careamics/config/transformations/transform_model.py +44 -0
  43. careamics/config/transformations/xy_random_rotate90_model.py +29 -0
  44. careamics/config/validators/__init__.py +5 -0
  45. careamics/config/validators/validator_utils.py +100 -0
  46. careamics/conftest.py +26 -0
  47. careamics/dataset/__init__.py +5 -0
  48. careamics/dataset/dataset_utils/__init__.py +19 -0
  49. careamics/dataset/dataset_utils/dataset_utils.py +100 -0
  50. careamics/dataset/dataset_utils/file_utils.py +140 -0
  51. careamics/dataset/dataset_utils/read_tiff.py +61 -0
  52. careamics/dataset/dataset_utils/read_utils.py +25 -0
  53. careamics/dataset/dataset_utils/read_zarr.py +56 -0
  54. careamics/dataset/in_memory_dataset.py +321 -131
  55. careamics/dataset/iterable_dataset.py +416 -0
  56. careamics/dataset/patching/__init__.py +8 -0
  57. careamics/dataset/patching/patch_transform.py +44 -0
  58. careamics/dataset/patching/patching.py +212 -0
  59. careamics/dataset/patching/random_patching.py +190 -0
  60. careamics/dataset/patching/sequential_patching.py +206 -0
  61. careamics/dataset/patching/tiled_patching.py +158 -0
  62. careamics/dataset/patching/validate_patch_dimension.py +60 -0
  63. careamics/dataset/zarr_dataset.py +149 -0
  64. careamics/lightning_datamodule.py +665 -0
  65. careamics/lightning_module.py +292 -0
  66. careamics/lightning_prediction_datamodule.py +390 -0
  67. careamics/lightning_prediction_loop.py +116 -0
  68. careamics/losses/__init__.py +4 -1
  69. careamics/losses/loss_factory.py +24 -13
  70. careamics/losses/losses.py +65 -5
  71. careamics/losses/noise_model_factory.py +40 -0
  72. careamics/losses/noise_models.py +524 -0
  73. careamics/model_io/__init__.py +8 -0
  74. careamics/model_io/bioimage/__init__.py +11 -0
  75. careamics/model_io/bioimage/_readme_factory.py +120 -0
  76. careamics/model_io/bioimage/bioimage_utils.py +48 -0
  77. careamics/model_io/bioimage/model_description.py +318 -0
  78. careamics/model_io/bmz_io.py +231 -0
  79. careamics/model_io/model_io_utils.py +80 -0
  80. careamics/models/__init__.py +4 -1
  81. careamics/models/activation.py +35 -0
  82. careamics/models/layers.py +244 -0
  83. careamics/models/model_factory.py +21 -202
  84. careamics/models/unet.py +46 -20
  85. careamics/prediction/__init__.py +1 -3
  86. careamics/prediction/stitch_prediction.py +73 -0
  87. careamics/transforms/__init__.py +41 -0
  88. careamics/transforms/n2v_manipulate.py +113 -0
  89. careamics/transforms/nd_flip.py +93 -0
  90. careamics/transforms/normalize.py +109 -0
  91. careamics/transforms/pixel_manipulation.py +383 -0
  92. careamics/transforms/struct_mask_parameters.py +18 -0
  93. careamics/transforms/tta.py +74 -0
  94. careamics/transforms/xy_random_rotate90.py +95 -0
  95. careamics/utils/__init__.py +10 -13
  96. careamics/utils/base_enum.py +32 -0
  97. careamics/utils/context.py +22 -2
  98. careamics/utils/metrics.py +0 -46
  99. careamics/utils/path_utils.py +24 -0
  100. careamics/utils/ram.py +13 -0
  101. careamics/utils/receptive_field.py +102 -0
  102. careamics/utils/running_stats.py +43 -0
  103. careamics/utils/torch_utils.py +89 -56
  104. careamics-0.1.0rc3.dist-info/METADATA +122 -0
  105. careamics-0.1.0rc3.dist-info/RECORD +109 -0
  106. {careamics-0.1.0rc1.dist-info → careamics-0.1.0rc3.dist-info}/WHEEL +1 -1
  107. careamics/bioimage/__init__.py +0 -15
  108. careamics/bioimage/docs/Noise2Void.md +0 -5
  109. careamics/bioimage/docs/__init__.py +0 -1
  110. careamics/bioimage/io.py +0 -271
  111. careamics/config/algorithm.py +0 -231
  112. careamics/config/config.py +0 -296
  113. careamics/config/config_filter.py +0 -44
  114. careamics/config/data.py +0 -194
  115. careamics/config/torch_optim.py +0 -118
  116. careamics/config/training.py +0 -534
  117. careamics/dataset/dataset_utils.py +0 -115
  118. careamics/dataset/patching.py +0 -493
  119. careamics/dataset/prepare_dataset.py +0 -174
  120. careamics/dataset/tiff_dataset.py +0 -211
  121. careamics/engine.py +0 -954
  122. careamics/manipulation/__init__.py +0 -4
  123. careamics/manipulation/pixel_manipulation.py +0 -158
  124. careamics/prediction/prediction_utils.py +0 -102
  125. careamics/utils/ascii_logo.txt +0 -9
  126. careamics/utils/augment.py +0 -65
  127. careamics/utils/normalization.py +0 -55
  128. careamics/utils/validators.py +0 -156
  129. careamics/utils/wandb.py +0 -121
  130. careamics-0.1.0rc1.dist-info/METADATA +0 -80
  131. careamics-0.1.0rc1.dist-info/RECORD +0 -46
  132. {careamics-0.1.0rc1.dist-info → careamics-0.1.0rc3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,596 @@
1
+ """Pydantic CAREamics configuration."""
2
+ from __future__ import annotations
3
+
4
+ import re
5
+ from pathlib import Path
6
+ from pprint import pformat
7
+ from typing import Dict, List, Literal, Union
8
+
9
+ import yaml
10
+ from bioimageio.spec.generic.v0_3 import CiteEntry
11
+ from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
12
+ from typing_extensions import Self
13
+
14
+ from .algorithm_model import AlgorithmModel
15
+ from .data_model import DataModel
16
+ from .references import (
17
+ CARE,
18
+ CUSTOM,
19
+ N2N,
20
+ N2V,
21
+ N2V2,
22
+ STRUCT_N2V,
23
+ STRUCT_N2V2,
24
+ CAREDescription,
25
+ CARERef,
26
+ N2NDescription,
27
+ N2NRef,
28
+ N2V2Description,
29
+ N2V2Ref,
30
+ N2VDescription,
31
+ N2VRef,
32
+ StructN2V2Description,
33
+ StructN2VDescription,
34
+ StructN2VRef,
35
+ )
36
+ from .support import SupportedAlgorithm, SupportedPixelManipulation, SupportedTransform
37
+ from .training_model import TrainingModel
38
+ from .transformations.n2v_manipulate_model import (
39
+ N2VManipulateModel,
40
+ )
41
+
42
+
43
+ class Configuration(BaseModel):
44
+ """
45
+ CAREamics configuration.
46
+
47
+ The configuration defines all parameters used to build and train a CAREamics model.
48
+ These parameters are validated to ensure that they are compatible with each other.
49
+
50
+ It contains three sub-configurations:
51
+
52
+ - AlgorithmModel: configuration for the algorithm training, which includes the
53
+ architecture, loss function, optimizer, and other hyperparameters.
54
+ - DataModel: configuration for the dataloader, which includes the type of data,
55
+ transformations, mean/std and other parameters.
56
+ - TrainingModel: configuration for the training, which includes the number of
57
+ epochs or the callbacks.
58
+
59
+ Attributes
60
+ ----------
61
+ experiment_name : str
62
+ Name of the experiment, used when saving logs and checkpoints.
63
+ algorithm : AlgorithmModel
64
+ Algorithm configuration.
65
+ data : DataModel
66
+ Data configuration.
67
+ training : TrainingModel
68
+ Training configuration.
69
+
70
+ Methods
71
+ -------
72
+ set_3D(is_3D: bool, axes: str, patch_size: List[int]) -> None
73
+ Switch configuration between 2D and 3D.
74
+ set_N2V2(use_n2v2: bool) -> None
75
+ Switch N2V algorithm between N2V and N2V2.
76
+ set_structN2V(
77
+ mask_axis: Literal["horizontal", "vertical", "none"], mask_span: int) -> None
78
+ Set StructN2V parameters.
79
+ model_dump(
80
+ exclude_defaults: bool = False, exclude_none: bool = True, **kwargs: Dict
81
+ ) -> Dict
82
+ Export configuration to a dictionary.
83
+
84
+ Raises
85
+ ------
86
+ ValueError
87
+ Configuration parameter type validation errors.
88
+ ValueError
89
+ If the experiment name contains invalid characters or is empty.
90
+ ValueError
91
+ If the algorithm is 3D but there is not "Z" in the data axes, or 2D algorithm
92
+ with "Z" in data axes.
93
+ ValueError
94
+ Algorithm, data or training validation errors.
95
+
96
+ Notes
97
+ -----
98
+ We provide convenience methods to create standards configurations, for instance
99
+ for N2V, in the `careamics.config.configuration_factory` module.
100
+ >>> from careamics.config.configuration_factory import create_n2v_configuration
101
+ >>> config = create_n2v_configuration(
102
+ ... experiment_name="n2v_experiment",
103
+ ... data_type="array",
104
+ ... axes="YX",
105
+ ... patch_size=[64, 64],
106
+ ... batch_size=32,
107
+ ... num_epochs=100
108
+ ... )
109
+
110
+ The configuration can be exported to a dictionary using the model_dump method:
111
+ >>> config_dict = config.model_dump()
112
+
113
+ Configurations can also be exported or imported from yaml files:
114
+ >>> from careamics.config import save_configuration, load_configuration
115
+ >>> path_to_config = save_configuration(config, my_path / "config.yml")
116
+ >>> other_config = load_configuration(path_to_config)
117
+
118
+ Examples
119
+ --------
120
+ Minimum example:
121
+ >>> from careamics.config import Configuration
122
+ >>> config_dict = {
123
+ ... "experiment_name": "N2V_experiment",
124
+ ... "algorithm_config": {
125
+ ... "algorithm": "n2v",
126
+ ... "loss": "n2v",
127
+ ... "model": {
128
+ ... "architecture": "UNet",
129
+ ... },
130
+ ... },
131
+ ... "training_config": {
132
+ ... "num_epochs": 200,
133
+ ... },
134
+ ... "data_config": {
135
+ ... "data_type": "tiff",
136
+ ... "patch_size": [64, 64],
137
+ ... "axes": "SYX",
138
+ ... },
139
+ ... }
140
+ >>> config = Configuration(**config_dict)
141
+ """
142
+
143
+ model_config = ConfigDict(
144
+ validate_assignment=True,
145
+ set_arbitrary_types_allowed=True,
146
+ )
147
+
148
+ # version
149
+ version: Literal["0.1.0"] = Field(
150
+ default="0.1.0", description="Version of the CAREamics configuration."
151
+ )
152
+
153
+ # required parameters
154
+ experiment_name: str = Field(
155
+ ..., description="Name of the experiment, used to name logs and checkpoints."
156
+ )
157
+
158
+ # Sub-configurations
159
+ algorithm_config: AlgorithmModel
160
+ data_config: DataModel
161
+ training_config: TrainingModel
162
+
163
+ @field_validator("experiment_name")
164
+ @classmethod
165
+ def no_symbol(cls, name: str) -> str:
166
+ """
167
+ Validate experiment name.
168
+
169
+ A valid experiment name is a non-empty string with only contains letters,
170
+ numbers, underscores, dashes and spaces.
171
+
172
+ Parameters
173
+ ----------
174
+ name : str
175
+ Name to validate.
176
+
177
+ Returns
178
+ -------
179
+ str
180
+ Validated name.
181
+
182
+ Raises
183
+ ------
184
+ ValueError
185
+ If the name is empty or contains invalid characters.
186
+ """
187
+ if len(name) == 0 or name.isspace():
188
+ raise ValueError("Experiment name is empty.")
189
+
190
+ # Validate using a regex that it contains only letters, numbers, underscores,
191
+ # dashes and spaces
192
+ if not re.match(r"^[a-zA-Z0-9_\- ]*$", name):
193
+ raise ValueError(
194
+ f"Experiment name contains invalid characters (got {name}). "
195
+ f"Only letters, numbers, underscores, dashes and spaces are allowed."
196
+ )
197
+
198
+ return name
199
+
200
+ @model_validator(mode="after")
201
+ def validate_3D(self: Self) -> Self:
202
+ """
203
+ Change algorithm dimensions to match data.axes.
204
+
205
+ Only for non-custom algorithms.
206
+
207
+ Returns
208
+ -------
209
+ Self
210
+ Validated configuration.
211
+ """
212
+ if self.algorithm_config.algorithm != SupportedAlgorithm.CUSTOM:
213
+ if "Z" in self.data_config.axes and not self.algorithm_config.model.is_3D():
214
+ # change algorithm to 3D
215
+ self.algorithm_config.model.set_3D(True)
216
+ elif (
217
+ "Z" not in self.data_config.axes and self.algorithm_config.model.is_3D()
218
+ ):
219
+ # change algorithm to 2D
220
+ self.algorithm_config.model.set_3D(False)
221
+
222
+ return self
223
+
224
+ @model_validator(mode="after")
225
+ def validate_algorithm_and_data(self: Self) -> Self:
226
+ """
227
+ Validate algorithm and data compatibility.
228
+
229
+ In particular, the validation does the following:
230
+
231
+ - If N2V is used, it enforces the presence of N2V_Maniuplate in the transforms
232
+ - If N2V2 is used, it enforces the correct manipulation strategy
233
+
234
+ Returns
235
+ -------
236
+ Self
237
+ Validated configuration.
238
+ """
239
+ if self.algorithm_config.algorithm == SupportedAlgorithm.N2V:
240
+ # if we have a list of transform (as opposed to Compose)
241
+ if self.data_config.has_transform_list():
242
+ # missing N2V_MANIPULATE
243
+ if not self.data_config.has_n2v_manipulate():
244
+ self.data_config.transforms.append(
245
+ N2VManipulateModel(
246
+ name=SupportedTransform.N2V_MANIPULATE.value,
247
+ )
248
+ )
249
+
250
+ median = SupportedPixelManipulation.MEDIAN.value
251
+ uniform = SupportedPixelManipulation.UNIFORM.value
252
+ strategy = median if self.algorithm_config.model.n2v2 else uniform
253
+ self.data_config.set_N2V2_strategy(strategy)
254
+ else:
255
+ # if we have a list of transform, remove N2V manipulate if present
256
+ if self.data_config.has_transform_list():
257
+ if self.data_config.has_n2v_manipulate():
258
+ self.data_config.remove_n2v_manipulate()
259
+
260
+ return self
261
+
262
+ def __str__(self) -> str:
263
+ """
264
+ Pretty string reprensenting the configuration.
265
+
266
+ Returns
267
+ -------
268
+ str
269
+ Pretty string.
270
+ """
271
+ return pformat(self.model_dump())
272
+
273
+ def set_3D(self, is_3D: bool, axes: str, patch_size: List[int]) -> None:
274
+ """
275
+ Set 3D flag and axes.
276
+
277
+ Parameters
278
+ ----------
279
+ is_3D : bool
280
+ Whether the algorithm is 3D or not.
281
+ axes : str
282
+ Axes of the data.
283
+ patch_size : List[int]
284
+ Patch size.
285
+ """
286
+ # set the flag and axes (this will not trigger validation at the config level)
287
+ self.algorithm_config.model.set_3D(is_3D)
288
+ self.data_config.set_3D(axes, patch_size)
289
+
290
+ # cheap hack: trigger validation
291
+ self.algorithm_config = self.algorithm_config
292
+
293
+ def set_N2V2(self, use_n2v2: bool) -> None:
294
+ """
295
+ Switch N2V algorithm between N2V and N2V2.
296
+
297
+ Parameters
298
+ ----------
299
+ use_n2v2 : bool
300
+ Whether to use N2V2 or not.
301
+
302
+ Raises
303
+ ------
304
+ ValueError
305
+ If the algorithm is not N2V.
306
+ """
307
+ if self.algorithm_config.algorithm == SupportedAlgorithm.N2V:
308
+ self.algorithm_config.model.n2v2 = use_n2v2
309
+ strategy = (
310
+ SupportedPixelManipulation.MEDIAN.value
311
+ if use_n2v2
312
+ else SupportedPixelManipulation.UNIFORM.value
313
+ )
314
+ self.data_config.set_N2V2_strategy(strategy)
315
+ else:
316
+ raise ValueError("N2V2 can only be set for N2V algorithm.")
317
+
318
+ def set_structN2V(
319
+ self, mask_axis: Literal["horizontal", "vertical", "none"], mask_span: int
320
+ ) -> None:
321
+ """
322
+ Set StructN2V parameters.
323
+
324
+ Parameters
325
+ ----------
326
+ mask_axis : Literal["horizontal", "vertical", "none"]
327
+ Axis of the structural mask.
328
+ mask_span : int
329
+ Span of the structural mask.
330
+ """
331
+ self.data_config.set_structN2V_mask(mask_axis, mask_span)
332
+
333
+ def get_algorithm_flavour(self) -> str:
334
+ """
335
+ Get the algorithm name.
336
+
337
+ Returns
338
+ -------
339
+ str
340
+ Algorithm name.
341
+ """
342
+ if self.algorithm_config.algorithm == SupportedAlgorithm.N2V:
343
+ use_n2v2 = self.algorithm_config.model.n2v2
344
+ use_structN2V = self.data_config.transforms[-1].struct_mask_axis != "none"
345
+
346
+ # return the n2v flavour
347
+ if use_n2v2 and use_structN2V:
348
+ return STRUCT_N2V2
349
+ elif use_n2v2:
350
+ return N2V2
351
+ elif use_structN2V:
352
+ return STRUCT_N2V
353
+ else:
354
+ return N2V
355
+ elif self.algorithm_config.algorithm == SupportedAlgorithm.N2N:
356
+ return N2N
357
+ elif self.algorithm_config.algorithm == SupportedAlgorithm.CARE:
358
+ return CARE
359
+ else:
360
+ return CUSTOM
361
+
362
+ def get_algorithm_description(self) -> str:
363
+ """
364
+ Return a description of the algorithm.
365
+
366
+ This method is used to generate the README of the BioImage Model Zoo export.
367
+
368
+ Returns
369
+ -------
370
+ str
371
+ Description of the algorithm.
372
+ """
373
+ algorithm_flavour = self.get_algorithm_flavour()
374
+
375
+ if algorithm_flavour == CUSTOM:
376
+ return f"Custom algorithm, named {self.algorithm_config.model.name}"
377
+ else: # currently only N2V flavours
378
+ if algorithm_flavour == N2V:
379
+ return N2VDescription().description
380
+ elif algorithm_flavour == N2V2:
381
+ return N2V2Description().description
382
+ elif algorithm_flavour == STRUCT_N2V:
383
+ return StructN2VDescription().description
384
+ elif algorithm_flavour == STRUCT_N2V2:
385
+ return StructN2V2Description().description
386
+ elif algorithm_flavour == N2N:
387
+ return N2NDescription().description
388
+ elif algorithm_flavour == CARE:
389
+ return CAREDescription().description
390
+
391
+ return ""
392
+
393
+ def get_algorithm_citations(self) -> List[CiteEntry]:
394
+ """
395
+ Return a list of citation entries of the current algorithm.
396
+
397
+ This is used to generate the model description for the BioImage Model Zoo.
398
+
399
+ Returns
400
+ -------
401
+ List[CiteEntry]
402
+ List of citation entries.
403
+ """
404
+ if self.algorithm_config.algorithm == SupportedAlgorithm.N2V:
405
+ use_n2v2 = self.algorithm_config.model.n2v2
406
+ use_structN2V = self.data_config.transforms[-1].struct_mask_axis != "none"
407
+
408
+ # return the (struct)N2V(2) references
409
+ if use_n2v2 and use_structN2V:
410
+ return [N2VRef, N2V2Ref, StructN2VRef]
411
+ elif use_n2v2:
412
+ return [N2VRef, N2V2Ref]
413
+ elif use_structN2V:
414
+ return [N2VRef, StructN2VRef]
415
+ else:
416
+ return [N2VRef]
417
+ elif self.algorithm_config.algorithm == SupportedAlgorithm.N2N:
418
+ return [N2NRef]
419
+ elif self.algorithm_config.algorithm == SupportedAlgorithm.CARE:
420
+ return [CARERef]
421
+
422
+ raise ValueError("Citation not available for custom algorithm.")
423
+
424
+ def get_algorithm_references(self) -> str:
425
+ """
426
+ Get the algorithm references.
427
+
428
+ This is used to generate the README of the BioImage Model Zoo export.
429
+
430
+ Returns
431
+ -------
432
+ str
433
+ Algorithm references.
434
+ """
435
+ if self.algorithm_config.algorithm == SupportedAlgorithm.N2V:
436
+ use_n2v2 = self.algorithm_config.model.n2v2
437
+ use_structN2V = self.data_config.transforms[-1].struct_mask_axis != "none"
438
+
439
+ references = [
440
+ N2VRef.text + " doi: " + N2VRef.doi,
441
+ N2V2Ref.text + " doi: " + N2V2Ref.doi,
442
+ StructN2VRef.text + " doi: " + StructN2VRef.doi,
443
+ ]
444
+
445
+ # return the (struct)N2V(2) references
446
+ if use_n2v2 and use_structN2V:
447
+ return "".join(references)
448
+ elif use_n2v2:
449
+ references.pop(-1)
450
+ return "".join(references)
451
+ elif use_structN2V:
452
+ references.pop(-2)
453
+ return "".join(references)
454
+ else:
455
+ return references[0]
456
+
457
+ return ""
458
+
459
+ def get_algorithm_keywords(self) -> List[str]:
460
+ """
461
+ Get algorithm keywords.
462
+
463
+ Returns
464
+ -------
465
+ List[str]
466
+ List of keywords.
467
+ """
468
+ if self.algorithm_config.algorithm == SupportedAlgorithm.N2V:
469
+ use_n2v2 = self.algorithm_config.model.n2v2
470
+ use_structN2V = self.data_config.transforms[-1].struct_mask_axis != "none"
471
+
472
+ keywords = [
473
+ "denoising",
474
+ "restoration",
475
+ "UNet",
476
+ "3D" if "Z" in self.data_config.axes else "2D",
477
+ "CAREamics",
478
+ "pytorch",
479
+ N2V,
480
+ ]
481
+
482
+ if use_n2v2:
483
+ keywords.append(N2V2)
484
+ if use_structN2V:
485
+ keywords.append(STRUCT_N2V)
486
+ else:
487
+ keywords = ["CAREamics"]
488
+
489
+ return keywords
490
+
491
+ def model_dump(
492
+ self,
493
+ exclude_defaults: bool = False,
494
+ exclude_none: bool = True,
495
+ **kwargs: Dict,
496
+ ) -> Dict:
497
+ """
498
+ Override model_dump method in order to set default values.
499
+
500
+ Parameters
501
+ ----------
502
+ exclude_defaults : bool, optional
503
+ Whether to exclude fields with default values or not, by default
504
+ True.
505
+ exclude_none : bool, optional
506
+ Whether to exclude fields with None values or not, by default True.
507
+ **kwargs : Dict
508
+ Keyword arguments.
509
+
510
+ Returns
511
+ -------
512
+ dict
513
+ Dictionary containing the model parameters.
514
+ """
515
+ dictionary = super().model_dump(
516
+ exclude_none=exclude_none, exclude_defaults=exclude_defaults, **kwargs
517
+ )
518
+
519
+ return dictionary
520
+
521
+
522
+ def load_configuration(path: Union[str, Path]) -> Configuration:
523
+ """
524
+ Load configuration from a yaml file.
525
+
526
+ Parameters
527
+ ----------
528
+ path : Union[str, Path]
529
+ Path to the configuration.
530
+
531
+ Returns
532
+ -------
533
+ Configuration
534
+ Configuration.
535
+
536
+ Raises
537
+ ------
538
+ FileNotFoundError
539
+ If the configuration file does not exist.
540
+ """
541
+ # load dictionary from yaml
542
+ if not Path(path).exists():
543
+ raise FileNotFoundError(
544
+ f"Configuration file {path} does not exist in " f" {Path.cwd()!s}"
545
+ )
546
+
547
+ dictionary = yaml.load(Path(path).open("r"), Loader=yaml.SafeLoader)
548
+
549
+ return Configuration(**dictionary)
550
+
551
+
552
+ def save_configuration(config: Configuration, path: Union[str, Path]) -> Path:
553
+ """
554
+ Save configuration to path.
555
+
556
+ Parameters
557
+ ----------
558
+ config : Configuration
559
+ Configuration to save.
560
+ path : Union[str, Path]
561
+ Path to a existing folder in which to save the configuration or to an existing
562
+ configuration file.
563
+
564
+ Returns
565
+ -------
566
+ Path
567
+ Path object representing the configuration.
568
+
569
+ Raises
570
+ ------
571
+ ValueError
572
+ If the path does not point to an existing directory or .yml file.
573
+ """
574
+ # make sure path is a Path object
575
+ config_path = Path(path)
576
+
577
+ # check if path is pointing to an existing directory or .yml file
578
+ if config_path.exists():
579
+ if config_path.is_dir():
580
+ config_path = Path(config_path, "config.yml")
581
+ elif config_path.suffix != ".yml" and config_path.suffix != ".yaml":
582
+ raise ValueError(
583
+ f"Path must be a directory or .yml or .yaml file (got {config_path})."
584
+ )
585
+ else:
586
+ if config_path.suffix != ".yml" and config_path.suffix != ".yaml":
587
+ raise ValueError(
588
+ f"Path must be a directory or .yml or .yaml file (got {config_path})."
589
+ )
590
+
591
+ # save configuration as dictionary to yaml
592
+ with open(config_path, "w") as f:
593
+ # dump configuration
594
+ yaml.dump(config.model_dump(), f, default_flow_style=False)
595
+
596
+ return config_path