Skip to content

Commit 20ee876

Browse files
committed
Strict Flag for Config Manager
1 parent 2f410dc commit 20ee876

File tree

4 files changed

+58
-63
lines changed

4 files changed

+58
-63
lines changed

README.md

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# ViData: Vision Data Management, Processing and Analysis
22

3-
> A unified Python toolkit for managing, processing, and analyzing vision datasets from raw data to task specific analytics.
3+
> A unified Python toolkit for managing, processing, and analyzing vision datasets, from raw data to task specific analytics.
44
>
5-
> streamlines the entire data pipeline for computer vision and medical imaging:
5+
> Streamlines the entire data pipeline for computer vision and medical imaging:
66
>
77
> - Load and save 2D/3D data across common formats with a consistent API.
88
> - Organize datasets using flexible file managers and YAML-based configs.
@@ -52,6 +52,7 @@ pip install napari-data-inspection
5252
- Use `load_xxx` / `save_xxx` for all supported formats.
5353
- **Images/arrays:** PNG/JPG/TIFF (`imageio`, `tifffile`), NIfTI/NRRD/MHA (`sitk`, `nibabel`), Blosc2, NumPy (`.npy`, `.npz`).
5454
- **Configs/metadata:** JSON, YAML, Pickle, TXT.
55+
- **Extendable** by custom _load_ and _save_ functions.
5556
- Functions always follow the same pattern:
5657

5758
```python
@@ -153,6 +154,52 @@ obj = load_txt("config.txt")
153154
save_txt(obj, "out.txt")
154155
```
155156

157+
### Custom Load and Save Functions
158+
159+
- Register a reader / writer with a decorator.
160+
- Reader must return (numpy_array, metadata_dict).
161+
- Writer must return list\[str\] of the file(s) it wrote.
162+
- Registration happens at import time—make sure this module is imported (e.g., from your package’s __init__.py).
163+
- See [here](https://github.com/MIC-DKFZ/ViData/blob/main/src/vidata/io/image_io.py) for an example.
164+
165+
```py
166+
# custom_io_template.py — fill in the TODOs and import this module somewhere at startup.
167+
import numpy as np
168+
from typing import Tuple, Dict, List
169+
170+
# TODO: import your backend library (e.g., imageio, tifffile, nibabel, SimpleITK, ...)
171+
# import imageio.v3 as iio
172+
173+
from vidata.registry import register_loader, register_writer
174+
175+
# --------------------------- READER ------------------------------------------
176+
# Replace file extension and backend name to your custom function
177+
@register_loader("image", ".png", ".jpg", ".jpeg", ".bmp", backend="imageio") # To Register Image Loading
178+
@register_loader("mask", ".png", ".bmp", backend="imageio") # To Register Label Loading
179+
def load_custom(file: str) -> tuple[np.ndarray, dict]:
180+
"""
181+
Load a file and return (data, metadata).
182+
metadata can be empty or include keys like: spacing, origin, direction, shear, dtype, etc.
183+
"""
184+
# data = iio.imread(file) # example for imageio
185+
data = ... # TODO: replace
186+
meta = {} # TODO: replace
187+
return data, meta
188+
# return meta # for metadata files like json or yaml
189+
190+
# --------------------------- WRITER ------------------------------------------
191+
# Replace file extension and backend name to your custom function
192+
@register_writer("image", ".png", ".jpg", ".jpeg", ".bmp", backend="imageio") # To Register Image Writer
193+
@register_writer("mask", ".png", ".bmp", backend="imageio") # To Register Label Writer
194+
def save_custom(data: np.ndarray, file: str) -> list[str]:
195+
"""
196+
Save array to `file`. Return all created paths (include sidecars if any).
197+
"""
198+
# TODO: write using your backend
199+
# iio.imwrite(file, data)
200+
return [file]
201+
```
202+
156203
</details>
157204

158205
# Loaders/Writers
@@ -579,8 +626,7 @@ This repository is developed and maintained by the Applied Computer Vision Lab (
579626
of [Helmholtz Imaging](https://www.helmholtz-imaging.de/) and the
580627
[Division of Medical Image Computing](https://www.dkfz.de/en/medical-image-computing) at DKFZ.
581628

582-
This [napari] plugin was generated with [copier] using the [napari-plugin-template].
629+
This repository was generated with [copier] using the [napari-plugin-template].
583630

584631
[copier]: https://copier.readthedocs.io/en/stable/
585-
[napari]: https://github.com/napari/napari
586632
[napari-plugin-template]: https://github.com/napari/napari-plugin-template

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "vidata"
33
dynamic = ["version"]
4-
description = "Data IO"
4+
description = "A unified Python toolkit for managing, processing, and analyzing vision datasets, from raw data to task specific analytics."
55
readme = "README.md"
66
license = {file = "LICENSE"}
77
authors = [

src/vidata/config_manager.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050

5151

5252
class LayerConfigManager:
53-
def __init__(self, layer_config, split_config=None, splits_file=None):
53+
def __init__(self, layer_config, split_config=None, splits_file=None, strict: bool = True):
5454
self.layer_config = layer_config
5555
self.split_config = split_config if split_config is not None else {}
5656

@@ -111,13 +111,13 @@ def __init__(self, layer_config, split_config=None, splits_file=None):
111111
f"Unknown layer type '{self.layer_config['type']}' for layer '{self.layer_config.get('name')}'"
112112
)
113113

114-
if errs != []:
114+
if errs != [] and strict:
115115
for err in errs:
116116
print(err)
117117
raise ValueError(f"Config for layer '{self.layer_config.get('name')}' is invalid")
118118

119119
# Check if splits_file is valid
120-
if splits_file is not None and not Path(splits_file).exists():
120+
if splits_file is not None and not Path(splits_file).exists() and strict:
121121
raise FileNotFoundError(f"splits_file not found: {splits_file}")
122122
self.splits_file = splits_file
123123

@@ -322,7 +322,7 @@ def task_manager(self) -> TaskManager:
322322

323323

324324
class ConfigManager:
325-
def __init__(self, config: dict | DictConfig | str | Path):
325+
def __init__(self, config: dict | DictConfig | str | Path, strict: bool = True):
326326
if isinstance(config, (str | Path)):
327327
self.config = OmegaConf.load(config)
328328
else:
@@ -337,7 +337,9 @@ def __init__(self, config: dict | DictConfig | str | Path):
337337
if split_cfg.get(k, {}) is not None and layer_cfg["name"] in split_cfg.get(k, {}):
338338
ovrds = split_cfg[k][layer_cfg["name"]]
339339
layer_split[k] = ovrds if ovrds is not None else {}
340-
lcm = LayerConfigManager(layer_cfg, layer_split, split_cfg.get("splits_file"))
340+
lcm = LayerConfigManager(
341+
layer_cfg, layer_split, split_cfg.get("splits_file"), strict=strict
342+
)
341343

342344
self.layers.append(lcm)
343345

src/vidata/io/image_io.py

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -16,56 +16,3 @@ def load_image(file: str):
1616
def save_image(data: np.ndarray, file: str) -> list[str]:
1717
iio.imwrite(file, data)
1818
return [file]
19-
20-
21-
# @register_loader("image", ".png", ".jpg", ".jpeg", ".bmp")
22-
# def load_rgb(file: str):
23-
# """Loads an image from file using OpenCV.
24-
#
25-
# Args:
26-
# file (Union[str, bytes]): Path to the image file.
27-
#
28-
# Returns:
29-
# np.ndarray: Loaded image as a NumPy array.
30-
# """
31-
# data = cv2.imread(file)
32-
# data = cv2.cvtColor(data, cv2.COLOR_BGR2RGB)
33-
#
34-
# return data, None
35-
#
36-
# # TODO How to handle RGB vs Grayscale data
37-
# @register_writer("image", ".png", ".jpg", ".jpeg", ".bmp")
38-
# def save_rgb(data: np.ndarray, file: str):
39-
# """Saves an image to file using OpenCV.
40-
#
41-
# Args:
42-
# data (np.ndarray): Image array to save.
43-
# file (Union[str, bytes]): Destination file path.
44-
# """
45-
# data = cv2.cvtColor(data, cv2.COLOR_RGB2BGR)
46-
# cv2.imwrite(file, data)
47-
#
48-
#
49-
# @register_loader("mask", ".png", ".bmp")
50-
# def load_mask(file: str):
51-
# """Loads an image from file using OpenCV.
52-
#
53-
# Args:
54-
# file (Union[str, bytes]): Path to the image file.
55-
#
56-
# Returns:
57-
# np.ndarray: Loaded image as a NumPy array.
58-
# """
59-
# data = cv2.imread(file, cv2.IMREAD_UNCHANGED)
60-
# return data, None
61-
#
62-
#
63-
# @register_writer("image", ".png", ".bmp")
64-
# def save_mask(data: np.ndarray, file: str):
65-
# """Saves an image to file using OpenCV.
66-
#
67-
# Args:
68-
# data (np.ndarray): Image array to save.
69-
# file (Union[str, bytes]): Destination file path.
70-
# """
71-
# cv2.imwrite(file, data)

0 commit comments

Comments
 (0)