From 998c2e9982c08613a5967510ce020a2e0e6d3c61 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:06:11 -0300 Subject: [PATCH 1/7] ENH: Add support for average M0 parameter in ASLData class --- asltk/asldata.py | 1 + 1 file changed, 1 insertion(+) diff --git a/asltk/asldata.py b/asltk/asldata.py index a72a56f..b368afe 100644 --- a/asltk/asldata.py +++ b/asltk/asldata.py @@ -86,6 +86,7 @@ def __init__( if kwargs.get('m0') is not None: average_m0 = kwargs.get('average_m0', False) + self._asl_image._average_m0 = average_m0 if isinstance(kwargs.get('m0'), str): m0_path = kwargs.get('m0') From 6ac608564ffe01a0e119e03166b67d3053d6365d Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:06:17 -0300 Subject: [PATCH 2/7] ENH: Refactor image handling in CBFMapping to use clone_image for output maps --- asltk/reconstruction/cbf_mapping.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/asltk/reconstruction/cbf_mapping.py b/asltk/reconstruction/cbf_mapping.py index 22d72be..8fb703d 100644 --- a/asltk/reconstruction/cbf_mapping.py +++ b/asltk/reconstruction/cbf_mapping.py @@ -14,7 +14,7 @@ from asltk.logging_config import get_logger, log_processing_step from asltk.models.signal_dynamic import asl_model_buxton from asltk.mri_parameters import MRIParameters -from asltk.utils.io import ImageIO +from asltk.utils.io import ImageIO, clone_image # Global variables to assist multi cpu threading cbf_map = None @@ -406,14 +406,22 @@ def create_map( ) # Prepare output maps - cbf_map_image = ImageIO(self._asl_data('m0').get_image_path()) - cbf_map_image.update_image_data(self._cbf_map) + base_volume = ImageIO(self._asl_data('m0').get_image_path()) + cbf_map_image = clone_image(base_volume) + cbf_map_image.update_image_data( + self._cbf_map, self._asl_data._asl_image._average_m0 + ) - cbf_map_norm_image = ImageIO(self._asl_data('m0').get_image_path()) - cbf_map_norm_image.update_image_data(self._cbf_map * (60 * 60 * 1000)) + cbf_map_norm_image = clone_image(base_volume) + cbf_map_norm_image.update_image_data( + self._cbf_map * (60 * 60 * 1000), + self._asl_data._asl_image._average_m0, + ) - att_map_image = ImageIO(self._asl_data('m0').get_image_path()) - att_map_image.update_image_data(self._att_map) + att_map_image = clone_image(base_volume) + att_map_image.update_image_data( + self._att_map, self._asl_data._asl_image._average_m0 + ) output_maps = { 'cbf': cbf_map_image, From a80cb55aa2dc540afa41db7e52bda20a5b0dde1f Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:06:25 -0300 Subject: [PATCH 3/7] DOC: Fix typo in TODO comment regarding T2 maps and mean T2 maps dimensions --- asltk/reconstruction/t2_mapping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asltk/reconstruction/t2_mapping.py b/asltk/reconstruction/t2_mapping.py index 03f96c5..28cb2a0 100644 --- a/asltk/reconstruction/t2_mapping.py +++ b/asltk/reconstruction/t2_mapping.py @@ -184,7 +184,7 @@ def create_map( ) # Prepare output maps - # TODO At the moment, the T2 maps and mean T2 maps are as ImageIO object, however, the Spacing, Dimension are not given as a 4D array. Ceck if can be imported from the m0 image is 3D. + # TODO At the moment, the T2 maps and mean T2 maps are as ImageIO object, however, the Spacing, Dimension are not given as a 4D array. Check if can be imported from the m0 image is 3D. t2_maps_image = ImageIO( image_array=np.array( [ From 04de10e59e9f9650907795d36c6c8984e0f5da5c Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:06:30 -0300 Subject: [PATCH 4/7] ENH: Refactor output map creation to use clone_image for improved memory handling and support for average M0 parameter --- asltk/reconstruction/multi_te_mapping.py | 26 ++++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/asltk/reconstruction/multi_te_mapping.py b/asltk/reconstruction/multi_te_mapping.py index fc6adc5..9bbb66f 100644 --- a/asltk/reconstruction/multi_te_mapping.py +++ b/asltk/reconstruction/multi_te_mapping.py @@ -12,7 +12,7 @@ from asltk.models.signal_dynamic import asl_model_multi_te from asltk.mri_parameters import MRIParameters from asltk.reconstruction import CBFMapping -from asltk.utils.io import ImageIO +from asltk.utils.io import ImageIO, clone_image # Global variables to assist multi cpu threading cbf_map = None @@ -388,19 +388,27 @@ def create_map( ) # Prepare output maps - cbf_map_image = ImageIO(self._asl_data('m0').get_image_path()) - cbf_map_image.update_image_data(self._cbf_map) + base_volume = ImageIO(self._asl_data('m0').get_image_path()) + cbf_map_image = clone_image(base_volume) + cbf_map_image.update_image_data( + self._cbf_map, self._asl_data._asl_image._average_m0 + ) - cbf_map_norm_image = ImageIO(self._asl_data('m0').get_image_path()) + cbf_map_norm_image = clone_image(base_volume) cbf_map_norm_image.update_image_data( - self._cbf_map * (60 * 60 * 1000) + self._cbf_map * (60 * 60 * 1000), + self._asl_data._asl_image._average_m0, ) - att_map_image = ImageIO(self._asl_data('m0').get_image_path()) - att_map_image.update_image_data(self._att_map) + att_map_image = clone_image(base_volume) + att_map_image.update_image_data( + self._att_map, self._asl_data._asl_image._average_m0 + ) - t1blgm_map_image = ImageIO(self._asl_data('m0').get_image_path()) - t1blgm_map_image.update_image_data(self._t1blgm_map) + t1blgm_map_image = clone_image(base_volume) + t1blgm_map_image.update_image_data( + self._t1blgm_map, self._asl_data._asl_image._average_m0 + ) # Create output maps dictionary output_maps = { From 2ad99485c800e29045d5be26ab43e22fae559da9 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:06:35 -0300 Subject: [PATCH 5/7] ENH: Add test for creating CBF map using average M0 option in ASLData --- tests/reconstruction/test_cbf_mapping.py | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/reconstruction/test_cbf_mapping.py b/tests/reconstruction/test_cbf_mapping.py index ea7a253..d8db922 100644 --- a/tests/reconstruction/test_cbf_mapping.py +++ b/tests/reconstruction/test_cbf_mapping.py @@ -194,3 +194,29 @@ def test_cbf_map_normalized_flag_true_result_cbf_map_rescaled(): out_norm_array[out_norm_array == 0] = np.nan mean_px_value = np.nanmean(out_norm_array) assert mean_px_value < 500 and mean_px_value > 50 + + +def test_cbf_object_create_map_success_using_average_m0_option(tmp_path): + base_m0 = ImageIO(M0).get_as_numpy() + m04d = np.stack([base_m0 for _ in range(3)], axis=0) + # Save m04d into a temporary .nii.gz file + ImageIO(image_array=m04d).save_image( + os.path.join(tmp_path, 'm0_4d.nii.gz') + ) + + asldata4d = ASLData( + pcasl=PCASL_MTE, + m0=os.path.join(tmp_path, 'm0_4d.nii.gz'), + ld_values=[100.0, 100.0, 150.0, 150.0, 400.0, 800.0, 1800.0], + pld_values=[170.0, 270.0, 370.0, 520.0, 670.0, 1070.0, 1870.0], + average_m0=True, + ) + mte = CBFMapping(asldata4d) + mask = ImageIO(M0_BRAIN_MASK) + + mte.set_brain_mask(mask) + + output = mte.create_map() + + assert output['cbf'].get_as_numpy().shape == base_m0.shape + assert output['att'].get_as_numpy().shape == base_m0.shape From 6b8581e4dfe674b65cecfb1d1b4f7332c6173029 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:06:41 -0300 Subject: [PATCH 6/7] ENH: Add test for creating map using average M0 option in MultiTE_ASLMapping --- tests/reconstruction/test_multi_te_mapping.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/reconstruction/test_multi_te_mapping.py b/tests/reconstruction/test_multi_te_mapping.py index 5d1dc22..a325839 100644 --- a/tests/reconstruction/test_multi_te_mapping.py +++ b/tests/reconstruction/test_multi_te_mapping.py @@ -209,3 +209,42 @@ def test_multite_asl_object_create_map_using_provided_cbf_att_maps(capfd): if re.search('multiTE-ASL', out): test_pass = True assert test_pass + + +def test_multite_asl_object_create_map_success_using_average_m0_option( + tmp_path, +): + base_m0 = ImageIO(M0).get_as_numpy() + m04d = np.stack([base_m0 for _ in range(3)], axis=0) + # Save m04d into a temporary .nii.gz file + ImageIO(image_array=m04d).save_image( + os.path.join(tmp_path, 'm0_4d.nii.gz') + ) + + asldata4d = ASLData( + pcasl=PCASL_MTE, + m0=os.path.join(tmp_path, 'm0_4d.nii.gz'), + ld_values=[100.0, 100.0, 150.0, 150.0, 400.0, 800.0, 1800.0], + pld_values=[170.0, 270.0, 370.0, 520.0, 670.0, 1070.0, 1870.0], + te_values=[ + 13.56, + 67.82, + 122.08, + 176.33, + 230.59, + 284.84, + 339.100, + 393.36, + ], + average_m0=True, + ) + mte = MultiTE_ASLMapping(asldata4d) + mask = ImageIO(M0_BRAIN_MASK) + + mte.set_brain_mask(mask) + + output = mte.create_map() + + assert output['cbf'].get_as_numpy().shape == base_m0.shape + assert output['att'].get_as_numpy().shape == base_m0.shape + assert output['t1blgm'].get_as_numpy().shape == base_m0.shape From d7cd8f55be1ff366ed2b2b48663fae5e6dfb8ad5 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:17:50 -0300 Subject: [PATCH 7/7] ENH: Ensure _average_m0 is set only if _asl_image exists in ASLData --- asltk/asldata.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/asltk/asldata.py b/asltk/asldata.py index b368afe..8cc3346 100644 --- a/asltk/asldata.py +++ b/asltk/asldata.py @@ -86,7 +86,8 @@ def __init__( if kwargs.get('m0') is not None: average_m0 = kwargs.get('average_m0', False) - self._asl_image._average_m0 = average_m0 + if self._asl_image: + self._asl_image._average_m0 = average_m0 if isinstance(kwargs.get('m0'), str): m0_path = kwargs.get('m0')