Skip to content

Commit adf3053

Browse files
committed
Add: template cli
1 parent ba84b5f commit adf3053

File tree

5 files changed

+112
-186
lines changed

5 files changed

+112
-186
lines changed

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ print(fm[0]) # > data/stacked_images/case_1_image | gives t
424424
- Config is a YAML **template for datasets**: always starts with a `name`.
425425
- Add one or more **layers** (e.g., `image`, `semseg`, `multilabel`) with fields like `path`, `file_type`, `channels`/`classes`.
426426
- **Optional splits**: define `train/val/test` overrides or point to a `splits_file`.
427+
- See [`examples/template.yaml`](examples/template.yaml) and/or run `vidata_template` for a template
427428

428429
<details>
429430
<summary> Expand for Full Details </summary>
@@ -537,17 +538,17 @@ print("Classes in labels:", tm.class_ids(arr))
537538

538539
# Data Analysis
539540

540-
The `data_analyze` CLI computes dataset statistics and writes them to the
541+
The `vidata_analyze` CLI computes dataset statistics and writes them to the
541542
specified output directory. Results include:
542543

543544
- **Image statistics**: sizes, resolutions, intensity distributions
544545
- **Label statistics**: class counts, frequencies, co-occurrence
545546
- **Split summaries**: optional per-split analysis
546547

547548
```bash
548-
data_analyze -c path/to/datasets/*.yaml -o <outputdir>
549+
vidata_analyze -c path/to/datasets/*.yaml -o <outputdir>
549550
# Analyze a specific split/fold
550-
data_analyze -c path/to/datasets/*.yaml -o <outputdir> -s <split> -f <fold>
551+
vidata_analyze -c path/to/datasets/*.yaml -o <outputdir> -s <split> -f <fold>
551552
```
552553

553554
# Data Inspection
@@ -562,9 +563,9 @@ pip install napari-data-inspection[all]
562563
Run the following
563564

564565
```bash
565-
data_inspections -c path/to/datasets/*.yaml
566+
data_inspection -c path/to/datasets/*.yaml
566567
# Inspect a specific split/fold
567-
data_inspections -c path/to/datasets/*.yaml -s <split> -f <fold>
568+
data_inspection -c path/to/datasets/*.yaml -s <split> -f <fold>
568569
```
569570

570571
# Acknowledgments

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,8 @@ fix = true
120120
[tool.pytest.ini_options]
121121
addopts = "--basetemp=./tests/temp/"
122122
testpaths = ["tests"]
123+
124+
125+
[project.scripts]
126+
vidata_analyze = "vidata.cli.analyze:main"
127+
vidata_template = "vidata.cli.template:main"

src/vidata/cli/template.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
from pathlib import Path
2+
3+
from omegaconf import OmegaConf
4+
5+
6+
def main():
7+
print("=== YAML Template Creator ===")
8+
project_name = input("Project Name: ")
9+
if project_name == "":
10+
raise Exception("Project Name cannot be empty")
11+
12+
output_path = Path.cwd() / (project_name + ".yaml")
13+
if output_path.exists() and not input("Project exists, overwrite? (Y/N)").lower() == "y":
14+
raise FileExistsError(f"Output path already exists: {output_path}")
15+
16+
n_ilayers = input("Number of Image layers: ")
17+
n_ilayers = None if n_ilayers == "" else int(n_ilayers)
18+
19+
n_llayers = input("Number of Label layers: ")
20+
n_llayers = None if n_llayers == "" else int(n_llayers)
21+
22+
f_type = input("File Type (e.g. .nii.gz, .png): ")
23+
f_type = None if f_type == "" else f_type
24+
if n_ilayers is not None and n_ilayers > 0:
25+
n_channels = input("Number of Image Channels: ")
26+
n_channels = None if n_channels == "" else int(n_channels)
27+
else:
28+
n_channels = "TODO"
29+
if n_llayers is not None and n_llayers > 0:
30+
n_classes = input("Number of Label Classes: ")
31+
n_classes = None if n_classes == "" else int(n_classes)
32+
task = input("Semantic Segmentation(S)/MultilabelSegmentation(M): ")
33+
if task.lower() == "s":
34+
task = "semseg"
35+
elif task.lower() == "m":
36+
task = "multilabel"
37+
else:
38+
task = "TODO - semseg|multilabel"
39+
else:
40+
n_classes = "TODO"
41+
task = "TODO - semseg|multilabel"
42+
43+
split = input("Create Split Template (Y/N): ")
44+
split = split.lower() == "y"
45+
46+
config = {"name": project_name}
47+
layers_i = [
48+
{
49+
"name": f"ImageLayer{i + 1}",
50+
"type": "image", # change to "labels"/"points" if needed
51+
"path": "TODO",
52+
"file_type": f_type,
53+
"pattern": None,
54+
"backend": None,
55+
"channel": n_channels, # optional
56+
"file_stack": False,
57+
}
58+
for i in range(n_ilayers)
59+
]
60+
layers_l = [
61+
{
62+
"name": f"LabelLayer{i + 1}",
63+
"type": task,
64+
"path": "TODO",
65+
"file_type": f_type,
66+
"pattern": None,
67+
"backend": None,
68+
"classes": n_classes,
69+
"file_stack": False,
70+
"ignore_bg": None,
71+
"ignore_index": None,
72+
}
73+
for i in range(n_llayers)
74+
]
75+
config["layers"] = layers_i + layers_l
76+
77+
if split:
78+
config["split"] = {"splits_file": None, "train": None, "val": None, "test": None}
79+
layer_names = {}
80+
for layer in config["layers"]:
81+
layer_names[layer["name"]] = None
82+
config["split"]["train"] = layer_names
83+
config["split"]["val"] = layer_names
84+
config["split"]["test"] = layer_names
85+
86+
OmegaConf.save(config, output_path)
87+
print(f"✔ Wrote template to: {output_path}")
88+
print(" - Fill out all 'TODO'")
89+
print(" - Optional - rename the layers")
90+
print(" - Optional - 'null' entries are optional, you can change or delete them")
91+
92+
93+
if __name__ == "__main__":
94+
main()

src/vidata/cli/verify_config.py

Lines changed: 0 additions & 156 deletions
This file was deleted.

src/vidata/config_manager.py

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def config(self, split: str | None = None, fold: int | None = None):
236236
_cfg["include_names"] = self.resolve_splits_file(split, fold)
237237
return _cfg
238238

239-
def resolve_splits_file(self, split: str, fold: int | None = None):
239+
def resolve_splits_file(self, split: str, fold: int | None = None) -> list[str]:
240240
if self.splits_file is None:
241241
raise ValueError(f"no splits file defined for {self.name}")
242242
splits = load_json(self.splits_file)
@@ -254,7 +254,10 @@ def resolve_splits_file(self, split: str, fold: int | None = None):
254254

255255
if split not in splits:
256256
raise ValueError(f"split {split} is not in splits_file with keys {list(splits.keys())}")
257-
return splits[split]
257+
258+
resolved = splits[split]
259+
assert isinstance(resolved, list) # Should be a list of files
260+
return resolved
258261

259262
def file_manager(self, split: str | None = None, fold: int | None = None) -> FileManager:
260263
_cfg = self.config(split=split)
@@ -319,8 +322,8 @@ def task_manager(self) -> TaskManager:
319322

320323

321324
class ConfigManager:
322-
def __init__(self, config: dict | DictConfig | str):
323-
if isinstance(config, str):
325+
def __init__(self, config: dict | DictConfig | str | Path):
326+
if isinstance(config, (str | Path)):
324327
self.config = OmegaConf.load(config)
325328
else:
326329
self.config = config
@@ -353,24 +356,3 @@ def layer_names(self):
353356

354357
def __len__(self):
355358
return len(self.layers)
356-
357-
358-
if __name__ == "__main__":
359-
360-
path = "../../../dataset_cfg/Cityscapes.yaml"
361-
cfg = dict(OmegaConf.load(path))
362-
print(cfg)
363-
cm = ConfigManager(cfg)
364-
for key in cm.layer_names():
365-
layer = cm.layer(key)
366-
fm = layer.file_manager()
367-
for i in fm:
368-
print(i)
369-
break
370-
# for layer in cm.layers:
371-
# for split in ["train", "val"]:
372-
# fm=layer.file_manager(split,fold=0)
373-
# print(layer.name,split,len(fm))
374-
# dalo=layer.data_loader()
375-
# data,meta=dalo.load(fm[0])
376-
# print(layer.name, split, data.shape)

0 commit comments

Comments
 (0)