Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions brukerapi/config/properties_2dseq_core.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,9 @@
],
"num_slice_packages": [
{
"cmd": "len(#VisuCoreSlicePacksSlices.nested)",
"conditions": []
},
{
"cmd": "1",
"cmd": "len(@slice_packages_index())",
"conditions": []
}

],
"slope": [
{
Expand Down
2 changes: 1 addition & 1 deletion brukerapi/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -1204,7 +1204,7 @@ def get_slice_packages(self):
"""
if self.type != "2dseq":
raise UnsupportedDatasetType(f"slice packages are only available for 2dseq, not {self.type}")
if self.num_slice_packages <= 1:
if len(self.slice_packages_index()) <= 1:
return [self]

from .splitters import SlicePackageSplitter
Expand Down
54 changes: 30 additions & 24 deletions brukerapi/splitters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import numpy as np

from .dataset import Dataset
from .exceptions import MissingProperty
from .jcampdx import JCAMPDX
from .utils import index_to_slice

SUPPORTED_FG = ["FG_ISA", "FG_IRMODE", "FG_ECHO"]
Expand Down Expand Up @@ -286,17 +286,11 @@ def split(self, dataset, write=None, path_out=None):
if write is None:
write = False

try:
VisuCoreSlicePacksSlices = dataset["VisuCoreSlicePacksSlices"].nested
except KeyError:
raise MissingProperty("Parameter VisuCoreSlicePacksSlices not found") from KeyError
packages = dataset.slice_packages_index()

# list of split data sets
datasets = []

# range of frames of given slice package
frame_range = range(0, 0)

# Which frame group holds the slices. Spec 7.10: VisuCoreSlicePacksDef is
# (fg_index, num_packages), and an fg_index of -1 is a documented sentinel
# meaning there is no slices frame group and every frame belongs to the
Expand All @@ -313,12 +307,8 @@ def split(self, dataset, write=None, path_out=None):
fg_abs_index = fg_rel_index + dataset.encoded_dim

# slice package split loop
for sp_index in range(len(VisuCoreSlicePacksSlices)):
# set range
frame_range = range(frame_range.stop, frame_range.stop + VisuCoreSlicePacksSlices[sp_index][1])

# number of frames contained in given slice package
frame_count = frame_range.stop - frame_range.start
for sp_index, (first_frame, frame_count) in enumerate(packages):
frame_range = range(first_frame, first_frame + frame_count)

# name of the data set created by the split
name = f"{dataset.path.parents[0].name}_sp_{sp_index}/2dseq"
Expand Down Expand Up @@ -373,9 +363,9 @@ def _split_parameters(self, dataset, frame_range, *, fg_rel_index, fg_abs_index,
self._split_VisuCoreTransposition(dataset, visu_pars_, frame_range, fg_rel_index)
self._split_VisuCoreFrameCount(dataset, visu_pars_, frame_count, fg_abs_index)
self._split_VisuFGOrderDesc(visu_pars_, fg_rel_index, frame_count)
self._split_VisuCoreSlicePacksDef(visu_pars_)
self._split_VisuCoreSlicePacksSlices(visu_pars_, sp_index)
self._split_VisuCoreSlicePacksSliceDist(visu_pars_, sp_index)
self._split_VisuCoreSlicePacksDef(visu_pars_, fg_rel_index)
self._split_VisuCoreSlicePacksSlices(visu_pars_, frame_count)
self._split_VisuCoreSlicePacksSliceDist(dataset, visu_pars_, sp_index)

return {"visu_pars": visu_pars_}

Expand Down Expand Up @@ -410,18 +400,34 @@ def _split_VisuFGOrderDesc(self, visu_pars, fg_rel_ind, frame_count):

VisuFGOrderDesc.value = value

def _split_VisuCoreSlicePacksDef(self, visu_pars):
@staticmethod
def _synthesise_parameter(visu_pars, key, value):
_, parameter = JCAMPDX.handle_jcampdx_line(f"##${key}={value}", visu_pars.version)
visu_pars.set_parameter(key, parameter)

def _split_VisuCoreSlicePacksDef(self, visu_pars, fg_rel_index):
if "VisuCoreSlicePacksDef" not in visu_pars:
self._synthesise_parameter(visu_pars, "VisuCoreSlicePacksDef", f"({fg_rel_index}, 1)")
return
VisuCoreSlicePacksDef = visu_pars["VisuCoreSlicePacksDef"]
value = VisuCoreSlicePacksDef.value
value[1] = 1
VisuCoreSlicePacksDef.value = value

def _split_VisuCoreSlicePacksSlices(self, visu_pars_, sp_index):
VisuCoreSlicePacksSlices = visu_pars_["VisuCoreSlicePacksSlices"]
VisuCoreSlicePacksSlices.value = [VisuCoreSlicePacksSlices.value[sp_index]]

def _split_VisuCoreSlicePacksSliceDist(self, visu_pars_, sp_index):
VisuCoreSlicePacksSliceDist = visu_pars_["VisuCoreSlicePacksSliceDist"]
def _split_VisuCoreSlicePacksSlices(self, visu_pars, frame_count):
if "VisuCoreSlicePacksSlices" not in visu_pars:
self._synthesise_parameter(visu_pars, "VisuCoreSlicePacksSlices", f"( 1 )\n(0, {frame_count})")
return
VisuCoreSlicePacksSlices = visu_pars["VisuCoreSlicePacksSlices"]
VisuCoreSlicePacksSlices.value = [[0, frame_count]]
VisuCoreSlicePacksSlices.size = (1,)

def _split_VisuCoreSlicePacksSliceDist(self, dataset, visu_pars, sp_index):
if "VisuCoreSlicePacksSliceDist" not in visu_pars:
distance = float(np.linalg.norm(dataset.affine_of_package(sp_index)[:3, 2]))
self._synthesise_parameter(visu_pars, "VisuCoreSlicePacksSliceDist", str(distance))
return
VisuCoreSlicePacksSliceDist = visu_pars["VisuCoreSlicePacksSliceDist"]
# 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])
Expand Down
31 changes: 30 additions & 1 deletion test/test_split.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from brukerapi.dataset import Dataset
from brukerapi.splitters import FrameGroupSplitter, SlicePackageSplitter, Splitter
from test.synthetic import stacked_positions, write_2dseq
from test.synthetic import axial_orientation, stacked_positions, write_2dseq


def test_split_transposition_is_noop_when_parameter_is_absent():
Expand Down Expand Up @@ -51,6 +51,35 @@ def test_splitSlicePkg(test_split_data, tmp_path):
assert len(datasets) == dataset["VisuCoreSlicePacksSlices"].size[0]


def test_slice_package_splitter_infers_and_synthesises_pv51_packages(tmp_path):
"""PV5.1 has no §7.10 package parameters; orientation delimits packages."""
sagittal = np.array([0.0, 1.0, 0.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0])
positions = np.vstack(
[
stacked_positions((-20.0, -20.0, -3.0), (0.0, 0.0, 1.5), 2),
stacked_positions((0.0, -20.0, -20.0), (-1.0, 0.0, 0.0), 3),
]
)
orientations = np.vstack([axial_orientation(2), np.tile(sagittal, (3, 1))])
path = write_2dseq(
tmp_path / "9" / "pdata" / "1",
creator_version="5.1",
frame_groups=(("FG_SLICE", 5),),
positions=positions,
orientations=orientations,
)

dataset = Dataset(path)
packages = SlicePackageSplitter().split(dataset, write=True)

assert dataset.num_slice_packages == 2
assert [package.data.shape[2] for package in packages] == [2, 3]
assert all(package.num_slice_packages == 1 for package in packages)
assert [package["VisuCoreSlicePacksSlices"].nested for package in packages] == [[[0, 2]], [[0, 3]]]
assert [package["VisuCoreSlicePacksDef"].value for package in packages] == [[0, 1], [0, 1]]
assert [Dataset(package.path).num_slice_packages for package in packages] == [1, 1]


def _echo_dataset(tmp_path, echoes=2, slices=3):
path = write_2dseq(
tmp_path / "5" / "pdata" / "1",
Expand Down
Loading