From dfff5444d212667ecbcc31d8de4a256d32af85ab Mon Sep 17 00:00:00 2001 From: Darren Christopher Lukas Date: Thu, 3 Jul 2025 14:57:45 +0700 Subject: [PATCH 1/2] Option to modify buffer size in `open()` Also fixed docstring --- suspect/io/twix.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/suspect/io/twix.py b/suspect/io/twix.py index 1ffd471..51c2e9a 100644 --- a/suspect/io/twix.py +++ b/suspect/io/twix.py @@ -4,6 +4,7 @@ import numpy #import quaternion import re +import io # This file largely relies on information from Siemens regarding the structure # of the TWIX file formats. Most of the parameters that are read use the same @@ -97,10 +98,14 @@ def get_meta_regex(regex_list, header_string, convert=1, default=None): Parameters ---------- - regex_list : List of regex string - header_string : TWIX header string - convert : Unit convertion, if value is number. Defaults to 1 (means no convertion) - default : Default value if match found, but value is empty. Defaults to None. + regex_list : list + List of regex string + header_string : string + TWIX header string + convert : int, optional + Unit convertion, if value is number. Defaults to 1 (means no convertion) + default + Default value if match found, but value is empty. Defaults to None. Returns ------- @@ -502,10 +507,24 @@ def load_twix_vd(fin, builder): # move the file pointer to the start of the next scan fin.seek(initial_position + DMA_length) +def load_twix(filename, buffering=io.DEFAULT_BUFFER_SIZE): + """ + Load TWIX data. -def load_twix(filename): - with open(filename, 'rb') as fin: + Parameters + ---------- + filename : str + File path of TWIX file + buffering : int, optional + Buffer size to be passed to `open()` function. Defaults to + `io.DEFAULT_BUFFER_SIZE` + Returns + ------- + suspect.MRSData + + """ + with open(filename, 'rb', buffering=buffering) as fin: # we can tell the type of file from the first two uints in the header first_uint, second_uint = struct.unpack("II", fin.read(8)) From 5b5ed291c3aa30f1ad182f921b7e25d71124b3b2 Mon Sep 17 00:00:00 2001 From: Darren Christopher Lukas Date: Fri, 4 Jul 2025 14:18:06 +0700 Subject: [PATCH 2/2] Accept binary stream in load_twix() --- suspect/io/dicom.py | 2 +- suspect/io/twix.py | 23 +++++++++++++++++------ tests/test_mrs/test_twix.py | 4 ++++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/suspect/io/dicom.py b/suspect/io/dicom.py index 6a7ede4..9c4d6a7 100644 --- a/suspect/io/dicom.py +++ b/suspect/io/dicom.py @@ -56,7 +56,7 @@ def load_dicom(filename): # versions of pydicom >2.0.0 require explicit conversion from bytestring to list if type(dataset[0x5600, 0x0020].value) == bytes: - data_iter = iter(np.fromstring(dataset[0x5600, 0x0020].value, dtype=np.float32)) + data_iter = iter(np.frombuffer(dataset[0x5600, 0x0020].value, dtype=np.float32)) elif type(dataset[0x5600, 0x0020].value) == list: data_iter = iter(dataset[0x5600, 0x0020].value) diff --git a/suspect/io/twix.py b/suspect/io/twix.py index 51c2e9a..753ad17 100644 --- a/suspect/io/twix.py +++ b/suspect/io/twix.py @@ -1,3 +1,4 @@ +from contextlib import contextmanager from suspect import MRSData, transformation_matrix, rotation_matrix import struct @@ -5,6 +6,7 @@ #import quaternion import re import io +import os # This file largely relies on information from Siemens regarding the structure # of the TWIX file formats. Most of the parameters that are read use the same @@ -507,24 +509,33 @@ def load_twix_vd(fin, builder): # move the file pointer to the start of the next scan fin.seek(initial_position + DMA_length) -def load_twix(filename, buffering=io.DEFAULT_BUFFER_SIZE): +def load_twix(source_file, buffering=io.DEFAULT_BUFFER_SIZE): """ Load TWIX data. Parameters ---------- - filename : str + source_file : str or file-like File path of TWIX file - buffering : int, optional - Buffer size to be passed to `open()` function. Defaults to - `io.DEFAULT_BUFFER_SIZE` Returns ------- suspect.MRSData """ - with open(filename, 'rb', buffering=buffering) as fin: + @contextmanager + def open_if_filepath(file_or_path, mode='r', **kwargs): + """Context manager to open a file if argument is string""" + if isinstance(file_or_path, (str, os.PathLike)): + f = open(file_or_path, mode, **kwargs) + try: + yield f + finally: + f.close() + else: + yield file_or_path + + with open_if_filepath(source_file, 'rb') as fin: # we can tell the type of file from the first two uints in the header first_uint, second_uint = struct.unpack("II", fin.read(8)) diff --git a/tests/test_mrs/test_twix.py b/tests/test_mrs/test_twix.py index f840ebe..af988e9 100644 --- a/tests/test_mrs/test_twix.py +++ b/tests/test_mrs/test_twix.py @@ -39,6 +39,10 @@ def test_veriofile(): [0, 0, 0, 1]] )) + # Test loading via file-like object and ensure same result + with open("tests/test_data/siemens/twix_vd.dat", "rb") as f: + data_from_binary_stream = suspect.io.load_twix(f) + assert numpy.all(data == data_from_binary_stream) #def test_skyra(): # data = suspect.io.load_twix("tests/test_data/twix_vd_csi.dat") # assert data.np == 2048