brkraw 0.3.11__py3-none-any.whl → 0.5.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.
Files changed (113) hide show
  1. brkraw/__init__.py +9 -3
  2. brkraw/apps/__init__.py +12 -0
  3. brkraw/apps/addon/__init__.py +30 -0
  4. brkraw/apps/addon/core.py +35 -0
  5. brkraw/apps/addon/dependencies.py +402 -0
  6. brkraw/apps/addon/installation.py +500 -0
  7. brkraw/apps/addon/io.py +21 -0
  8. brkraw/apps/hook/__init__.py +25 -0
  9. brkraw/apps/hook/core.py +636 -0
  10. brkraw/apps/loader/__init__.py +10 -0
  11. brkraw/apps/loader/core.py +622 -0
  12. brkraw/apps/loader/formatter.py +288 -0
  13. brkraw/apps/loader/helper.py +797 -0
  14. brkraw/apps/loader/info/__init__.py +11 -0
  15. brkraw/apps/loader/info/scan.py +85 -0
  16. brkraw/apps/loader/info/scan.yaml +90 -0
  17. brkraw/apps/loader/info/study.py +69 -0
  18. brkraw/apps/loader/info/study.yaml +156 -0
  19. brkraw/apps/loader/info/transform.py +92 -0
  20. brkraw/apps/loader/types.py +220 -0
  21. brkraw/cli/__init__.py +5 -0
  22. brkraw/cli/commands/__init__.py +2 -0
  23. brkraw/cli/commands/addon.py +327 -0
  24. brkraw/cli/commands/config.py +205 -0
  25. brkraw/cli/commands/convert.py +903 -0
  26. brkraw/cli/commands/hook.py +348 -0
  27. brkraw/cli/commands/info.py +74 -0
  28. brkraw/cli/commands/init.py +214 -0
  29. brkraw/cli/commands/params.py +106 -0
  30. brkraw/cli/commands/prune.py +288 -0
  31. brkraw/cli/commands/session.py +371 -0
  32. brkraw/cli/hook_args.py +80 -0
  33. brkraw/cli/main.py +83 -0
  34. brkraw/cli/utils.py +60 -0
  35. brkraw/core/__init__.py +13 -0
  36. brkraw/core/config.py +380 -0
  37. brkraw/core/entrypoints.py +25 -0
  38. brkraw/core/formatter.py +367 -0
  39. brkraw/core/fs.py +495 -0
  40. brkraw/core/jcamp.py +600 -0
  41. brkraw/core/layout.py +451 -0
  42. brkraw/core/parameters.py +781 -0
  43. brkraw/core/zip.py +1121 -0
  44. brkraw/dataclasses/__init__.py +14 -0
  45. brkraw/dataclasses/node.py +139 -0
  46. brkraw/dataclasses/reco.py +33 -0
  47. brkraw/dataclasses/scan.py +61 -0
  48. brkraw/dataclasses/study.py +131 -0
  49. brkraw/default/__init__.py +3 -0
  50. brkraw/default/pruner_specs/deid4share.yaml +42 -0
  51. brkraw/default/rules/00_default.yaml +4 -0
  52. brkraw/default/specs/metadata_dicom.yaml +236 -0
  53. brkraw/default/specs/metadata_transforms.py +92 -0
  54. brkraw/resolver/__init__.py +7 -0
  55. brkraw/resolver/affine.py +539 -0
  56. brkraw/resolver/datatype.py +69 -0
  57. brkraw/resolver/fid.py +90 -0
  58. brkraw/resolver/helpers.py +36 -0
  59. brkraw/resolver/image.py +188 -0
  60. brkraw/resolver/nifti.py +370 -0
  61. brkraw/resolver/shape.py +235 -0
  62. brkraw/schema/__init__.py +3 -0
  63. brkraw/schema/context_map.yaml +62 -0
  64. brkraw/schema/meta.yaml +57 -0
  65. brkraw/schema/niftiheader.yaml +95 -0
  66. brkraw/schema/pruner.yaml +55 -0
  67. brkraw/schema/remapper.yaml +128 -0
  68. brkraw/schema/rules.yaml +154 -0
  69. brkraw/specs/__init__.py +10 -0
  70. brkraw/specs/hook/__init__.py +12 -0
  71. brkraw/specs/hook/logic.py +31 -0
  72. brkraw/specs/hook/validator.py +22 -0
  73. brkraw/specs/meta/__init__.py +5 -0
  74. brkraw/specs/meta/validator.py +156 -0
  75. brkraw/specs/pruner/__init__.py +15 -0
  76. brkraw/specs/pruner/logic.py +361 -0
  77. brkraw/specs/pruner/validator.py +119 -0
  78. brkraw/specs/remapper/__init__.py +27 -0
  79. brkraw/specs/remapper/logic.py +924 -0
  80. brkraw/specs/remapper/validator.py +314 -0
  81. brkraw/specs/rules/__init__.py +6 -0
  82. brkraw/specs/rules/logic.py +263 -0
  83. brkraw/specs/rules/validator.py +103 -0
  84. brkraw-0.5.0.dist-info/METADATA +81 -0
  85. brkraw-0.5.0.dist-info/RECORD +88 -0
  86. {brkraw-0.3.11.dist-info → brkraw-0.5.0.dist-info}/WHEEL +1 -2
  87. brkraw-0.5.0.dist-info/entry_points.txt +13 -0
  88. brkraw/lib/__init__.py +0 -4
  89. brkraw/lib/backup.py +0 -641
  90. brkraw/lib/bids.py +0 -0
  91. brkraw/lib/errors.py +0 -125
  92. brkraw/lib/loader.py +0 -1220
  93. brkraw/lib/orient.py +0 -194
  94. brkraw/lib/parser.py +0 -48
  95. brkraw/lib/pvobj.py +0 -301
  96. brkraw/lib/reference.py +0 -245
  97. brkraw/lib/utils.py +0 -471
  98. brkraw/scripts/__init__.py +0 -0
  99. brkraw/scripts/brk_backup.py +0 -106
  100. brkraw/scripts/brkraw.py +0 -744
  101. brkraw/ui/__init__.py +0 -0
  102. brkraw/ui/config.py +0 -17
  103. brkraw/ui/main_win.py +0 -214
  104. brkraw/ui/previewer.py +0 -225
  105. brkraw/ui/scan_info.py +0 -72
  106. brkraw/ui/scan_list.py +0 -73
  107. brkraw/ui/subj_info.py +0 -128
  108. brkraw-0.3.11.dist-info/METADATA +0 -25
  109. brkraw-0.3.11.dist-info/RECORD +0 -28
  110. brkraw-0.3.11.dist-info/entry_points.txt +0 -3
  111. brkraw-0.3.11.dist-info/top_level.txt +0 -2
  112. tests/__init__.py +0 -0
  113. {brkraw-0.3.11.dist-info → brkraw-0.5.0.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+
3
+ from .study import Study
4
+ from .scan import Scan
5
+ from .reco import Reco
6
+ from .node import DatasetNode
7
+
8
+
9
+ __all__ = [
10
+ 'Study',
11
+ 'Scan',
12
+ 'Reco',
13
+ 'DatasetNode'
14
+ ]
@@ -0,0 +1,139 @@
1
+ from __future__ import annotations
2
+
3
+ import io
4
+ from typing import Any, List, Union, Optional, TYPE_CHECKING, cast, Dict
5
+
6
+
7
+ from ..core.fs import DatasetFS
8
+ from ..core.parameters import Parameters
9
+
10
+ if TYPE_CHECKING:
11
+ from ..core.fs import DatasetFile
12
+ from ..core.zip import ZippedFile
13
+
14
+ def _is_probably_text(data: bytes) -> bool:
15
+ """Heuristic to decide if data should be treated as text."""
16
+ if not data:
17
+ return True
18
+ sample = data[:1024]
19
+ if b"\x00" in sample:
20
+ return False
21
+
22
+ text_chars = set(range(0x20, 0x7F)) | {0x09, 0x0A, 0x0D, 0x08, 0x0C, 0x1B}
23
+ nontext = sum(b not in text_chars for b in sample)
24
+ return (nontext / len(sample)) < 0.30
25
+
26
+
27
+ class DatasetNode:
28
+ """Shared utilities for dataset-backed nodes (Study, Scan, Reco)."""
29
+
30
+ fs: "DatasetFS"
31
+ relroot: str
32
+ _cache: Dict[str, Any]
33
+
34
+ def _full_path(self, name: str) -> str:
35
+ relroot = self.relroot.strip("/")
36
+ name = name.strip("/")
37
+ return f"{relroot}/{name}" if relroot else name
38
+
39
+ def _candidates(self, name: str) -> List[str]:
40
+ """Generate possible dataset entry names for an attribute-like token."""
41
+ if not name:
42
+ return []
43
+ candidates = [name]
44
+
45
+ def add(candidate: str) -> None:
46
+ if candidate and candidate not in candidates:
47
+ candidates.append(candidate)
48
+
49
+ stripped = name[5:] if name.startswith("file_") else None
50
+ add(stripped or "")
51
+
52
+ dotted = name.replace("_", ".")
53
+ add(dotted if dotted != name else "")
54
+
55
+ if stripped:
56
+ dotted_stripped = stripped.replace("_", ".")
57
+ add(dotted_stripped)
58
+
59
+ return candidates
60
+
61
+ def _resolve_entry(self, relpath: str) -> Optional[Union["ZippedFile", "DatasetFile"]]:
62
+ """Return the file-like entry object for a dataset-relative path."""
63
+ relpath = relpath.strip("/")
64
+ parent, _, leaf = relpath.rpartition("/")
65
+ dirpath = parent
66
+ entries = self.fs.iterdir(dirpath)
67
+ for entry in entries:
68
+ if entry.name == leaf and entry.is_file():
69
+ return cast(Union["ZippedFile", "DatasetFile"], entry)
70
+ return None
71
+
72
+ def open(self, name: str):
73
+ """Open a dataset-relative entry with best-effort typing.
74
+
75
+ Attempts to parse JCAMP parameters first; falls back to text or binary
76
+ buffers when parsing fails.
77
+ """
78
+ if name.startswith("_"):
79
+ raise FileNotFoundError(name)
80
+
81
+ for candidate in self._candidates(name):
82
+ path = self._full_path(candidate)
83
+ cache_key = f"{path}"
84
+ cached = self._cache.get(cache_key)
85
+ if cached is not None:
86
+ return cached
87
+
88
+ entry = self._resolve_entry(path)
89
+ if entry is None:
90
+ continue
91
+
92
+ data = entry.read()
93
+
94
+ param = None
95
+ if Parameters._looks_like_jcamp(data):
96
+ try:
97
+ param = Parameters(data)
98
+ except Exception:
99
+ param = None
100
+ if param is not None:
101
+ self._cache[cache_key] = param
102
+ return param
103
+
104
+ if _is_probably_text(data):
105
+ try:
106
+ text = data.decode("utf-8")
107
+ obj = io.StringIO(text)
108
+ except UnicodeDecodeError:
109
+ obj = io.BytesIO(data)
110
+ else:
111
+ obj = io.BytesIO(data)
112
+
113
+ self._cache[cache_key] = obj
114
+ return obj
115
+
116
+ raise FileNotFoundError(name)
117
+
118
+ def __getattr__(self, name: str):
119
+ if name.startswith("_"):
120
+ raise AttributeError(name)
121
+ try:
122
+ return self.open(name)
123
+ except FileNotFoundError as exc:
124
+ raise AttributeError(name) from exc
125
+
126
+ def __getitem__(self, key: str):
127
+ """Dictionary-style access to files (supports names not valid as attributes)."""
128
+ return self.open(str(key))
129
+
130
+ def listdir(self, relpath: str = "") -> List[str]:
131
+ """List entries under this node (dirs first, then files)."""
132
+ target = self._full_path(relpath)
133
+ return self.fs.listdir(target)
134
+
135
+ def iterdir(self, relpath: str = ""):
136
+ """Iterate over entries under this node as objects (dirs first, then files)."""
137
+ target = self._full_path(relpath)
138
+ for entry in self.fs.iterdir(target):
139
+ yield entry
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass, field
3
+
4
+ from ..core.fs import DatasetFS
5
+ from .node import DatasetNode
6
+ from typing import TYPE_CHECKING, Dict
7
+ if TYPE_CHECKING:
8
+ from .scan import Scan
9
+
10
+
11
+ @dataclass
12
+ class Reco(DatasetNode):
13
+ fs: DatasetFS
14
+ scan_id: int
15
+ reco_id: int
16
+ relroot: str # e.g.: "3/pdata/1"
17
+ _cache: Dict[str, object] = field(default_factory=dict, init=False, repr=False)
18
+
19
+ @classmethod
20
+ def from_fs(cls, fs: DatasetFS, scan: "Scan", reco_id: int, relroot: str) -> "Reco":
21
+ return cls(fs=fs, scan_id=scan.scan_id, reco_id=reco_id, relroot=relroot)
22
+
23
+ def __repr__(self) -> str:
24
+ image_type = None
25
+ try:
26
+ reco_obj = getattr(self, "reco")
27
+ image_type = getattr(reco_obj, "RECO_image_type", None)
28
+ except Exception:
29
+ image_type = None
30
+ type_part = f" type={image_type!r}" if image_type is not None else ""
31
+ return f"Reco(scan_id={self.scan_id} id={self.reco_id} rel='/{self.relroot}'{type_part})"
32
+
33
+ __all__ = ['Reco']
@@ -0,0 +1,61 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass, field
3
+ from typing import Dict, Optional, Mapping
4
+
5
+ from ..core.fs import DatasetFS
6
+ from .node import DatasetNode
7
+ from .reco import Reco
8
+
9
+
10
+ @dataclass
11
+ class Scan(DatasetNode):
12
+ fs: DatasetFS
13
+ scan_id: int
14
+ relroot: str
15
+ recos: Dict[int, Reco] = field(default_factory=dict)
16
+ _cache: Dict[str, object] = field(default_factory=dict, init=False, repr=False)
17
+
18
+ @classmethod
19
+ def from_fs(cls, fs: DatasetFS, scan_id: int, relroot: str) -> "Scan":
20
+ scan = cls(fs, scan_id, relroot)
21
+ scan._index_recos(top=relroot)
22
+ return scan
23
+
24
+ def _index_recos(self, top: Optional[str] = None) -> None:
25
+ """Find pdata/<reco_id> dirs under this scan and attach PVReco."""
26
+ import re
27
+ pdata_prefix = f"{self.relroot}/pdata"
28
+
29
+ for dirpath, dirnames, filenames in self.fs.walk(top=top or ""):
30
+ rel = self.fs.strip_anchor(dirpath)
31
+ if not rel.startswith(pdata_prefix):
32
+ continue
33
+ m = re.fullmatch(rf"{self.relroot}/pdata/(\d+)", rel)
34
+ if not m:
35
+ continue
36
+ reco_id = int(m.group(1))
37
+ self.recos[reco_id] = Reco.from_fs(
38
+ fs=self.fs,
39
+ scan=self,
40
+ reco_id=reco_id,
41
+ relroot=rel,
42
+ )
43
+
44
+ def get_reco(self, reco_id: int) -> Reco:
45
+ return self.recos[reco_id]
46
+
47
+ @property
48
+ def avail(self) -> Mapping[int, Reco]:
49
+ return {k: self.recos[k] for k in sorted(self.recos)}
50
+
51
+ def __repr__(self) -> str:
52
+ method_val = None
53
+ try:
54
+ method_obj = getattr(self, "method")
55
+ method_val = getattr(method_obj, "Method", None)
56
+ except Exception:
57
+ method_val = None
58
+ method_part = f" Method={method_val!r}" if method_val is not None else ""
59
+ return f"Scan(id={self.scan_id} rel='/{self.relroot}'{method_part})"
60
+
61
+ __all__ = ['Scan']
@@ -0,0 +1,131 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass, field
3
+ from pathlib import Path
4
+ from typing import Dict, Mapping, Union, TYPE_CHECKING, List
5
+
6
+ from ..core.fs import DatasetFS
7
+ from .node import DatasetNode
8
+ from .scan import Scan
9
+
10
+ if TYPE_CHECKING:
11
+ from ..apps.loader.types import ScanLoader
12
+
13
+
14
+ @dataclass
15
+ class Study(DatasetNode):
16
+ fs: DatasetFS
17
+ relroot: str = ""
18
+ scans: Dict[int, Scan] = field(default_factory=dict)
19
+ _cache: Dict[str, object] = field(default_factory=dict, init=False, repr=False)
20
+
21
+ @classmethod
22
+ def from_path(cls, path: Union[str, Path]) -> "Study":
23
+ """Load a study rooted at path, preferring bottom-up discovery."""
24
+ fs = DatasetFS.from_path(path)
25
+ found = cls.discover(fs)
26
+ if not found:
27
+ raise ValueError(f"No Paravision study found under {path}")
28
+
29
+ anchor = fs.anchor
30
+ if anchor:
31
+ for study in found:
32
+ if study.relroot == anchor:
33
+ return study
34
+
35
+ if len(found) == 1:
36
+ return found[0]
37
+
38
+ raise ValueError(
39
+ f"Multiple studies found under {path}; "
40
+ f"cannot choose automatically ({[s.relroot for s in found]})"
41
+ )
42
+
43
+ @classmethod
44
+ def discover(cls, fs: DatasetFS) -> List["Study"]:
45
+ """Bottom-up discovery using reco markers (2dseq + visu_pars)."""
46
+ reco_dirs: List[str] = []
47
+ for dirpath, dirnames, filenames in fs.walk():
48
+ rel = fs.strip_anchor(dirpath)
49
+ names = set(filenames)
50
+ if "2dseq" in names and "visu_pars" in names:
51
+ reco_dirs.append(rel)
52
+
53
+ studies: Dict[str, Study] = {}
54
+ for reco_dir in reco_dirs:
55
+ parts = [p for p in reco_dir.split("/") if p]
56
+ if "pdata" not in parts:
57
+ continue
58
+ pdata_idx = parts.index("pdata")
59
+ if pdata_idx < 1 or pdata_idx + 1 >= len(parts):
60
+ continue
61
+
62
+ scan_id_part = parts[pdata_idx - 1]
63
+ if not scan_id_part.isdigit():
64
+ continue
65
+ scan_id = int(scan_id_part)
66
+
67
+ reco_id_part = parts[pdata_idx + 1]
68
+ if not reco_id_part.isdigit():
69
+ continue
70
+
71
+ scan_root = "/".join(parts[:pdata_idx])
72
+ study_root = "/".join(parts[:pdata_idx - 1])
73
+
74
+ if not (
75
+ fs.exists(f"{scan_root}/method")
76
+ and fs.exists(f"{scan_root}/acqp")
77
+ and fs.exists(f"{reco_dir}/reco")
78
+ ):
79
+ continue
80
+
81
+ study = studies.get(study_root)
82
+ if study is None:
83
+ study = cls(fs=fs, relroot=study_root, scans={})
84
+ studies[study_root] = study
85
+
86
+ if scan_id not in study.scans:
87
+ study.scans[scan_id] = Scan.from_fs(fs, scan_id, scan_root)
88
+
89
+ return [studies[k] for k in sorted(studies.keys())]
90
+
91
+ @property
92
+ def avail(self) -> Mapping[int, Union[Scan, "ScanLoader"]]:
93
+ return {k: self.scans[k] for k in sorted(self.scans)}
94
+
95
+ def get_scan(self, scan_id: int) -> Scan:
96
+ return self.scans[scan_id]
97
+
98
+ @property
99
+ def has_subject(self) -> bool:
100
+ target = f"{self.relroot}/subject" if self.relroot else "subject"
101
+ return self.fs.exists(target)
102
+
103
+ def __repr__(self) -> str:
104
+ root_label = self.relroot or self.fs.root.name
105
+ mode = getattr(self.fs, "_mode", "dir")
106
+
107
+ subject_part = ""
108
+ try:
109
+ subj = getattr(self, "subject")
110
+ from ..core.parameters import Parameters # local import to avoid cycle
111
+
112
+ if isinstance(subj, Parameters):
113
+ sid = getattr(subj, "SUBJECT_id", None)
114
+ name = getattr(subj, "SUBJECT_name_string", None)
115
+ study_name = getattr(subj, "SUBJECT_study_name", None)
116
+ study_nr = getattr(subj, "SUBJECT_study_nr", None)
117
+ bits = []
118
+ if name is not None:
119
+ bits.append(f"name={name!r}")
120
+ if sid is not None:
121
+ bits.append(f"id={sid}")
122
+ if study_name is not None:
123
+ bits.append(f"study={study_name!r}")
124
+ if study_nr is not None:
125
+ bits.append(f"nr={study_nr}")
126
+ if bits:
127
+ subject_part = " subject(" + " ".join(bits) + ")"
128
+ except Exception:
129
+ subject_part = ""
130
+
131
+ return f"Study(root='{root_label}' mode={mode}{subject_part})"
@@ -0,0 +1,3 @@
1
+ from __future__ import annotations
2
+
3
+ __all__ = []
@@ -0,0 +1,42 @@
1
+ __meta__:
2
+ name: "deid4share"
3
+ version: "0.2.0"
4
+ description: >
5
+ De-identification prune spec for Bruker ParaVision datasets. Drops the
6
+ subject file and redacts potential PII from JCAMP-DX parameters. Supports
7
+ template variables: $subject_id, $subject_name, $study_id.
8
+ category: "pruner_spec"
9
+
10
+ mode: "keep"
11
+
12
+ files:
13
+ - "method"
14
+ - "acqp"
15
+ - "reco"
16
+ - "visu_pars"
17
+ - "2dseq"
18
+
19
+ update_params:
20
+ acqp:
21
+ ACQ_operator: ""
22
+ ACQ_institution: ""
23
+ ACQ_station: ""
24
+ ACQ_time: "<00:00:00 01 Jan 2000>"
25
+ ACQ_abs_time: "0"
26
+
27
+ visu_pars:
28
+ VisuInstitution: ""
29
+ VisuSubjectId: "$subject_id"
30
+ VisuSubjectName: "$subject_name"
31
+ VisuStudyId: "$study_id"
32
+ VisuStudyDate: "<00:00:00 01 Jan 2000>"
33
+ VisuStudyUid: "<1.2.3.4.5>"
34
+ VisuSeriesUid: "<1.2.3.4.5.1>"
35
+ VisuFrameUid: "<1.2.3.4.5.2>"
36
+ VisuSystemOrderNumber: "<>"
37
+ VisuInstitution: "<>"
38
+
39
+ reco:
40
+ RECO_base_image_uid: "<1.2.3.4.5.6>"
41
+
42
+ add_root: true
@@ -0,0 +1,4 @@
1
+ metadata_spec:
2
+ - name: "metadata-dicom"
3
+ description: "Default metadata mapping for Bruker scans."
4
+ use: "specs/metadata_dicom.yaml"
@@ -0,0 +1,236 @@
1
+ __meta__:
2
+ name: "metadata_dicom"
3
+ version: "0.0.1"
4
+ description: "DICOM metadata mapping for Bruker PvDataset."
5
+ category: "metadata_spec"
6
+ transforms_source: "metadata_transforms.py"
7
+
8
+ ImageType:
9
+ sources:
10
+ - file: visu_pars
11
+ key: VisuSeriesTypeId
12
+ transform: strip_jcamp_string
13
+
14
+ AcquisitionDateTime:
15
+ sources:
16
+ - file: visu_pars
17
+ key: VisuAcqDate
18
+ transform: strip_jcamp_string
19
+
20
+ MagneticFieldStrength:
21
+ sources:
22
+ - file: visu_pars
23
+ key: VisuMagneticFieldStrength
24
+
25
+ ScanningSequence:
26
+ sources:
27
+ - file: visu_pars
28
+ key: VisuAcqSequenceName
29
+ transform: strip_jcamp_string
30
+
31
+ SequenceVariant:
32
+ sources:
33
+ - file: visu_pars
34
+ key: VisuAcqSequenceName
35
+ transform: strip_jcamp_string
36
+
37
+ ScanOptions:
38
+ sources:
39
+ - file: visu_pars
40
+ key: VisuAcqSpectralSuppression
41
+ transform: strip_jcamp_string
42
+
43
+ RepetitionTime:
44
+ sources:
45
+ - file: visu_pars
46
+ key: VisuAcqRepetitionTime
47
+
48
+ EchoTime:
49
+ sources:
50
+ - file: visu_pars
51
+ key: VisuAcqEchoTime
52
+
53
+ InversionTime:
54
+ sources:
55
+ - file: visu_pars
56
+ key: VisuAcqInversionTime
57
+
58
+ FlipAngle:
59
+ sources:
60
+ - file: visu_pars
61
+ key: VisuAcqFlipAngle
62
+
63
+ SliceThickness:
64
+ sources:
65
+ - file: visu_pars
66
+ key: VisuCoreSliceThickness
67
+
68
+ PixelBandwidth:
69
+ sources:
70
+ - file: visu_pars
71
+ key: VisuAcqPixelBandwidth
72
+
73
+ InPlanePhaseEncodingDirection:
74
+ sources:
75
+ - file: visu_pars
76
+ key: VisuAcqGradEncoding
77
+
78
+ PercentSampling:
79
+ sources:
80
+ - file: visu_pars
81
+ key: VisuAcqPartialFourier
82
+
83
+ PercentPhaseFieldOfView:
84
+ sources:
85
+ - file: visu_pars
86
+ key: VisuCoreExtent
87
+
88
+ PixelSpacing:
89
+ inputs:
90
+ extent:
91
+ sources:
92
+ - file: visu_pars
93
+ key: VisuCoreExtent
94
+ size:
95
+ sources:
96
+ - file: visu_pars
97
+ key: VisuCoreSize
98
+ transform: pixel_spacing_from_extent
99
+
100
+ SliceThickness_FG:
101
+ sources:
102
+ - file: visu_pars
103
+ key: VisuCoreSliceThickness
104
+
105
+ ImagePositionPatient:
106
+ sources:
107
+ - file: visu_pars
108
+ key: VisuCorePosition
109
+ transform: as_list
110
+
111
+ ImageOrientationPatient:
112
+ sources:
113
+ - file: visu_pars
114
+ key: VisuCoreOrientation
115
+ transform: as_list
116
+
117
+ RescaleSlope_FG:
118
+ sources:
119
+ - file: visu_pars
120
+ key: VisuCoreDataSlope
121
+
122
+ RescaleIntercept_FG:
123
+ sources:
124
+ - file: visu_pars
125
+ key: VisuCoreDataOffs
126
+
127
+ RepetitionTime_FG:
128
+ sources:
129
+ - file: visu_pars
130
+ key: VisuAcqRepetitionTime
131
+
132
+ EchoTime_FG:
133
+ sources:
134
+ - file: visu_pars
135
+ key: VisuAcqEchoTime
136
+
137
+ FlipAngle_FG:
138
+ sources:
139
+ - file: visu_pars
140
+ key: VisuAcqFlipAngle
141
+
142
+ EchoTrainLength:
143
+ sources:
144
+ - file: visu_pars
145
+ key: VisuAcqEchoTrainLength
146
+
147
+ NumberOfAverages:
148
+ sources:
149
+ - file: visu_pars
150
+ key: VisuAcqNAverages
151
+
152
+ PartialFourier:
153
+ sources:
154
+ - file: visu_pars
155
+ key: VisuAcqPartialFourier
156
+
157
+ PhaseEncodingDirectionPositive:
158
+ sources:
159
+ - file: visu_pars
160
+ key: VisuAcqGradEncoding
161
+
162
+ DiffusionBValue_FG:
163
+ sources:
164
+ - file: visu_pars
165
+ key: VisuAcqDiffusionBMatrix
166
+
167
+ DiffusionGradientOrientation_FG:
168
+ sources:
169
+ - file: visu_pars
170
+ key: VisuAcqDiffusionGradOrient
171
+ transform: as_list
172
+
173
+ MRSpectroscopyAcquisitionType:
174
+ sources:
175
+ - file: visu_pars
176
+ key: VisuMrsAcquisitionType
177
+ transform: strip_jcamp_string
178
+
179
+ ResonantNucleus:
180
+ sources:
181
+ - file: visu_pars
182
+ key: VisuMrsResonantNuclei
183
+ transform: strip_jcamp_string
184
+
185
+ SpectralWidth_MRS:
186
+ sources:
187
+ - file: visu_pars
188
+ key: VisuMrsSpectralWidth
189
+
190
+ TransmitterFrequency_MRS:
191
+ sources:
192
+ - file: visu_pars
193
+ key: VisuMrsTransmitterFreq
194
+
195
+ ChemicalShiftReference:
196
+ sources:
197
+ - file: visu_pars
198
+ key: VisuMrsChemicalShiftRef
199
+
200
+ NumberOfZeroFills:
201
+ sources:
202
+ - file: visu_pars
203
+ key: VisuMrsZeroFill
204
+
205
+ KSpaceFiltering:
206
+ sources:
207
+ - file: visu_pars
208
+ key: VisuAcqKSpaceFiltering
209
+
210
+ PulseSequenceName_MRS:
211
+ sources:
212
+ - file: visu_pars
213
+ key: VisuAcqSequenceName
214
+ transform: strip_jcamp_string
215
+
216
+ EchoTime_MRS:
217
+ sources:
218
+ - file: visu_pars
219
+ key: VisuAcqEchoTime
220
+
221
+ RepetitionTime_MRS:
222
+ sources:
223
+ - file: visu_pars
224
+ key: VisuAcqRepetitionTime
225
+
226
+ WaterSuppression:
227
+ sources:
228
+ - file: visu_pars
229
+ key: VisuAcqSpectralSuppression
230
+ transform: strip_jcamp_string
231
+
232
+ VolumeLocalizationTechnique:
233
+ sources:
234
+ - file: visu_pars
235
+ key: VisuMrsLocalizationTechnique
236
+ transform: strip_jcamp_string