diff --git a/brukerapi/cli.py b/brukerapi/cli.py index 623fcf9..7677968 100644 --- a/brukerapi/cli.py +++ b/brukerapi/cli.py @@ -112,9 +112,9 @@ def split(args): dataset = Dataset(args.path_in) if args.slice_package: - SlicePackageSplitter().split(dataset, write=True) + SlicePackageSplitter().split(dataset, write=True, path_out=args.path_out) elif args.frame_group: - FrameGroupSplitter(args.frame_group).split(dataset, write=True) + FrameGroupSplitter(args.frame_group).split(dataset, write=True, path_out=args.path_out) def report(args): @@ -124,25 +124,25 @@ def report(args): :return: """ input = Path(args.input) - - if args.output is None: - output = None - else: - output = Path(args.output) + output = None if args.output is None else Path(args.output) if input.is_dir(): # folder in-place if output is None: Folder(input).report(format_=args.format, props=args.props, verbose=args.verbose) - elif output.is_dir(): - # folder to folder + else: + # folder to folder -- an output folder that does not exist yet is + # created rather than silently matching no branch and exiting 0 + output.mkdir(parents=True, exist_ok=True) Folder(input).report(path_out=output, format_=args.format, props=args.props, verbose=args.verbose) # dataset in-place elif output is None: - Dataset(input, add_parameters=["subject"]).report(props=args.props, verbose=args.verbose) + Dataset(input, add_parameters=["subject"]).report(props=args.props, verbose=args.verbose, format_=args.format) # dataset to folder, or dataset to file else: - Dataset(input, add_parameters=["subject"]).report(path=output, props=args.props, verbose=args.verbose) + if not output.suffix: + output.mkdir(parents=True, exist_ok=True) + Dataset(input, add_parameters=["subject"]).report(path=output, props=args.props, verbose=args.verbose, format_=args.format) def filter(args): diff --git a/brukerapi/config/properties_2dseq_core.json b/brukerapi/config/properties_2dseq_core.json index 7627b43..fe07ef3 100644 --- a/brukerapi/config/properties_2dseq_core.json +++ b/brukerapi/config/properties_2dseq_core.json @@ -1,8 +1,9 @@ { "pv_version": [ { - "cmd": "#VisuCreatorVersion[1:-1]", - "conditions": [] + "cmd": "#VisuCreatorVersion[1:-1].split(';')[0]", + "conditions": [], + "comment": "spec 7.1: VisuCreator/VisuCreatorVersion are semicolon-separated lists; the first creator is the one that wrote the dataset" } ], "numpy_dtype": [ @@ -181,11 +182,11 @@ ], "dim_type": [ { - "cmd": "#VisuCoreDimDesc.list + ['spatial',] + #VisuFGOrderDesc.sub_list(1)", + "cmd": "#VisuCoreDimDesc.list + ['frame',] + #VisuFGOrderDesc.sub_list(1)", "conditions": ["@is_single_slice==True"] }, { - "cmd": "#VisuCoreDimDesc.list + ['spatial',]", + "cmd": "#VisuCoreDimDesc.list + ['frame',]", "conditions": ["@is_single_slice==True"] }, { diff --git a/brukerapi/config/properties_2dseq_custom.json b/brukerapi/config/properties_2dseq_custom.json index d35e797..21bb166 100644 --- a/brukerapi/config/properties_2dseq_custom.json +++ b/brukerapi/config/properties_2dseq_custom.json @@ -58,7 +58,7 @@ { "cmd": "np.array([#PVM_VoxArrSize[0,0] * #VisuAcqSize[1], #PVM_VoxArrSize[0,1] * #VisuAcqSize[2], #VisuCoreFrameThickness])", "conditions": [ - "#VisuCreatorVersion in ['<5.1>']", + "@pv_version.split('.')[0]=='5'", "#VisuCoreDimDesc.list[0]=='spectroscopic'", "#VisuCoreDimDesc.list[1]=='spatial'", "#VisuCoreDimDesc.list[2]=='spatial'"] @@ -66,7 +66,7 @@ { "cmd": "np.array([#PVM_VoxArrSize[0,0], #PVM_VoxArrSize[0,1], #PVM_VoxArrSize[0,2]])", "conditions": [ - "#VisuCreatorVersion in ['<6.0.1>']", + "@pv_version.split('.')[0]!='5'", "#VisuCoreDimDesc.list[0]=='spectroscopic'", "#VisuCoreDimDesc.list[1]=='spatial'", "#VisuCoreDimDesc.list[2]=='spatial'"] @@ -97,7 +97,7 @@ { "cmd": "np.array([#PVM_VoxArrSize[0,0], #PVM_VoxArrSize[0,1], #VisuCoreFrameThickness])", "conditions": [ - "#VisuCreatorVersion in ['<5.1>']", + "@pv_version.split('.')[0]=='5'", "#VisuCoreDimDesc.list[0]=='spectroscopic'", "#VisuCoreDimDesc.list[1]=='spatial'", "#VisuCoreDimDesc.list[2]=='spatial'"] @@ -105,41 +105,26 @@ { "cmd": "np.array([#PVM_VoxArrSize[0,0] / #VisuCoreSize[1], #PVM_VoxArrSize[0,1] / #VisuCoreSize[2], #PVM_VoxArrSize[0,2]])", "conditions": [ - "#VisuCreatorVersion in ['<6.0.1>']", + "@pv_version.split('.')[0]!='5'", "#VisuCoreDimDesc.list[0]=='spectroscopic'", "#VisuCoreDimDesc.list[1]=='spatial'", "#VisuCoreDimDesc.list[2]=='spatial'"] }, { - "cmd": "np.array([#VisuCoreExtent[0] / #VisuCoreSize[0], #VisuCoreExtent[1] / #VisuCoreSize[1], #VisuCoreFrameThickness + #VisuCoreSlicePacksSliceDist.list[0]])", - "conditions": [ - "#VisuCreatorVersion in ['<6.0.1>']", - "#VisuCoreDim==2"] - }, - { - "cmd": "np.array([#VisuCoreExtent[0] / #VisuCoreSize[0], #VisuCoreExtent[1] / #VisuCoreSize[1], abs(#VisuCorePosition[0,2] - #VisuCorePosition[1,2])])", + "cmd": "np.array([#VisuCoreExtent[0] / #VisuCoreSize[0], #VisuCoreExtent[1] / #VisuCoreSize[1], np.linalg.norm(#VisuCorePosition[1,:] - #VisuCorePosition[0,:])])", "conditions": [ - "#VisuCreatorVersion in ['<5.1>']", "#VisuCorePosition.size[0]>1", - "#VisuCoreDim==2"] + "#VisuCoreDim==2"], + "comment": "spec 7.10: the distance between two measured slice centres. VisuCoreSlicePacksSliceDist is already centre-to-centre, so it must not be added to VisuCoreFrameThickness" }, { - "cmd": "np.array([#VisuCoreExtent[0] / #VisuCoreSize[0], #VisuCoreExtent[1] / #VisuCoreSize[1], #VisuCoreFrameThickness])", + "cmd": "np.array([#VisuCoreExtent[0] / #VisuCoreSize[0], #VisuCoreExtent[1] / #VisuCoreSize[1], #VisuCoreSlicePacksSliceDist.list[0]])", "conditions": [ - "#VisuCreatorVersion in ['<5.1>']", - "#VisuCorePosition.size[0]==1", - "#VisuCoreDim==2"] - }, - { - "cmd": "np.array([#VisuCoreExtent[0] / #VisuCoreSize[0], #VisuCoreExtent[1] / #VisuCoreSize[1], np.linalg.norm(#VisuCorePosition[1,:] - #VisuCorePosition[0,:])])", - "conditions": [ - "#VisuCorePosition.size[0]>1", "#VisuCoreDim==2"] }, { "cmd": "np.array([#VisuCoreExtent[0] / #VisuCoreSize[0], #VisuCoreExtent[1] / #VisuCoreSize[1], #VisuCoreFrameThickness])", "conditions": [ - "#VisuCorePosition.size[0]==1", "#VisuCoreDim==2"] }, { @@ -150,96 +135,6 @@ "#VisuCoreDimDesc.list[2]=='spatial'"] } ], - "position_matrix": [ - { - "cmd": "np.array([[np.cos(np.pi), -np.sin(np.pi), 0], [np.sin(np.pi), np.cos(np.pi), 0], [0, 0, 1]])", - "conditions": [ - "#VisuSubjectPosition=='Head_Supine'" - ], - "reference": "This recipe was taken from the version BrkRaw repository (https://github.com/BrkRaw/bruker) version 0.3.4 (10.5281/zenodo.3907018)" - }, - { - "cmd": "np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])", - "conditions": [ - "#VisuSubjectPosition=='Head_Prone'" - ], - "reference": "This recipe was taken from the version BrkRaw repository (https://github.com/BrkRaw/bruker) version 0.3.4 (10.5281/zenodo.3907018)" - }, - { - "cmd": "np.array([[np.cos(np.pi/2), -np.sin(np.pi/2), 0], [np.sin(np.pi/2), np.cos(np.pi/2), 0], [0, 0, 1]])", - "conditions": [ - "#VisuSubjectPosition=='Head_Left'" - ], - "reference": "This recipe was taken from the version BrkRaw repository (https://github.com/BrkRaw/bruker) version 0.3.4 (10.5281/zenodo.3907018)" - }, - { - "cmd": "np.array([[np.cos(-np.pi/2), -np.sin(-np.pi/2), 0], [np.sin(-np.pi/2), np.cos(-np.pi/2), 0], [0, 0, 1]])", - "conditions": [ - "#VisuSubjectPosition=='Head_Right'" - ], - "reference": "This recipe was taken from the version BrkRaw repository (https://github.com/BrkRaw/bruker) version 0.3.4 (10.5281/zenodo.3907018)" - }, - { - "cmd": "np.array([[1, 0, 0], [0, np.cos(np.pi), -np.sin(np.pi)], [0, np.sin(np.pi), np.cos(np.pi)]]).astype('float')", - "conditions": [ - "#VisuSubjectPosition in ['Foot_Supine', 'Tail_Supine']" - ], - "reference": "This recipe was taken from the version BrkRaw repository (https://github.com/BrkRaw/bruker) version 0.3.4 (10.5281/zenodo.3907018)" - }, - { - "cmd": "np.array([[np.cos(np.pi), 0, np.sin(np.pi)], [0, 1, 0], [-np.sin(np.pi), 0, np.cos(np.pi)]]).astype('float')", - "conditions": [ - "#VisuSubjectPosition in ['Foot_Prone', 'Tail_Prone']" - ], - "reference": "This recipe was taken from the version BrkRaw repository (https://github.com/BrkRaw/bruker) version 0.3.4 (10.5281/zenodo.3907018)" - }, - { - "cmd": "np.array([[np.cos(np.pi/2), -np.sin(np.pi/2), 0], [np.sin(np.pi/2), np.cos(np.pi/2), 0], [0, 0, 1]])", - "conditions": [ - "#VisuSubjectPosition in ['Foot_Left', 'Tail_Left']" - ], - "reference": "This recipe was taken from the version BrkRaw repository (https://github.com/BrkRaw/bruker) version 0.3.4 (10.5281/zenodo.3907018)" - }, - { - "cmd": "np.array([[np.cos(-np.pi/2), -np.sin(-np.pi/2), 0], [np.sin(-np.pi/2), np.cos(-np.pi/2), 0], [0, 0, 1]])", - "conditions": [ - "#VisuSubjectPosition in ['Foot_Right', 'Tail_Right']" - ], - "reference": "This recipe was taken from the version BrkRaw repository (https://github.com/BrkRaw/bruker) version 0.3.4 (10.5281/zenodo.3907018)" - } - ], - "rotation": [ - { - "cmd": "np.dot(@position_matrix, np.reshape(#VisuCoreOrientation[0,:],(3,3)).T.dot(np.diag(@resolution)))", - "conditions": [ - ], - "reference": "This recipe was taken from the version BrkRaw repository (https://github.com/BrkRaw/bruker) version 0.3.4 (10.5281/zenodo.3907018)" - } - ], - "position": [ - { - "cmd": "#PVM_VoxArrPosition[0,:].astype(np.float64)", - "conditions": [ - "#VisuCoreDimDesc.list[0]=='spectroscopic'", - "#VisuCoreDimDesc.list[1]=='spatial'", - "#VisuCoreDimDesc.list[2]=='spatial'"] - }, - { - "cmd": "np.array([#VisuCorePosition[0,0] + @extent[0], #VisuCorePosition[0,1] + @extent[1], #VisuCorePosition[0,2]])", - "conditions": [] - } - ], - "affine": [ - { - "cmd": "np.array([[@rotation[0,0], @rotation[0,1], @rotation[0,2], @position[0]],[@rotation[1,0], @rotation[1,1], @rotation[1,2], @position[1]],[@rotation[2,0], @rotation[2,1], @rotation[2,2], @position[2]],[0, 0, 0, 1]])", - "conditions": [] - }, - { - "cmd": "np.identity(4)", - "conditions": [], - "comment": "default value" - } - ], "TE": [ { "cmd": "#VisuAcqEchoTime", diff --git a/brukerapi/config/properties_fid_core.json b/brukerapi/config/properties_fid_core.json index 62567a9..542d2d2 100644 --- a/brukerapi/config/properties_fid_core.json +++ b/brukerapi/config/properties_fid_core.json @@ -1,4 +1,15 @@ { + "pv_version": [ + { + "cmd": "#ACQ_sw_version[1:-1].replace('PV-','').replace('PV ','').strip()", + "conditions": [], + "comment": "spec 7.1/13: version gates compare on this parsed version, so an unlisted point release is not silently unsupported" + }, + { + "cmd": "''", + "conditions": [] + } + ], "numpy_dtype": [ { "cmd": "np.dtype('int32').newbyteorder('<')", @@ -47,7 +58,7 @@ "conditions": [ "#ACQ_word_size=='_32_BIT'", "#BYTORDA=='little'", - "#ACQ_sw_version=='' or #ACQ_sw_version.value.startswith('' or #ACQ_sw_version.value.startswith('' or #ACQ_sw_version.value.startswith('' or #ACQ_sw_version.value.startswith('" - ] - ] + "@pv_version.split('.')[0]=='360'" ] } ], @@ -125,7 +132,7 @@ "cmd": "#ACQ_size.tuple[0] * @channels", "conditions": [ ["#PULPROG", ["",""]], - "#ACQ_sw_version==''" + "@pv_version.split('.')[0]=='5'" ] }, { @@ -134,28 +141,17 @@ "#GO_block_size=='Standard_KBlock_Format'" ] }, - { - "cmd": "int(2 * #PVM_DigNp * @channels // #NSegments)", - "conditions": [ - "#GO_block_size!='Standard_KBlock_Format'", - "#PULPROG[1:-1]=='EPSI.ppg'" - ] - }, { "cmd": "int(2 * #PVM_DigNp * @channels)", "conditions": [ - "#GO_block_size!='Standard_KBlock_Format'", - "#PULPROG[1:-1]!='EPSI.ppg'" - ] + "#GO_block_size!='Standard_KBlock_Format'" + ], + "comment": "spec 3.1: for GO_block_size=continuous the whole block is digitized data. Dividing by NSegments here dropped every segment but the last, because block_count already counts segments" }, { "cmd": "#ACQ_jobs[0][0]", "conditions": [ - ["#ACQ_sw_version", - [ - "" - ] - ] + "@pv_version.split('.')[0]=='360'" ] } ], @@ -288,8 +284,7 @@ "SPIRAL.ppg", "DtiSpiral.ppg" ] - ], - "#ACQ_sw_version in ['', '', '', '']" + ] ] }, { @@ -326,7 +321,7 @@ ] }, { - "cmd": "#PVM_EncMatrix[1]*#PVM_EncMatrix[2]*#PVM_NEchoImages", + "cmd": "#PVM_EncMatrix[1]*#PVM_EncMatrix[2]*#PVM_NEchoImages*#NR", "conditions": [ "@scheme_id=='FIELD_MAP'" ] @@ -343,6 +338,13 @@ "@scheme_id=='EPI'" ] }, + { + "cmd": "#NSegments*#ACQ_size[2]*#NI*#NR", + "conditions": [ + "#GO_block_size!='Standard_KBlock_Format'", + "#PULPROG[1:-1]=='EPSI.ppg'" + ] + }, { "cmd": "#NSegments*#NI*#NR*(#ACQ_size[2] if len(#ACQ_size)>2 else 1)", "conditions": [ @@ -359,32 +361,28 @@ "cmd": "#ACQ_size[1] * #ACQ_size[2]", "conditions": [ "@scheme_id=='CSI'", - "#ACQ_sw_version in ['', '', '', '']" + "@pv_version.split('.')[0]!='360'" ] }, { "cmd": "#ACQ_spatial_size_0 * #ACQ_spatial_size_1", "conditions": [ "@scheme_id=='CSI'", - ["#ACQ_sw_version", - [ - "" - ] - ] + "@pv_version.split('.')[0]=='360'" ] }, { "cmd": "#PVM_SpiralNbOfInterleaves*#NI*#NR", "conditions": [ "@scheme_id=='SPIRAL'", - "#ACQ_sw_version in ['', '']" + "@pv_version.split('.')[0]!='5'" ] }, { "cmd": "#ACQ_size[1]*#NI*#NR", "conditions": [ "@scheme_id=='SPIRAL'", - "#ACQ_sw_version==''" + "@pv_version.split('.')[0]=='5'" ] }, { @@ -444,7 +442,8 @@ "#PVM_EncNReceivers", "#PVM_NEchoImages", "#PVM_EncMatrix[1]", - "#PVM_EncMatrix[2]" + "#PVM_EncMatrix[2]", + "#NR" ], "conditions": [ "@scheme_id=='FIELD_MAP'" @@ -475,6 +474,19 @@ "@scheme_id=='EPI'" ] }, + { + "cmd": [ + "#PVM_EncMatrix[0]", + "#PVM_EncNReceivers", + "#PVM_DigNp // #PVM_EncMatrix[0]", + "#NSegments", + "#ACQ_size[2]" + ], + "conditions": [ + "#GO_block_size!='Standard_KBlock_Format'", + "#PULPROG[1:-1]=='EPSI.ppg'" + ] + }, { "cmd": [ "#PVM_EncMatrix[0] * #PVM_EncMatrix[1] // #NSegments", @@ -506,7 +518,7 @@ ], "conditions": [ "@scheme_id=='CSI'", - "#ACQ_sw_version in ['', '', '', '']" + "@pv_version.split('.')[0]!='360'" ] }, { @@ -517,11 +529,7 @@ ], "conditions": [ "@scheme_id=='CSI'", - ["#ACQ_sw_version", - [ - "" - ] - ] + "@pv_version.split('.')[0]=='360'" ] }, { @@ -534,7 +542,7 @@ ], "conditions": [ "@scheme_id=='SPIRAL'", - "#ACQ_sw_version in ['', '']" + "@pv_version.split('.')[0]!='5'" ] }, { @@ -547,7 +555,7 @@ ], "conditions": [ "@scheme_id=='SPIRAL'", - "#ACQ_sw_version==''" + "@pv_version.split('.')[0]=='5'" ] }, { @@ -587,7 +595,7 @@ ] }, { - "cmd": [0,3,4,2,1], + "cmd": [0,3,4,2,5,1], "conditions": [ "@scheme_id in ['FIELD_MAP']" ] @@ -604,6 +612,13 @@ "@scheme_id in ['EPI']" ] }, + { + "cmd": [0,4,3,2,1], + "conditions": [ + "#GO_block_size!='Standard_KBlock_Format'", + "#PULPROG[1:-1]=='EPSI.ppg'" + ] + }, { "cmd": [0,2,3,4,1,5], "conditions": [ @@ -671,6 +686,7 @@ "#PVM_EncMatrix[1]", "#PVM_EncMatrix[2]", "#PVM_NEchoImages", + "#NR", "#PVM_EncNReceivers" ], "conditions": [ @@ -701,6 +717,19 @@ "@scheme_id=='EPI'" ] }, + { + "cmd": [ + "#PVM_EncMatrix[0]", + "#ACQ_size[2]", + "#NSegments * (#PVM_DigNp // #PVM_EncMatrix[0])", + "#PVM_EncNReceivers" + ], + "conditions": [ + "#GO_block_size!='Standard_KBlock_Format'", + "#PULPROG[1:-1]=='EPSI.ppg'" + ], + "comment": "spec 6.3: matches RECO_inp_size -- read x phase x spectral points x receivers" + }, { "cmd": [ "#PVM_EncMatrix[0]", @@ -732,7 +761,7 @@ ], "conditions": [ "@scheme_id=='CSI'", - "#ACQ_sw_version in ['', '', '', '']" + "@pv_version.split('.')[0]!='360'" ] }, { @@ -743,11 +772,7 @@ ], "conditions": [ "@scheme_id=='CSI'", - ["#ACQ_sw_version", - [ - "" - ] - ] + "@pv_version.split('.')[0]=='360'" ] }, { @@ -760,7 +785,7 @@ ], "conditions": [ "@scheme_id=='SPIRAL'", - "#ACQ_sw_version in ['', '']" + "@pv_version.split('.')[0]!='5'" ] }, { @@ -773,7 +798,7 @@ ], "conditions": [ "@scheme_id=='SPIRAL'", - "#ACQ_sw_version==''" + "@pv_version.split('.')[0]=='5'" ] }, { @@ -808,6 +833,7 @@ "'k_space_encode_step_0'", "'k_space_encode_step_1'", "'k_space_encode_step_2'", + "'object'", "'repetition'", "'channel'" ], @@ -822,7 +848,19 @@ "cmd": [ "'k_space_encode_step_0'", "'k_space_encode_step_1'", - "'slice'", + "'k_space_encode_step_2'", + "'channel'" + ], + "conditions": [ + "#GO_block_size!='Standard_KBlock_Format'", + "#PULPROG[1:-1]=='EPSI.ppg'" + ] + }, + { + "cmd": [ + "'k_space_encode_step_0'", + "'k_space_encode_step_1'", + "'object'", "'repetition'", "'channel'", "'k_space_encode_step_2'" @@ -835,7 +873,7 @@ "cmd": [ "'k_space_encode_step_0'", "'k_space_encode_step_1'", - "'slice'", + "'object'", "'repetition'", "'channel'" ], @@ -848,18 +886,32 @@ "'k_space_encode_step_0'", "'k_space_encode_step_1'", "'k_space_encode_step_2'", + "'echo'", "'repetition'", "'channel'" ], "conditions": [ - ["@scheme_id",["CART_3D","FIELD_MAP"]] - ] + "@scheme_id=='FIELD_MAP'" + ], + "comment": "spec 5.2: a field map's fourth axis counts echo images, and NR is a stored axis of its own" }, { "cmd": [ "'k_space_encode_step_0'", + "'k_space_encode_step_1'", + "'k_space_encode_step_2'", "'repetition'", - "'average'" + "'channel'" + ], + "conditions": [ + ["@scheme_id",["CART_3D"]] + ] + }, + { + "cmd": [ + "'k_space_encode_step_0'", + "'object'", + "'repetition'" ], "conditions": [ "@scheme_id=='SPECTROSCOPY'" diff --git a/brukerapi/config/properties_rawdata_core.json b/brukerapi/config/properties_rawdata_core.json index e843616..250d535 100644 --- a/brukerapi/config/properties_rawdata_core.json +++ b/brukerapi/config/properties_rawdata_core.json @@ -1,4 +1,15 @@ { + "pv_version": [ + { + "cmd": "#ACQ_sw_version[1:-1].replace('PV-','').replace('PV ','').strip()", + "conditions": [], + "comment": "spec 7.1/13: version gates compare on this parsed version, so an unlisted point release is not silently unsupported" + }, + { + "cmd": "''", + "conditions": [] + } + ], "numpy_dtype": [ { "cmd": "np.dtype('int32').newbyteorder('<')", @@ -47,7 +58,7 @@ "conditions": [ "#ACQ_word_size=='_32_BIT'", "#BYTORDA=='little'", - "#ACQ_sw_version=='' or #ACQ_sw_version.value.startswith('' or #ACQ_sw_version.value.startswith('' or #ACQ_sw_version.value.startswith('' or #ACQ_sw_version.value.startswith(' {path!s}") @@ -852,6 +905,8 @@ def report(self, path=None, props=None, verbose=None): self.to_json(path, props=props) elif path.suffix == ".yml": self.to_yaml(path, props=props) + else: + raise ValueError(f"unsupported report file name {path.name!r}, expected a .json or .yml suffix") def to_json(self, path=None, props=None): """ @@ -891,6 +946,7 @@ def to_dict(self, props=None): if not props: props = list(vars(self).keys()) + props += [name for name in COMPUTED_REPORT_PROPERTIES if name not in props] # list of Dataset properties to be excluded from the export reserved = { @@ -915,7 +971,16 @@ def to_dict(self, props=None): properties = {} for var in props: - properties[var] = self._encode_property(self.__getattribute__(var)) + if var in COMPUTED_REPORT_PROPERTIES: + # Computed rather than stored, and not available for every dataset: + # a report of a whole folder must not fail on a spectroscopy scan. + try: + value = getattr(self, var) + except (AttributeError, IndexError, KeyError, UnsupportedDatasetType): + continue + else: + value = self.__getattribute__(var) + properties[var] = self._encode_property(value) return properties @@ -939,6 +1004,9 @@ def _encode_property(self, var): return [self._encode_property(var_) for var_ in var] if isinstance(var, tuple): return self._encode_property(list(var)) + if isinstance(var, dict): + # grouped metadata is a dict of dicts of parameter values + return {key: self._encode_property(value) for key, value in var.items()} if isinstance(var, (datetime.datetime, str)): return str(var) return var @@ -992,6 +1060,123 @@ def data(self): def data(self, value): self._data = value + def _frame_geometry(self): + """Per-frame ``(position, orientation)`` in the order the data array is in. + + ``VisuCoreDiskSliceOrder = disk_reverse_slice_order`` reverses the stored + frame order, and :class:`~brukerapi.schemas.Schema2dseq` flips the data + accordingly, so the geometry of a 2-D stack has to be reversed with it + (spec 7.2/7.3). For a 3-D volume ``VisuCorePosition`` already refers to + the first voxel of the *last* stored frame, which is where the flip puts + index 0, so nothing moves. + """ + position = self._parameter_value("VisuCorePosition") + orientation = self._parameter_value("VisuCoreOrientation") + if position is None or orientation is None: + raise UnsupportedDatasetType(f"an image affine for {self.path}, which carries no VisuCorePosition/VisuCoreOrientation (spec 7.2),") + + position = np.atleast_2d(np.asarray(position, dtype=float)) + orientation = np.atleast_2d(np.asarray(orientation, dtype=float)) + if orientation.shape[1] != 9 or position.shape[1] != 3: + raise UnsupportedDatasetType(f"an image affine for {self.path}, whose frames carry no 3x3 orientation and 3-vector position (spec 7.2),") + + disk_order = str(self._parameter_value("VisuCoreDiskSliceOrder", "")).strip("<>").lower() + if disk_order == "disk_reverse_slice_order" and int(self._parameter_value("VisuCoreDim", 2)) < 3 and position.shape[0] > 1: + position = position[::-1] + if orientation.shape[0] == position.shape[0]: + orientation = orientation[::-1] + + return position, orientation + + def slice_packages_index(self): + """``[(first_frame, n_slices)]`` per slice package -- spec 7.10. + + Packages may have different slice counts, so each package carries its own + count. PV5.1 writes no slice-package parameters at all; there, frames + sharing one orientation are grouped instead. + """ + packs = self._parameter_value("VisuCoreSlicePacksSlices") + if packs is not None: + return [(int(first), int(count)) for first, count in np.atleast_2d(np.asarray(packs, dtype=int))] + + position, orientation = self._frame_geometry() + count = position.shape[0] + if orientation.shape[0] < count: + return [(0, count)] + + packages, start = [], 0 + for index in range(1, count + 1): + if index == count or not np.allclose(orientation[index], orientation[start], atol=1e-9): + packages.append((start, index - start)) + start = index + return packages + + def affine_of_package(self, package=0): + """4x4 voxel-index -> patient-coordinate transform of one slice package. + + Built straight from the parameters that define the geometry (spec 7.2, + 7.10, 12): ``VisuCoreOrientation`` maps patient to image coordinates + (``i = M.p``), so its transpose maps image to patient, and + ``VisuCorePosition`` is the centre of the first voxel transferred, which + is the translation. + + The result is in the Visu/DICOM patient frame (R->L, A->P, F->H). A + NIfTI writer converts with ``np.diag([-1, -1, 1, 1]) @ affine``; the + ParaVision user-interface frame needs both ends transformed, per spec 12. + """ + dimension = int(self._parameter_value("VisuCoreDim", 2)) + size = np.atleast_1d(np.asarray(self._parameter_value("VisuCoreSize"), dtype=float)) + extent = np.atleast_1d(np.asarray(self._parameter_value("VisuCoreExtent"), dtype=float)) + position, orientation = self._frame_geometry() + + first, count = self.slice_packages_index()[package] + rotation = orientation[first if orientation.shape[0] > first else 0].reshape(3, 3).T + origin = position[first if position.shape[0] > first else 0] + + columns = [ + rotation[:, 0] * (extent[0] / size[0]), + rotation[:, 1] * (extent[1] / size[1] if dimension >= 2 else 1.0), + ] + if dimension >= 3: + columns.append(rotation[:, 2] * (extent[2] / size[2])) + elif count > 1 and position.shape[0] > first + 1 and not np.allclose(position[first + 1], origin): + # The step between two measured slice centres carries direction *and* + # spacing; VisuCoreSlicePacksSliceDist gives only an unsigned distance. + columns.append(position[first + 1] - origin) + else: + distance = self._parameter_value("VisuCoreSlicePacksSliceDist") + if distance is not None: + distances = np.atleast_1d(np.asarray(distance, dtype=float)) + step = float(distances[package if distances.size > package else 0]) + else: + step = float(np.atleast_1d(np.asarray(self._parameter_value("VisuCoreFrameThickness", 1.0), dtype=float))[0]) + columns.append(rotation[:, 2] * step) + + affine = np.eye(4) + affine[:3, :3] = np.column_stack(columns) + affine[:3, 3] = origin + return affine + + @property + def affine(self): + """4x4 voxel-index -> patient-coordinate transform of the first slice package. + + :raise: :UnsupportedDatasetType: if the frames are not purely spatial, or + carry no geometry at all + """ + descriptors = np.atleast_1d(np.asarray(self._parameter_value("VisuCoreDimDesc", []))).astype(str) + if descriptors.size and any(descriptor != "spatial" for descriptor in descriptors): + raise UnsupportedDatasetType( + f"an image affine for {self.path}, whose frames are {sorted(set(descriptors))} rather than purely spatial (spec 7.2)," + ) + if len(self.slice_packages_index()) > 1: + warnings.warn( + f"{self.path} has multiple slice packages; a single affine cannot describe them -- use get_slice_packages() / affine_of_package(i)", + RuntimeWarning, + stacklevel=2, + ) + return self.affine_of_package(0) + def get_slice_packages(self): """Return one in-memory 2dseq dataset per slice package. @@ -1065,21 +1250,23 @@ def frame_group_values(self): descriptors = [descriptors] grouped = {} - for dependency in dependencies: - if len(dependency) < 2: - continue - name = str(dependency[0]).strip("<>") + # Spec 7.4: ownership runs from the frame-group descriptor, whose + # (valsStart, valsCnt) is a window into VisuGroupDepVals. + # VisuGroupDepVals[k][1] is that entry's start index in the *dependent + # parameter* array, not an index into VisuFGOrderDesc -- and it is + # almost always 0, which used to put every dependent parameter on frame + # group 0. + for descriptor in descriptors: try: - descriptor_index = int(dependency[1]) - group_name = str(descriptors[descriptor_index][1]).strip("<>") - axis = next( - axis - for axis, dim_type in enumerate(self.dim_type) - if str(dim_type).strip("<>") == group_name - ) - except (IndexError, KeyError, StopIteration, TypeError, ValueError): + vals_start, vals_count = int(descriptor[3]), int(descriptor[4]) + group_name = str(descriptor[1]).strip("<>") + axis = next(axis for axis, dim_type in enumerate(self.dim_type) if str(dim_type).strip("<>") == group_name) + except (IndexError, StopIteration, TypeError, ValueError): continue - grouped.setdefault(name, []).append(axis) + for index in range(vals_start, vals_start + vals_count): + if not (0 <= index < len(dependencies)) or len(dependencies[index]) < 2: + continue + grouped.setdefault(str(dependencies[index][0]).strip("<>"), []).append(axis) values = {} data_shape = self._data.shape @@ -1115,26 +1302,41 @@ def frame_group_values(self): values[name] = np.reshape(aligned, target_shape) return values + @staticmethod + def _metadata_field(name, prefix): + """Snake-case `name` with `prefix` removed, if it ends on a word boundary. + + ``VisuAcq`` is a prefix of ``VisuAcquisitionProtocol`` without being its + group prefix, and stripping it blindly yields `uisition_protocol`. + """ + field = name.removeprefix(prefix) + boundary = not prefix or prefix.endswith("_") or not field or field[0].isupper() or field[0] == "_" + if not boundary: + return None + return re.sub(r"(? exp = self._get_exp(exp_id) if proc_id is not None: return exp._get_proc(proc_id)["2dseq"] - return exp["fid"] + try: + return exp["fid"] + except KeyError: + # ParaVision 360 writes no file named `fid`; raw data lives in + # rawdata.jobN (spec 13.1). + jobs = sorted( + (child for child in exp.children if isinstance(child, Dataset) and re.fullmatch(r"rawdata\.job\d+", child.path.name)), + key=lambda child: int(child.path.name.rsplit("job", 1)[1]), + ) + if jobs: + return jobs[0] + raise def _get_exp(self, exp_id): for exp in self.get_experiment_list(): @@ -540,7 +561,7 @@ def filter_pass(self, node): def filter_eval(self, node): if isinstance(node, Dataset): - with node(add_properties=["subject"]) as n: + with node(add_parameters=["subject"]) as n: n.query(self.query) else: raise FilterEvalFalse diff --git a/brukerapi/jcampdx.py b/brukerapi/jcampdx.py index d33a25c..8804870 100644 --- a/brukerapi/jcampdx.py +++ b/brukerapi/jcampdx.py @@ -21,7 +21,6 @@ "EQUAL_SIGN": "=", "SINGLE_NUMBER": r"-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?", "PARALLEL_BRACKET": r"\) ", - "GEO_OBJ": r"\(\(\([\s\S]*\)[\s\S]*\)[\s\S]*\)", "HEADER": "TITLE|JCAMPDX|JCAMP-DX|DATA TYPE|DATATYPE|ORIGIN|OWNER", "VERSION_TITLE": "JCAMPDX|JCAMP-DX", } @@ -39,10 +38,15 @@ _SIZE_BRACKET_RE = _COMPILED_GRAMMAR["SIZE_BRACKET"] _SINGLE_NUMBER_RE = _COMPILED_GRAMMAR["SINGLE_NUMBER"] _PARALLEL_BRACKET_RE = _COMPILED_GRAMMAR["PARALLEL_BRACKET"] -_GEO_OBJ_RE = _COMPILED_GRAMMAR["GEO_OBJ"] _HEADER_RE = _COMPILED_GRAMMAR["HEADER"] _VERSION_TITLE_RE = _COMPILED_GRAMMAR["VERSION_TITLE"] _PARAMETER_RE = re.compile(GRAMMAR["PARAMETER"], re.MULTILINE) +# Spec 2.2/10.1: the text inside <...> is free-form, and ParaVision writes +# escaped delimiters (\< and \>) in the reco filter-graph descriptors. A +# backslash is not always an escape though -- an empty study description is +# written as `<\>` -- so the escaped reading is tried first and a string that +# never terminates under it is re-read with the backslash as content. +_STRING_RE = re.compile(r"<(?:\\.|[^<>\\])*>|<[^<>]*>") class Parameter: @@ -224,7 +228,13 @@ def value(self): if isinstance(value, np.ndarray) and self.size: if "str" not in value.dtype.name: - return np.reshape(value, self.size, order="C") + try: + return np.reshape(value, self.size, order="C") + except ValueError as error: + # spec 2.3: the element count has to match the product of the + # declared dimensions, and a mismatch is a diagnosable + # condition, not an internal error + raise InvalidJcampdxFile(f"{self.key}: {value.size} values do not fill the declared size {self.size}") from error return value return value @@ -288,10 +298,15 @@ def size(self): size = match.group(1).strip() elif "," in size_str: - size_str = size_str.split(",") - size = tuple(np.array(size_str, dtype="int32")) + try: + size = tuple(np.array(size_str.split(","), dtype="int32")) + except ValueError as error: + raise InvalidJcampdxFile(f"{self.key}: size bracket {self.size_str.strip()!r} is not a list of integers") from error else: - size = (int(size_str),) + try: + size = (int(size_str),) + except ValueError as error: + raise InvalidJcampdxFile(f"{self.key}: size bracket {self.size_str.strip()!r} is not an integer") from error return size @@ -318,24 +333,45 @@ def size(self, size): self.size_str = size_str @classmethod - def _split_outside_angle_brackets(cls, value, delimiter): + def _split_outside_angle_brackets(cls, value, delimiter, *, respect_parens=False, escapes=True): + """Split on `delimiter`, ignoring occurrences inside `<...>` strings. + + With ``respect_parens`` the split also ignores anything inside a nested + ``(...)`` group, which is what keeps a struct array's elements intact + (spec 2.3). A backslash escapes the character after it, so ``\\>`` does + not close a string (spec 2.2/10.1) -- unless reading it that way leaves a + string open, in which case the backslash was content and the split is + redone without escapes. + """ parts = [] start = 0 angle_depth = 0 + paren_depth = 0 index = 0 while index < len(value): - if value[index] == "<": + char = value[index] + if escapes and char == "\\" and index + 1 < len(value): + index += 2 + continue + if char == "<": angle_depth += 1 - elif value[index] == ">" and angle_depth: + elif char == ">" and angle_depth: angle_depth -= 1 - elif angle_depth == 0 and value.startswith(delimiter, index): + elif angle_depth == 0 and respect_parens and char == "(": + paren_depth += 1 + elif angle_depth == 0 and respect_parens and char == ")" and paren_depth: + paren_depth -= 1 + elif angle_depth == 0 and paren_depth == 0 and value.startswith(delimiter, index): parts.append(value[start:index]) index += len(delimiter) start = index continue index += 1 + if escapes and angle_depth: + return cls._split_outside_angle_brackets(value, delimiter, respect_parens=respect_parens, escapes=False) + parts.append(value[start:]) return parts @@ -345,7 +381,7 @@ def parse_value(cls, val_str, size_bracket=None): # sharp string if val_str.startswith("<") and val_str.endswith(">"): - val_strs = re.findall("<[^<>]*>", val_str) + val_strs = _STRING_RE.findall(val_str) if len(val_strs) == 1: return val_strs[0] @@ -367,7 +403,7 @@ def parse_value(cls, val_str, size_bracket=None): # list if val_str.startswith("(") and val_str.endswith(")"): - val_strs = cls._split_outside_angle_brackets(val_str[1:-1], ", ") + val_strs = cls._split_outside_angle_brackets(val_str[1:-1], ", ", respect_parens=True) value = [] for val_str in val_strs: @@ -397,7 +433,13 @@ def parse_value(cls, val_str, size_bracket=None): @staticmethod def _normalize_line_breaks(value): - return re.sub(r"[ \t]*\r?\n[ \t]*", " ", value) + # Spec 2.2: ParaVision hard-wraps a value near column 80 by INSERTING a + # newline; no character of the value is removed, and the wrap can fall + # inside a <...> string or a struct tuple. Undoing it therefore means + # deleting the newline and nothing else -- substituting a space invents + # one that was never on disk, and stripping the blanks around it deletes + # data (leading blanks on a continuation line belong to the value). + return re.sub(r"\r?\n", "", value) @classmethod def serialize_value(cls, value, version): @@ -516,43 +558,6 @@ def size(self): return None -class GeometryParameter(Parameter): - def __init__(self, key_str, size_str, val_str, version): - super().__init__(key_str, size_str, val_str, version) - - @property - def value(self): - return None - - @value.setter - def value(self, value): - self.val_str = value - - # @property - # def affine(self): - # """ - # - # :return: 4x4 3D Affine Transformation Matrix - # """ - # # TODO support for multiple slice packages - # match = re.match(r'\(\(\([^\)]*\)', self.val_str) - # affine_str = self.val_str[match.start() + 3: match.end() - 1] - # orient, shift = affine_str.split(', ') - # - # orient = GenericParameter.parse_value(orient) - # shift = GenericParameter.parse_value(shift) - # affine = np.zeros(shape=(4,4)) - # affine[0:3, 0:3] = np.reshape(orient, (3,3)) - # affine[0:3, 3] = shift - # - # return affine - - def to_dict(self): - # result = {'affine': self._encode_parameter(self.affine)} - result = {} - return result - - class DataParameter(Parameter): def __init__(self, key_str, size_str, val_str, version): super().__init__(key_str, size_str, val_str, version) @@ -633,14 +638,10 @@ def __str__(self, file=None): jcampdx_serial = "" for param in self.params.values(): - param_str = str(param) - - if len(param_str) > 78: - param_str = JCAMPDX.wrap_lines(param_str) - - jcampdx_serial += f"{param_str}\n" + jcampdx_serial += f"{JCAMPDX.wrap_lines(str(param))}\n" - return jcampdx_serial[0:-1] + "\n##END=" + tail = [*getattr(self, "_end_comments", []), "##END=", *getattr(self, "_trailing_comments", [])] + return jcampdx_serial + "\n".join(tail) def __enter__(self): self.load() @@ -666,10 +667,12 @@ def load(self): self.load_parameters() def load_parameters(self): - self.params = JCAMPDX.read_jcampdx(self.path) + self.params, self._end_comments, self._trailing_comments = JCAMPDX.read_jcampdx(self.path, with_comments=True) def unload(self): self.params = {} + self._end_comments = [] + self._trailing_comments = [] def to_dict(self): parameters = {} @@ -859,32 +862,58 @@ def load_parameter(cls, path, key): return key, parameter @classmethod - def read_jcampdx(cls, path): + def read_jcampdx(cls, path, *, with_comments=False): + """Parse `path` into a dict of parameters. + + With ``with_comments=True`` the comment records that belong to no + parameter are returned as well, as + ``(params, comments_before_end, comments_after_end)``, so that writing + the file back reproduces them. + """ path = as_path(path) params = {} - with path.open() as f: - try: + try: + with path.open() as f: content = f.read() - except (UnicodeDecodeError, OSError) as e: - raise JcampdxFileError(f"file {path} is not a text file") from e + except FileNotFoundError: + # a caller distinguishes "not there" from "not readable": an optional + # parameter file is allowed to be absent + raise + except (UnicodeDecodeError, OSError) as e: + raise JcampdxFileError(f"file {path} is not a text file") from e comments_by_parameter = [] pending_comments = [] + end_comments = [] + trailing_comments = [] content_without_comments = [] + seen_end = False for line in content.splitlines(keepends=True): if line.lstrip().startswith("$$"): - pending_comments.append(line.rstrip("\r\n")) + # Spec 2.1: a $$ line is a comment record of its own. The ones + # around ##END= -- the last @vis block and the "File finished by + # PARX" trailer -- belong to no parameter, so they are kept + # separately rather than dropped. + (trailing_comments if seen_end else pending_comments).append(line.rstrip("\r\n")) continue if line.startswith("##"): - comments_by_parameter.append(pending_comments) + if line.startswith("##END="): + seen_end = True + end_comments = pending_comments + else: + comments_by_parameter.append(pending_comments) pending_comments = [] content_without_comments.append(line) content = "".join(content_without_comments) - # split into individual entries - content = _PARAMETER_RE.split(content)[1:-1] + # split into individual entries; spec 2.1 warns that a file may not end + # at ##END=, and dropping the last chunk unconditionally then loses a + # real parameter without a word + content = _PARAMETER_RE.split(content)[1:] + if content and content[-1].startswith("END="): + content = content[:-1] # strip trailing EOL content = [_TRAILING_EOL_RE.sub("", x) for x in content] @@ -900,15 +929,16 @@ def read_jcampdx(cls, path): key, parameter = JCAMPDX.handle_jcampdx_line(f"##{line}", version) parameter.comments_before = comments_by_parameter[index] params[key] = parameter + + if with_comments: + return params, end_comments, trailing_comments return params @classmethod def handle_jcampdx_line(cls, line, version): key_str, size_str, val_str = cls.divide_jcampdx_line(line) - if _GEO_OBJ_RE.search(line) is not None: - parameter = GeometryParameter(key_str, size_str, val_str, version) - elif _DATA_LABEL_RE.search(line): + if _DATA_LABEL_RE.search(line): parameter = DataParameter(key_str, size_str, val_str, version) elif _HEADER_RE.search(key_str): parameter = HeaderParameter(key_str, size_str, val_str, version) @@ -957,31 +987,30 @@ def strip_size_bracket(cls, val_str): @classmethod def wrap_lines(cls, line): + """Hard-wrap a record the way ParaVision does: by inserting newlines. + + Splitting on whitespace and rebuilding, as this used to do, deletes the + character it breaks at and collapses runs of blanks, so the value read + back differs from the one written (spec 2.2). Wrapping a ``$$`` line is + worse still: its tail no longer starts with ``$$``, so on re-read it is + parsed as value data of the preceding parameter (spec 2.1). + """ line_wraps = [] for physical_line in line.split("\n"): - if len(physical_line) <= MAX_LINE_LEN: + if physical_line.lstrip().startswith("$$"): line_wraps.append(physical_line) continue - words = physical_line.split() - if not words: - line_wraps.append(physical_line) - continue - - wrapped = [] - current = words[0] - - for word in words[1:]: - candidate = f"{current} {word}" - if len(candidate) > MAX_LINE_LEN: - wrapped.append(current) - current = f" {word}" - else: - current = candidate - - wrapped.append(current) - line_wraps.extend(wrapped) + rest = physical_line + while len(rest) > MAX_LINE_LEN: + cut = rest.rfind(" ", 1, MAX_LINE_LEN) + # Keep the space on the left-hand line: the wrap inserts a + # newline, it does not consume the character it breaks at. + cut = MAX_LINE_LEN if cut <= 0 else cut + 1 + line_wraps.append(rest[:cut]) + rest = rest[cut:] + line_wraps.append(rest) return "\n".join(line_wraps) @@ -992,4 +1021,4 @@ def write(self, path): :return: """ with Path(path).open("w") as f: - f.write(str(self)) + f.write(f"{self!s}\n") diff --git a/brukerapi/schemas.py b/brukerapi/schemas.py index 0fbfe47..2e08214 100644 --- a/brukerapi/schemas.py +++ b/brukerapi/schemas.py @@ -27,6 +27,11 @@ "k_space_encode_step_2": BART_PHS2_DIM, "channel": BART_COIL_DIM, "repetition": BART_TIME_DIM, + "echo": BART_TIME2_DIM, + # spec 5.2: the NI axis counts acquisition objects (slices x echoes x movie + # frames), which is what BART calls its slice dimension for a plain + # multi-slice scan. + "object": BART_SLICE_DIM, "slice": BART_SLICE_DIM, "average": BART_AVG_DIM, } @@ -264,9 +269,10 @@ def to_kspace(self, data=None, *, bart=False): return self._as_bart(data, axes) def _reorder_objects(self, data, dir="FW"): - """Map the stored acquisition-object order onto the labelled slice axis.""" + """Apply ACQ_obj_order to the axis that counts acquisition objects (spec 5.2).""" dim_type = getattr(self._dataset, "dim_type", ()) - if "slice" not in dim_type: + axis_label = next((label for label in ("object", "slice") if label in dim_type), None) + if axis_label is None: return data try: @@ -274,7 +280,7 @@ def _reorder_objects(self, data, dir="FW"): except KeyError: return data - axis = dim_type.index("slice") + axis = dim_type.index(axis_label) if object_order.size != data.shape[axis] or np.array_equal(object_order, np.arange(object_order.size)): return data @@ -580,11 +586,14 @@ def layouts(self): def deserialize(self, data, layouts): if self._dataset.subtype == "orig" and self._primary_schema is not None: return self._primary_schema.deserialize(data, layouts) - return data[0::2] + 1j * data[1::2] + decoded = data[0::2] + 1j * data[1::2] + shape = getattr(self._dataset, "shape_final", None) + return decoded if shape is None else np.reshape(decoded, shape, order="F") def serialize(self, data, layouts): if self._dataset.subtype == "orig" and self._primary_schema is not None: return self._primary_schema.serialize(data, layouts) + data = np.reshape(np.asarray(data), (-1,), order="F") serialized = np.empty(data.size * 2, dtype=self._dataset.numpy_dtype) serialized[0::2] = np.real(data) serialized[1::2] = np.imag(data) @@ -820,7 +829,57 @@ def scale(self): data = self._apply_disk_slice_order(data) self._dataset.data = self._combine_complex_frames(data) + def _apply_core_transposition(self, data, layouts, *, inverse=False): + """Undo the per-frame dimension exchange recorded by VisuCoreTransposition. + + Spec 7.2: a nonzero value means frame f is stored with two of its + dimensions exchanged relative to VisuCoreSize -- `n < VisuCoreDim` + exchanges `n` and `n-1`, `VisuCoreDim` exchanges `0` and + `VisuCoreDim - 1`. Such a frame has to be read in its real on-disk shape + and swapped back, otherwise the Fortran-order reshape interleaves its + rows. + + The exchange is skipped when the two dimensions have equal length. There + the on-disk layout is unchanged, and the frames measure as already + consistent with VisuCoreOrientation: on a 256x256 three-package + localizer, sampling the intersection line of an untransposed and a + transposed frame correlates 0.99 as delivered and -0.05 once swapped, + while on a 110x120 localizer -- where the exchange does change the + layout -- the same measurement goes from -0.27 to +0.64. + """ + transposition = self._dataset._parameter_value("VisuCoreTransposition") + if transposition is None: + return data + transposition = np.atleast_1d(np.asarray(transposition)).astype(int) + if not transposition.any(): + return data + + block = tuple(int(size) for size in layouts["shape_block"]) + core_dim = len(block) + frames = layouts.get("frame_index", range(data.shape[-1])) + out = None + for position, frame in enumerate(frames): + value = int(transposition[frame]) if frame < transposition.size else 0 + if value == 0: + continue + first, second = (0, core_dim - 1) if value >= core_dim else (value - 1, value) + if first == second or block[first] == block[second]: + continue + stored = list(block) + stored[first], stored[second] = stored[second], stored[first] + if out is None: + out = np.array(data) + if inverse: + swapped = np.swapaxes(np.asarray(data[..., position]), first, second) + out[..., position] = np.reshape(swapped.flatten(order="F"), block, order="F") + else: + frame_data = np.reshape(np.asarray(data[..., position]).flatten(order="F"), stored, order="F") + out[..., position] = np.swapaxes(frame_data, first, second) + return data if out is None else out + def deserialize(self, data, layouts): + data = self._apply_core_transposition(data, layouts) + # scale if self._dataset._state["scale"]: data = self._scale_frames(data, layouts, "FW") @@ -871,6 +930,10 @@ def _complex_frame_axis(self, data): return None axis = data.ndim - 1 + if data.shape[axis] == 1: + # a random-access selection of only the real or only the imaginary + # component: there is nothing to combine, keep the axis as it is + return None if data.shape[axis] != 2: raise InvalidDataset( f"complex 2dseq requires a two-element real/imag frame-group axis, got shape {data.shape} on axis {axis}" @@ -955,7 +1018,7 @@ def serialize(self, data, layout): data = self._apply_disk_slice_order(data) data = self._framegroups_to_frames(data, layout) data = self._scale_frames(data, layout, "BW") - return data + return self._apply_core_transposition(data, layout, inverse=True) def _frames_to_vector(self, data): return data.flatten(order="F") @@ -1007,6 +1070,10 @@ def _get_ra_layouts(self, slice_full): layouts_ra["mask"] = np.zeros(layouts["shape_fg"], dtype=bool, order="F") layouts_ra["mask"][slice_full[self._dataset.encoded_dim :]] = True + # Which frames of the whole dataset the selection covers: per-frame + # parameters (VisuCoreTransposition) must be indexed by that absolute + # frame number, not by the position within the selection. + layouts_ra["frame_index"] = np.flatnonzero(layouts_ra["mask"].flatten(order="F")) layouts_ra["shape_fg"], layouts_ra["offset_fg"] = self._get_ra_shape(layouts_ra["mask"]) layouts_ra["shape_frames"] = (np.prod(layouts_ra["shape_fg"], dtype=int),) layouts_ra["shape_storage"] = layouts_ra["shape_block"] + layouts_ra["shape_frames"] diff --git a/brukerapi/splitters.py b/brukerapi/splitters.py index 613d801..2831698 100644 --- a/brukerapi/splitters.py +++ b/brukerapi/splitters.py @@ -1,5 +1,4 @@ import copy -import os from pathlib import Path import numpy as np @@ -12,12 +11,19 @@ class Splitter: + @staticmethod + def _inherited_state(dataset): + """State of `dataset` to give a split part, without loading anything.""" + state = {key: value for key, value in dataset._state.items() if key not in {"load", "mmap", "parameter_files", "property_files"}} + state["property_files"] = [] + state["load"] = 0 + return state + def write(self, datasets, path_out=None): for dataset in datasets: - if path_out: - dataset.write(f"{Path(path_out)}/{dataset.path.parents[0].name}/{dataset.path.name}") - else: - dataset.write(dataset.path) + target = Path(path_out) / dataset.path.parents[0].name / dataset.path.name if path_out else Path(dataset.path) + target.parent.mkdir(parents=True, exist_ok=True) + dataset.write(target) def _split_data(self, dataset, range, fg_abs_index): """ @@ -157,14 +163,13 @@ def split(self, dataset, select=None, write=None, path_out=None, **kwargs): datasets = [] for select_ in select: - # construct a new Dataset, without loading data, the data will be supplied later name = f"{dataset.path.parents[0].name}_{self.fg}_{select_}/2dseq" - dset_path = dataset.path.parents[1] / name - os.makedirs(dset_path, exist_ok=True) - - # construct a new Dataset, without loading data, the data will be supplied later - dataset_ = Dataset(dataset.path.parents[1] / name, load=0) + # A load=0 Dataset does not need its path to exist. Creating it here + # put a *directory* where the 2dseq file belongs, which made every + # write fail with IsADirectoryError and gave the in-memory split a + # filesystem side effect. + dataset_ = Dataset(dataset.path.parents[1] / name, **self._inherited_state(dataset)) dataset_.parameters = self._split_params(dataset, select_, fg_abs_index, fg_rel_index, fg_size) @@ -309,8 +314,10 @@ def split(self, dataset, write=None, path_out=None): # name of the data set created by the split name = f"{dataset.path.parents[0].name}_sp_{sp_index}/2dseq" - # construct a new Dataset, without loading data, the data will be supplied later - dataset_ = Dataset(dataset.path.parents[1] / name, load=0) + # construct a new Dataset, without loading data, the data will be supplied later. + # The parent's state (property_files, scale, combine_complex, ...) is + # carried over so a package is read the same way its source was. + dataset_ = Dataset(dataset.path.parents[1] / name, **self._inherited_state(dataset)) # SPLIT parameters dataset_.parameters = self._split_parameters( @@ -406,6 +413,8 @@ def _split_VisuCoreSlicePacksSlices(self, visu_pars_, sp_index): def _split_VisuCoreSlicePacksSliceDist(self, visu_pars_, sp_index): VisuCoreSlicePacksSliceDist = visu_pars_["VisuCoreSlicePacksSliceDist"] - value = int(VisuCoreSlicePacksSliceDist.array[sp_index]) + # spec 7.10: the inter-slice distance is a double, and truncating it + # loses the fractional millimetres of every non-integer slice spacing + value = float(VisuCoreSlicePacksSliceDist.array[sp_index]) VisuCoreSlicePacksSliceDist.value = value - VisuCoreSlicePacksSliceDist.size = 1 + VisuCoreSlicePacksSliceDist.size = (1,) diff --git a/docs/source/compatibility.rst b/docs/source/compatibility.rst index bf09030..3dd1ce9 100644 --- a/docs/source/compatibility.rst +++ b/docs/source/compatibility.rst @@ -59,11 +59,31 @@ Data contract and limitations * ``COMPLEX_IMAGE``/``FG_COMPLEX`` reconstructions are returned as complex arrays by default. Use ``combine_complex=False`` to retain the real frame axis. -* ``RECO_transposition``/``VisuCoreTransposition`` is applied on read and - inverted on write. FID ``ACQ_obj_order`` is likewise normalized to slice - order on read. +* ``RECO_transposition`` records what the reconstruction already did and is + not re-applied. Per-frame ``VisuCoreTransposition`` describes how a frame is + stored, so a frame whose two exchanged dimensions differ in length is read in + its stored shape and swapped back on read, and restored on write. FID + ``ACQ_obj_order`` is normalized on read; the axis it orders is labelled + ``object``, because ``NI`` counts acquisition objects (slices x echoes x + movie frames), not slices. * d3proc is an optional compatibility source for legacy/minimal 2dseq word type and image-size metadata after Visu and RECO metadata have been tried. -* Patient/scanner-space affine and orientation output is not yet a supported - geometry API. Consumers requiring physical-space placement must validate - their own transform until asymmetric reference data is available. +* ``Dataset.metadata`` groups the Visu parameters the way the format defines + them -- administration, subject, study, series, equipment and acquisition -- + and the ``SUBJECT_*`` parameters of the study file, which ``subject`` in + ``add_parameters=`` loads. +* Version-dependent behaviour is selected on a parsed ``pv_version`` + (``5.1``, ``6.0.1``, ``7.0.0``, ``360.3.7``, ...) rather than on an exact + version string, so an unlisted point release is not silently unsupported. +* ``Dataset.affine`` is a voxel-index to patient-coordinate transform derived + from ``VisuCorePosition``/``VisuCoreOrientation``: index ``(0, 0, 0)`` maps + onto the centre of the first voxel transferred, and the slice column carries + the measured direction and spacing between slice centres. It is expressed in + the Visu/DICOM patient frame (R->L, A->P, F->H); a NIfTI writer converts with + ``np.diag([-1, -1, 1, 1]) @ affine``, and the ParaVision user-interface frame + needs both ends transformed. Frames that are not purely spatial + (spectroscopy, CSI) have no image geometry and raise + ``UnsupportedDatasetType`` rather than returning an identity matrix. A + dataset with several slice packages cannot be described by one affine; it + warns, and ``affine_of_package(i)`` or ``slice_packages`` gives the + per-package transform. diff --git a/docs/source/tutorials/how-to-fid.rst b/docs/source/tutorials/how-to-fid.rst index d450951..1b6f6ba 100644 --- a/docs/source/tutorials/how-to-fid.rst +++ b/docs/source/tutorials/how-to-fid.rst @@ -22,7 +22,7 @@ Data is typically and n-dimensional array, the physical meaning of individual di .. code-block:: python >> dataset.dim_type - >> ['kspace_encode_step_0', 'kspace_encode_step_1', 'slice', 'repetition', 'channel'] + >> ['k_space_encode_step_0', 'k_space_encode_step_1', 'object', 'repetition', 'channel'] ``Dataset.data`` contains ordered raw k-space, not a reconstructed image. RARE/EPI line ordering is applied, while ramp-sampling regridding remains a diff --git a/test/config/properties_0.2H2.json b/test/config/properties_0.2H2.json index f7aaf48..fac8476 100644 --- a/test/config/properties_0.2H2.json +++ b/test/config/properties_0.2H2.json @@ -4,16 +4,16 @@ "TR": 4, "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -50,51 +50,12 @@ "offset": [ 0 ], - "position": [ - 25, - 25, - -25 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 0.390625 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 0.390625 - ] - ], "shape_block": [ 128, 128, @@ -127,16 +88,16 @@ "TR": 20, "affine": [ [ - -0.09765625, - -1.1959441397923371e-17, + 0.09765625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 1.1959441397923371e-17, - -0.09765625, 0.0, - 25.0 + 0.09765625, + 0.0, + -25.0 ], [ 0.0, @@ -155,7 +116,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 6.666666666666667e-06, @@ -174,51 +135,12 @@ 0, 0 ], - "position": [ - 25, - 25, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.09765625, 0.09765625, 1.0 ], - "rotation": [ - [ - -0.09765625, - -1.1959441397923371e-17, - 0.0 - ], - [ - 1.1959441397923371e-17, - -0.09765625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 512, 512 @@ -251,16 +173,16 @@ "TR": 1500, "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -310,51 +232,12 @@ 0, 0 ], - "position": [ - 25.0, - 25.0, - -2.5 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 5.0 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 5.0 - ] - ], "shape_block": [ 128, 128 @@ -399,16 +282,16 @@ "TR": 1000, "affine": [ [ - -0.78125, - -9.567553118338697e-17, + 0.78125, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 9.567553118338697e-17, - -0.78125, 0.0, - 25.0 + 0.78125, + 0.0, + -25.0 ], [ 0.0, @@ -447,51 +330,12 @@ 0, 0 ], - "position": [ - 25.0, - 25.0, - -7.5 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.78125, 0.78125, 5.0 ], - "rotation": [ - [ - -0.78125, - -9.567553118338697e-17, - 0.0 - ], - [ - 9.567553118338697e-17, - -0.78125, - 0.0 - ], - [ - 0.0, - 0.0, - 5.0 - ] - ], "shape_block": [ 64, 64 @@ -524,16 +368,16 @@ "TR": 4000, "affine": [ [ - -0.78125, - -9.567553118338697e-17, + 0.78125, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 9.567553118338697e-17, - -0.78125, 0.0, - 25.0 + 0.78125, + 0.0, + -25.0 ], [ 0.0, @@ -552,7 +396,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "", "" ], @@ -580,51 +424,12 @@ 0, 0 ], - "position": [ - 25.0, - 25.0, - 0.0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.78125, 0.78125, 1.5 ], - "rotation": [ - [ - -0.78125, - -9.567553118338697e-17, - 0.0 - ], - [ - 9.567553118338697e-17, - -0.78125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.5 - ] - ], "shape_block": [ 64, 64 @@ -667,16 +472,16 @@ "TR": 1500.001, "affine": [ [ - -0.78125, - -9.567553118338697e-17, + 0.78125, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 9.567553118338697e-17, - -0.78125, 0.0, - 25.0 + 0.78125, + 0.0, + -25.0 ], [ 0.0, @@ -739,51 +544,12 @@ 0, 0 ], - "position": [ - 25, - 25, - -5 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.78125, 0.78125, 5.0 ], - "rotation": [ - [ - -0.78125, - -9.567553118338697e-17, - 0.0 - ], - [ - 9.567553118338697e-17, - -0.78125, - 0.0 - ], - [ - 0.0, - 0.0, - 5.0 - ] - ], "shape_block": [ 64, 64 @@ -841,16 +607,16 @@ "TR": 10000, "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -869,7 +635,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 2.5e-06, @@ -902,51 +668,12 @@ 0, 0 ], - "position": [ - 25, - 25, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 2.0 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 128, 128 @@ -1002,16 +729,16 @@ "TR": 11553.716, "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -1167,51 +894,12 @@ 0, 0 ], - "position": [ - 25, - 25, - -14 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 2.0 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 128, 128 @@ -1367,16 +1055,16 @@ "TR": 1500, "affine": [ [ - -0.78125, - -9.567553118338697e-17, + 0.78125, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 9.567553118338697e-17, - -0.78125, 0.0, - 25.0 + 0.78125, + 0.0, + -25.0 ], [ 0.0, @@ -1395,7 +1083,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 2.5e-06, @@ -1416,51 +1104,12 @@ 0, 0 ], - "position": [ - 25, - 25, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.78125, 0.78125, 2.0 ], - "rotation": [ - [ - -0.78125, - -9.567553118338697e-17, - 0.0 - ], - [ - 9.567553118338697e-17, - -0.78125, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 64, 64 @@ -1495,16 +1144,16 @@ "TR": 200, "affine": [ [ - -0.234375, - -2.8702659355016093e-17, + 0.234375, 0.0, - 30.0 + 0.0, + -30.0 ], [ - 2.8702659355016093e-17, - -0.234375, 0.0, - 30.0 + 0.234375, + 0.0, + -30.0 ], [ 0.0, @@ -1554,51 +1203,12 @@ 0, 0 ], - "position": [ - 30, - 30, - -4 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.234375, 0.234375, 2.0 ], - "rotation": [ - [ - -0.234375, - -2.8702659355016093e-17, - 0.0 - ], - [ - 2.8702659355016093e-17, - -0.234375, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 256, 256 @@ -1642,16 +1252,16 @@ "TR": 30, "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -1690,51 +1300,12 @@ 0, 0 ], - "position": [ - 25.0, - 25.0, - -2.25 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 1.5 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.5 - ] - ], "shape_block": [ 128, 128 @@ -1767,16 +1338,16 @@ "TR": 8, "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -1813,51 +1384,12 @@ "offset": [ 0 ], - "position": [ - 25, - 25, - -25 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 0.390625 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 0.390625 - ] - ], "shape_block": [ 128, 128, @@ -1890,16 +1422,16 @@ "TR": 4, "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -1936,51 +1468,12 @@ "offset": [ 0 ], - "position": [ - 25, - 25, - -25 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 0.390625 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 0.390625 - ] - ], "shape_block": [ 128, 128, @@ -2010,32 +1503,6 @@ }, "2DSEQ_24_1_LEGO_PHANTOM_2": { "TR": 1200, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-16 15:13:18", "dim_type": [ "spectroscopic", @@ -2053,23 +1520,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "shape_block": [ 2048, @@ -2106,16 +1556,16 @@ "TR": 20, "affine": [ [ - -4.7837765591693485e-17, 0.0, - 0.390625, - 75.0 + 0.0, + -0.390625, + 25.0 ], [ - -0.390625, + 0.390625, 0.0, - -4.7837765591693485e-17, - 25.0 + 0.0, + -25.0 ], [ 0.0, @@ -2153,50 +1603,11 @@ 0, 0 ], - "position": [ - 75, - 25, - 25 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], - "pv_version": "5.1", - "resolution": [ - 0.390625, - 0.390625, - 0.390625 - ], - "rotation": [ - [ - -4.7837765591693485e-17, - 0.0, - 0.390625 - ], - [ - -0.390625, - 0.0, - -4.7837765591693485e-17 - ], - [ - 0.0, - -0.390625, - 0.0 - ] + "pv_version": "5.1", + "resolution": [ + 0.390625, + 0.390625, + 0.390625 ], "shape_block": [ 128, @@ -2229,32 +1640,6 @@ "2DSEQ_26_1_LEGO_PHANTOM_2": { "TE": 0, "TR": 800, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-16 15:13:18", "dim_type": [ "spectroscopic", @@ -2272,23 +1657,6 @@ 0, 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "shape_block": [ 1024 @@ -2315,32 +1683,6 @@ }, "2DSEQ_27_1_LEGO_PHANTOM_2": { "TE": 0, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-16 15:13:18", "dim_type": [ "spectroscopic", @@ -2356,23 +1698,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "shape_block": [ 2048 @@ -2397,32 +1722,6 @@ }, "2DSEQ_28_1_LEGO_PHANTOM_2": { "TE": 0, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-16 15:13:18", "dim_type": [ "spectroscopic", @@ -2438,23 +1737,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "shape_block": [ 2048 @@ -2482,16 +1764,16 @@ "TR": 100, "affine": [ [ - -0.1953125, - -2.3918882795846743e-17, + 0.1953125, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 2.3918882795846743e-17, - -0.1953125, 0.0, - 25.0 + 0.1953125, + 0.0, + -25.0 ], [ 0.0, @@ -2529,51 +1811,12 @@ 0, 0 ], - "position": [ - 25, - 25, - -4 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.1953125, 0.1953125, 4.0 ], - "rotation": [ - [ - -0.1953125, - -2.3918882795846743e-17, - 0.0 - ], - [ - 2.3918882795846743e-17, - -0.1953125, - 0.0 - ], - [ - 0.0, - 0.0, - 4.0 - ] - ], "shape_block": [ 256, 256 @@ -2618,16 +1861,16 @@ ], "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -2646,7 +1889,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "", "" ], @@ -2694,51 +1937,12 @@ 0, 0 ], - "position": [ - 25, - 25, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 1.0 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 128, 128 @@ -2807,16 +2011,16 @@ ], "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -2835,7 +2039,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "", "" ], @@ -2882,51 +2086,12 @@ 0, 0 ], - "position": [ - 25, - 25, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], - "pv_version": "5.1;5.1", + "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 1.0 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 128, 128 @@ -2994,16 +2159,16 @@ ], "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -3022,7 +2187,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "", "" ], @@ -3064,51 +2229,12 @@ 0, 0 ], - "position": [ - 25, - 25, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], - "pv_version": "5.1;5.1", + "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 1.0 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 128, 128 @@ -3166,16 +2292,16 @@ "TR": 3800, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 20.0 + 0.3125, + 0.0, + -20.0 ], [ 0.0, @@ -3736,51 +2862,12 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -10.5 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.3125, 0.3125, 1.5 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.5 - ] - ], "shape_block": [ 128, 128 @@ -4334,16 +3421,16 @@ "2DSEQ_32_2_LEGO_PHANTOM_2": { "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 20.0 + 0.3125, + 0.0, + -20.0 ], [ 0.0, @@ -4708,51 +3795,12 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -10.5 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], - "pv_version": "5.1;5.1", + "pv_version": "5.1", "resolution": [ 0.3125, 0.3125, 1.5 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.5 - ] - ], "shape_block": [ 128, 128 @@ -5130,16 +4178,16 @@ "TR": 2500, "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -5158,7 +4206,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 8.4e-06, @@ -5191,51 +4239,12 @@ 0, 0 ], - "position": [ - 25, - 25, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 1.0 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 128, 128 @@ -5280,16 +4289,16 @@ "2DSEQ_33_2_LEGO_PHANTOM_2": { "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -5308,7 +4317,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": null, @@ -5329,51 +4338,12 @@ 0, 0 ], - "position": [ - 25, - 25, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], - "pv_version": "5.1;5.1", + "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 1.0 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 128, 128 @@ -5407,16 +4377,16 @@ "2DSEQ_33_3_LEGO_PHANTOM_2": { "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -5435,7 +4405,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": null, @@ -5456,51 +4426,12 @@ 0, 0 ], - "position": [ - 25, - 25, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], - "pv_version": "5.1;5.1", + "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 1.0 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 128, 128 @@ -5536,16 +4467,16 @@ "TR": 8, "affine": [ [ - -0.1953125, - -2.3918882795846743e-17, + 0.1953125, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 2.3918882795846743e-17, - -0.1953125, 0.0, - 25.0 + 0.1953125, + 0.0, + -25.0 ], [ 0.0, @@ -5564,7 +4495,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "", "" ], @@ -5592,51 +4523,12 @@ 0, 0 ], - "position": [ - 25, - 25, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.1953125, 0.1953125, 1.0 ], - "rotation": [ - [ - -0.1953125, - -2.3918882795846743e-17, - 0.0 - ], - [ - 2.3918882795846743e-17, - -0.1953125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 256, 256 @@ -5689,16 +4581,16 @@ "TR": 1500, "affine": [ [ - -0.23437499891734498, - 2.2527639967002548e-05, + 0.23437499891734498, + -2.2527639967031252e-05, 0.0, - 30.0028836764956 + -29.9971163235044 ], [ - -2.2527639967002548e-05, - -0.23437499891734498, + 2.2527639967031252e-05, + 0.23437499891734498, 0.0, - 29.9971166006641 + -30.0028833993359 ], [ 0.0, @@ -5802,31 +4694,9 @@ 0, 0, 0, - 0, - 0, - 0 - ], - "position": [ - 30.0028836764956, - 29.9971166006641, - -7.0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] + 0, + 0, + 0 ], "pv_version": "5.1", "resolution": [ @@ -5834,23 +4704,6 @@ 0.234375, 2.0 ], - "rotation": [ - [ - -0.23437499891734498, - 2.2527639967002548e-05, - 0.0 - ], - [ - -2.2527639967002548e-05, - -0.23437499891734498, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 256, 256 @@ -5956,16 +4809,16 @@ "TR": 1000, "affine": [ [ - -0.1953125, - -2.3918882795846743e-17, + 0.1953125, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 2.3918882795846743e-17, - -0.1953125, 0.0, - 25.0 + 0.1953125, + 0.0, + -25.0 ], [ 0.0, @@ -6009,51 +4862,12 @@ 0, 0 ], - "position": [ - 25, - 25, - -6 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.1953125, 0.1953125, 4.0 ], - "rotation": [ - [ - -0.1953125, - -2.3918882795846743e-17, - 0.0 - ], - [ - 2.3918882795846743e-17, - -0.1953125, - 0.0 - ], - [ - 0.0, - 0.0, - 4.0 - ] - ], "shape_block": [ 256, 256 @@ -6095,16 +4909,16 @@ "TR": 5000, "affine": [ [ - -0.1953125, - -2.3918882795846743e-17, + 0.1953125, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 2.3918882795846743e-17, - -0.1953125, 0.0, - 25.0 + 0.1953125, + 0.0, + -25.0 ], [ 0.0, @@ -6159,51 +4973,12 @@ 0, 0 ], - "position": [ - 25, - 25, - -3 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.1953125, 0.1953125, 3.0 ], - "rotation": [ - [ - -0.1953125, - -2.3918882795846743e-17, - 0.0 - ], - [ - 2.3918882795846743e-17, - -0.1953125, - 0.0 - ], - [ - 0.0, - 0.0, - 3.0 - ] - ], "shape_block": [ 256, 256 @@ -6254,16 +5029,16 @@ "TR": 3000, "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 25.0 + 0.390625, + 0.0, + -25.0 ], [ 0.0, @@ -6282,7 +5057,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "", "", "" @@ -6321,51 +5096,12 @@ 0, 0 ], - "position": [ - 25, - 25, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 1.0 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 128, 128 @@ -6425,16 +5161,16 @@ "TR": 1000, "affine": [ [ - -0.390625, - -4.7837765591693485e-17, + 0.390625, + 0.0, 0.0, - 24.5 + -25.5 ], [ - 4.7837765591693485e-17, - -0.390625, 0.0, - 24.7 + 0.390625, + 0.0, + -25.3 ], [ 0.0, @@ -6491,51 +5227,12 @@ 0, 0 ], - "position": [ - 24.5, - 24.7, - -5.0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.390625, 0.390625, 2.5 ], - "rotation": [ - [ - -0.390625, - -4.7837765591693485e-17, - 0.0 - ], - [ - 4.7837765591693485e-17, - -0.390625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.5 - ] - ], "shape_block": [ 128, 128 @@ -6588,16 +5285,16 @@ "TR": 3000, "affine": [ [ - -0.1953125, - -2.3918882795846743e-17, + 0.1953125, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 2.3918882795846743e-17, - -0.1953125, 0.0, - 25.0 + 0.1953125, + 0.0, + -25.0 ], [ 0.0, @@ -6616,7 +5313,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 5e-06, @@ -6635,51 +5332,12 @@ 0, 0 ], - "position": [ - 25, - 25, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.1953125, 0.1953125, 1.0 ], - "rotation": [ - [ - -0.1953125, - -2.3918882795846743e-17, - 0.0 - ], - [ - 2.3918882795846743e-17, - -0.1953125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 256, 256 @@ -6712,16 +5370,16 @@ "TR": 4000, "affine": [ [ - -0.1953125, - -2.3918882795846743e-17, + 0.1953125, 0.0, - 25.0 + 0.0, + -25.0 ], [ - 2.3918882795846743e-17, - -0.1953125, 0.0, - 25.0 + 0.1953125, + 0.0, + -25.0 ], [ 0.0, @@ -6758,51 +5416,12 @@ "offset": [ 0 ], - "position": [ - 25.0, - 25.0, - -2.5 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "5.1", "resolution": [ 0.1953125, 0.1953125, 0.5 ], - "rotation": [ - [ - -0.1953125, - -2.3918882795846743e-17, - 0.0 - ], - [ - 2.3918882795846743e-17, - -0.1953125, - 0.0 - ], - [ - 0.0, - 0.0, - 0.5 - ] - ], "shape_block": [ 256, 256, @@ -6872,6 +5491,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_3D", "shape_storage": [ 256, @@ -6890,7 +5510,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6921,6 +5541,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 1024, @@ -6939,7 +5560,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6970,6 +5591,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -6988,7 +5610,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7017,6 +5639,7 @@ 4, 1 ], + "pv_version": "5.1", "scheme_id": "EPI", "shape_storage": [ 2690, @@ -7035,7 +5658,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7064,6 +5687,7 @@ 4, 1 ], + "pv_version": "5.1", "scheme_id": "EPI", "shape_storage": [ 2562, @@ -7082,7 +5706,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel", "k_space_encode_step_2" @@ -7115,6 +5739,7 @@ 1, 5 ], + "pv_version": "5.1", "scheme_id": "dEPI", "shape_storage": [ 1536, @@ -7133,7 +5758,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7162,6 +5787,7 @@ 4, 1 ], + "pv_version": "5.1", "scheme_id": "EPI", "shape_storage": [ 16384, @@ -7180,7 +5806,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7209,6 +5835,7 @@ 4, 1 ], + "pv_version": "5.1", "scheme_id": "EPI", "shape_storage": [ 32768, @@ -7227,7 +5854,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7256,6 +5883,7 @@ 4, 1 ], + "pv_version": "5.1", "scheme_id": "EPI", "shape_storage": [ 8192, @@ -7274,7 +5902,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7303,6 +5931,7 @@ 4, 1 ], + "pv_version": "5.1", "scheme_id": "SPIRAL", "shape_storage": [ 1024, @@ -7321,7 +5950,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7352,6 +5981,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -7370,7 +6000,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7399,6 +6029,7 @@ 4, 1 ], + "pv_version": "5.1", "scheme_id": "SPIRAL", "shape_storage": [ 512, @@ -7417,7 +6048,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7448,6 +6079,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "RADIAL", "shape_storage": [ 256, @@ -7466,7 +6098,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7497,6 +6129,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "RADIAL", "shape_storage": [ 256, @@ -7514,7 +6147,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7545,6 +6178,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "ZTE", "shape_storage": [ 1024, @@ -7584,6 +6218,7 @@ 1, 2 ], + "pv_version": "5.1", "scheme_id": "CSI", "shape_storage": [ 4096, @@ -7603,6 +6238,7 @@ "k_space_encode_step_0", "k_space_encode_step_1", "k_space_encode_step_2", + "echo", "repetition", "channel" ], @@ -7613,7 +6249,8 @@ 1, 2, 128, - 128 + 128, + 1 ], "id": "FID_25_0_2", "k_space": [ @@ -7621,6 +6258,7 @@ 128, 128, 2, + 1, 1 ], "numpy_dtype": "int32", @@ -7629,8 +6267,10 @@ 3, 4, 2, + 5, 1 ], + "pv_version": "5.1", "scheme_id": "FIELD_MAP", "shape_storage": [ 256, @@ -7647,8 +6287,8 @@ "date": "2020-06-16 16:21:40", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 2e-05, "encoded_dim": 1, @@ -7669,6 +6309,7 @@ 1, 2 ], + "pv_version": "5.1", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 2048, @@ -7686,8 +6327,8 @@ "date": "2020-06-16 16:24:44", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 0.0001247999999999999, "encoded_dim": 1, @@ -7708,6 +6349,7 @@ 1, 2 ], + "pv_version": "5.1", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 4096, @@ -7725,8 +6367,8 @@ "date": "2020-06-16 16:26:45", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 8.320000000000006e-05, "encoded_dim": 1, @@ -7747,6 +6389,7 @@ 1, 2 ], + "pv_version": "5.1", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 4096, @@ -7765,7 +6408,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7796,6 +6439,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -7813,7 +6457,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7844,6 +6488,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -7862,7 +6507,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel", "k_space_encode_step_2" @@ -7895,6 +6540,7 @@ 1, 5 ], + "pv_version": "5.1", "scheme_id": "dEPI", "shape_storage": [ 4974, @@ -7913,7 +6559,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7944,6 +6590,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -7962,7 +6609,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7993,6 +6640,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -8011,7 +6659,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8042,6 +6690,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8060,7 +6709,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8091,6 +6740,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8109,7 +6759,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8140,6 +6790,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8158,7 +6809,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8189,6 +6840,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -8206,7 +6858,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8237,6 +6889,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -8255,7 +6908,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8286,6 +6939,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8335,6 +6989,7 @@ 5, 1 ], + "pv_version": "5.1", "scheme_id": "CART_3D", "shape_storage": [ 512, diff --git a/test/config/properties_20200612_094625_lego_phantom_3_1_2.json b/test/config/properties_20200612_094625_lego_phantom_3_1_2.json index 94ef0d6..549b739 100644 --- a/test/config/properties_20200612_094625_lego_phantom_3_1_2.json +++ b/test/config/properties_20200612_094625_lego_phantom_3_1_2.json @@ -11,13 +11,13 @@ 0.15625, 0.0, 0.0, - 19.5 + -20.5 ], [ 0.0, 0.3125, 0.0, - 19.4 + -20.6 ], [ 0.0, @@ -36,7 +36,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 1e-05, @@ -56,51 +56,12 @@ 0, 0 ], - "position": [ - 19.5, - 19.4, - 0.7 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.3125, 1.16 ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.16 - ] - ], "shape_block": [ 256, 128 @@ -137,18 +98,18 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.15625, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 3.66, + 2.5, -1.7 ], [ @@ -194,50 +155,11 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -1.7 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 3.66 - ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 3.66 - ] + 2.5 ], "shape_block": [ 256, @@ -287,13 +209,13 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.3125, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -330,51 +252,12 @@ "offset": [ 0 ], - "position": [ - 20.0, - 20.0, - -19.375 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.3125, 1.25 ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.25 - ] - ], "shape_block": [ 256, 128, @@ -410,18 +293,18 @@ 0.15625, 0.0, 0.0, - 20.3 + -19.7 ], [ 0.0, 0.15625, 0.0, - 19.45 + -20.55 ], [ 0.0, 0.0, - 4.0, + 1.9999999999999996, -4.89 ], [ @@ -464,50 +347,11 @@ 0, 0 ], - "position": [ - 20.3, - 19.45, - -4.89 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 4.0 - ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 4.0 - ] + 1.9999999999999996 ], "shape_block": [ 256, @@ -554,18 +398,18 @@ 0.3125, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.3125, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 8.0, + 7.0, -7.0 ], [ @@ -602,50 +446,11 @@ 0, 0 ], - "position": [ - 20, - 20, - -7 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.3125, 0.3125, - 8.0 - ], - "rotation": [ - [ - 0.3125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 8.0 - ] + 7.0 ], "shape_block": [ 128, @@ -686,18 +491,18 @@ 0.3125, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.3125, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 4.0, + 2.0, 0.0 ], [ @@ -740,50 +545,11 @@ 0, 0 ], - "position": [ - 20, - 20, - 0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.3125, 0.3125, - 4.0 - ], - "rotation": [ - [ - 0.3125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 4.0 - ] + 2.0 ], "shape_block": [ 128, @@ -830,18 +596,18 @@ 0.4166666666666667, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.4166666666666667, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 4.0, + 2.0, 0.0 ], [ @@ -855,7 +621,7 @@ "dim_type": [ "spatial", "spatial", - "spatial" + "frame" ], "dwell_s": 2.5000000000000044e-06, "encoded_dim": 2, @@ -872,50 +638,11 @@ "offset": [ 0 ], - "position": [ - 20, - 20, - 0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.4166666666666667, 0.4166666666666667, - 4.0 - ], - "rotation": [ - [ - 0.4166666666666667, - 0.0, - 0.0 - ], - [ - 0.0, - 0.4166666666666667, - 0.0 - ], - [ - 0.0, - 0.0, - 4.0 - ] + 2.0 ], "shape_block": [ 96, @@ -956,13 +683,13 @@ 0.4166666666666667, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.625, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -981,7 +708,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "", "" ], @@ -1005,51 +732,12 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - 0.0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.4166666666666667, 0.625, 1.16 ], - "rotation": [ - [ - 0.4166666666666667, - 0.0, - 0.0 - ], - [ - 0.0, - 0.625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.16 - ] - ], "shape_block": [ 96, 64 @@ -1091,18 +779,18 @@ 0.6666666666666666, 0.0, 0.0, - 32.0 + -32.0 ], [ 0.0, 1.0, 0.0, - 32.0 + -32.0 ], [ 0.0, 0.0, - 4.0, + 2.0, -4.0 ], [ @@ -1154,50 +842,11 @@ 0, 0 ], - "position": [ - 32, - 32, - -4 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.6666666666666666, 1.0, - 4.0 - ], - "rotation": [ - [ - 0.6666666666666666, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 4.0 - ] + 2.0 ], "shape_block": [ 96, @@ -1254,13 +903,13 @@ 0.4166666666666667, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.625, 0.0, - 19.5 + -20.5 ], [ 0.0, @@ -1279,7 +928,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 2.5000000000000044e-06, @@ -1325,51 +974,12 @@ 0, 0 ], - "position": [ - 20.0, - 19.5, - 2.5 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.4166666666666667, 0.625, 2.0 ], - "rotation": [ - [ - 0.4166666666666667, - 0.0, - 0.0 - ], - [ - 0.0, - 0.625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 96, 64 @@ -1432,13 +1042,13 @@ 0.4166666666666667, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.4166666666666667, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -1457,7 +1067,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 2.5000000000000044e-06, @@ -1482,51 +1092,12 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - 0.0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.4166666666666667, 0.4166666666666667, 1.8 ], - "rotation": [ - [ - 0.4166666666666667, - 0.0, - 0.0 - ], - [ - 0.0, - 0.4166666666666667, - 0.0 - ], - [ - 0.0, - 0.0, - 1.8 - ] - ], "shape_block": [ 96, 96 @@ -1574,13 +1145,13 @@ 0.625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.625, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -1599,7 +1170,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 3.3333333333333333e-06, @@ -1621,51 +1192,12 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - 0.0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.625, 0.625, 1.2 ], - "rotation": [ - [ - 0.625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.2 - ] - ], "shape_block": [ 64, 64 @@ -1709,13 +1241,13 @@ 1.0, 0.0, 0.0, - 32.0 + -32.0 ], [ 0.0, 1.0, 0.0, - 32.0 + -32.0 ], [ 0.0, @@ -1734,7 +1266,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 3.3333333333333333e-06, @@ -1755,51 +1287,12 @@ 0, 0 ], - "position": [ - 32, - 32, - 0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 1.0, 1.0, 1.0 ], - "rotation": [ - [ - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 64, 64 @@ -1837,18 +1330,18 @@ 0.3125, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.3125, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 3.0, + 1.5, -1.5 ], [ @@ -1881,50 +1374,11 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -1.5 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.3125, 0.3125, - 3.0 - ], - "rotation": [ - [ - 0.3125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 3.0 - ] + 1.5 ], "shape_block": [ 128, @@ -1960,13 +1414,13 @@ 0.625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.625, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -1985,7 +1439,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 2.5e-06, @@ -2033,51 +1487,12 @@ 0, 0 ], - "position": [ - 20, - 20, - 0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.625, 0.625, 2.0 ], - "rotation": [ - [ - 0.625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 64, 64 @@ -2142,18 +1557,18 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.15625, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 4.0, + 2.0, 0.5 ], [ @@ -2167,7 +1582,7 @@ "dim_type": [ "spatial", "spatial", - "spatial" + "frame" ], "dwell_s": 5e-06, "encoded_dim": 2, @@ -2184,50 +1599,11 @@ "offset": [ 0 ], - "position": [ - 20.0, - 20.0, - 0.5 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 4.0 - ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 4.0 - ] + 2.0 ], "shape_block": [ 256, @@ -2260,14 +1636,14 @@ [ 0.0, 0.0, - -4.0, - 40.0 + -2.0, + 0.0 ], [ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -2286,7 +1662,7 @@ "dim_type": [ "spatial", "spatial", - "spatial" + "frame" ], "dwell_s": 5e-06, "encoded_dim": 2, @@ -2303,50 +1679,11 @@ "offset": [ 0 ], - "position": [ - 40, - 20, - 20 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 4.0 - ], - "rotation": [ - [ - 0.0, - 0.0, - -4.0 - ], - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - -0.15625, - 0.0 - ] + 2.0 ], "shape_block": [ 256, @@ -2380,13 +1717,13 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 4.0, - 40.0 + 2.0, + 0.0 ], [ 0.0, @@ -2405,7 +1742,7 @@ "dim_type": [ "spatial", "spatial", - "spatial" + "frame" ], "dwell_s": 5e-06, "encoded_dim": 2, @@ -2422,50 +1759,11 @@ "offset": [ 0 ], - "position": [ - 20, - 40, - 20 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 4.0 - ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 4.0 - ], - [ - 0.0, - -0.15625, - 0.0 - ] + 2.0 ], "shape_block": [ 256, @@ -2499,13 +1797,13 @@ 0.3125, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.3125, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -2542,51 +1840,12 @@ "offset": [ 0 ], - "position": [ - 20.0, - 20.0, - -19.84375 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.3125, 0.3125, 0.3125 ], - "rotation": [ - [ - 0.3125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 0.3125 - ] - ], "shape_block": [ 128, 128, @@ -2622,13 +1881,13 @@ 0.3125, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.3125, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -2665,51 +1924,12 @@ "offset": [ 0 ], - "position": [ - 20.0, - 20.0, - -19.84375 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.3125, 0.3125, 0.3125 ], - "rotation": [ - [ - 0.3125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 0.3125 - ] - ], "shape_block": [ 128, 128, @@ -2739,32 +1959,6 @@ }, "2DSEQ_30_1_lego_phantom_3_2": { "TR": 1000, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-12 09:46:25.256000+02:00", "dim_type": [ "spectroscopic", @@ -2782,23 +1976,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "shape_block": [ 2048, @@ -2834,13 +2011,13 @@ 0.3125, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.3125, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -2877,51 +2054,12 @@ "offset": [ 0 ], - "position": [ - 20.0, - 20.0, - -19.84375 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.3125, 0.3125, 0.3125 ], - "rotation": [ - [ - 0.3125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 0.3125 - ] - ], "shape_block": [ 128, 128, @@ -2952,32 +2090,6 @@ "2DSEQ_32_1_lego_phantom_3_2": { "TE": 0, "TR": 1000, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-12 09:46:25.256000+02:00", "dim_type": [ "spectroscopic", @@ -3017,23 +2129,6 @@ 0, 0 ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "shape_block": [ 2048 @@ -3083,32 +2178,6 @@ "2DSEQ_33_1_lego_phantom_3_2": { "TE": 0, "TR": 1000, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-12 09:46:25.256000+02:00", "dim_type": [ "spectroscopic", @@ -3126,23 +2195,6 @@ 0, 0 ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "shape_block": [ 2048 @@ -3170,32 +2222,6 @@ "2DSEQ_34_1_lego_phantom_3_2": { "TE": 11.54, "TR": 1000, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-12 09:46:25.256000+02:00", "dim_type": [ "spatial", @@ -3213,23 +2239,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "shape_block": [ 96, @@ -3260,32 +2269,6 @@ }, "2DSEQ_35_1_lego_phantom_3_2": { "TE": 0, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-12 09:46:25.256000+02:00", "dim_type": [ "spectroscopic", @@ -3302,23 +2285,6 @@ 0, 0 ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "shape_block": [ 2048 @@ -3344,32 +2310,6 @@ }, "2DSEQ_36_1_lego_phantom_3_2": { "TE": 0, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-12 09:46:25.256000+02:00", "dim_type": [ "spectroscopic", @@ -3385,23 +2325,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "shape_block": [ 2048 @@ -3426,32 +2349,6 @@ }, "2DSEQ_37_1_lego_phantom_3_2": { "TE": 0, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-12 09:46:25.256000+02:00", "dim_type": [ "spectroscopic", @@ -3467,23 +2364,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "shape_block": [ 2048 @@ -3522,32 +2402,6 @@ 240 ], "TR": 5000, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-12 09:46:25.256000+02:00", "dim_type": [ "spectroscopic", @@ -3574,23 +2428,6 @@ 0, 0 ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "shape_block": [ 256 @@ -3627,32 +2464,6 @@ "2DSEQ_39_1_lego_phantom_3_2": { "TE": 13.804, "TR": 500, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2020-06-12 09:46:25.256000+02:00", "dim_type": [ "spectroscopic", @@ -3668,23 +2479,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "shape_block": [ 256 @@ -3715,18 +2509,18 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.15625, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 2.0, + 1.0, 0.0 ], [ @@ -3759,50 +2553,11 @@ 0, 0 ], - "position": [ - 20, - 20, - 0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 2.0 - ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] + 28.284271247461902 ], "shape_block": [ 256, @@ -3838,18 +2593,18 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.15625, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 2.25, + 1.25, -0.625 ], [ @@ -3881,50 +2636,11 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -0.625 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 2.25 - ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.25 - ] + 1.25 ], "shape_block": [ 256, @@ -3992,13 +2708,13 @@ 0.3125, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.3125, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -4017,7 +2733,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "", "" ], @@ -4067,51 +2783,12 @@ 0, 0 ], - "position": [ - 20, - 20, - 0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.3125, 0.3125, 1.0 ], - "rotation": [ - [ - 0.3125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 128, 128 @@ -4186,13 +2863,13 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.20833333333333334, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -4211,7 +2888,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 6.4e-06, @@ -4234,51 +2911,12 @@ 0, 0 ], - "position": [ - 20, - 20, - 0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.20833333333333334, 1.0 ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.20833333333333334, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 256, 192 @@ -4316,13 +2954,13 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.20833333333333334, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -4341,7 +2979,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": null, @@ -4357,56 +2995,17 @@ "numpy_dtype": "int32", "offset": [ 0, - 0, - 0, - 0, - 0 - ], - "position": [ - 20, - 20, - 0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] + 0, + 0, + 0, + 0 ], - "pv_version": "6.0.1;6.0.1", + "pv_version": "6.0.1", "resolution": [ 0.15625, 0.20833333333333334, 1.0 ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.20833333333333334, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 256, 192 @@ -4476,18 +3075,18 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.15625, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 1.9000000000000001, + 1.1, -2.2 ], [ @@ -4668,50 +3267,11 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -2.2 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 1.9000000000000001 - ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.9000000000000001 - ] + 1.1 ], "shape_block": [ 256, @@ -4894,13 +3454,13 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.15625, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -4960,51 +3520,12 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -2.2 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], - "pv_version": "6.0.1;6.0.1", + "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, 1.1 ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.1 - ] - ], "shape_block": [ 256, 256 @@ -5063,18 +3584,18 @@ 0.3125, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.4166666666666667, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 2.25, + 1.25, -2.5 ], [ @@ -5109,50 +3630,11 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -2.5 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.3125, 0.4166666666666667, - 2.25 - ], - "rotation": [ - [ - 0.3125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.4166666666666667, - 0.0 - ], - [ - 0.0, - 0.0, - 2.25 - ] + 1.25 ], "shape_block": [ 128, @@ -5190,18 +3672,18 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.15625, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 2.3, + 1.3000000000000003, -5.2 ], [ @@ -5240,50 +3722,11 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -5.2 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 2.3 - ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.3 - ] + 1.3000000000000003 ], "shape_block": [ 256, @@ -5325,13 +3768,13 @@ 0.3125, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.4166666666666667, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -5412,51 +3855,12 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -3.75 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.3125, 0.4166666666666667, 0.5 ], - "rotation": [ - [ - 0.3125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.4166666666666667, - 0.0 - ], - [ - 0.0, - 0.0, - 0.5 - ] - ], "shape_block": [ 128, 96, @@ -5534,13 +3938,13 @@ 0.3125, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.4166666666666667, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -5597,51 +4001,12 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -3.75 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], - "pv_version": "6.0.1;6.0.1", + "pv_version": "6.0.1", "resolution": [ 0.3125, 0.4166666666666667, 0.5 ], - "rotation": [ - [ - 0.3125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.4166666666666667, - 0.0 - ], - [ - 0.0, - 0.0, - 0.5 - ] - ], "shape_block": [ 128, 96, @@ -5698,18 +4063,18 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.15625, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 2.0, + 1.0, 0.0 ], [ @@ -5742,50 +4107,11 @@ 0, 0 ], - "position": [ - 20, - 20, - 0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 2.0 - ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] + 28.284271247461902 ], "shape_block": [ 256, @@ -5821,18 +4147,18 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.15625, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 4.0, + 2.0, -3.0 ], [ @@ -5895,50 +4221,11 @@ 0, 0 ], - "position": [ - 20, - 20, - -3 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 4.0 - ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 4.0 - ] + 2.0 ], "shape_block": [ 256, @@ -6012,18 +4299,18 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.15625, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 7.4, + 6.2, -12.4 ], [ @@ -6076,58 +4363,19 @@ 0, 0, 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "position": [ - 20.0, - 20.0, - -12.4 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] + 0, + 0, + 0, + 0, + 0, + 0, + 0 ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 7.4 - ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 7.4 - ] + 6.2 ], "shape_block": [ 256, @@ -6195,13 +4443,13 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.15625, 0.0, - 20.0 + -20.0 ], [ 0.0, @@ -6220,7 +4468,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 1e-05, @@ -6239,51 +4487,12 @@ 0, 0 ], - "position": [ - 20, - 20, - 0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, 2.0 ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 256, 256 @@ -6319,18 +4528,18 @@ 0.15625, 0.0, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.15625, 0.0, - 20.0 + -20.0 ], [ 0.0, 0.0, - 3.0, + 1.5, -3.0 ], [ @@ -6373,50 +4582,11 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -3.0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, - 3.0 - ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 3.0 - ] + 1.5 ], "shape_block": [ 256, @@ -6462,13 +4632,13 @@ 0.15625, 0.0, 0.0, - 22.0 + -18.0 ], [ 0.0, 0.15625, 0.0, - 18.0 + -22.0 ], [ 0.0, @@ -6487,7 +4657,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "", "" ], @@ -6511,51 +4681,12 @@ 0, 0 ], - "position": [ - 22, - 18, - -3 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "6.0.1", "resolution": [ 0.15625, 0.15625, 2.0 ], - "rotation": [ - [ - 0.15625, - 0.0, - 0.0 - ], - [ - 0.0, - 0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 256, 256 @@ -6599,7 +4730,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6630,6 +4761,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -6648,7 +4780,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6679,6 +4811,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -6727,6 +4860,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_3D", "shape_storage": [ 512, @@ -6745,7 +4879,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6776,6 +4910,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -6794,7 +4929,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6825,6 +4960,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -6843,7 +4979,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6874,6 +5010,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -6892,7 +5029,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6921,6 +5058,7 @@ 4, 1 ], + "pv_version": "6.0.1", "scheme_id": "EPI", "shape_storage": [ 6362, @@ -6939,7 +5077,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6968,6 +5106,7 @@ 4, 1 ], + "pv_version": "6.0.1", "scheme_id": "EPI", "shape_storage": [ 3072, @@ -6986,7 +5125,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7015,6 +5154,7 @@ 4, 1 ], + "pv_version": "6.0.1", "scheme_id": "EPI", "shape_storage": [ 6144, @@ -7033,7 +5173,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel", "k_space_encode_step_2" @@ -7066,6 +5206,7 @@ 1, 5 ], + "pv_version": "6.0.1", "scheme_id": "dEPI", "shape_storage": [ 6670, @@ -7084,7 +5225,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7113,6 +5254,7 @@ 4, 1 ], + "pv_version": "6.0.1", "scheme_id": "EPI", "shape_storage": [ 2304, @@ -7131,7 +5273,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7160,6 +5302,7 @@ 4, 1 ], + "pv_version": "6.0.1", "scheme_id": "EPI", "shape_storage": [ 4096, @@ -7178,7 +5321,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7207,6 +5350,7 @@ 4, 1 ], + "pv_version": "6.0.1", "scheme_id": "EPI", "shape_storage": [ 4458, @@ -7225,7 +5369,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7254,6 +5398,7 @@ 4, 1 ], + "pv_version": "6.0.1", "scheme_id": "SPIRAL", "shape_storage": [ 2048, @@ -7272,7 +5417,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7301,6 +5446,7 @@ 4, 1 ], + "pv_version": "6.0.1", "scheme_id": "SPIRAL", "shape_storage": [ 768, @@ -7319,7 +5465,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7350,6 +5496,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "RADIAL", "shape_storage": [ 512, @@ -7368,7 +5515,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7399,6 +5546,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "RADIAL", "shape_storage": [ 512, @@ -7417,7 +5565,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7448,6 +5596,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "RADIAL", "shape_storage": [ 512, @@ -7466,7 +5615,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7497,6 +5646,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "RADIAL", "shape_storage": [ 256, @@ -7514,7 +5664,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7545,6 +5695,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "ZTE", "shape_storage": [ 512, @@ -7584,6 +5735,7 @@ 1, 2 ], + "pv_version": "6.0.1", "scheme_id": "CSI", "shape_storage": [ 4096, @@ -7603,6 +5755,7 @@ "k_space_encode_step_0", "k_space_encode_step_1", "k_space_encode_step_2", + "echo", "repetition", "channel" ], @@ -7613,7 +5766,8 @@ 1, 2, 128, - 128 + 128, + 1 ], "id": "FID_31_lego_phantom_3_2", "k_space": [ @@ -7621,6 +5775,7 @@ 128, 128, 2, + 1, 1 ], "numpy_dtype": "int32", @@ -7629,8 +5784,10 @@ 3, 4, 2, + 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "FIELD_MAP", "shape_storage": [ 256, @@ -7647,8 +5804,8 @@ "date": "2020-06-12 12:38:12.350000+02:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 6.240000000000003e-05, "encoded_dim": 1, @@ -7669,6 +5826,7 @@ 1, 2 ], + "pv_version": "6.0.1", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 4096, @@ -7685,8 +5843,8 @@ "date": "2020-06-12 12:40:13.577000+02:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 6.240000000000003e-05, "encoded_dim": 1, @@ -7707,6 +5865,7 @@ 1, 2 ], + "pv_version": "6.0.1", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 4096, @@ -7717,7 +5876,7 @@ "FID_34_lego_phantom_3_2": { "TE": 0.48, "TR": 1000, - "acq_length": 3072, + "acq_length": 12288, "block_count": 256, "block_size": 12288, "channels": 1, @@ -7725,39 +5884,34 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", - "repetition", - "channel", - "k_space_encode_step_2" + "k_space_encode_step_2", + "channel" ], "dwell_s": 2.5e-06, "encoded_dim": 3, "encoding_space": [ - 1536, + 96, 1, + 64, 4, - 1, - 1, 64 ], "id": "FID_34_lego_phantom_3_2", "k_space": [ 96, 64, - 1, - 1, - 1, - 64 + 256, + 1 ], "numpy_dtype": "int32", "permute": [ 0, - 2, - 3, 4, - 1, - 5 + 3, + 2, + 1 ], + "pv_version": "6.0.1", "scheme_id": "dEPI", "shape_storage": [ 12288, @@ -7775,8 +5929,8 @@ "date": "2020-06-12 12:45:49.984000+02:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 6.240000000000003e-05, "encoded_dim": 1, @@ -7797,6 +5951,7 @@ 1, 2 ], + "pv_version": "6.0.1", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 4096, @@ -7814,8 +5969,8 @@ "date": "2020-06-12 12:50:25.880000+02:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 6.240000000000003e-05, "encoded_dim": 1, @@ -7836,6 +5991,7 @@ 1, 2 ], + "pv_version": "6.0.1", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 4096, @@ -7852,8 +6008,8 @@ "date": "2020-06-12 12:52:57.018000+02:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 6.240000000000003e-05, "encoded_dim": 1, @@ -7874,6 +6030,7 @@ 1, 2 ], + "pv_version": "6.0.1", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 4096, @@ -7890,8 +6047,8 @@ "date": "2020-06-12 12:58:01.847000+02:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 2e-05, "encoded_dim": 1, @@ -7912,6 +6069,7 @@ 1, 2 ], + "pv_version": "6.0.1", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 512, @@ -7929,8 +6087,8 @@ "date": "2020-06-12 12:58:45.903000+02:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 1e-05, "encoded_dim": 1, @@ -7951,6 +6109,7 @@ 1, 2 ], + "pv_version": "6.0.1", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 512, @@ -7969,7 +6128,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8000,6 +6159,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8018,7 +6178,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8049,6 +6209,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8067,7 +6228,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8098,6 +6259,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -8115,7 +6277,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8146,6 +6308,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8164,7 +6327,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8195,6 +6358,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8213,7 +6377,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8242,6 +6406,7 @@ 4, 1 ], + "pv_version": "6.0.1", "scheme_id": "EPI", "shape_storage": [ 20480, @@ -8260,7 +6425,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8291,6 +6456,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8309,7 +6475,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel", "k_space_encode_step_2" @@ -8342,6 +6508,7 @@ 1, 5 ], + "pv_version": "6.0.1", "scheme_id": "dEPI", "shape_storage": [ 15360, @@ -8360,7 +6527,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8391,6 +6558,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8409,7 +6577,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8440,6 +6608,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8458,7 +6627,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8489,6 +6658,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8507,7 +6677,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8538,6 +6708,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8556,7 +6727,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -8587,6 +6758,7 @@ 5, 1 ], + "pv_version": "6.0.1", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -8598,6 +6770,11 @@ "TE": 5, "TR": 1000, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job0_30_lego_phantom_3_2", "job_desc": [ 4096, @@ -8610,20 +6787,21 @@ 427 ], "numpy_dtype": "int32", + "pv_version": "6.0.1", "shape_storage": [ 4096, 1, 427 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job0_33_lego_phantom_3_2": { "TR": 1000, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job0_33_lego_phantom_3_2", "job_desc": [ 4096, @@ -8636,21 +6814,22 @@ 24 ], "numpy_dtype": "int32", + "pv_version": "6.0.1", "shape_storage": [ 4096, 1, 24 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job0_35_lego_phantom_3_2": { "TE": 20, "TR": 2000, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job0_35_lego_phantom_3_2", "job_desc": [ 4096, @@ -8663,21 +6842,22 @@ 128 ], "numpy_dtype": "int32", + "pv_version": "6.0.1", "shape_storage": [ 4096, 1, 128 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job0_36_lego_phantom_3_2": { "TE": 15, "TR": 2000, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job0_36_lego_phantom_3_2", "job_desc": [ 4096, @@ -8690,20 +6870,21 @@ 64 ], "numpy_dtype": "int32", + "pv_version": "6.0.1", "shape_storage": [ 4096, 1, 64 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job0_37_lego_phantom_3_2": { "TR": 2000, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job0_37_lego_phantom_3_2", "job_desc": [ 4096, @@ -8716,21 +6897,22 @@ 128 ], "numpy_dtype": "int32", + "pv_version": "6.0.1", "shape_storage": [ 4096, 1, 128 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job0_5_lego_phantom_3_2": { "TE": 3, "TR": 32.3528791208791, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job0_5_lego_phantom_3_2", "job_desc": [ 512, @@ -8743,21 +6925,22 @@ 81920 ], "numpy_dtype": "int32", + "pv_version": "6.0.1", "shape_storage": [ 512, 1, 81920 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job1_5_lego_phantom_3_2": { "TE": 3, "TR": 32.3528791208791, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job1_5_lego_phantom_3_2", "job_desc": [ 64, @@ -8770,15 +6953,11 @@ 81920 ], "numpy_dtype": "int32", + "pv_version": "6.0.1", "shape_storage": [ 64, 1, 81920 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] } } diff --git a/test/config/properties_20210128_122257_LEGO_PHANTOM_API_TEST_1_1.json b/test/config/properties_20210128_122257_LEGO_PHANTOM_API_TEST_1_1.json index dc9f8e9..f02970b 100644 --- a/test/config/properties_20210128_122257_LEGO_PHANTOM_API_TEST_1_1.json +++ b/test/config/properties_20210128_122257_LEGO_PHANTOM_API_TEST_1_1.json @@ -4,16 +4,16 @@ "TR": 1000, "affine": [ [ - -0.4166666666666667, - -7.654042494670958e-17, + 0.4166666666666667, 0.0, - 20.0 + 0.0, + -20.0 ], [ - 5.102694996447305e-17, - -0.625, 0.0, - 20.0 + 0.625, + 0.0, + -20.0 ], [ 0.0, @@ -32,7 +32,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 2.5000000000000044e-06, @@ -56,51 +56,12 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - 0.0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.4166666666666667, 0.625, 1.16 ], - "rotation": [ - [ - -0.4166666666666667, - -7.654042494670958e-17, - 0.0 - ], - [ - 5.102694996447305e-17, - -0.625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.16 - ] - ], "shape_block": [ 96, 64 @@ -138,16 +99,16 @@ "TR": 1000, "affine": [ [ - -0.625, - -7.654042494670958e-17, + 0.625, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 7.654042494670958e-17, - -0.625, 0.0, - 20.0 + 0.625, + 0.0, + -20.0 ], [ 0.0, @@ -166,7 +127,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 2.5e-06, @@ -184,51 +145,12 @@ "offset": [ 0 ], - "position": [ - 20.0, - 20.0, - 0.0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.625, 0.625, 1.16 ], - "rotation": [ - [ - -0.625, - -7.654042494670958e-17, - 0.0 - ], - [ - 7.654042494670958e-17, - -0.625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.16 - ] - ], "shape_block": [ 64, 64 @@ -260,16 +182,16 @@ "TR": 500, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, + 0.0, 0.0, - 19.0 + -21.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 18.0 + 0.3125, + 0.0, + -22.0 ], [ 0.0, @@ -314,51 +236,12 @@ 0, 0 ], - "position": [ - 19.0, - 18.0, - -7.64 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 1.1599999999999993 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.1599999999999993 - ] - ], "shape_block": [ 128, 128 @@ -398,16 +281,16 @@ "TR": 1000, "affine": [ [ - -0.4166666666666667, - -5.102694996447305e-17, + 0.4166666666666667, 0.0, - 20.0 + 0.0, + -20.0 ], [ - 5.102694996447305e-17, - -0.4166666666666667, 0.0, - 17.0 + 0.4166666666666667, + 0.0, + -23.0 ], [ 0.0, @@ -426,7 +309,7 @@ "dim_type": [ "spatial", "spatial", - "spatial" + "frame" ], "dwell_s": 2.5000000000000044e-06, "encoded_dim": 2, @@ -443,51 +326,12 @@ "offset": [ 0 ], - "position": [ - 20.0, - 17.0, - 0.0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.4166666666666667, 0.4166666666666667, 1.5 ], - "rotation": [ - [ - -0.4166666666666667, - -5.102694996447305e-17, - 0.0 - ], - [ - 5.102694996447305e-17, - -0.4166666666666667, - 0.0 - ], - [ - 0.0, - 0.0, - 1.5 - ] - ], "shape_block": [ 96, 96 @@ -517,16 +361,16 @@ "TR": 1000, "affine": [ [ - -0.4166666666666667, - -7.654042494670958e-17, + 0.4166666666666667, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 5.102694996447305e-17, - -0.625, 0.0, - 17.0 + 0.625, + 0.0, + -23.0 ], [ 0.0, @@ -545,7 +389,7 @@ "dim_type": [ "spatial", "spatial", - "spatial" + "frame" ], "dwell_s": 2.5000000000000044e-06, "encoded_dim": 2, @@ -562,51 +406,12 @@ "offset": [ 0 ], - "position": [ - 20.0, - 17.0, - 0.0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.4166666666666667, 0.625, 1.5 ], - "rotation": [ - [ - -0.4166666666666667, - -7.654042494670958e-17, - 0.0 - ], - [ - 5.102694996447305e-17, - -0.625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.5 - ] - ], "shape_block": [ 96, 64 @@ -634,32 +439,6 @@ "2DSEQ_15_1_LEGO_PHANTOM_1": { "TE": 11.54, "TR": 1000, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2021-01-28 12:22:57.085000+01:00", "dim_type": [ "spatial", @@ -677,23 +456,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "shape_block": [ 96, @@ -727,16 +489,16 @@ "TR": 3000, "affine": [ [ - -0.3854166666666667, - -4.719992871713757e-17, + 0.3854166666666667, 0.0, - 18.5 + 0.0, + -18.5 ], [ - 4.719992871713757e-17, - -0.3854166666666667, 0.0, - 18.5 + 0.3854166666666667, + 0.0, + -18.5 ], [ 0.0, @@ -797,51 +559,12 @@ 0, 0 ], - "position": [ - 18.5, - 18.5, - -0.75 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3854166666666667, 0.3854166666666667, 1.5 ], - "rotation": [ - [ - -0.3854166666666667, - -4.719992871713757e-17, - 0.0 - ], - [ - 4.719992871713757e-17, - -0.3854166666666667, - 0.0 - ], - [ - 0.0, - 0.0, - 1.5 - ] - ], "shape_block": [ 96, 96 @@ -898,16 +621,16 @@ "TR": 1502.8461870228, "affine": [ [ - -0.15625, - -1.9135106236677395e-17, + 0.15625, 0.0, - 20.0 + 0.0, + -20.0 ], [ - 1.9135106236677395e-17, - -0.15625, 0.0, - 18.0 + 0.15625, + 0.0, + -22.0 ], [ 0.0, @@ -949,51 +672,12 @@ 0, 0 ], - "position": [ - 20, - 18, - -1 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.15625, 0.15625, 1.0 ], - "rotation": [ - [ - -0.15625, - -1.9135106236677395e-17, - 0.0 - ], - [ - 1.9135106236677395e-17, - -0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 256, 256 @@ -1030,16 +714,16 @@ "TR": 5, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 20.0 + 0.3125, + 0.0, + -20.0 ], [ 0.0, @@ -1085,51 +769,12 @@ 0, 0 ], - "position": [ - 20, - 20, - -1 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 2.0 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 128, 128 @@ -1170,16 +815,16 @@ "TR": 100, "affine": [ [ - -0.068359375, - -9.567553118338697e-18, + 0.068359375, + 0.0, 0.0, - 17.5 + -17.5 ], [ - 8.37160897854636e-18, - -0.078125, 0.0, - 20.0 + 0.078125, + 0.0, + -20.0 ], [ 0.0, @@ -1198,7 +843,7 @@ "dim_type": [ "spatial", "spatial", - "spatial" + "frame" ], "dwell_s": 1e-05, "encoded_dim": 2, @@ -1215,51 +860,12 @@ "offset": [ 0 ], - "position": [ - 17.5, - 20.0, - 0.0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.068359375, 0.078125, 2.0 ], - "rotation": [ - [ - -0.068359375, - -9.567553118338697e-18, - 0.0 - ], - [ - 8.37160897854636e-18, - -0.078125, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 512, 512 @@ -1289,16 +895,16 @@ "TR": 50, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, 0.0, - 20.0 + 0.0, + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 18.0 + 0.3125, + 0.0, + -22.0 ], [ 0.0, @@ -1317,7 +923,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 5e-06, @@ -1336,51 +942,12 @@ 0, 0 ], - "position": [ - 20.0, - 18.0, - 0.0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 1.16 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.16 - ] - ], "shape_block": [ 128, 128 @@ -1413,16 +980,16 @@ "TR": 100, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 18.0 + 0.3125, + 0.0, + -22.0 ], [ 0.0, @@ -1514,51 +1081,12 @@ 0, 0 ], - "position": [ - 20.0, - 18.0, - -2.4 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 0.7999999999999998 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 0.7999999999999998 - ] - ], "shape_block": [ 128, 128 @@ -1644,16 +1172,16 @@ "TR": 35, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 20.0 + 0.3125, + 0.0, + -20.0 ], [ 0.0, @@ -1690,51 +1218,12 @@ "offset": [ 0 ], - "position": [ - 20.0, - 20.0, - -19.84375 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 0.3125 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 0.3125 - ] - ], "shape_block": [ 128, 128, @@ -1767,16 +1256,16 @@ "TR": 10, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 20.0 + 0.3125, + 0.0, + -20.0 ], [ 0.0, @@ -1795,7 +1284,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 5e-06, @@ -1820,51 +1309,12 @@ 0, 0 ], - "position": [ - 20, - 20, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 1.0 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 128, 128 @@ -1903,16 +1353,16 @@ "TR": 2308.91237404559, "affine": [ [ - -0.15625, - -3.827021247335479e-17, + 0.15625, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 1.9135106236677395e-17, - -0.3125, 0.0, - 20.0 + 0.3125, + 0.0, + -20.0 ], [ 0.0, @@ -1949,51 +1399,12 @@ "offset": [ 0 ], - "position": [ - 20.0, - 20.0, - -19.375 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.15625, 0.3125, 1.25 ], - "rotation": [ - [ - -0.15625, - -3.827021247335479e-17, - 0.0 - ], - [ - 1.9135106236677395e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.25 - ] - ], "shape_block": [ 256, 128, @@ -2035,16 +1446,16 @@ "TR": 232.202197802198, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 18.0 + 0.3125, + 0.0, + -22.0 ], [ 0.0, @@ -2120,51 +1531,12 @@ 0, 0 ], - "position": [ - 20, - 18, - -2 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 1.0 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 128, 128 @@ -2239,16 +1611,16 @@ "TR": 1000, "affine": [ [ - -0.15625, - -1.9135106236677395e-17, + 0.15625, 0.0, - 20.0 + 0.0, + -20.0 ], [ - 1.9135106236677395e-17, - -0.15625, 0.0, - 20.0 + 0.15625, + 0.0, + -20.0 ], [ 0.0, @@ -2293,51 +1665,12 @@ 0, 0 ], - "position": [ - 20, - 20, - -2 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.15625, 0.15625, 2.0 ], - "rotation": [ - [ - -0.15625, - -1.9135106236677395e-17, - 0.0 - ], - [ - 1.9135106236677395e-17, - -0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 256, 256 @@ -2375,32 +1708,6 @@ "2DSEQ_28_1_LEGO_PHANTOM_1": { "TE": 0, "TR": 1000, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2021-01-28 12:22:57.085000+01:00", "dim_type": [ "spectroscopic", @@ -2416,23 +1723,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "shape_block": [ 2048 @@ -2458,32 +1748,6 @@ "2DSEQ_28_2_LEGO_PHANTOM_1": { "TE": 0, "TR": 1000, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2021-01-28 12:22:57.085000+01:00", "dim_type": [ "spectroscopic", @@ -2500,23 +1764,6 @@ 0, 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "shape_block": [ 2048 @@ -2542,32 +1789,6 @@ }, "2DSEQ_30_1_LEGO_PHANTOM_1": { "TE": 0, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2021-01-28 12:22:57.085000+01:00", "dim_type": [ "spectroscopic", @@ -2586,23 +1807,6 @@ 0, 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "shape_block": [ 2048 @@ -2630,32 +1834,6 @@ }, "2DSEQ_30_2_LEGO_PHANTOM_1": { "TE": 0, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2021-01-28 12:22:57.085000+01:00", "dim_type": [ "spectroscopic", @@ -2679,23 +1857,6 @@ 0, 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "shape_block": [ 2048 @@ -2732,16 +1893,16 @@ "TR": 1655.725, "affine": [ [ - -0.078125, - -9.567553118338697e-18, + 0.078125, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 9.567553118338697e-18, - -0.078125, 0.0, - 18.0 + 0.078125, + 0.0, + -22.0 ], [ 0.0, @@ -2781,51 +1942,12 @@ 0, 0 ], - "position": [ - 20, - 18, - -2 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.078125, 0.078125, 1.0 ], - "rotation": [ - [ - -0.078125, - -9.567553118338697e-18, - 0.0 - ], - [ - 9.567553118338697e-18, - -0.078125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 512, 512 @@ -2863,16 +1985,16 @@ ], "affine": [ [ - -0.15625, - -3.827021247335479e-17, + 0.15625, 0.0, - 20.0 + 0.0, + -20.0 ], [ - 1.9135106236677395e-17, - -0.3125, 0.0, - 20.0 + 0.3125, + 0.0, + -20.0 ], [ 0.0, @@ -2929,51 +2051,12 @@ 0, 0 ], - "position": [ - 20, - 20, - -3 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.15625, 0.3125, 1.0 ], - "rotation": [ - [ - -0.15625, - -3.827021247335479e-17, - 0.0 - ], - [ - 1.9135106236677395e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 256, 128 @@ -3025,16 +2108,16 @@ "TR": 3000, "affine": [ [ - -0.453125, - -5.5491808086364444e-17, + 0.453125, 0.0, - 29.0 + 0.0, + -29.0 ], [ - 5.5491808086364444e-17, - -0.453125, 0.0, - 29.0 + 0.453125, + 0.0, + -29.0 ], [ 0.0, @@ -3053,7 +2136,7 @@ "dim_type": [ "spatial", "spatial", - "spatial" + "frame" ], "dwell_s": 5e-06, "encoded_dim": 2, @@ -3070,51 +2153,12 @@ "offset": [ 0 ], - "position": [ - 29.0, - 29.0, - 0.0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.453125, 0.453125, 1.16 ], - "rotation": [ - [ - -0.453125, - -5.5491808086364444e-17, - 0.0 - ], - [ - 5.5491808086364444e-17, - -0.453125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.16 - ] - ], "shape_block": [ 128, 128 @@ -3142,32 +2186,6 @@ "2DSEQ_34_1_LEGO_PHANTOM_1": { "TE": 13.804, "TR": 500, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2021-01-28 12:22:57.085000+01:00", "dim_type": [ "spectroscopic", @@ -3183,23 +2201,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "shape_block": [ 256 @@ -3225,32 +2226,6 @@ "2DSEQ_35_1_LEGO_PHANTOM_1": { "TE": 0, "TR": 1000, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2021-01-28 12:22:57.085000+01:00", "dim_type": [ "spectroscopic", @@ -3266,23 +2241,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "shape_block": [ 2048 @@ -3310,16 +2268,16 @@ "TR": 1000, "affine": [ [ - -0.15625, - -1.9135106236677395e-17, + 0.15625, 0.0, - 20.0 + 0.0, + -20.0 ], [ - 1.9135106236677395e-17, - -0.15625, 0.0, - 20.0 + 0.15625, + 0.0, + -20.0 ], [ 0.0, @@ -3338,7 +2296,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 2.5e-06, @@ -3358,51 +2316,12 @@ 0, 0 ], - "position": [ - 20, - 20, - 0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.15625, 0.15625, 2.0 ], - "rotation": [ - [ - -0.15625, - -1.9135106236677395e-17, - 0.0 - ], - [ - 1.9135106236677395e-17, - -0.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 256, 256 @@ -3433,32 +2352,6 @@ }, "2DSEQ_37_1_LEGO_PHANTOM_1": { "TE": 0, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2021-01-28 12:22:57.085000+01:00", "dim_type": [ "spectroscopic", @@ -3474,23 +2367,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "shape_block": [ 2048 @@ -3515,32 +2391,6 @@ }, "2DSEQ_37_2_LEGO_PHANTOM_1": { "TE": 0, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2021-01-28 12:22:57.085000+01:00", "dim_type": [ "spectroscopic", @@ -3557,23 +2407,6 @@ 0, 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "shape_block": [ 2048 @@ -3602,16 +2435,16 @@ "TR": 100, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 20.0 + 0.3125, + 0.0, + -20.0 ], [ 0.0, @@ -3630,7 +2463,7 @@ "dim_type": [ "spatial", "spatial", - "spatial" + "frame" ], "dwell_s": 1e-05, "encoded_dim": 2, @@ -3647,51 +2480,12 @@ "offset": [ 0 ], - "position": [ - 20.0, - 20.0, - 0.0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 1.5 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.5 - ] - ], "shape_block": [ 128, 128 @@ -3721,16 +2515,16 @@ "TR": 3000, "affine": [ [ - -0.3854166666666667, - -7.079989307570636e-17, + 0.3854166666666667, + 0.0, 0.0, - 18.5 + -18.5 ], [ - 4.719992871713757e-17, - -0.578125, 0.0, - 16.5 + 0.578125, + 0.0, + -20.5 ], [ 0.0, @@ -3814,51 +2608,12 @@ 0, 0 ], - "position": [ - 18.5, - 16.5, - -3.75 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3854166666666667, 0.578125, 1.5 ], - "rotation": [ - [ - -0.3854166666666667, - -7.079989307570636e-17, - 0.0 - ], - [ - 4.719992871713757e-17, - -0.578125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.5 - ] - ], "shape_block": [ 96, 64 @@ -3942,16 +2697,16 @@ "TR": 1000, "affine": [ [ - -0.578125, - -1.415997861514127e-16, + 0.578125, 0.0, - 18.5 + 0.0, + -18.5 ], [ - 7.079989307570636e-17, - -1.15625, 0.0, - 16.5 + 1.15625, + 0.0, + -20.5 ], [ 0.0, @@ -4009,52 +2764,13 @@ 0, 0, 0, - 0 - ], - "position": [ - 18.5, - 16.5, - -3.75 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], - "pv_version": "7.0.0", - "resolution": [ - 0.578125, - 1.15625, - 1.5 - ], - "rotation": [ - [ - -0.578125, - -1.415997861514127e-16, - 0.0 - ], - [ - 7.079989307570636e-17, - -1.15625, - 0.0 - ], - [ - 0.0, - 0.0, - 1.5 - ] + 0 + ], + "pv_version": "7.0.0", + "resolution": [ + 0.578125, + 1.15625, + 1.5 ], "shape_block": [ 64, @@ -4115,16 +2831,16 @@ "TR": 1000, "affine": [ [ - -0.578125, - -7.079989307570636e-17, + 0.578125, + 0.0, 0.0, - 18.5 + -18.5 ], [ - 7.079989307570636e-17, - -0.578125, 0.0, - 16.5 + 0.578125, + 0.0, + -20.5 ], [ 0.0, @@ -4184,51 +2900,12 @@ 0, 0 ], - "position": [ - 18.5, - 16.5, - -3.75 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.578125, 0.578125, 1.5 ], - "rotation": [ - [ - -0.578125, - -7.079989307570636e-17, - 0.0 - ], - [ - 7.079989307570636e-17, - -0.578125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.5 - ] - ], "shape_block": [ 64, 64 @@ -4283,16 +2960,16 @@ "TR": 30, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, 0.0, - 20.0 + 0.0, + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 20.0 + 0.3125, + 0.0, + -20.0 ], [ 0.0, @@ -4335,51 +3012,12 @@ 0, 0 ], - "position": [ - 20.0, - 20.0, - -5.95 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 1.7000000000000002 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.7000000000000002 - ] - ], "shape_block": [ 128, 128 @@ -4416,16 +3054,16 @@ "TR": 2.5, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, 0.0, - 20.0 + 0.0, + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 20.0 + 0.3125, + 0.0, + -20.0 ], [ 0.0, @@ -4462,51 +3100,12 @@ "offset": [ 0 ], - "position": [ - 20.0, - 20.0, - -19.84375 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 0.3125 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 0.3125 - ] - ], "shape_block": [ 128, 128, @@ -4539,16 +3138,16 @@ "TR": 4, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, 0.0, - 20.0 + 0.0, + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 20.0 + 0.3125, + 0.0, + -20.0 ], [ 0.0, @@ -4585,51 +3184,12 @@ "offset": [ 0 ], - "position": [ - 20.0, - 20.0, - -19.84375 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 0.3125 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 0.3125 - ] - ], "shape_block": [ 128, 128, @@ -4662,16 +3222,16 @@ "TR": 15, "affine": [ [ - -0.20833333333333334, - -2.5513474982236526e-17, + 0.20833333333333334, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 2.5513474982236526e-17, - -0.20833333333333334, 0.0, - 20.0 + 0.20833333333333334, + 0.0, + -20.0 ], [ 0.0, @@ -4721,51 +3281,12 @@ 0, 0 ], - "position": [ - 20, - 20, - -4 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.20833333333333334, 0.20833333333333334, 2.0 ], - "rotation": [ - [ - -0.20833333333333334, - -2.5513474982236526e-17, - 0.0 - ], - [ - 2.5513474982236526e-17, - -0.20833333333333334, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 192, 192 @@ -4812,16 +3333,16 @@ "TR": 11.829403240324, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, + 0.0, 0.0, - 20.0 + -20.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 20.0 + 0.3125, + 0.0, + -20.0 ], [ 0.0, @@ -4867,51 +3388,12 @@ 0, 0 ], - "position": [ - 20, - 20, - -4 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 2.0 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 2.0 - ] - ], "shape_block": [ 128, 128 @@ -4963,32 +3445,6 @@ 240 ], "TR": 5000, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2021-01-28 12:22:57.085000+01:00", "dim_type": [ "spectroscopic", @@ -5015,23 +3471,6 @@ 0, 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "shape_block": [ 256 @@ -5052,47 +3491,21 @@ ], "slope": [ 3.76210752008514, - 3.76210752008514, - 3.76210752008514, - 3.76210752008514, - 3.76210752008514, - 3.76210752008514, - 3.76210752008514, - 3.76210752008514, - 3.76210752008514, - 3.76210752008514, - 3.76210752008514, - 3.76210752008514 - ] - }, - "2DSEQ_8_1_LEGO_PHANTOM_1": { - "TR": 1000, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], + 3.76210752008514, + 3.76210752008514, + 3.76210752008514, + 3.76210752008514, + 3.76210752008514, + 3.76210752008514, + 3.76210752008514, + 3.76210752008514, + 3.76210752008514, + 3.76210752008514, + 3.76210752008514 + ] + }, + "2DSEQ_8_1_LEGO_PHANTOM_1": { + "TR": 1000, "date": "2021-01-28 12:22:57.085000+01:00", "dim_type": [ "spectroscopic", @@ -5110,23 +3523,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "shape_block": [ 2048, @@ -5157,32 +3553,6 @@ }, "2DSEQ_8_2_LEGO_PHANTOM_1": { "TR": 1000, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2021-01-28 12:22:57.085000+01:00", "dim_type": [ "spectroscopic", @@ -5201,23 +3571,6 @@ 0, 0 ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "shape_block": [ 2048, @@ -5255,16 +3608,16 @@ "TR": 10, "affine": [ [ - -0.3125, - -3.827021247335479e-17, + 0.3125, 0.0, - 19.0 + 0.0, + -21.0 ], [ - 3.827021247335479e-17, - -0.3125, 0.0, - 16.0 + 0.3125, + 0.0, + -24.0 ], [ 0.0, @@ -5283,7 +3636,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "", "" ], @@ -5309,51 +3662,12 @@ 0, 0 ], - "position": [ - 19.0, - 16.0, - 2.0 - ], - "position_matrix": [ - [ - -1.0, - -1.2246467991473532e-16, - 0.0 - ], - [ - 1.2246467991473532e-16, - -1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "pv_version": "7.0.0", "resolution": [ 0.3125, 0.3125, 1.16 ], - "rotation": [ - [ - -0.3125, - -3.827021247335479e-17, - 0.0 - ], - [ - 3.827021247335479e-17, - -0.3125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.16 - ] - ], "shape_block": [ 128, 128 @@ -5400,7 +3714,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel", "k_space_encode_step_2" @@ -5433,6 +3747,7 @@ 1, 5 ], + "pv_version": "7.0.0", "scheme_id": "dEPI", "shape_storage": [ 6666, @@ -5451,7 +3766,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -5480,6 +3795,7 @@ 4, 1 ], + "pv_version": "7.0.0", "scheme_id": "SPIRAL", "shape_storage": [ 1792, @@ -5498,7 +3814,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -5529,6 +3845,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -5547,7 +3864,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -5576,6 +3893,7 @@ 4, 1 ], + "pv_version": "7.0.0", "scheme_id": "EPI", "shape_storage": [ 5130, @@ -5594,7 +3912,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -5623,6 +3941,7 @@ 4, 1 ], + "pv_version": "7.0.0", "scheme_id": "EPI", "shape_storage": [ 12288, @@ -5633,7 +3952,7 @@ "FID_15_LEGO_PHANTOM_1": { "TE": 0.48, "TR": 1000, - "acq_length": 3072, + "acq_length": 12288, "block_count": 256, "block_size": 12288, "channels": 1, @@ -5641,39 +3960,34 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", - "repetition", - "channel", - "k_space_encode_step_2" + "k_space_encode_step_2", + "channel" ], "dwell_s": 2.5e-06, "encoded_dim": 3, "encoding_space": [ - 1536, + 96, 1, + 64, 4, - 1, - 1, 64 ], "id": "FID_15_LEGO_PHANTOM_1", "k_space": [ 96, 64, - 1, - 1, - 1, - 64 + 256, + 1 ], "numpy_dtype": "int32", "permute": [ 0, - 2, - 3, 4, - 1, - 5 + 3, + 2, + 1 ], + "pv_version": "7.0.0", "scheme_id": "dEPI", "shape_storage": [ 12288, @@ -5692,7 +4006,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -5721,6 +4035,7 @@ 4, 1 ], + "pv_version": "7.0.0", "scheme_id": "EPI", "shape_storage": [ 9216, @@ -5739,7 +4054,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -5770,6 +4085,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -5788,7 +4104,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -5819,6 +4135,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -5837,7 +4154,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -5868,6 +4185,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 1024, @@ -5886,7 +4204,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -5917,6 +4235,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -5935,7 +4254,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -5966,6 +4285,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -5985,6 +4305,7 @@ "k_space_encode_step_0", "k_space_encode_step_1", "k_space_encode_step_2", + "echo", "repetition", "channel" ], @@ -5995,7 +4316,8 @@ 1, 2, 128, - 128 + 128, + 1 ], "id": "FID_22_LEGO_PHANTOM_1", "k_space": [ @@ -6003,6 +4325,7 @@ 128, 128, 2, + 1, 1 ], "numpy_dtype": "int32", @@ -6011,8 +4334,10 @@ 3, 4, 2, + 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "FIELD_MAP", "shape_storage": [ 256, @@ -6061,6 +4386,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_3D", "shape_storage": [ 512, @@ -6079,7 +4405,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6110,6 +4436,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -6128,7 +4455,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6159,6 +4486,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -6175,8 +4503,8 @@ "date": "2021-01-28 14:14:41.056000+01:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 6.240000000000003e-05, "encoded_dim": 1, @@ -6197,6 +4525,7 @@ 1, 2 ], + "pv_version": "7.0.0", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 4096, @@ -6214,8 +4543,8 @@ "date": "2021-01-28 14:15:30.037000+01:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 6.240000000000003e-05, "encoded_dim": 1, @@ -6236,6 +4565,7 @@ 1, 2 ], + "pv_version": "7.0.0", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 4096, @@ -6254,7 +4584,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6285,6 +4615,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 1024, @@ -6302,7 +4633,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6333,6 +4664,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -6351,7 +4683,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6382,6 +4714,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -6399,8 +4732,8 @@ "date": "2021-01-28 14:23:25.316000+01:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 1e-05, "encoded_dim": 1, @@ -6421,6 +4754,7 @@ 1, 2 ], + "pv_version": "7.0.0", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 512, @@ -6437,8 +4771,8 @@ "date": "2021-01-28 14:23:49.210000+01:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 6.240000000000003e-05, "encoded_dim": 1, @@ -6459,6 +4793,7 @@ 1, 2 ], + "pv_version": "7.0.0", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 4096, @@ -6477,7 +4812,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6506,6 +4841,7 @@ 4, 1 ], + "pv_version": "7.0.0", "scheme_id": "SPIRAL", "shape_storage": [ 7168, @@ -6523,8 +4859,8 @@ "date": "2021-01-28 14:25:55.493000+01:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 6.240000000000003e-05, "encoded_dim": 1, @@ -6545,6 +4881,7 @@ 1, 2 ], + "pv_version": "7.0.0", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 4096, @@ -6563,7 +4900,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6592,6 +4929,7 @@ 4, 1 ], + "pv_version": "7.0.0", "scheme_id": "EPI", "shape_storage": [ 12288, @@ -6610,7 +4948,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6639,6 +4977,7 @@ 4, 1 ], + "pv_version": "7.0.0", "scheme_id": "EPI", "shape_storage": [ 4096, @@ -6657,7 +4996,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6686,6 +5025,7 @@ 4, 1 ], + "pv_version": "7.0.0", "scheme_id": "EPI", "shape_storage": [ 8192, @@ -6704,7 +5044,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6735,6 +5075,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "RADIAL", "shape_storage": [ 256, @@ -6753,7 +5094,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6784,6 +5125,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "RADIAL", "shape_storage": [ 256, @@ -6801,7 +5143,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6832,6 +5174,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "ZTE", "shape_storage": [ 512, @@ -6850,7 +5193,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6881,6 +5224,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "RADIAL", "shape_storage": [ 256, @@ -6899,7 +5243,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6930,6 +5274,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 512, @@ -6948,7 +5293,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -6979,6 +5324,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -6995,8 +5341,8 @@ "date": "2021-01-28 12:55:47.297000+01:00", "dim_type": [ "k_space_encode_step_0", - "repetition", - "average" + "object", + "repetition" ], "dwell_s": 2e-05, "encoded_dim": 1, @@ -7017,6 +5363,7 @@ 1, 2 ], + "pv_version": "7.0.0", "scheme_id": "SPECTROSCOPY", "shape_storage": [ 512, @@ -7056,6 +5403,7 @@ 1, 2 ], + "pv_version": "7.0.0", "scheme_id": "CSI", "shape_storage": [ 4096, @@ -7074,7 +5422,7 @@ "dim_type": [ "k_space_encode_step_0", "k_space_encode_step_1", - "slice", + "object", "repetition", "channel" ], @@ -7105,6 +5453,7 @@ 5, 1 ], + "pv_version": "7.0.0", "scheme_id": "CART_2D", "shape_storage": [ 256, @@ -7116,6 +5465,11 @@ "TE": 3, "TR": 10, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job0_24_LEGO_PHANTOM_1", "job_desc": [ 256, @@ -7128,20 +5482,21 @@ 10240 ], "numpy_dtype": "int32", + "pv_version": "7.0.0", "shape_storage": [ 256, 1, 10240 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job0_28_LEGO_PHANTOM_1": { "TR": 1000, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job0_28_LEGO_PHANTOM_1", "job_desc": [ 4096, @@ -7154,21 +5509,22 @@ 1 ], "numpy_dtype": "int32", + "pv_version": "7.0.0", "shape_storage": [ 4096, 1, 1 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job0_30_LEGO_PHANTOM_1": { "TE": 20, "TR": 2000, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job0_30_LEGO_PHANTOM_1", "job_desc": [ 4096, @@ -7181,21 +5537,22 @@ 4 ], "numpy_dtype": "int32", + "pv_version": "7.0.0", "shape_storage": [ 4096, 1, 4 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job0_37_LEGO_PHANTOM_1": { "TE": 15, "TR": 2000, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job0_37_LEGO_PHANTOM_1", "job_desc": [ 4096, @@ -7208,20 +5565,21 @@ 1 ], "numpy_dtype": "int32", + "pv_version": "7.0.0", "shape_storage": [ 4096, 1, 1 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job0_38_LEGO_PHANTOM_1": { "TE": 4, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job0_38_LEGO_PHANTOM_1", "job_desc": [ 256, @@ -7234,21 +5592,22 @@ 128 ], "numpy_dtype": "int32", + "pv_version": "7.0.0", "shape_storage": [ 256, 1, 128 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job0_8_LEGO_PHANTOM_1": { "TE": 5, "TR": 1000, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job0_8_LEGO_PHANTOM_1", "job_desc": [ 4096, @@ -7261,21 +5620,22 @@ 1024 ], "numpy_dtype": "int32", + "pv_version": "7.0.0", "shape_storage": [ 4096, 1, 1024 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job1_24_LEGO_PHANTOM_1": { "TE": 3, "TR": 10, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job1_24_LEGO_PHANTOM_1", "job_desc": [ 64, @@ -7288,21 +5648,22 @@ 10240 ], "numpy_dtype": "int32", + "pv_version": "7.0.0", "shape_storage": [ 64, 1, 10240 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] }, "RawData_job1_8_LEGO_PHANTOM_1": { "TE": 5, "TR": 1000, "channels": 1, + "dim_type": [ + "sample", + "channel", + "scan" + ], "id": "RawData_job1_8_LEGO_PHANTOM_1", "job_desc": [ 272, @@ -7315,15 +5676,11 @@ 1024 ], "numpy_dtype": "int32", + "pv_version": "7.0.0", "shape_storage": [ 272, 1, 1024 - ], - "dim_type": [ - "sample", - "channel", - "scan" ] } } diff --git a/test/config/properties_PV360_StdData.json b/test/config/properties_PV360_StdData.json index 468b92c..1c6d3c7 100644 --- a/test/config/properties_PV360_StdData.json +++ b/test/config/properties_PV360_StdData.json @@ -6,14 +6,14 @@ [ -0.14053933504956034, 0.0, - -0.03664447153762601, - 27.099161408259967 + -0.03664447153762751, + 9.099161408259969 ], [ 0.0, -0.1171875, 0.0, - 24.843749999999996 + 9.843749999999996 ], [ -0.004907741723789199, @@ -224,51 +224,12 @@ 0, 0 ], - "position": [ - 27.099161408259967, - 24.843749999999996, - -2.682515618469071 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.140625, 0.1171875, 1.0499999999999998 ], - "rotation": [ - [ - -0.14053933504956034, - 0.0, - -0.03664447153762601 - ], - [ - 0.0, - -0.1171875, - 0.0 - ], - [ - -0.004907741723789199, - 0.0, - 1.0493603683700503 - ] - ], "shape_block": [ 128, 128 @@ -476,14 +437,14 @@ [ -0.14053933504956034, 0.0, - -0.03664447153762601, - 27.099161408259967 + -0.03664447153762751, + 9.099161408259969 ], [ 0.0, -0.1171875, 0.0, - 24.843749999999996 + 9.843749999999996 ], [ -0.004907741723789199, @@ -634,51 +595,12 @@ 0, 0 ], - "position": [ - 27.099161408259967, - 24.843749999999996, - -2.682515618469071 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.140625, 0.1171875, 1.0499999999999998 ], - "rotation": [ - [ - -0.14053933504956034, - 0.0, - -0.03664447153762601 - ], - [ - 0.0, - -0.1171875, - 0.0 - ], - [ - -0.004907741723789199, - 0.0, - 1.0493603683700503 - ] - ], "shape_block": [ 128, 128 @@ -826,14 +748,14 @@ [ -0.14053933504956034, 0.0, - -0.03664447153762601, - 27.099161408259967 + -0.03664447153762751, + 9.099161408259969 ], [ 0.0, -0.1171875, 0.0, - 24.843749999999996 + 9.843749999999996 ], [ -0.004907741723789199, @@ -1194,51 +1116,12 @@ 0, 0 ], - "position": [ - 27.099161408259967, - 24.843749999999996, - -2.682515618469071 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.140625, 0.1171875, 1.0499999999999998 ], - "rotation": [ - [ - -0.14053933504956034, - 0.0, - -0.03664447153762601 - ], - [ - 0.0, - -0.1171875, - 0.0 - ], - [ - -0.004907741723789199, - 0.0, - 1.0493603683700503 - ] - ], "shape_block": [ 128, 128 @@ -1596,14 +1479,14 @@ [ -0.14053933504956034, 0.0, - -0.03664447153762601, - 27.099161408259967 + -0.03664447153762751, + 9.099161408259969 ], [ 0.0, -0.1171875, 0.0, - 24.843749999999996 + 9.843749999999996 ], [ -0.004907741723789199, @@ -1754,51 +1637,12 @@ 0, 0 ], - "position": [ - 27.099161408259967, - 24.843749999999996, - -2.682515618469071 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.140625, 0.1171875, 1.0499999999999998 ], - "rotation": [ - [ - -0.14053933504956034, - 0.0, - -0.03664447153762601 - ], - [ - 0.0, - -0.1171875, - 0.0 - ], - [ - -0.004907741723789199, - 0.0, - 1.0493603683700503 - ] - ], "shape_block": [ 128, 128 @@ -1941,32 +1785,6 @@ }, "2DSEQ_PRESS_1H_1_std_PV360_3.6^^^^_1": { "TE": 0, - "affine": [ - [ - 1.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 1.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 1.0 - ] - ], "date": "2024-07-25 09:02:12.259000+02:00", "dim_type": [ "spectroscopic", @@ -1982,23 +1800,6 @@ "offset": [ 0 ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "shape_block": [ 2048 @@ -2028,14 +1829,14 @@ [ -0.05205160557391124, 0.0, - -0.03489949670250094, - 30.172446716490782 + -0.0348994967025007, + 10.172446716490784 ], [ 0.0, -0.052083333333333336, 0.0, - 31.874999999999975 + 11.874999999999975 ], [ -0.0018176821199219256, @@ -2079,51 +1880,12 @@ 0, 0 ], - "position": [ - 30.172446716490782, - 31.874999999999975, - -5.6345497981863195 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.052083333333333336, 0.052083333333333336, 0.9999999999999992 ], - "rotation": [ - [ - -0.05205160557391124, - 0.0, - -0.03489949670250094 - ], - [ - 0.0, - -0.052083333333333336, - 0.0 - ], - [ - -0.0018176821199219256, - 0.0, - 0.999390827019095 - ] - ], "shape_block": [ 384, 384 @@ -2164,13 +1926,13 @@ -0.12492385337738697, 0.0, -0.004362437087812621, - 31.018881150809918 + 11.01888115080992 ], [ 0.0, -0.125, 0.0, - 30.85937529802322 + 10.85937529802322 ], [ -0.004362437087812621, @@ -2207,51 +1969,12 @@ "offset": [ 0 ], - "position": [ - 31.018881150809918, - 30.85937529802322, - -8.329769001025065 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.125, 0.125, 0.125 ], - "rotation": [ - [ - -0.12492385337738697, - 0.0, - -0.004362437087812621 - ], - [ - 0.0, - -0.125, - 0.0 - ], - [ - -0.004362437087812621, - 0.0, - 0.12492385337738697 - ] - ], "shape_block": [ 160, 160, @@ -2286,19 +2009,19 @@ [ -0.07807740836086685, 0.0, - -0.03489949670250097, - 30.63973670071701 + -0.0348994967025007, + 10.63973670071701 ], [ 0.0, -0.078125, 0.0, - 30.0 + 10.0 ], [ -0.0027265231798828883, 0.0, - 0.9993908270190958, + 0.9993908270190959, -5.712431731289857 ], [ @@ -2337,51 +2060,12 @@ 0, 0 ], - "position": [ - 30.63973670071701, - 30.0, - -5.712431731289857 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.078125, 0.078125, 1.0 ], - "rotation": [ - [ - -0.07807740836086685, - 0.0, - -0.03489949670250097 - ], - [ - 0.0, - -0.078125, - 0.0 - ], - [ - -0.0027265231798828883, - 0.0, - 0.9993908270190958 - ] - ], "shape_block": [ 256, 256 @@ -2421,14 +2105,14 @@ [ -0.07807740836086685, 0.0, - -0.03489949670250094, - 30.194853044637156 + -0.0348994967025007, + 10.194853044637156 ], [ 0.0, -0.078125, 0.0, - 30.937500596046444 + 10.937500596046444 ], [ -0.0027265231798828883, @@ -2472,51 +2156,12 @@ 0, 0 ], - "position": [ - 30.194853044637156, - 30.937500596046444, - -5.405310489894727 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.078125, 0.078125, 0.9999999999999992 ], - "rotation": [ - [ - -0.07807740836086685, - 0.0, - -0.03489949670250094 - ], - [ - 0.0, - -0.078125, - 0.0 - ], - [ - -0.0027265231798828883, - 0.0, - 0.999390827019095 - ] - ], "shape_block": [ 256, 256 @@ -2568,14 +2213,14 @@ [ -0.10410321114782248, 0.0, - -0.04536934571325127, - 30.27935074918269 + -0.04536934571325091, + 10.279350749182692 ], [ 0.0, -0.10416666666666667, 0.0, - 30.0 + 10.0 ], [ -0.0036353642398438512, @@ -2666,51 +2311,12 @@ 0, 0 ], - "position": [ - 30.27935074918269, - 30.0, - -4.469047219148908 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.10416666666666667, 0.10416666666666667, 1.3000000000000003 ], - "rotation": [ - [ - -0.10410321114782248, - 0.0, - -0.04536934571325127 - ], - [ - 0.0, - -0.10416666666666667, - 0.0 - ], - [ - -0.0036353642398438512, - 0.0, - 1.2992080751248247 - ] - ], "shape_block": [ 192, 192 @@ -2798,14 +2404,14 @@ [ -0.10410321114782248, 0.0, - -0.04536934571325127, - 30.27935074918269 + -0.04536934571325091, + 10.279350749182692 ], [ 0.0, -0.10416666666666667, 0.0, - 30.0 + 10.0 ], [ -0.0036353642398438512, @@ -2871,51 +2477,12 @@ 0, 0 ], - "position": [ - 30.27935074918269, - 30.0, - -4.469047219148908 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.10416666666666667, 0.10416666666666667, 1.3000000000000003 ], - "rotation": [ - [ - -0.10410321114782248, - 0.0, - -0.04536934571325127 - ], - [ - 0.0, - -0.10416666666666667, - 0.0 - ], - [ - -0.0036353642398438512, - 0.0, - 1.2992080751248247 - ] - ], "shape_block": [ 192, 192 @@ -2978,14 +2545,14 @@ [ -0.1561548167217337, 0.0, - -0.04362437087812621, - 30.325479389193394 + -0.04362437087812587, + 10.325479389193394 ], [ 0.0, -0.20833333333333334, 0.0, - 31.289062360301614 + 11.289062360301614 ], [ -0.005453046359765777, @@ -3025,51 +2592,12 @@ 0, 0 ], - "position": [ - 30.325479389193394, - 31.289062360301614, - -4.197139084123697 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.15625, 0.20833333333333334, 1.25 ], - "rotation": [ - [ - -0.1561548167217337, - 0.0, - -0.04362437087812621 - ], - [ - 0.0, - -0.20833333333333334, - 0.0 - ], - [ - -0.005453046359765777, - 0.0, - 1.2492385337738696 - ] - ], "shape_block": [ 128, 96 @@ -3115,13 +2643,13 @@ -0.078125, 0.0, 0.0, - 29.80529546737671 + 9.805295467376709 ], [ 0.0, -0.078125, 0.0, - 31.40624940395355 + 11.406249403953549 ], [ 0.0, @@ -3140,7 +2668,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 6.4e-06, @@ -3165,51 +2693,12 @@ 0, 0 ], - "position": [ - 29.80529546737671, - 31.40624940395355, - -1.679687059606703 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.078125, 0.078125, 1.0 ], - "rotation": [ - [ - -0.078125, - 0.0, - 0.0 - ], - [ - 0.0, - -0.078125, - 0.0 - ], - [ - 0.0, - 0.0, - 1.0 - ] - ], "shape_block": [ 256, 256 @@ -3265,18 +2754,18 @@ -0.078125, 0.0, 0.0, - 29.80529546737671 + 9.805295467376709 ], [ 0.0, -0.078125, 0.0, - 31.40624940395355 + 11.406249403953549 ], [ 0.0, 0.0, - 0.0, + 1.0, -1.679687059606703 ], [ @@ -3312,51 +2801,12 @@ 0, 0 ], - "position": [ - 29.80529546737671, - 31.40624940395355, - -1.679687059606703 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.078125, 0.078125, 0.0 ], - "rotation": [ - [ - -0.078125, - 0.0, - 0.0 - ], - [ - 0.0, - -0.078125, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ] - ], "shape_block": [ 256, 256 @@ -3403,13 +2853,13 @@ 0.078125, 0.0, 0.0, - 7.362637540715545 + -12.637362459284455 ], [ 0.0, 0.0, 0.8, - 33.46154021292801 + 13.461540212928009 ], [ 0.0, @@ -3428,7 +2878,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 6.7e-06, @@ -3453,51 +2903,12 @@ 0, 0 ], - "position": [ - 7.362637540715545, - 33.46154021292801, - 10.0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.078125, 0.078125, 0.8 ], - "rotation": [ - [ - 0.078125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.8 - ], - [ - 0.0, - -0.078125, - 0.0 - ] - ], "shape_block": [ 256, 256 @@ -3553,13 +2964,13 @@ 0.078125, 0.0, 0.0, - 7.362637540715545 + -12.637362459284455 ], [ 0.0, 0.0, - 0.0, - 33.46154021292801 + 0.8, + 13.461540212928009 ], [ 0.0, @@ -3600,51 +3011,12 @@ 0, 0 ], - "position": [ - 7.362637540715545, - 33.46154021292801, - 10.0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.078125, 0.078125, 0.0 ], - "rotation": [ - [ - 0.078125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - -0.078125, - 0.0 - ] - ], "shape_block": [ 256, 256 @@ -3691,13 +3063,13 @@ 0.078125, 0.0, 0.0, - 7.362637540715545 + -12.637362459284455 ], [ 0.0, 0.0, 0.8, - 33.46154021292801 + 13.461540212928009 ], [ 0.0, @@ -3716,7 +3088,7 @@ "dim_type": [ "spatial", "spatial", - "spatial", + "frame", "" ], "dwell_s": 6.7e-06, @@ -3741,51 +3113,12 @@ 0, 0 ], - "position": [ - 7.362637540715545, - 33.46154021292801, - 10.0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.078125, 0.078125, 0.8 ], - "rotation": [ - [ - 0.078125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.8 - ], - [ - 0.0, - -0.078125, - 0.0 - ] - ], "shape_block": [ 256, 256 @@ -3841,13 +3174,13 @@ 0.078125, 0.0, 0.0, - 7.362637540715545 + -12.637362459284455 ], [ 0.0, 0.0, - 0.0, - 33.46154021292801 + 0.8, + 13.461540212928009 ], [ 0.0, @@ -3888,51 +3221,12 @@ 0, 0 ], - "position": [ - 7.362637540715545, - 33.46154021292801, - 10.0 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.078125, 0.078125, 0.0 ], - "rotation": [ - [ - 0.078125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - -0.078125, - 0.0 - ] - ], "shape_block": [ 256, 256 @@ -3970,13 +3264,13 @@ 0.1953125, 0.0, 0.0, - 12.538940459489822 + -12.461059540510178 ], [ 0.0, 0.1953125, 0.0, - 12.5 + -12.5 ], [ 0.0, @@ -4013,51 +3307,12 @@ "offset": [ 0 ], - "position": [ - 12.538940459489822, - 12.5, - -13.220101211951155 - ], - "position_matrix": [ - [ - 1, - 0, - 0 - ], - [ - 0, - 1, - 0 - ], - [ - 0, - 0, - 1 - ] - ], "pv_version": "360.3.6", "resolution": [ 0.1953125, 0.1953125, 0.1953125 ], - "rotation": [ - [ - 0.1953125, - 0.0, - 0.0 - ], - [ - 0.0, - 0.1953125, - 0.0 - ], - [ - 0.0, - 0.0, - 0.1953125 - ] - ], "shape_block": [ 128, 128, @@ -4107,6 +3362,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 272, 4, @@ -4135,6 +3391,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 5808, 4, @@ -4163,6 +3420,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 5808, 4, @@ -4191,6 +3449,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 4096, 4, @@ -4219,6 +3478,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 320, 4, @@ -4247,6 +3507,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 400, 4, @@ -4275,6 +3536,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 512, 4, @@ -4303,6 +3565,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 512, 4, @@ -4331,6 +3594,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 384, 4, @@ -4359,6 +3623,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 20480, 4, @@ -4387,6 +3652,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 512, 4, @@ -4415,6 +3681,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 512, 1, @@ -4443,6 +3710,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 512, 1, @@ -4471,6 +3739,7 @@ "" ], "numpy_dtype": "int32", + "pv_version": "360.3.6", "shape_storage": [ 148, 4, diff --git a/test/synthetic.py b/test/synthetic.py new file mode 100644 index 0000000..dcf9572 --- /dev/null +++ b/test/synthetic.py @@ -0,0 +1,227 @@ +"""Builders for small, self-contained ParaVision datasets. + +The parameter shapes here are copied from real ParaVision files (PV5.1, PV6.0.1, +PV7.0.0 and PV360 3.x scans), reduced to the minimum a reader needs. Tests can +therefore exercise the file format -- geometry, frame groups, scaling, slice +packages -- without any vendor data being present. + +Values are written the way ParaVision writes them: + +* a scalar goes on the assignment line (``##$VisuCoreDim=2``), +* an array declares its size and puts the values on the following lines + (``##$VisuCoreSize=( 2 )`` / ``256 256``), +* long value blocks are hard-wrapped near column 80 **at a space**, +* ``$$`` comment lines may appear anywhere between records. + +Pass :class:`Verbatim` when a test needs a record written exactly as given -- +for example to place a wrap inside a ``<...>`` string. +""" + +import numpy as np + +MAX_LINE_LEN = 78 + + +class Verbatim: + """A record body written exactly as given, size bracket included.""" + + def __init__(self, text): + self.text = text + + +def _wrap(text): + """Hard-wrap a value block at a space, the way ParaVision does.""" + lines = [] + for physical_line in text.split("\n"): + rest = physical_line + while len(rest) > MAX_LINE_LEN: + cut = rest.rfind(" ", 0, MAX_LINE_LEN + 1) + if cut <= 0: + break + lines.append(rest[: cut + 1]) + rest = rest[cut + 1 :] + lines.append(rest) + return "\n".join(lines) + + +def _format_scalar(value): + if isinstance(value, (bool, np.bool_)): + return "Yes" if value else "No" + if isinstance(value, (int, np.integer)): + return str(int(value)) + if isinstance(value, (float, np.floating)): + return repr(float(value)) + return str(value) + + +def format_record(key, value): + """Serialize one ``##$key=value`` record.""" + if isinstance(value, Verbatim): + return f"##${key}={value.text}" + + if isinstance(value, (list, tuple, np.ndarray)): + array = np.asarray(value) + if array.dtype.kind in "US" or (array.dtype == object): + size = f"( {array.size}, 65 )" if array.ndim == 1 else f"( {', '.join(str(n) for n in array.shape)}, 65 )" + body = " ".join(_format_scalar(item) for item in array.reshape(-1)) + else: + size = f"( {array.shape[0]} )" if array.ndim == 1 else f"( {', '.join(str(n) for n in array.shape)} )" + body = " ".join(_format_scalar(item) for item in array.reshape(-1)) + return f"##${key}={size}\n{_wrap(body)}" + + return f"##${key}={_format_scalar(value)}" + + +def write_jcampdx(path, records, *, version="4.24", title="Parameter List, synthetic", owner="brukerapi", comments=()): + """Write `records` (a mapping) as a JCAMP-DX parameter file.""" + lines = [ + f"##TITLE={title}", + f"##JCAMPDX={version}", + "##DATATYPE=Parameter Values", + "##ORIGIN=Bruker BioSpin MRI GmbH", + f"##OWNER={owner}", + *(f"$$ {comment}" for comment in comments), + ] + lines.extend(format_record(key, value) for key, value in records.items()) + lines.append("##END=") + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("\n".join(lines) + "\n") + return path + + +def write_binary(path, array, dtype): + """Write `array` as a Bruker binary file (Fortran order, like the vendor).""" + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(np.asarray(array, dtype=dtype).tobytes(order="F")) + return path + + +def axial_orientation(count): + """`count` copies of the identity orientation matrix, as VisuCoreOrientation.""" + return np.tile(np.eye(3).reshape(-1), (count, 1)) + + +def stacked_positions(first, step, count): + """Slice-centre positions advancing by `step` from `first`.""" + return np.asarray(first, dtype=float) + np.outer(np.arange(count), np.asarray(step, dtype=float)) + + +def visu_pars_records( + *, + size=(4, 4), + dim=2, + dim_desc=("spatial", "spatial"), + extent=(40.0, 40.0), + frame_thickness=1.5, + positions=None, + orientations=None, + frame_groups=(("FG_SLICE", 3),), + creator_version="6.0.1", + subject_position="Head_Supine", + word_type="_16BIT_SGN_INT", + slice_packs=None, + slice_pack_distance=None, + slope=1.0, + offset=0.0, + extra=None, +): + """Records of a `visu_pars` describing one reconstructed image series.""" + frame_count = int(np.prod([group[1] for group in frame_groups])) if frame_groups else 1 + positions = np.atleast_2d(np.asarray(positions if positions is not None else stacked_positions((-20.0, -20.0, -3.0), (0.0, 0.0, frame_thickness), frame_count), dtype=float)) + orientations = np.atleast_2d(np.asarray(orientations if orientations is not None else axial_orientation(positions.shape[0]), dtype=float)) + + records = { + "VisuVersion": 3, + "VisuCreator": [""], + "VisuCreatorVersion": [f"<{creator_version}>"], + "VisuCoreFrameCount": frame_count, + "VisuCoreDim": dim, + "VisuCoreSize": np.asarray(size, dtype=int), + "VisuCoreDimDesc": Verbatim(f"( {len(dim_desc)} )\n{' '.join(dim_desc)}"), + "VisuCoreExtent": np.asarray(extent, dtype=float), + "VisuCoreFrameThickness": np.atleast_1d(np.asarray(frame_thickness, dtype=float)), + "VisuCoreUnits": [""] * len(dim_desc), + "VisuCoreOrientation": orientations, + "VisuCorePosition": positions, + "VisuCoreDataMin": np.zeros(frame_count), + "VisuCoreDataMax": np.full(frame_count, 1000.0), + "VisuCoreDataOffs": np.full(frame_count, float(offset)), + "VisuCoreDataSlope": np.full(frame_count, float(slope)), + "VisuCoreFrameType": Verbatim("( 1 )\nMAGNITUDE_IMAGE"), + "VisuCoreWordType": word_type, + "VisuCoreByteOrder": "littleEndian", + "VisuSubjectPosition": subject_position, + "VisuSubjectName": [""], + "VisuStudyNumber": 1, + } + + if slice_packs is not None: + records["VisuCoreSlicePacksDef"] = Verbatim(f"({slice_packs[0]}, {len(slice_packs[1])})") + records["VisuCoreSlicePacksSlices"] = Verbatim(f"( {len(slice_packs[1])} )\n" + " ".join(f"({first}, {count})" for first, count in slice_packs[1])) + if slice_pack_distance is not None: + distances = np.atleast_1d(np.asarray(slice_pack_distance, dtype=float)) + records["VisuCoreSlicePacksSliceDist"] = distances + + if frame_groups: + # A descriptor is (len, groupId, groupComment, valsStart, valsCnt); the last two + # index the VisuGroupDepVals window owned by that group (spec 7.4). + records["VisuFGOrderDescDim"] = len(frame_groups) + descriptors = [f"({group[1]}, <{group[0]}>, <>, {group[2] if len(group) > 2 else 0}, {group[3] if len(group) > 3 else 0})" for group in frame_groups] + records["VisuFGOrderDesc"] = Verbatim(f"( {len(descriptors)} )\n" + " ".join(descriptors)) + + if extra: + records.update(extra) + return records + + +FID_DTYPES = { + "GO_32BIT_SGN_INT": np.dtype("int32"), + "GO_16BIT_SGN_INT": np.dtype("int16"), + "GO_32BIT_FLOAT": np.dtype("float32"), +} + + +def write_fid(directory, acqp, method, *, blocks=1, data=None): + """Write an experiment folder (`acqp`, `method`, `fid`) and return the fid path. + + Without `data` the binary is sized from the records themselves -- + ``ACQ_size[0]`` words per block for ``GO_block_size = continuous`` -- and + filled with distinct values, so a test can tell which samples were used. + """ + directory.mkdir(parents=True, exist_ok=True) + write_jcampdx(directory / "acqp", acqp) + write_jcampdx(directory / "method", method) + + dtype = FID_DTYPES[str(acqp["GO_raw_data_format"])].newbyteorder("<" if str(acqp["BYTORDA"]) == "little" else ">") + if data is None: + block_size = int(np.atleast_1d(acqp["ACQ_size"])[0]) * int(method["PVM_EncNReceivers"]) + data = np.arange(1, block_size * blocks + 1, dtype=dtype) + write_binary(directory / "fid", data, dtype) + return directory / "fid" + + +WORD_TYPES = { + "_8BIT_UNSGN_INT": np.dtype("uint8"), + "_16BIT_SGN_INT": np.dtype("int16"), + "_32BIT_SGN_INT": np.dtype("int32"), + "_32BIT_FLOAT": np.dtype("float32"), +} + + +def write_2dseq(directory, records=None, data=None, **kwargs): + """Write a complete ``pdata`` reconstruction and return the 2dseq path. + + `records` overrides the generated `visu_pars`; any other keyword argument is + forwarded to :func:`visu_pars_records`. + """ + records = {**visu_pars_records(**kwargs), **(records or {})} + directory.mkdir(parents=True, exist_ok=True) + write_jcampdx(directory / "visu_pars", records) + + size = tuple(int(length) for length in np.atleast_1d(records["VisuCoreSize"])) + frames = int(records["VisuCoreFrameCount"]) + dtype = WORD_TYPES[records["VisuCoreWordType"]] + if data is None: + data = np.arange(int(np.prod(size)) * frames, dtype=dtype).reshape(size + (frames,), order="F") + write_binary(directory / "2dseq", data, dtype) + return directory / "2dseq" diff --git a/test/test_api.py b/test/test_api.py new file mode 100644 index 0000000..8b07cfe --- /dev/null +++ b/test/test_api.py @@ -0,0 +1,194 @@ +"""Public API surface: metadata groups, reports, the CLI and split state. + +Synthetic datasets only -- see test/synthetic.py. +""" + +import json +from types import SimpleNamespace + +import numpy as np +import pytest + +from brukerapi.cli import report as cli_report +from brukerapi.cli import split as cli_split +from brukerapi.dataset import LOAD_STAGES, Dataset +from brukerapi.folders import Folder +from brukerapi.splitters import SlicePackageSplitter +from test.synthetic import stacked_positions, write_2dseq, write_jcampdx + + +def study(tmp_path, **kwargs): + """A one-experiment study with a subject file, and its 2dseq path.""" + root = tmp_path / "20200612_094625_study_1_1" + write_jcampdx(root / "subject", {"SUBJECT_id": [""], "SUBJECT_study_nr": 1}) + return write_2dseq(root / "8" / "pdata" / "1", **kwargs) + + +EQUIPMENT = { + "VisuManufacturer": [""], + "VisuAcqSoftwareVersion": [""], + "VisuInstitution": [""], + "VisuStation": [""], + "VisuAcquisitionProtocol": [""], + "VisuAcqEchoTime": 3.5, + "VisuExperimentNumber": 8, + "VisuProcessingNumber": 1, + "VisuUid": ["<2.16.756.5.5.100.1>"], + "VisuInstanceType": "STANDARD_INSTANCE", +} + + +def test_metadata_groups_follow_the_specification(tmp_path): + """Spec 7.1/7.7/7.8: several groups cannot be recognised from a name prefix. + + No parameter is called VisuEquipment*, so that bucket was structurally + always empty, VisuManufacturer/VisuInstitution/VisuStation and the + experiment/processing numbers matched no group at all, and the 7.1 + administration group was missing. Stripping the VisuAcq prefix without + checking for a word boundary also produced `acquisition_protocol` as + `uisition_protocol`. + """ + dataset = Dataset(study(tmp_path, extra=EQUIPMENT), load=LOAD_STAGES["properties"]) + + metadata = dataset.metadata + + assert metadata["visu_equipment"] == { + "manufacturer": "", + "acq_software_version": "", + "institution": "", + "station": "", + } + assert metadata["visu_instance"]["uid"] == "<2.16.756.5.5.100.1>" + assert metadata["visu_instance"]["type"] == "STANDARD_INSTANCE" + assert metadata["visu_instance"]["creator_version"] == "<6.0.1>" + assert metadata["visu_series"]["experiment_number"] == 8 + assert metadata["visu_series"]["processing_number"] == 1 + assert metadata["visu_acq"]["protocol"] == "" + assert metadata["visu_acq"]["echo_time"] == 3.5 + assert "uisition_protocol" not in metadata["visu_acq"] + + +def test_metadata_can_be_exported(tmp_path): + dataset = Dataset(study(tmp_path, extra=EQUIPMENT), load=LOAD_STAGES["properties"]) + + exported = dataset.to_dict(props=["metadata"]) + + # arrays inside the grouped dict used to make json.dump fail outright + assert json.loads(json.dumps(exported))["metadata"]["visu_equipment"]["station"] == "" + + +def test_report_honours_the_requested_format(tmp_path): + """`-f yml` was ignored for a single dataset: report always appended .json.""" + dataset = Dataset(study(tmp_path), load=LOAD_STAGES["properties"]) + out = tmp_path / "out" + out.mkdir() + + dataset.report(out, props=["id"], format_="yml") + dataset.report(props=["id"], format_="yml") + + assert [file.name for file in out.iterdir()] == [f"{dataset.id}.yml"] + assert (dataset.path.parent / f"{dataset.id}.yml").is_file() + with pytest.raises(ValueError, match="unsupported report format"): + dataset.report(props=["id"], format_="csv") + + +def test_cli_report_creates_an_output_folder_that_does_not_exist_yet(tmp_path): + """`-i -o ` matched no branch and exited 0 having done nothing.""" + path = study(tmp_path) + out = tmp_path / "reports" + + cli_report(SimpleNamespace(input=str(path.parents[3]), output=str(out), format="json", props=["id"], verbose=False)) + + assert [file.suffix for file in out.iterdir()] == [".json"] + + +def test_cli_split_writes_to_the_output_folder(tmp_path): + """`bruker split -o out/` parsed path_out and then never passed it on.""" + path = study( + tmp_path, + frame_groups=(("FG_ECHO", 2),), + positions=stacked_positions((-20.0, -20.0, 0.0), (0.0, 0.0, 1.0), 2), + extra={"VisuAcqEchoTime": np.array([11.0, 22.0])}, + ) + out = tmp_path / "split" + + cli_split(SimpleNamespace(path_in=str(path), path_out=str(out), slice_package=False, frame_group="FG_ECHO")) + + assert sorted(file.relative_to(out).as_posix() for file in out.rglob("2dseq")) == ["1_FG_ECHO_0/2dseq", "1_FG_ECHO_1/2dseq"] + + +def test_folder_to_json_serialises_instead_of_recursing(tmp_path): + """`to_json` called itself forever, and report(write=False) yielded strings.""" + study(tmp_path) + folder = Folder(tmp_path) + + exported = json.loads(folder.to_json(props=["id"])) + reported = folder.report(write=False, props=["id"]) + + assert list(exported) == list(reported) + assert all(isinstance(value, dict) for value in reported.values()) + + +def test_slice_package_split_keeps_the_parent_state(tmp_path): + """A package used to be constructed from DEFAULT_STATES only, so the + parent's scale/combine_complex/property_files were dropped.""" + path = study( + tmp_path, + frame_groups=(("FG_SLICE", 4),), + positions=stacked_positions((-20.0, -20.0, -1.5), (0.0, 0.0, 1.0), 4), + slice_packs=(0, [(0, 2), (2, 2)]), + slice_pack_distance=[1.0, 1.0], + slope=2.0, + offset=1.0, + ) + dataset = Dataset(path, scale=False) + + packages = SlicePackageSplitter().split(dataset, write=False) + + assert [package._state["scale"] for package in packages] == [False, False] + assert np.array_equal(packages[0].data, dataset.data[:, :, :2]) + + +def test_procno_files_are_reachable(tmp_path): + """Spec 13/13.2 document methreco and pvmeta; both were missing from + RELATIVE_PATHS, so add_parameter_file raised KeyError.""" + path = study(tmp_path) + write_jcampdx(path.parent / "methreco", {"RecoMethMode": "Default"}) + write_jcampdx(path.parent / "pvmeta", {"RefCopyId": 1}) + + dataset = Dataset(path, load=LOAD_STAGES["parameters"]) + dataset.add_parameter_file("methreco") + dataset.add_parameter_file("pvmeta") + + assert dataset["RecoMethMode"].value == "Default" + assert dataset["RefCopyId"].value == 1 + + +def test_single_slice_datasets_do_not_claim_a_third_spatial_axis(tmp_path): + """The synthetic axis a single-slice dataset gets is not an encoding axis; + labelling it `spatial` made dim_type[encoded_dim:] start with a bogus entry.""" + path = study( + tmp_path, + frame_groups=(("FG_MOVIE", 3),), + positions=np.array([[-20.0, -20.0, 0.0]]), + ) + dataset = Dataset(path) + + assert dataset.is_single_slice + assert dataset.dim_type == ["spatial", "spatial", "frame", ""] + assert len(dataset.dim_type) == dataset.data.ndim + + +def test_random_access_can_select_one_half_of_a_complex_axis(tmp_path): + """Selecting only the real (or only the imaginary) component raised + InvalidDataset instead of returning that component.""" + path = study( + tmp_path, + frame_groups=(("FG_COMPLEX", 2),), + positions=np.array([[-20.0, -20.0, 0.0]]), + ) + full = Dataset(path) + selected = Dataset(path, mmap=True) + + assert np.iscomplexobj(full.data) + assert np.array_equal(selected.data[:, :, 0, 0], np.real(full.data[:, :, 0])) diff --git a/test/test_dataset.py b/test/test_dataset.py index 0fa51b4..67693ab 100644 --- a/test/test_dataset.py +++ b/test/test_dataset.py @@ -13,6 +13,7 @@ from brukerapi.dataset import LOAD_STAGES, Dataset from brukerapi.exceptions import FilterEvalFalse, IncompleteDataset, InvalidDataset, TrajNotLoaded, UnknownAcqSchemeException, UnsupportedDatasetType from brukerapi.schemas import Schema2dseq, SchemaFid, SchemaRawdata +from test.synthetic import write_2dseq, write_jcampdx data = 0 PV51_STUDY_PATH = Path("test/test_data/PV51/0.2H2") @@ -147,11 +148,15 @@ def test_fid_companions_load_as_auxiliary_subdatasets(fid_path, subtypes): for subtype, companion in dataset.fid_companions.items(): assert isinstance(companion, Dataset) assert companion.path == Path(fid_path).with_suffix(f".{subtype}") - assert companion.data.ndim == (dataset.data.ndim if subtype == "orig" else 1) assert np.iscomplexobj(companion.data) assert companion.data.size * 2 * companion.numpy_dtype.itemsize == companion.path.stat().st_size if subtype == "orig": + assert companion.data.ndim == dataset.data.ndim assert companion.data.shape == dataset.data.shape + else: + # spec 3.5: a stream of acquisitions of PVM_DigNp complex points + assert companion.dim_type == ["sample", "acquisition"] + assert companion.data.shape[0] == dataset["PVM_DigNp"].value @pytest.mark.skipif(not PV51_STUDY_PATH.is_dir(), reason="PV51 test data is not available") @@ -250,9 +255,11 @@ def test_3d_radial_stack_of_stars_uses_partition_axis(): "k_space_encode_step_0", "k_space_encode_step_1", "k_space_encode_step_2", + "object", "repetition", "channel", ] + assert len(dataset.dim_type) == len(dataset.k_space) def test_projection_metadata_without_radial_evidence_is_not_inferred_as_radial(tmp_path): @@ -837,8 +844,13 @@ def test_report_uses_path_and_type_fallback_when_dataset_has_no_id(tmp_path): assert reported_paths == [(tmp_path / "23_traj.json", ["scheme_id"])] -@pytest.mark.parametrize("parameter", ["RECO_transposition", "VisuCoreTransposition"]) +@pytest.mark.parametrize("parameter", ["RECO_transposition"]) def test_2dseq_transposition_metadata_does_not_change_stored_data(parameter): + """RECO_transposition records what the reconstruction already did (spec 6.9). + + VisuCoreTransposition is the other kind: it describes how each frame is + *stored*, so it does have to be undone -- see test_frames.py. + """ dataset = SimpleNamespace( path=Path("transposed/2dseq"), encoded_dim=2, @@ -893,9 +905,9 @@ def test_schema_warns_when_dim_type_does_not_describe_layout_rank(): SchemaRawdata(dataset) -def test_fid_object_order_reorders_slice_axis_and_is_reversible(): +def test_fid_object_order_reorders_the_object_axis_and_is_reversible(): class ObjectOrderDataset: - dim_type = ["k_space_encode_step_0", "slice"] + dim_type = ["k_space_encode_step_0", "object"] def __init__(self): self.parameters = {"ACQ_obj_order": SimpleNamespace(value=np.array([0, 2, 4, 1, 3]))} @@ -927,6 +939,8 @@ def test_fid_to_kspace_matches_decoded_data_and_supports_bart_layout(): "k_space_encode_step_2": 2, "channel": 3, "repetition": 9, + "echo": 10, + "object": 13, "slice": 13, "average": 14, } @@ -1081,6 +1095,13 @@ def test_data_load(test_data): if np.array_equal(legacy_plane, reference): return + if actual.size > reference.size and np.isin(reference.reshape(-1), actual.reshape(-1)).all(): + # An EPSI cache predates the fix that stops discarding all but one + # segment of every block (spec 3.1), so it holds a fraction of the + # samples the reader now returns. What it does cover must still be + # there. + return + # Other caches predate phase-line reordering and contain the same # complete FID values in a different logical order. assert actual.size == reference.size @@ -1117,3 +1138,27 @@ def test_data_save(test_data, tmp_path, WRITE_TOLERANCE): # TODO since the id property of the 2dseq dataset type relies on the name of the experiment folder, # which is a problem when the dataset is writen to the test folder, solution might be to delete the id key here # assert d_test.to_dict() == test_data[1] + + +def test_add_parameters_loads_the_named_parameter_file(tmp_path): + """`add_parameters=` is the documented way to pull in the study `subject`. + + It used to be stored as an inert state key, so nothing was loaded and no + report carried subject identity (spec 9 / 7.5). + """ + study = tmp_path / "20200612_094625_study_1_1" + write_jcampdx( + study / "subject", + { + "SUBJECT_id": [""], + "SUBJECT_study_nr": 2, + "SUBJECT_name_string": [""], + }, + ) + path = write_2dseq(study / "8" / "pdata" / "1") + + dataset = Dataset(path, add_parameters=["subject"], load=LOAD_STAGES["properties"]) + + assert "subject" in dataset.parameters + assert dataset["SUBJECT_id"].value == "" + assert dataset.metadata["subject"]["id"] == "" diff --git a/test/test_folders.py b/test/test_folders.py index 37d0c4c..599f809 100644 --- a/test/test_folders.py +++ b/test/test_folders.py @@ -2,10 +2,12 @@ import pickle from pathlib import Path +import numpy as np import pytest from brukerapi.dataset import Dataset from brukerapi.folders import DEFAULT_DATASET_STATE, Folder, Processing, Study, TypeFilter +from test.synthetic import Verbatim, write_binary, write_jcampdx PV51_STUDY_PATH = Path("test/test_data/PV51/0.2H2") @@ -146,3 +148,40 @@ def test_study_get_dataset_returns_fid_and_2dseq(): with fid, reconstructed: assert fid.data.size > 0 assert reconstructed.data.size > 0 + + +def test_study_get_dataset_falls_back_to_rawdata_when_there_is_no_fid(tmp_path): + """Spec 13.1: ParaVision 360 writes no file named `fid`. + + Raw data lives in rawdata.jobN, so an unconditional exp["fid"] makes + Study.get_dataset unusable for every PV360 study. + """ + study = tmp_path / "20250814_100419_std_PV360_1_1" + write_jcampdx(study / "subject", {"SUBJECT_id": [""], "SUBJECT_study_nr": 1}) + experiment = study / "26" + write_jcampdx( + experiment / "acqp", + { + "ACQ_word_size": "_32_BIT", + "ACQ_sw_version": "", + "BYTORDA": "little", + "ACQ_jobs": Verbatim("( 1 )\n(8, 20, 5, 4, 101, 178571.4, 4, 1, )"), + }, + ) + write_jcampdx(experiment / "method", {"Method": "", "PVM_EncNReceivers": 1}) + write_binary(experiment / "rawdata.job0", np.arange(8 * 1 * 4, dtype=""], "SUBJECT_study_nr": 1}) + write_jcampdx(study / "26" / "acqp", {"ACQ_scan_name": [""]}) + + with pytest.raises(KeyError, match="fid"): + Study(study).get_dataset("26") diff --git a/test/test_frames.py b/test/test_frames.py new file mode 100644 index 0000000..2056d1f --- /dev/null +++ b/test/test_frames.py @@ -0,0 +1,96 @@ +"""Frame layout and frame-group metadata: FILE_FORMAT.md 7.2 and 7.4. + +Synthetic datasets only -- see test/synthetic.py. +""" + +import numpy as np + +from brukerapi.dataset import Dataset +from test.synthetic import Verbatim, stacked_positions, write_2dseq + +IMAGE = np.arange(12, dtype="int16").reshape(3, 4) + + +def transposed_dataset(tmp_path, size=(3, 4), image=None, **state): + """Two frames of the same image, the second stored with its axes exchanged.""" + image = IMAGE if image is None else image + stored = np.concatenate([image.flatten(order="F"), image.T.flatten(order="F")]) + path = write_2dseq( + tmp_path / "5" / "pdata" / "1", + size=size, + frame_groups=(("FG_SLICE", 2),), + positions=stacked_positions((-20.0, -20.0, 0.0), (0.0, 0.0, 1.0), 2), + data=stored.reshape(tuple(size) + (2,), order="F"), + extra={"VisuCoreTransposition": np.array([0, 1])}, + ) + return Dataset(path, **state) + + +def test_a_transposed_frame_is_read_in_its_stored_shape(tmp_path): + """Spec 7.2: a nonzero VisuCoreTransposition exchanges two stored dimensions. + + Reshaping such a frame with VisuCoreSize interleaves its rows, which turns + an image into diagonal-stripe noise without any error. + """ + dataset = transposed_dataset(tmp_path) + + assert np.array_equal(dataset.data[..., 0], IMAGE) + assert np.array_equal(dataset.data[..., 1], IMAGE) + + +def test_a_square_transposed_frame_is_delivered_unchanged(tmp_path): + """An exchange between two equal-length dimensions does not move any pixel. + + Those frames measure as already consistent with VisuCoreOrientation, so + transposing them would introduce the error instead of fixing one. + """ + image = np.arange(16, dtype="int16").reshape(4, 4) + dataset = transposed_dataset(tmp_path, size=(4, 4), image=image) + + assert np.array_equal(dataset.data[..., 0], image) + assert np.array_equal(dataset.data[..., 1], image.T) + + +def test_writing_restores_the_stored_frame_layout(tmp_path): + dataset = transposed_dataset(tmp_path) + original = dataset.path.read_bytes() + + dataset.write(tmp_path / "out" / "2dseq") + + assert (tmp_path / "out" / "2dseq").read_bytes() == original + assert np.array_equal(Dataset(tmp_path / "out" / "2dseq").data, dataset.data) + + +def test_random_access_indexes_transposition_by_absolute_frame(tmp_path): + dataset = transposed_dataset(tmp_path, mmap=True) + + assert np.array_equal(dataset.data[:, :, 1], IMAGE) + + +def test_frame_group_values_follow_the_descriptor_window(tmp_path): + """Spec 7.4: the (valsStart, valsCnt) of a descriptor owns its dependents. + + VisuGroupDepVals[k][1] is a start index into the dependent *parameter* + array, not an index into VisuFGOrderDesc; reading it as the latter put + every dependent parameter on frame group 0, and a size-matching rescue + hides that only while the two groups have different lengths. + """ + positions = stacked_positions((-20.0, -20.0, -2.0), (0.0, 0.0, 2.0), 3) + path = write_2dseq( + tmp_path / "27" / "pdata" / "1", + frame_groups=(("FG_ECHO", 3, 0, 1), ("FG_SLICE", 3, 1, 2)), + positions=positions, + extra={ + "VisuAcqEchoTime": np.array([5.0, 10.0, 15.0]), + "VisuGroupDepVals": Verbatim("( 3 )\n(, 0) (, 0) (, 0)"), + }, + ) + dataset = Dataset(path) + + values = dataset.frame_group_values + + assert dataset.dim_type == ["spatial", "spatial", "", ""] + assert values["VisuAcqEchoTime"].shape == (1, 1, 3, 1) + assert values["VisuCorePosition"].shape == (1, 1, 1, 3, 3) + assert values["VisuCoreOrientation"].shape == (1, 1, 1, 3, 9) + assert np.array_equal(np.squeeze(values["VisuCorePosition"]), positions) diff --git a/test/test_geometry.py b/test/test_geometry.py new file mode 100644 index 0000000..6ec2156 --- /dev/null +++ b/test/test_geometry.py @@ -0,0 +1,233 @@ +"""Image geometry: FILE_FORMAT.md 7.2 (VisuCore), 7.10 (slice packages), 12 (frames). + +Every dataset here is synthetic, built by test/synthetic.py from the parameter +shapes of real ParaVision files, so the geometry rules are exercised without any +vendor data. +""" + +import numpy as np +import pytest + +from brukerapi.dataset import LOAD_STAGES, Dataset +from brukerapi.exceptions import UnsupportedDatasetType +from test.synthetic import axial_orientation, stacked_positions, write_2dseq + +SAGITTAL_ORIENTATION = np.array([0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0]) + + +def load(tmp_path, **kwargs): + path = write_2dseq(tmp_path / "9" / "pdata" / "1", **kwargs) + return Dataset(path, load=LOAD_STAGES["properties"]) + + +def test_affine_maps_the_first_voxel_onto_visucoreposition(tmp_path): + positions = stacked_positions((-20.0, -17.0, -3.0), (0.0, 0.0, 1.5), 5) + dataset = load(tmp_path, frame_groups=(("FG_SLICE", 5),), positions=positions) + + origin = dataset.affine @ np.array([0.0, 0.0, 0.0, 1.0]) + + assert np.allclose(origin[:3], positions[0]) + + +def test_affine_maps_every_slice_index_onto_its_own_position(tmp_path): + positions = stacked_positions((-20.0, -20.0, -3.0), (0.0, 0.0, 1.5), 5) + dataset = load(tmp_path, frame_groups=(("FG_SLICE", 5),), positions=positions) + + for index, position in enumerate(positions): + assert np.allclose((dataset.affine @ np.array([0.0, 0.0, float(index), 1.0]))[:3], position) + + +def test_affine_slice_column_keeps_the_direction_of_a_descending_stack(tmp_path): + positions = stacked_positions((-20.0, -20.0, 5.0), (0.0, 0.0, -2.5), 4) + dataset = load(tmp_path, frame_groups=(("FG_SLICE", 4),), positions=positions) + + # The stack advances against the third row of the orientation matrix, so a + # magnitude-only spacing would silently reverse it. + assert np.allclose(dataset.affine[:3, 2], [0.0, 0.0, -2.5]) + assert np.linalg.det(dataset.affine) != 0.0 + + +def test_affine_is_the_same_whichever_way_the_subject_lies(tmp_path): + positions = stacked_positions((-15.0, -17.0, -3.0), (0.0, 0.0, 1.0), 3) + supine = load(tmp_path / "supine", frame_groups=(("FG_SLICE", 3),), positions=positions, subject_position="Head_Supine") + prone = load(tmp_path / "prone", frame_groups=(("FG_SLICE", 3),), positions=positions, subject_position="Head_Prone") + + # VisuCoreOrientation/VisuCorePosition are already in the DICOM patient frame + # (spec 7.2/12); re-applying VisuSubjectPosition mirrors x and y. + assert np.allclose(supine.affine, prone.affine) + assert np.allclose(np.diag(supine.affine)[:2], [10.0, 10.0]) + assert np.allclose(supine.affine[:3, 3], positions[0]) + + +def test_affine_of_a_3d_volume_uses_the_third_extent(tmp_path): + dataset = load( + tmp_path, + dim=3, + dim_desc=("spatial", "spatial", "spatial"), + size=(4, 4, 8), + extent=(40.0, 40.0, 16.0), + frame_groups=(), + positions=np.array([[-20.0, -20.0, -8.0]]), + orientations=axial_orientation(1), + ) + + assert np.allclose(np.diag(dataset.affine)[:3], [10.0, 10.0, 2.0]) + assert np.allclose(dataset.affine[:3, 3], [-20.0, -20.0, -8.0]) + + +def test_affine_refuses_frames_that_are_not_purely_spatial(tmp_path): + dataset = load( + tmp_path, + dim=3, + dim_desc=("spectroscopic", "spatial", "spatial"), + size=(64, 4, 4), + extent=(1.0, 40.0, 40.0), + frame_groups=(), + positions=np.array([[-20.0, -20.0, 0.0]]), + orientations=axial_orientation(1), + ) + + # Spec 7.2: such scans must be detected and skipped, not handed a plausible + # identity affine at the scanner origin. + with pytest.raises(UnsupportedDatasetType, match="rather than purely spatial"): + _ = dataset.affine + + +def test_report_carries_the_affine_and_omits_it_for_spectroscopy(tmp_path): + image = load(tmp_path / "image", frame_groups=(("FG_SLICE", 3),)) + spectroscopy = load( + tmp_path / "spectroscopy", + dim=1, + dim_desc=("spectroscopic",), + size=(64,), + extent=(1.0,), + frame_groups=(), + positions=np.array([[0.0, 0.0, 0.0]]), + orientations=axial_orientation(1), + ) + + assert np.allclose(image.to_dict()["affine"], image.affine) + assert "affine" not in spectroscopy.to_dict() + + +def test_slice_spacing_is_centre_to_centre_not_thickness_plus_distance(tmp_path): + dataset = load( + tmp_path, + frame_groups=(("FG_SLICE", 5),), + positions=stacked_positions((-20.0, -20.0, -3.0), (0.0, 0.0, 1.5), 5), + frame_thickness=1.5, + slice_packs=(0, [(0, 5)]), + slice_pack_distance=1.5, + ) + + # Spec 7.10: VisuCoreSlicePacksSliceDist is the inter-slice distance, so it is + # not additive with VisuCoreFrameThickness. + assert np.allclose(dataset.resolution, [10.0, 10.0, 1.5]) + assert np.isclose(np.linalg.norm(dataset.affine[:3, 2]), 1.5) + + +def test_slice_spacing_of_a_non_axial_stack_is_not_measured_along_z(tmp_path): + positions = stacked_positions((0.0, -20.0, -20.0), (-1.0, 0.0, 0.0), 4) + dataset = load( + tmp_path, + creator_version="5.1", + frame_groups=(("FG_SLICE", 4),), + positions=positions, + orientations=np.tile(SAGITTAL_ORIENTATION, (4, 1)), + ) + + # A sagittal stack advances in x; taking the z component alone collapses the + # volume and makes the affine singular. + assert np.isclose(dataset.resolution[2], 1.0) + assert np.linalg.det(dataset.affine) != 0.0 + assert np.allclose(dataset.affine[:3, 2], [-1.0, 0.0, 0.0]) + + +def test_single_slice_spacing_falls_back_to_the_slice_distance(tmp_path): + dataset = load( + tmp_path, + frame_groups=(("FG_SLICE", 1),), + positions=np.array([[-20.0, -20.0, 0.0]]), + orientations=axial_orientation(1), + frame_thickness=0.8, + slice_packs=(0, [(0, 1)]), + slice_pack_distance=2.0, + ) + + assert np.isclose(np.linalg.norm(dataset.affine[:3, 2]), 2.0) + assert np.isclose(dataset.resolution[2], 2.0) + + +def test_single_slice_spacing_falls_back_to_the_frame_thickness(tmp_path): + dataset = load( + tmp_path, + frame_groups=(("FG_SLICE", 1),), + positions=np.array([[-20.0, -20.0, 0.0]]), + orientations=axial_orientation(1), + frame_thickness=0.8, + ) + + assert np.isclose(np.linalg.norm(dataset.affine[:3, 2]), 0.8) + + +def test_each_slice_package_gets_its_own_affine(tmp_path): + positions = np.vstack( + [ + stacked_positions((-20.0, -20.0, -3.0), (0.0, 0.0, 1.5), 5), + stacked_positions((0.0, -20.0, -20.0), (-1.0, 0.0, 0.0), 3), + ] + ) + orientations = np.vstack([axial_orientation(5), np.tile(SAGITTAL_ORIENTATION, (3, 1))]) + dataset = load( + tmp_path, + frame_groups=(("FG_SLICE", 8),), + positions=positions, + orientations=orientations, + slice_packs=(0, [(0, 5), (5, 3)]), + slice_pack_distance=[1.5, 1.0], + ) + + assert dataset.slice_packages_index() == [(0, 5), (5, 3)] + assert np.allclose(dataset.affine_of_package(0)[:3, 3], positions[0]) + assert np.allclose(dataset.affine_of_package(0)[:3, 2], [0.0, 0.0, 1.5]) + assert np.allclose(dataset.affine_of_package(1)[:3, 3], positions[5]) + assert np.allclose(dataset.affine_of_package(1)[:3, 2], [-1.0, 0.0, 0.0]) + + with pytest.warns(RuntimeWarning, match="multiple slice packages"): + assert np.allclose(dataset.affine, dataset.affine_of_package(0)) + + +def test_slice_packages_are_inferred_from_orientation_when_pv51_omits_them(tmp_path): + positions = np.vstack( + [ + stacked_positions((-20.0, -20.0, -3.0), (0.0, 0.0, 1.5), 4), + stacked_positions((0.0, -20.0, -20.0), (-1.0, 0.0, 0.0), 2), + ] + ) + orientations = np.vstack([axial_orientation(4), np.tile(SAGITTAL_ORIENTATION, (2, 1))]) + dataset = load( + tmp_path, + creator_version="5.1", + frame_groups=(("FG_SLICE", 6),), + positions=positions, + orientations=orientations, + ) + + # PV5.1 defines none of the slice-package parameters (spec 7.10). + assert "VisuCoreSlicePacksSlices" not in dataset + assert dataset.slice_packages_index() == [(0, 4), (4, 2)] + + +def test_geometry_follows_the_data_when_frames_are_stored_in_reverse(tmp_path): + positions = stacked_positions((-20.0, -20.0, -3.0), (0.0, 0.0, 1.5), 4) + dataset = load( + tmp_path, + frame_groups=(("FG_SLICE", 4),), + positions=positions, + extra={"VisuCoreDiskSliceOrder": "disk_reverse_slice_order"}, + ) + + # Schema2dseq flips the slice axis for disk_reverse_slice_order, so index 0 of + # the data array is the last frame on disk (spec 7.2/7.3). + assert np.allclose(dataset.affine[:3, 3], positions[-1]) + assert np.allclose(dataset.affine[:3, 2], [0.0, 0.0, -1.5]) diff --git a/test/test_jcampdx.py b/test/test_jcampdx.py index 9f39837..f7f316a 100644 --- a/test/test_jcampdx.py +++ b/test/test_jcampdx.py @@ -2,12 +2,7 @@ import pytest from brukerapi.exceptions import InvalidJcampdxFile -from brukerapi.jcampdx import ( - JCAMPDX, - DataParameter, - GenericParameter, - GeometryParameter, -) +from brukerapi.jcampdx import JCAMPDX, DataParameter, GenericParameter # @pytest.mark.skip(reason="in progress") @@ -184,13 +179,36 @@ def test_jcampdx_data_parameter_setter_round_trip(tmp_path): assert np.array_equal(JCAMPDX(output).get_value("POINTS"), expected) -def test_geometry_parameter_setter_stores_raw_value(): - parameter = GeometryParameter("##$GEOMETRY", "", "old", "4.24") +def test_a_geometry_object_is_an_ordinary_nested_struct(tmp_path): + """Spec 2.2/2.3 give `(((...)...)...)` records no special status. - parameter.value = "(((1, 0, 0), (0, 1, 0), (0, 0, 1)), (1, 2, 3))" + Routing them to a parameter class whose value is None hid the rotation + matrix, offset and axis labels that 5.4/12 make load-bearing, and turned + `get_array` into an AttributeError. + """ + path = tmp_path / "method" + path.write_text( + "##TITLE=Parameter List\n" + "##JCAMPDX=4.24\n" + "##DATATYPE=Parameter Values\n" + "##$PVM_SliceGeo=( 2 )\n" + "(((1 0 0 0 -1 0 0 0 -1, 0 0 0), 25 25 9, <+R;read> <+P;phase> <+S;slice>, 0), \n" + "5, 1, 256, 1, 0, No) (((0 -1 0 0 0 -1 1 0 0, 0 0 0), 25 25 9, <+P;phase> \n" + "<+R;read> <+S;slice>, 1), 5, 1, 256, 1, 0, No)\n" + "##END=\n" + ) + + parameter = JCAMPDX(path)["PVM_SliceGeo"] + first, second = parameter.value + rotation, offset = first[0][0] - assert parameter.val_str == "(((1, 0, 0), (0, 1, 0), (0, 0, 1)), (1, 2, 3))" - assert str(parameter).endswith(parameter.val_str) + assert parameter.size == (2,) + assert np.array_equal(rotation, [1, 0, 0, 0, -1, 0, 0, 0, -1]) + assert np.array_equal(offset, [0, 0, 0]) + assert np.array_equal(first[0][1], [25, 25, 9]) + assert np.array_equal(first[0][2], ["<+R;read>", "<+P;phase>", "<+S;slice>"]) + assert first[1:] == [5, 1, 256, 1, 0, "No"] + assert np.array_equal(second[0][0][0], [0, -1, 0, 0, 0, -1, 1, 0, 0]) def test_generic_parameter_from_values_preserves_constructor_fields(): @@ -211,17 +229,21 @@ def test_parameter_subclass_constructors_support_named_fields(): assert np.array_equal(data.value, np.array([[1, 2], [3, 4]])) -def test_generic_parameter_treats_newlines_as_token_separators(): - parameter = GenericParameter("##$VALUES", "( 4 )", "1 2\n3 4", "5.0") +def test_generic_parameter_joins_a_wrapped_value_block(): + # The wrap is inserted after the separating space, which stays on the + # left-hand line, so joining is a pure newline deletion (spec 2.2). + parameter = GenericParameter("##$VALUES", "( 4 )", "1 2 \n3 4", "5.0") assert np.array_equal(parameter.value, np.array([1, 2, 3, 4])) -def test_parse_value_normalizes_newlines_between_parallel_lists(): +def test_parse_value_joins_a_wrap_between_parallel_lists(): + # ParaVision wraps by inserting a newline, so the space that separates two + # struct tuples is still there on the left-hand line (spec 2.2). value = GenericParameter( "##$VALUES", "( 2 )", - "(1, )\n(2, )", + "(1, ) \n(2, )", "5.0", ).value @@ -352,7 +374,7 @@ def test_jcampdx_round_trip_preserves_comments_and_end_marker(tmp_path): assert "$$ comment attached to VALUE\n$$ second comment\n##$VALUE=1" in serialized assert "$$ comment attached to OTHER\n##$OTHER=2" in serialized - assert serialized.endswith("##END=") + assert serialized.endswith("##END=\n") assert JCAMPDX(output).get_value("VALUE") == 1 assert JCAMPDX(output).get_value("OTHER") == 2 @@ -374,3 +396,251 @@ def test_jcampdx_version_detection_is_label_based_within_header(tmp_path): assert jcamp.version == "4.24" assert jcamp.get_value("VALUE") == 42 + + +def test_a_wrap_inside_a_string_does_not_invent_a_space(tmp_path): + """Spec 2.2: the wrap inserts a newline, so undoing it must delete only that. + + ParaVision hard-wraps near column 80 wherever the limit falls, including in + the middle of a `<...>` string. Replacing the break with a space changes the + value -- an RF pulse shape, a coil serial number -- for exactly the datasets + whose wrap landed mid-token. + """ + path = tmp_path / "acqp" + path.write_text( + "##TITLE=Parameter List\n" + "##JCAMPDX=4.24\n" + "##DATATYPE=Parameter Values\n" + "##$ACQ_coil_elements=( 2 )\n" + "(0, <1H>, txrx) (0, <1H\n" + ">, txrx)\n" + "##$ExcPulse=( 2 )\n" + "(1000, )\n" + "##END=\n" + ) + + parameters = JCAMPDX(path) + + assert [element[1] for element in parameters["ACQ_coil_elements"].value] == ["<1H>", "<1H>"] + assert parameters["ExcPulse"].value[1] == "" + + +def test_a_wrap_at_a_space_keeps_exactly_one_space(tmp_path): + path = tmp_path / "acqp" + path.write_text( + "##TITLE=Parameter List\n" + "##JCAMPDX=4.24\n" + "##DATATYPE=Parameter Values\n" + "##$ACQ_size=( 4 )\n" + "128 64 \n" + "32 16\n" + "##END=\n" + ) + + assert np.array_equal(JCAMPDX(path)["ACQ_size"].value, np.array([128, 64, 32, 16])) + + +def test_wrap_lines_inserts_breaks_without_deleting_characters(): + tokens = "##$LONG=" + " ".join(["1234567890"] * 20) + unbreakable = "##$BLOB=" + "x" * 200 + + for line in (tokens, unbreakable): + wrapped = JCAMPDX.wrap_lines(line) + + assert wrapped.replace("\n", "") == line + assert all(len(part) <= 78 for part in wrapped.splitlines()) + + +def test_write_does_not_wrap_comment_records(tmp_path): + """Spec 2.1: a `$$` line is a record of its own. + + Wrapping one leaves a tail that no longer starts with `$$`, so re-reading it + appends the tail to the preceding parameter's value. + """ + comment = "$$ /opt/PV6.0.1/data/imag/20200913_160003_In_situ_experiment_with_a_very_long_name/74/acqp" + path = tmp_path / "acqp" + path.write_text( + "##TITLE=Parameter List\n" + "##JCAMPDX=4.24\n" + "##DATATYPE=Parameter Values\n" + "##OWNER=imag\n" + f"{comment}\n" + "##$ACQ_size=( 2 )\n" + "128 64\n" + "##END=\n" + ) + + original = JCAMPDX(path) + original.write(tmp_path / "acqp.written") + written = (tmp_path / "acqp.written").read_text() + + assert comment in written.splitlines() + assert JCAMPDX(tmp_path / "acqp.written")["OWNER"].value == "imag" + + +def test_write_reproduces_every_record_and_is_a_fixed_point(tmp_path): + path = tmp_path / "visu_pars" + path.write_text( + "##TITLE=Parameter List, ParaVision 6.0.1\n" + "##JCAMPDX=4.24\n" + "##DATATYPE=Parameter Values\n" + "##ORIGIN=Bruker BioSpin MRI GmbH\n" + "##OWNER=imag\n" + "$$ /opt/PV6.0.1/data/imag/20200913_160003_In_situ_experiment_with_a_long_name/74/pdata/1/visu_pars\n" + "##$VisuCoreSize=( 2 )\n" + "256 256\n" + "$$ @vis= VisuCoreFrameCount VisuCoreDim VisuCoreSize VisuCoreDimDesc\n" + "##$VisuCoreDataSlope=( 4 )\n" + "0.000739417036989118 0.000739417036989118 0.000739417036989118 \n" + "0.000739417036989118\n" + "##$VisuSubjectPosition=Head_Supine\n" + "$$ @vis= VisuSubjectPosition VisuSeriesTypeId VisuSeries VisuCoilReceive\n" + "##END=\n" + "$$ File finished by PARX at 2020-09-13 16:00:05.361 +0200\n" + ) + + original = JCAMPDX(path) + original.write(tmp_path / "first") + first = JCAMPDX(tmp_path / "first") + first.write(tmp_path / "second") + + assert set(first.params) == set(original.params) + for key, parameter in original.params.items(): + assert np.array_equal(first.params[key].value, parameter.value) + assert (tmp_path / "first").read_text() == (tmp_path / "second").read_text() + assert (tmp_path / "first").read_text() == path.read_text() + + +def test_write_keeps_the_comments_around_the_end_marker(tmp_path): + path = tmp_path / "acqp" + path.write_text( + "##TITLE=Parameter List\n" + "##JCAMPDX=4.24\n" + "##DATATYPE=Parameter Values\n" + "##$ACQ_size=( 2 )\n" + "128 64\n" + "$$ @vis= ACQ_size ACQP\n" + "##END=\n" + "$$ File finished by PARX at 2020-06-12 10:46:05.429 +0200\n" + ) + + JCAMPDX(path).write(tmp_path / "written") + written = (tmp_path / "written").read_text().splitlines() + + assert written[-3:] == [ + "$$ @vis= ACQ_size ACQP", + "##END=", + "$$ File finished by PARX at 2020-06-12 10:46:05.429 +0200", + ] + + +def test_escaped_delimiters_inside_a_string_are_not_delimiters(tmp_path): + """Spec 2.2: `\\<` and `\\>` are escaped characters, not string delimiters. + + ParaVision writes them in the reco filter-graph descriptors. Matching + `<[^<>]*>` and keeping only what matched cut every descriptor short and + threw the rest of the record away. + """ + path = tmp_path / "reco" + path.write_text( + "##TITLE=Parameter List\n" + "##JCAMPDX=4.24\n" + "##DATATYPE=Parameter Values\n" + "##$RecoStageEdges=( 2 )\n" + "(, 0, S>) (, 0, CAST0>)\n" + "##$RecoStageNodes=( 1 )\n" + "(, 0, ;}>)\n" + "##END=\n" + ) + + parameters = JCAMPDX(path) + + assert parameters["RecoStageEdges"].value == [ + ["", 0, "S>"], + ["", 0, "CAST0>"], + ] + assert parameters["RecoStageNodes"].value[2] == ";}>" + + +def test_a_nested_struct_keeps_its_inner_tuple(tmp_path): + """Spec 2.3: struct arrays nest, so the splitter has to track parentheses.""" + path = tmp_path / "configscan" + path.write_text( + "##TITLE=Parameter List\n" + "##JCAMPDX=4.24\n" + "##DATATYPE=Parameter Values\n" + "##$AdjKnownList=( 1 )\n" + "((EMPTY, , <>, on_demand, HANDLE_ACQUISITION), No, No)\n" + "##END=\n" + ) + + assert JCAMPDX(path)["AdjKnownList"].value == [ + ["EMPTY", "", "<>", "on_demand", "HANDLE_ACQUISITION"], + "No", + "No", + ] + + +def test_a_trailing_backslash_in_a_string_is_content_not_an_escape(tmp_path): + """`<\\>` is how ParaVision writes an empty study description. + + Reading the backslash as an escape leaves the string unterminated, which + would drop the record. + """ + path = tmp_path / "visu_pars" + path.write_text( + "##TITLE=Parameter List\n" + "##JCAMPDX=4.24\n" + "##DATATYPE=Parameter Values\n" + "##$VisuStudyDescription=( 2048 )\n" + "<\\\n" + ">\n" + "##END=\n" + ) + + assert JCAMPDX(path)["VisuStudyDescription"].value == "<\\>" + + +def test_the_last_parameter_survives_a_file_without_an_end_marker(tmp_path): + """Spec 2.1 shows ##END= but warns the file may not end there. + + The record stream was split and the last chunk dropped unconditionally, so + a truncated or third-party-written file lost its final parameter with no + exception and no warning. + """ + path = tmp_path / "visu_pars" + path.write_text( + "##TITLE=Parameter List\n" + "##JCAMPDX=4.24\n" + "##DATATYPE=Parameter Values\n" + "##$VisuCoreDim=2\n" + "##$VisuRespSynchUsed=No\n" + ) + + parameters = JCAMPDX(path) + + assert parameters["VisuRespSynchUsed"].value == "No" + assert parameters["VisuCoreDim"].value == 2 + + +def test_a_malformed_record_raises_a_typed_error(tmp_path): + """Spec 2.3: an element count that does not fill the declared size is a + diagnosable condition, not a raw numpy ValueError.""" + path = tmp_path / "acqp" + path.write_text( + "##TITLE=Parameter List\n" + "##JCAMPDX=4.24\n" + "##DATATYPE=Parameter Values\n" + "##$SHORT=( 3, 3 )\n" + "1 2 3 4\n" + "##$BADSIZE=( a )\n" + "1 2\n" + "##END=\n" + ) + parameters = JCAMPDX(path) + + with pytest.raises(InvalidJcampdxFile, match="do not fill the declared size"): + _ = parameters["SHORT"].value + with pytest.raises(InvalidJcampdxFile, match="is not an integer"): + _ = parameters["BADSIZE"].size diff --git a/test/test_paths.py b/test/test_paths.py index b14fee9..dbabc52 100644 --- a/test/test_paths.py +++ b/test/test_paths.py @@ -15,6 +15,7 @@ from brukerapi.folders import Experiment, Folder from brukerapi.jcampdx import JCAMPDX from brukerapi.paths import as_path, file_size, listdir, traverse, with_suffix +from test.synthetic import write_binary, write_jcampdx VISU_PARS = """##TITLE=Parameter List, ParaVision 6.0.1 ##JCAMPDX=4.24 @@ -119,3 +120,48 @@ def test_path_helpers_work_for_both_kinds(study_dir, study_zip): assert traverse(proc, "../../acqp").name == "acqp" assert traverse(proc, "../../acqp").exists() assert with_suffix(proc / "2dseq", ".job0").name == "2dseq.job0" + + +TRAJ_PROJECTIONS = 4 +TRAJ_SAMPLES = 5 +TRAJ = np.arange(2 * TRAJ_SAMPLES * TRAJ_PROJECTIONS, dtype="f8").reshape((2, TRAJ_SAMPLES, TRAJ_PROJECTIONS), order="F") + + +@pytest.fixture +def radial_zip(tmp_path): + """A radial experiment -- acqp, method and traj -- inside an archive.""" + experiment = tmp_path / "radial" / "23" + write_jcampdx( + experiment / "acqp", + { + "ACQ_dim": 2, + "PULPROG": "", + "NPro": TRAJ_PROJECTIONS, + "GO_raw_data_format": "GO_32BIT_SGN_INT", + "BYTORDA": "little", + }, + ) + write_jcampdx(experiment / "method", {"Method": "", "PVM_EncNReceivers": 1}) + write_binary(experiment / "traj", TRAJ, np.dtype("f8")) + + archive = tmp_path / "radial.zip" + with zipfile.ZipFile(archive, "w") as zf: + for path in sorted((tmp_path / "radial").rglob("*")): + if path.is_file(): + zf.write(path, str(path.relative_to(tmp_path / "radial"))) + return zipfile.Path(zipfile.ZipFile(archive)) + + +def test_traj_reads_from_an_archive(radial_zip, tmp_path): + """A traj sized itself with os.stat, which an archive member cannot answer. + + The failure was a bare TypeError, and a radial fid read from an archive + silently lost its trajectory: the error was downgraded to a warning and + dataset.traj then raised TrajNotLoaded as if none existed. + """ + from_zip = Dataset(radial_zip / "23" / "traj") + from_dir = Dataset(tmp_path / "radial" / "23" / "traj") + + assert from_zip.data.shape == (2, TRAJ_SAMPLES, TRAJ_PROJECTIONS) + assert np.array_equal(from_zip.data, from_dir.data) + assert np.array_equal(from_zip.data, TRAJ) diff --git a/test/test_property_configs.py b/test/test_property_configs.py index 9392508..d2c55ca 100644 --- a/test/test_property_configs.py +++ b/test/test_property_configs.py @@ -27,34 +27,39 @@ def test_fid_dtype_and_block_layout_are_not_version_gated(): standard_block_size_branches = config["block_size"][:2] assert all(not _contains_sw_version_gate(branch["conditions"]) for branch in standard_block_size_branches) - standard_acq_length_branches = config["acq_length"][1:4] + standard_acq_length_branches = config["acq_length"][1:3] assert all(not _contains_sw_version_gate(branch["conditions"]) for branch in standard_acq_length_branches) -def test_fid_common_scheme_detection_is_not_version_gated(): +def test_fid_scheme_detection_is_not_version_gated(): config = _load_config("properties_fid_core.json") for branch in config["scheme_id"]: - if branch["cmd"] == "'SPIRAL'": - continue assert not _contains_sw_version_gate(branch["conditions"]) + assert "pv_version" not in " ".join(str(condition) for condition in branch["conditions"]) -def test_rawdata_standard_dtype_is_not_version_gated_but_pv360_v1_branch_remains(): +def test_rawdata_standard_dtype_is_not_version_gated_but_pv360_branch_remains(): config = _load_config("properties_rawdata_core.json") standard_dtype_branches = config["numpy_dtype"][:6] assert all(not _contains_sw_version_gate(branch["conditions"]) for branch in standard_dtype_branches) - assert _contains_sw_version_gate(config["numpy_dtype"][6]["conditions"]) + assert "@pv_version.split('.')[0]=='360'" in config["numpy_dtype"][6]["conditions"] -def test_rawdata_pv360_v3_uses_prefix_matching(): +def test_rawdata_pv360_branches_match_on_the_parsed_major_version(): + """Spec 7.1/13: an exact ACQ_sw_version string misses unlisted point releases. + + Matching `` or a `' or #ACQ_sw_version.value.startswith('", + "NI": 1, + "NR": 1, + }, + method={ + "Method": "", + "NSegments": EPSI_SEGMENTS, + "PVM_DigNp": EPSI_DIGITIZED, + "PVM_EncMatrix": np.array([EPSI_READ, EPSI_PHASE]), + "PVM_EncNReceivers": 1, + "PVM_EncSteps1": np.arange(EPSI_PHASE) - EPSI_PHASE // 2, + }, + blocks=EPSI_SEGMENTS * EPSI_PHASE, + ) + return Dataset(path, **state) + + +def test_epsi_accounts_for_every_acquired_sample(tmp_path): + """Spec 3.1: with GO_block_size=continuous the whole block is digitized data. + + The EPSI layout divided the per-block sample count by NSegments while + block_count already multiplied by it, so all but the last segment of every + block was discarded -- 75 % of the file on a four-segment scan. + """ + dataset = epsi_dataset(tmp_path, load=LOAD_STAGES["properties"]) + + assert dataset.acq_length == dataset.block_size + assert dataset.acq_length * dataset.block_count == dataset.path.stat().st_size // dataset.numpy_dtype.itemsize + + +def test_epsi_k_space_has_a_full_spectral_axis(tmp_path): + dataset = epsi_dataset(tmp_path) + + # Spec 6.3: the reconstruction input is read x phase x spectral points. + assert tuple(dataset.k_space) == (EPSI_READ, EPSI_PHASE, EPSI_SEGMENTS * EPSI_SPECTRAL_PER_SEGMENT, 1) + assert dataset.data.shape == (EPSI_READ, EPSI_PHASE, EPSI_SEGMENTS * EPSI_SPECTRAL_PER_SEGMENT, 1) + assert dataset.dim_type == [ + "k_space_encode_step_0", + "k_space_encode_step_1", + "k_space_encode_step_2", + "channel", + ] + + +def test_epsi_keeps_every_stored_complex_sample(tmp_path): + dataset = epsi_dataset(tmp_path) + stored = np.fromfile(dataset.path, dtype=dataset.numpy_dtype) + expected = stored[0::2] + 1j * stored[1::2] + + assert dataset.data.size == expected.size + assert np.array_equal(np.sort_complex(dataset.data.reshape(-1)), np.sort_complex(expected)) + + +def kblock_fid(tmp_path, acqp, method, *, blocks, samples_per_block): + """A Standard_KBlock_Format fid: real samples first, then zero padding.""" + block_size = int(np.ceil(int(np.atleast_1d(acqp["ACQ_size"])[0]) * int(method["PVM_EncNReceivers"]) * 4 / 1024.0) * 1024 // 4) + stored = np.zeros((block_size, blocks), dtype="", + "NPro": projections, + "NI": 1, + "NR": 1, + }, + method={"Method": "", "PVM_EncMatrix": np.array([4, 4, partitions]), "PVM_EncNReceivers": 1}, + blocks=projections * partitions, + samples_per_block=8, + ) + + dataset = Dataset(path) + + assert dataset.dim_type == [ + "k_space_encode_step_0", + "k_space_encode_step_1", + "k_space_encode_step_2", + "object", + "repetition", + "channel", + ] + assert len(dataset.dim_type) == dataset.data.ndim + assert dataset.to_kspace(bart=True).ndim == 16 + + +def test_spectroscopy_labels_objects_and_repetitions(tmp_path): + """Spec 5.2: NI is objects per repetition, NR repetitions -- NA is not stored. + + Labelling NR `average` sends a dynamic series to BART's average dimension, + where a caller that averages over it destroys the series. + """ + objects, repetitions = 2, 3 + path = write_fid( + tmp_path / "32", + acqp={ + "GO_block_size": "continuous", + "GO_raw_data_format": "GO_32BIT_SGN_INT", + "BYTORDA": "little", + "ACQ_dim": 1, + "ACQ_dim_desc": "Spectroscopic", + "ACQ_size": np.array([8]), + "PULPROG": "", + "NI": objects, + "NR": repetitions, + "NA": 4, + }, + method={"Method": "", "PVM_DigNp": 4, "PVM_EncNReceivers": 1}, + blocks=objects * repetitions, + ) + + dataset = Dataset(path) + bart = dataset.to_kspace(bart=True) + + assert dataset.dim_type == ["k_space_encode_step_0", "object", "repetition"] + assert dataset.data.shape == (4, objects, repetitions) + assert [(axis, size) for axis, size in enumerate(bart.shape) if size > 1] == [(0, 4), (9, repetitions), (13, objects)] + + +def test_field_map_labels_echoes_and_counts_repetitions(tmp_path): + """Spec 5.2: PVM_NEchoImages is an echo axis and NR a stored repetition axis. + + The echo axis was labelled `repetition` (so BART received echoes on its + time dimension), and block_count carried no NR factor at all -- masked by + every corpus field map having NR = 1. + """ + echoes, phase, partitions, repetitions = 2, 3, 2, 2 + path = kblock_fid( + tmp_path / "31", + acqp={ + "GO_block_size": "Standard_KBlock_Format", + "GO_raw_data_format": "GO_32BIT_SGN_INT", + "BYTORDA": "little", + "ACQ_dim": 3, + "ACQ_dim_desc": Verbatim("( 3 )\nSpatial Spatial Spatial"), + "ACQ_size": np.array([8, phase, partitions]), + "ACQ_phase_factor": 1, + "PULPROG": "", + "NI": echoes, + "NR": repetitions, + }, + method={ + "Method": "", + "PVM_EncMatrix": np.array([4, phase, partitions]), + "PVM_NEchoImages": echoes, + "PVM_EncNReceivers": 1, + }, + blocks=phase * partitions * echoes * repetitions, + samples_per_block=8, + ) + + dataset = Dataset(path) + + assert dataset.dim_type == [ + "k_space_encode_step_0", + "k_space_encode_step_1", + "k_space_encode_step_2", + "echo", + "repetition", + "channel", + ] + assert dataset.data.shape == (4, phase, partitions, echoes, repetitions, 1) + assert [(axis, size) for axis, size in enumerate(dataset.to_kspace(bart=True).shape) if size > 1] == [ + (0, 4), + (1, phase), + (2, partitions), + (9, repetitions), + (10, echoes), + ] + + +def test_a_fid_companion_is_shaped_into_acquisitions(tmp_path): + """Spec 3.5: fid.spiral / fid.navFid hold whole acquisitions of PVM_DigNp + complex points; returning a flat vector makes the caller rediscover that.""" + digitized, acquisitions, phase = 4, 3, 2 + experiment = tmp_path / "35" + path = kblock_fid( + experiment, + acqp={ + "GO_block_size": "Standard_KBlock_Format", + "GO_raw_data_format": "GO_32BIT_SGN_INT", + "BYTORDA": "little", + "ACQ_dim": 2, + "ACQ_dim_desc": Verbatim("( 2 )\nSpatial Spatial"), + "ACQ_size": np.array([2 * digitized, phase]), + "ACQ_phase_factor": 1, + "PULPROG": "", + "NI": 1, + "NR": 1, + }, + method={"Method": "", "PVM_DigNp": digitized, "PVM_EncMatrix": np.array([digitized, phase]), "PVM_EncNReceivers": 1}, + blocks=phase, + samples_per_block=2 * digitized, + ) + write_binary(experiment / "fid.navFid", np.arange(1, 2 * digitized * acquisitions + 1, dtype="