From 73d15d8fb7656e5cb6faa44bfad722422d574602 Mon Sep 17 00:00:00 2001 From: Helge Herthum Date: Tue, 3 Feb 2026 15:17:11 +0100 Subject: [PATCH 1/2] write image orientation to mrd --- src/console/interfaces/acquisition_data.py | 1 + .../data/write_acquisition_to_mrd.py | 34 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/console/interfaces/acquisition_data.py b/src/console/interfaces/acquisition_data.py index 1f6e6d99..f42244cf 100644 --- a/src/console/interfaces/acquisition_data.py +++ b/src/console/interfaces/acquisition_data.py @@ -210,6 +210,7 @@ def save_ismrmrd( header=header, sequence=self.sequence, dataset_path=dataset_path, + channel_assignment=self.acquisition_parameters.channel_assignment.to_dict(), ) log.warning("Invalid MRD header, could not write MRD file.") diff --git a/src/console/utilities/data/write_acquisition_to_mrd.py b/src/console/utilities/data/write_acquisition_to_mrd.py index 62125aa3..99bd6c02 100644 --- a/src/console/utilities/data/write_acquisition_to_mrd.py +++ b/src/console/utilities/data/write_acquisition_to_mrd.py @@ -17,6 +17,7 @@ def write_acquisition_to_mrd( header: ismrmrd.xsd.ismrmrdHeader, sequence: Sequence, dataset_path: Path, + channel_assignment: dict[str, int | float], ) -> Path: """Write imaging data to ISMRMRD.""" with ismrmrd.Dataset(dataset_path) as dataset: @@ -25,9 +26,36 @@ def write_acquisition_to_mrd( # Create acquisition acq = ismrmrd.Acquisition() acq.version = int(version("ismrmrd")[0]) - acq.read_dir[0] = 1.0 - acq.phase_dir[1] = 1.0 - acq.slice_dir[2] = 1.0 + + # Set raw data orientation directions in LPS coordinates + # This information is shared for all acquisitions + # The assumption is that the console output channels to physical gradient orientations are as follows + # ch output1 -> gradient along I to S + # ch output2 -> gradient along P to A + # ch output3 -> gradient along R to L + # For LPS coordinates the direction P to A needs to be inverted, i.e., read_dir = [0,-1,0] + + # Map logical gradient axes to physical directions in LPS coordinates + direction_map = { + 1: (2, 1.0), # I to S -> [0, 0, 1] + 2: (1, -1.0), # P to A -> [0, -1, 0] (inverted for LPS) + 3: (0, 1.0), # R to L -> [1, 0, 0] + } + + # Set readout direction + if channel_assignment['x'] in direction_map: + idx, val = direction_map[int(channel_assignment['x'])] + acq.read_dir[idx] = val + + # Set phase encoding direction + if channel_assignment['y'] in direction_map: + idx, val = direction_map[int(channel_assignment['y'])] + acq.phase_dir[idx] = val + + # Set slice direction + if channel_assignment['z'] in direction_map: + idx, val = direction_map[int(channel_assignment['z'])] + acq.slice_dir[idx] = val trajectory_position = 0 none_counter = 0 From 797e8a98c07efc7515c17372d964c022833742e4 Mon Sep 17 00:00:00 2001 From: Helge Herthum Date: Thu, 5 Feb 2026 14:22:34 +0100 Subject: [PATCH 2/2] use DImension instead of dict --- src/console/interfaces/acquisition_data.py | 2 +- .../data/write_acquisition_to_mrd.py | 26 +++++++++---------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/console/interfaces/acquisition_data.py b/src/console/interfaces/acquisition_data.py index f42244cf..1762bf51 100644 --- a/src/console/interfaces/acquisition_data.py +++ b/src/console/interfaces/acquisition_data.py @@ -210,7 +210,7 @@ def save_ismrmrd( header=header, sequence=self.sequence, dataset_path=dataset_path, - channel_assignment=self.acquisition_parameters.channel_assignment.to_dict(), + channel_assignment=self.acquisition_parameters.channel_assignment, ) log.warning("Invalid MRD header, could not write MRD file.") diff --git a/src/console/utilities/data/write_acquisition_to_mrd.py b/src/console/utilities/data/write_acquisition_to_mrd.py index 99bd6c02..f10ea30f 100644 --- a/src/console/utilities/data/write_acquisition_to_mrd.py +++ b/src/console/utilities/data/write_acquisition_to_mrd.py @@ -6,6 +6,7 @@ import numpy as np from pypulseq.Sequence.sequence import Sequence +from console.interfaces.dimensions import Dimensions from console.interfaces.rx_data import RxData from console.utilities.data import get_logger, mrd_helper @@ -17,7 +18,7 @@ def write_acquisition_to_mrd( header: ismrmrd.xsd.ismrmrdHeader, sequence: Sequence, dataset_path: Path, - channel_assignment: dict[str, int | float], + channel_assignment: Dimensions, ) -> Path: """Write imaging data to ISMRMRD.""" with ismrmrd.Dataset(dataset_path) as dataset: @@ -27,12 +28,12 @@ def write_acquisition_to_mrd( acq = ismrmrd.Acquisition() acq.version = int(version("ismrmrd")[0]) - # Set raw data orientation directions in LPS coordinates + # Set raw data orientation directions in LPS coordinates, assuming patient is lying supine, head first. # This information is shared for all acquisitions # The assumption is that the console output channels to physical gradient orientations are as follows - # ch output1 -> gradient along I to S - # ch output2 -> gradient along P to A - # ch output3 -> gradient along R to L + # ch output1 -> gradient from front to back (Patient: I to S) + # ch output2 -> gradient from top to bottom (Patient: P to A) + # ch output3 -> gradient from left to right (Patient: R to L) # For LPS coordinates the direction P to A needs to be inverted, i.e., read_dir = [0,-1,0] # Map logical gradient axes to physical directions in LPS coordinates @@ -43,19 +44,16 @@ def write_acquisition_to_mrd( } # Set readout direction - if channel_assignment['x'] in direction_map: - idx, val = direction_map[int(channel_assignment['x'])] - acq.read_dir[idx] = val + idx, val = direction_map[int(channel_assignment.x)] + acq.read_dir[idx] = val # Set phase encoding direction - if channel_assignment['y'] in direction_map: - idx, val = direction_map[int(channel_assignment['y'])] - acq.phase_dir[idx] = val + idx, val = direction_map[int(channel_assignment.y)] + acq.phase_dir[idx] = val # Set slice direction - if channel_assignment['z'] in direction_map: - idx, val = direction_map[int(channel_assignment['z'])] - acq.slice_dir[idx] = val + idx, val = direction_map[int(channel_assignment.z)] + acq.slice_dir[idx] = val trajectory_position = 0 none_counter = 0