From 404ca7ccf03a689484477aa9122fe18f75707910 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:06:11 -0300 Subject: [PATCH 01/12] 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 6d1d29f18433e031239c6e97f6c63d9db715446c Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:06:17 -0300 Subject: [PATCH 02/12] 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 215c1d4d6307f58b4a39f570de92e1e84cd68d02 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:06:25 -0300 Subject: [PATCH 03/12] 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 93037a014ecfbcc84969229e34d526ccd4b16b92 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:06:30 -0300 Subject: [PATCH 04/12] 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 6201c1dbfe58156803ff42f1ce0419a7731bb513 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:06:35 -0300 Subject: [PATCH 05/12] 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 04fcaac0130170eb07302a5e1fb31e2864b10537 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:06:41 -0300 Subject: [PATCH 06/12] 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 d2a3343a62c4f35c0a4e075c1e2a7c6aa97bee22 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Wed, 10 Sep 2025 18:17:50 -0300 Subject: [PATCH 07/12] 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') From 18e313b7d4da4f8f8a52073f39472b1574eddf9b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 10 Sep 2025 22:08:44 +0000 Subject: [PATCH 08/12] =?UTF-8?q?Bump=20version:=201.0.1=20=E2=86=92=201.0?= =?UTF-8?q?.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 3d3602e..9fec98a 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.0.1 +current_version = 1.0.2 commit = True tag = True tag_name = v{new_version} diff --git a/pyproject.toml b/pyproject.toml index a9f941b..3a703d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "asltk" -version = "1.0.1" +version = "1.0.2" description = "A quick to use library to process images for MRI Arterial Spin Labeling imaging protocols." authors = ["Antonio Senra Filho "] readme = "README.md" From 7d2b9d5834049e3496b021c23d7e3c9584fc0794 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Tue, 3 Feb 2026 13:01:59 -0300 Subject: [PATCH 09/12] ENH: Update assertions in test_multite_asl_object_create_map_success to ensure output values are greater than zero --- tests/reconstruction/test_multi_te_mapping.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/reconstruction/test_multi_te_mapping.py b/tests/reconstruction/test_multi_te_mapping.py index a325839..090a359 100644 --- a/tests/reconstruction/test_multi_te_mapping.py +++ b/tests/reconstruction/test_multi_te_mapping.py @@ -138,11 +138,11 @@ def test_multite_asl_object_create_map_success(): mte = MultiTE_ASLMapping(asldata_te) out = mte.create_map() assert isinstance(out['cbf'], ImageIO) - assert np.mean(out['cbf'].get_as_numpy()) < 0.0001 + assert np.mean(out['cbf'].get_as_numpy()) > 0 assert isinstance(out['att'], ImageIO) - assert np.mean(out['att'].get_as_numpy()) > 10 + assert np.mean(out['att'].get_as_numpy()) > 0 assert isinstance(out['t1blgm'], ImageIO) - assert np.mean(out['t1blgm'].get_as_numpy()) > 50 + assert np.mean(out['t1blgm'].get_as_numpy()) > 0 def test_multite_asl_object_raises_error_if_asldata_does_not_have_pcasl_or_m0_image(): From ac29f313d24cfd68b59e051ebf6a9f98db795c33 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Tue, 3 Feb 2026 13:02:09 -0300 Subject: [PATCH 10/12] ENH: Rename test functions for UltraLongTE_ASLMapping to improve clarity and consistency --- .../test_ultralong_te_mapping.py | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/reconstruction/test_ultralong_te_mapping.py b/tests/reconstruction/test_ultralong_te_mapping.py index bd86621..978c097 100644 --- a/tests/reconstruction/test_ultralong_te_mapping.py +++ b/tests/reconstruction/test_ultralong_te_mapping.py @@ -39,49 +39,49 @@ def test_ultralongte_asl_object_constructor_created_sucessfully(): assert ulte.get_constant('T2bl') == 100 -def test_multite_asl_set_brain_mask_success(): +def test_ultralong_te_asl_set_brain_mask_success(): ulte = UltraLongTE_ASLMapping(asldata_te) mask = ImageIO(M0_BRAIN_MASK) ulte.set_brain_mask(mask) assert isinstance(ulte._brain_mask, np.ndarray) -def test_multite_asl_set_cbf_map_success(): +def test_ultralong_te_asl_set_cbf_map_success(): ulte = UltraLongTE_ASLMapping(asldata_te) fake_cbf = ImageIO(image_array=np.ones((10, 10)) * 20) ulte.set_cbf_map(fake_cbf) assert np.mean(ulte._cbf_map) == 20 -def test_multite_asl_get_cbf_map_success(): +def test_ultralong_te_asl_get_cbf_map_success(): ulte = UltraLongTE_ASLMapping(asldata_te) fake_cbf = ImageIO(image_array=np.ones((10, 10)) * 20) ulte.set_cbf_map(fake_cbf) assert np.mean(ulte.get_cbf_map()) == 20 -def test_multite_asl_set_att_map_success(): +def test_ultralong_te_asl_set_att_map_success(): ulte = UltraLongTE_ASLMapping(asldata_te) fake_att = ImageIO(image_array=np.ones((10, 10)) * 20) ulte.set_att_map(fake_att) assert np.mean(ulte._att_map) == 20 -def test_multite_asl_get_att_map_success(): +def test_ultralong_te_asl_get_att_map_success(): ulte = UltraLongTE_ASLMapping(asldata_te) fake_att = ImageIO(image_array=np.ones((10, 10)) * 20) ulte.set_att_map(fake_att) assert np.mean(ulte.get_att_map()) == 20 -def test_multite_asl_get_t1csfgm_map_attribution_success(): +def test_ultralong_te_asl_get_t1csfgm_map_attribution_success(): ulte = UltraLongTE_ASLMapping(asldata_te) fake_att = np.ones((10, 10)) * 20 ulte._t1csfgm_map = fake_att assert np.mean(ulte.get_t1csfgm_map()) == 20 -def test_multite_asl_get_t1csfgm_map_create_map_update_success(): +def test_ultralong_te_asl_get_t1csfgm_map_create_map_update_success(): ulte = UltraLongTE_ASLMapping(asldata_te) out = ulte.create_map() @@ -90,7 +90,7 @@ def test_multite_asl_get_t1csfgm_map_create_map_update_success(): @pytest.mark.parametrize('label', [(3), (-1), (1000000), (-1.1), (2.1)]) -def test_multite_asl_set_brain_mask_set_label_value_raise_error_value_not_found_in_mask( +def test_ultralong_te_asl_set_brain_mask_set_label_value_raise_error_value_not_found_in_mask( label, ): ulte = UltraLongTE_ASLMapping(asldata_te) @@ -100,7 +100,7 @@ def test_multite_asl_set_brain_mask_set_label_value_raise_error_value_not_found_ assert e.value.args[0] == 'Label value is not found in the mask provided.' -def test_multite_asl_set_brain_mask_verify_if_input_is_a_label_mask(): +def test_ultralong_te_asl_set_brain_mask_verify_if_input_is_a_label_mask(): ulte = UltraLongTE_ASLMapping(asldata_te) not_mask = ImageIO(M0) with pytest.warns(UserWarning): @@ -115,7 +115,7 @@ def test_multite_asl_set_brain_mask_verify_if_input_is_a_label_mask(): ) -def test_multite_asl_set_brain_mask_raise_error_if_image_dimension_is_different_from_3d_volume(): +def test_ultralong_te_asl_set_brain_mask_raise_error_if_image_dimension_is_different_from_3d_volume(): ulte = UltraLongTE_ASLMapping(asldata_te) pcasl_3d_vol = ImageIO(PCASL_MTE).get_as_numpy()[0, 0, :, :, :] fake_mask = ImageIO(image_array=np.array(((1, 1, 1), (0, 1, 0)))) @@ -127,7 +127,7 @@ def test_multite_asl_set_brain_mask_raise_error_if_image_dimension_is_different_ ) -def test_multite_mapping_get_brain_mask_return_adjusted_brain_mask_image_in_the_object(): +def test_ultralong_mapping_get_brain_mask_return_adjusted_brain_mask_image_in_the_object(): ulte = UltraLongTE_ASLMapping(asldata_te) assert np.mean(ulte.get_brain_mask()) == 1 @@ -136,18 +136,18 @@ def test_multite_mapping_get_brain_mask_return_adjusted_brain_mask_image_in_the_ assert np.unique(ulte.get_brain_mask()).tolist() == [0, 1] -def test_multite_asl_object_create_map_success(): +def test_ultralong_te_asl_object_create_map_success(): ulte = UltraLongTE_ASLMapping(asldata_te) out = ulte.create_map() assert isinstance(out['cbf'], ImageIO) - assert np.mean(out['cbf'].get_as_numpy()) < 0.0001 + assert np.mean(out['cbf'].get_as_numpy()) > 0 assert isinstance(out['att'], ImageIO) - assert np.mean(out['att'].get_as_numpy()) > 10 + assert np.mean(out['att'].get_as_numpy()) > 0 assert isinstance(out['t1csfgm'], ImageIO) - assert np.mean(out['t1csfgm'].get_as_numpy()) > 50 + assert np.mean(out['t1csfgm'].get_as_numpy()) > 0 -def test_multite_asl_object_raises_error_if_asldata_does_not_have_pcasl_or_m0_image(): +def test_ultralong_te_asl_object_raises_error_if_asldata_does_not_have_pcasl_or_m0_image(): with pytest.raises(Exception) as error: ulte = UltraLongTE_ASLMapping(incomplete_asldata) @@ -157,7 +157,7 @@ def test_multite_asl_object_raises_error_if_asldata_does_not_have_pcasl_or_m0_im ) -def test_multite_asl_object_raises_error_if_asldata_does_not_have_te_values(): +def test_ultralong_te_asl_object_raises_error_if_asldata_does_not_have_te_values(): incompleted_asldata = ASLData( pcasl=PCASL_MTE, m0=M0, @@ -173,7 +173,7 @@ def test_multite_asl_object_raises_error_if_asldata_does_not_have_te_values(): ) -def test_multite_asl_object_set_cbf_and_att_maps_before_create_map(): +def test_ultralong_te_asl_object_set_cbf_and_att_maps_before_create_map(): ulte = UltraLongTE_ASLMapping(asldata_te) assert np.mean(ulte.get_brain_mask()) == 1 @@ -197,7 +197,7 @@ def test_multite_asl_object_set_cbf_and_att_maps_before_create_map(): ) -def test_multite_asl_object_create_map_using_provided_cbf_att_maps(capfd): +def test_ultralong_te_asl_object_create_map_using_provided_cbf_att_maps(capfd): ulte = UltraLongTE_ASLMapping(asldata_te) mask = ImageIO(M0_BRAIN_MASK) cbf = ImageIO(image_array=np.ones(mask.get_as_numpy().shape) * 100) From 925d78829c1e121dd87900f9622155d0ae837aa2 Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Tue, 3 Feb 2026 13:02:16 -0300 Subject: [PATCH 11/12] ENH: Update parameter initialization and enhance image limit adjustment in UltraLongTE_ASLMapping --- asltk/reconstruction/ultralong_te_mapping.py | 37 ++++++++++++++------ 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/asltk/reconstruction/ultralong_te_mapping.py b/asltk/reconstruction/ultralong_te_mapping.py index 2b41ee9..af77cac 100644 --- a/asltk/reconstruction/ultralong_te_mapping.py +++ b/asltk/reconstruction/ultralong_te_mapping.py @@ -211,7 +211,7 @@ def create_map( self, ub: list = [np.inf], lb: list = [0.0], - par0: list = [400], + par0: list = [80000], cores: Union[int, str] = 'auto', smoothing=None, smoothing_params=None, @@ -427,6 +427,7 @@ def create_map( ) # Adjusting output image boundaries + # TODO O ADJUST_LIMITS TEM QUE SER TESTADO E AJUSTADO PARA O CASO DO CSF-GM --> OK para a nova forma de adjust_image. Fazer a mesma coisa para multiTE e commitar o codigo self._t1csfgm_map = self._adjust_image_limits( self._t1csfgm_map, par0[0] ) @@ -460,15 +461,31 @@ def create_map( ) def _adjust_image_limits(self, map, init_guess): - img = sitk.GetImageFromArray(map) - thr_filter = sitk.ThresholdImageFilter() - thr_filter.SetUpper( - 4 * init_guess - ) # assuming upper to 4x the initial guess - thr_filter.SetLower(0.0) - img = thr_filter.Execute(img) - - return sitk.GetArrayFromImage(img) + """Adjust image limits by rescaling values within realistic bounds. + + This method removes outliers and rescales T1csfGM values to a realistic + physiological range based on the initial guess parameter. + + Args: + map (np.ndarray): The T1csfGM map to adjust + init_guess (float): Initial guess value used for determining bounds + + Returns: + np.ndarray: Adjusted map with values rescaled to [0, 2*init_guess] + """ + # Remove voxels that failed fitting (still at initial guess) + img = sitk.GetImageFromArray(map * (map != init_guess)) + + upper_limit = 2 * init_guess + lower_limit = 0.0 + + rescaler = sitk.RescaleIntensityImageFilter() + rescaler.SetOutputMaximum(upper_limit) + rescaler.SetOutputMinimum(lower_limit) + + img_rescaled = rescaler.Execute(img) + + return sitk.GetArrayFromImage(img_rescaled) def _multite_init_globals( From 12f6fdc811657964ddb28fe9b31b57c25b20dc3e Mon Sep 17 00:00:00 2001 From: acsenrafilho Date: Tue, 3 Feb 2026 13:02:20 -0300 Subject: [PATCH 12/12] ENH: Improve _adjust_image_limits method to rescale T1csfGM values and enhance outlier removal --- asltk/reconstruction/multi_te_mapping.py | 34 +++++++++++++++++------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/asltk/reconstruction/multi_te_mapping.py b/asltk/reconstruction/multi_te_mapping.py index 9bbb66f..4070bb8 100644 --- a/asltk/reconstruction/multi_te_mapping.py +++ b/asltk/reconstruction/multi_te_mapping.py @@ -424,15 +424,31 @@ def create_map( ) def _adjust_image_limits(self, map, init_guess): - img = sitk.GetImageFromArray(map) - thr_filter = sitk.ThresholdImageFilter() - thr_filter.SetUpper( - 4 * init_guess - ) # assuming upper to 4x the initial guess - thr_filter.SetLower(0.0) - img = thr_filter.Execute(img) - - return sitk.GetArrayFromImage(img) + """Adjust image limits by rescaling values within realistic bounds. + + This method removes outliers and rescales T1csfGM values to a realistic + physiological range based on the initial guess parameter. + + Args: + map (np.ndarray): The T1csfGM map to adjust + init_guess (float): Initial guess value used for determining bounds + + Returns: + np.ndarray: Adjusted map with values rescaled to [0, 2*init_guess] + """ + # Remove voxels that failed fitting (still at initial guess) + img = sitk.GetImageFromArray(map * (map != init_guess)) + + upper_limit = 2 * init_guess + lower_limit = 0.0 + + rescaler = sitk.RescaleIntensityImageFilter() + rescaler.SetOutputMaximum(upper_limit) + rescaler.SetOutputMinimum(lower_limit) + + img_rescaled = rescaler.Execute(img) + + return sitk.GetArrayFromImage(img_rescaled) def _multite_init_globals(