Skip to content

Commit 7906a6d

Browse files
authored
[REF] Support spm25 for pet-surface (aramis-lab#1609)
* Refactor reformat_surfname * Refactor reformat_surfname 2 * Remove typo * Modify m file * Remove command depending on platform * Revert m file * Correct m file * No spm12 hardcoding * Modify m script for standalone * Fix typo in m script * Get rid of matlab setup * Modify script lines * Remove unnecessary spacing
1 parent f3d1c17 commit 7906a6d

File tree

3 files changed

+46
-126
lines changed

3 files changed

+46
-126
lines changed

clinica/pipelines/pet_surface/applyInverseDeformationField.m

Lines changed: 0 additions & 26 deletions
This file was deleted.

clinica/pipelines/pet_surface/pet_surface_pipeline.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,6 @@ def _build_core_nodes(self):
284284
"acq_label",
285285
"csv_segmentation",
286286
"suvr_reference_region",
287-
"matscript_folder_inverse_deformation",
288287
"desikan_left",
289288
"desikan_right",
290289
"destrieux_left",
@@ -329,9 +328,6 @@ def _build_core_nodes(self):
329328
"label_conversion_gtmsegmentation.csv",
330329
)
331330
)
332-
full_pipe.inputs.matscript_folder_inverse_deformation = os.path.abspath(
333-
os.path.dirname(os.path.realpath(__file__))
334-
)
335331
full_pipe.inputs.is_longitudinal = self.parameters["longitudinal"]
336332

337333
# Connection

clinica/pipelines/pet_surface/pet_surface_utils.py

Lines changed: 46 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,6 @@ def isclose(a, b, rel_tol=1e-9, abs_tol=0.0):
202202
dst_val = numpy.asarray(dst)
203203
dst_val = dst_val.astype("int")
204204

205-
reg = list(convert_lut.REGION)
206-
207205
# Check that each label of original volume (old_label) has a matching transformation in the csv file
208206
for i in range(old_labels.size):
209207
index = numpy.argwhere(src_val == old_labels[i])
@@ -212,15 +210,12 @@ def isclose(a, b, rel_tol=1e-9, abs_tol=0.0):
212210
raise Exception(
213211
f"Could not find label {old_labels[i]} on conversion table. Add it manually in CSV file to correct error"
214212
)
215-
# else:
216-
# cprint(str(old_labels[i]) + " has been found on conversion table")
217213

218214
# Instantiation of final volume, with same dtype as original volume
219215
new_volume = numpy.zeros(volume.shape, dtype=volume.dtype)
220216
# Computing the transformation
221217
for i in range(len(src)):
222218
new_volume[volume == src_val[i]] = dst_val[i]
223-
# cprint("Region " + reg[i] + " transformed")
224219
# Get unique list of new label
225220
new_labels = numpy.unique(new_volume)
226221
new_labels = new_labels.astype("int")
@@ -235,7 +230,6 @@ def isclose(a, b, rel_tol=1e-9, abs_tol=0.0):
235230
current_path = os.path.abspath(current_path)
236231
nib.save(myNifti, current_path)
237232
list_of_regions.append(current_path)
238-
# cprint("Label " + str(new_labels[i]) + " created in " + current_path)
239233
allsum = allsum + region_volume
240234

241235
# The sum of a voxel location across the fourth dimension should be 1
@@ -248,61 +242,66 @@ def isclose(a, b, rel_tol=1e-9, abs_tol=0.0):
248242
return list_of_regions
249243

250244

251-
def runApplyInverseDeformationField_SPM_standalone(
252-
target, deformation_field, img, matscript_folder
253-
):
245+
def runApplyInverseDeformationField_SPM_standalone(target, deformation_field, img):
254246
"""
255247
This function does the exact same job as runApplyInverseDeformationField but with SPM standalone. We directly create
256248
a batch file that SPM standalone can run. This function does not check whether SPM standalone must be used. Previous
257249
check when building the pipeline ensures that all the env vars exists ($SPMSTANDALONE_HOME and $MCR_HOME)
258250
"""
259251
import os
260-
import platform
261252
import subprocess
262253
from os.path import abspath, basename, exists, join
254+
from textwrap import dedent
255+
256+
from clinica.utils.check_dependency import get_spm_standalone_home
257+
from clinica.utils.spm import _get_real_spm_standalone_file
263258

264259
prefix = "subject_space_"
265260

266261
# Write SPM batch command directly in a script that is readable by SPM standalone
267262
script_location = abspath("./m_script.m")
268-
script_file = open(script_location, "w+")
269-
script_file.write(
270-
"jobs{1}.spm.util.defs.comp{1}.inv.comp{1}.def = {'"
271-
+ deformation_field
272-
+ "'};\n"
263+
script_file = dedent(
264+
"""
265+
spm('Defaults', 'fMRI');
266+
spm_jobman('initcfg');
267+
268+
jobs{{1}}.spm.util.defs.comp{{1}}.inv.comp{{1}}.def = {{'{deformation_field}'}};
269+
jobs{{1}}.spm.util.defs.comp{{1}}.inv.space = {{'{target}'}};
270+
jobs{{1}}.spm.util.defs.out{{1}}.pull.fnames = {{'{img}'}};
271+
jobs{{1}}.spm.util.defs.out{{1}}.pull.savedir.saveusr = {{'{output_dir}'}};
272+
jobs{{1}}.spm.util.defs.out{{1}}.pull.interp = 4;
273+
jobs{{1}}.spm.util.defs.out{{1}}.pull.mask = 1;
274+
jobs{{1}}.spm.util.defs.out{{1}}.pull.fwhm = [0 0 0];
275+
jobs{{1}}.spm.util.defs.out{{1}}.pull.prefix = '{prefix}';
276+
277+
spm_jobman('run', jobs);
278+
"""
273279
)
274-
script_file.write("jobs{1}.spm.util.defs.comp{1}.inv.space = {'" + target + "'};\n")
275-
script_file.write("jobs{1}.spm.util.defs.out{1}.pull.fnames = {'" + img + "'};\n")
276-
script_file.write(
277-
"jobs{1}.spm.util.defs.out{1}.pull.savedir.saveusr = {'"
278-
+ abspath(os.getcwd())
279-
+ "'};\n"
280+
281+
script_file = script_file.format(
282+
deformation_field=deformation_field,
283+
target=target,
284+
img=img,
285+
output_dir=abspath(os.getcwd()),
286+
prefix=prefix,
280287
)
281-
script_file.write("jobs{1}.spm.util.defs.out{1}.pull.interp = 4;\n")
282-
script_file.write("jobs{1}.spm.util.defs.out{1}.pull.mask = 1;\n")
283-
script_file.write("jobs{1}.spm.util.defs.out{1}.pull.fwhm = [0 0 0];\n")
284-
script_file.write("jobs{1}.spm.util.defs.out{1}.pull.prefix = '" + prefix + "';\n")
285-
script_file.close()
286-
287-
# Generate command line to run
288-
# SPM standalone must be run directly from its root folder
289-
if platform.system() == "Darwin":
290-
# Mac OS
291-
cmdline = f"cd $SPMSTANDALONE_HOME && ./run_spm12.sh $MCR_HOME batch {script_location}"
292-
elif platform.system() == "Linux":
293-
# Linux OS
294-
cmdline = f"$SPMSTANDALONE_HOME/run_spm12.sh $MCR_HOME batch {script_location}"
295-
else:
296-
raise SystemError("Only support Mac OS and Linux")
288+
289+
with open(script_location, "w", encoding="utf-8") as f:
290+
f.write(script_file)
291+
292+
# TODO : This might not even be needed with cmd line setting done prior
293+
spm_file = _get_real_spm_standalone_file(get_spm_standalone_home())
294+
cmdline = f"$SPMSTANDALONE_HOME/{spm_file} $MCR_HOME batch {script_location}"
295+
297296
subprocess_run = subprocess.run(
298297
cmdline,
299298
shell=True,
300299
stdout=subprocess.DEVNULL,
301300
stderr=subprocess.DEVNULL,
302301
)
303-
if subprocess_run.returncode != 0:
302+
if code := subprocess_run.returncode != 0:
304303
raise ValueError(
305-
"runApplyInverseDeformationField_SPM_standalone failed, returned non-zero code"
304+
f"runApplyInverseDeformationField_SPM_standalone failed, returned non-zero code with {code}"
306305
)
307306

308307
output_file = join(abspath("./"), prefix + basename(img))
@@ -317,40 +316,6 @@ def runApplyInverseDeformationField_SPM_standalone(
317316
return output_file
318317

319318

320-
def runApplyInverseDeformationField(target, deformation_field, img, matscript_folder):
321-
import os
322-
import sys
323-
324-
from nipype.interfaces.matlab import MatlabCommand, get_matlab_command
325-
326-
prefix = "subject_space_"
327-
328-
MatlabCommand.set_default_matlab_cmd(get_matlab_command())
329-
matlab = MatlabCommand()
330-
if sys.platform.startswith("linux"):
331-
matlab.inputs.args = "-nosoftwareopengl"
332-
333-
matlab.inputs.paths = matscript_folder
334-
matlab.inputs.script = """
335-
applyInverseDeformationField('%s', '%s', '%s', './', '%s')
336-
""" % (
337-
target,
338-
deformation_field,
339-
img,
340-
prefix,
341-
)
342-
matlab.inputs.single_comp_thread = False
343-
matlab.inputs.logfile = os.path.join("./", "matlab_output.log")
344-
matlab.run()
345-
346-
output_file = os.path.join(os.path.abspath("./"), prefix + os.path.basename(img))
347-
if not os.path.exists(output_file):
348-
raise IOError(
349-
f"Something went wrong, please check {os.path.abspath(matlab.inputs.logfile)} for more information"
350-
)
351-
return output_file
352-
353-
354319
def suvr_normalization(pet_path, mask):
355320
"""suvr_normalization is a way of getting suvr from your pet image, based on the segmentation performed by
356321
gtmsegmentation. The Standard Uptake Value ratio is computed by dividing the whole PET volume by the mean value
@@ -789,16 +754,13 @@ def get_mid_surface(in_surfaces):
789754

790755

791756
def reformat_surfname(hemi, left_surface, right_surface):
792-
res = None
793757
if hemi == "lh":
794-
res = left_surface
795-
elif hemi == "rh":
796-
res = right_surface
797-
else:
798-
raise Exception(
799-
"First input of this reformat_surfname function must be either lh or rh"
800-
)
801-
return res
758+
return left_surface
759+
if hemi == "rh":
760+
return right_surface
761+
raise ValueError(
762+
f"First input of this reformat_surfname function must be either lh or rh. Here it is : {hemi}"
763+
)
802764

803765

804766
def produce_tsv(pet, atlas_files):
@@ -890,7 +852,6 @@ def get_wf(
890852
acq_label: str,
891853
csv_segmentation,
892854
suvr_reference_region: str,
893-
matscript_folder_inverse_deformation,
894855
destrieux_left,
895856
destrieux_right,
896857
desikan_left,
@@ -912,7 +873,6 @@ def get_wf(
912873
acq_label (string):
913874
csv_segmentation (string): Path to the CSV for the segmentation (problems encountered while using __file__)
914875
suvr_reference_region (string): Label of the SUVR reference region
915-
matscript_folder_inverse_deformation (string): Path to the current folder containing the matlab script used to call the spm function for the inverse deformation
916876
destrieux_left (string):
917877
destrieux_right (string):
918878
desikan_left (string):
@@ -1037,23 +997,14 @@ def get_wf(
1037997
name="normalize_to_MNI",
1038998
)
1039999

1040-
if using_spm_standalone:
1041-
fun_apply_inverse_deformation = (
1042-
utils.runApplyInverseDeformationField_SPM_standalone
1043-
)
1044-
else:
1045-
fun_apply_inverse_deformation = utils.runApplyInverseDeformationField
10461000
apply_inverse_deformation = pe.Node(
10471001
niu.Function(
1048-
input_names=["target", "deformation_field", "img", "matscript_folder"],
1002+
input_names=["target", "deformation_field", "img"],
10491003
output_names=["freesurfer_space_eroded_mask"],
1050-
function=fun_apply_inverse_deformation,
1004+
function=utils.runApplyInverseDeformationField_SPM_standalone,
10511005
),
10521006
name="applyInverseDeformation",
10531007
)
1054-
apply_inverse_deformation.inputs.matscript_folder = (
1055-
matscript_folder_inverse_deformation
1056-
)
10571008

10581009
pons_normalization = pe.Node(
10591010
niu.Function(
@@ -1222,7 +1173,6 @@ def get_wf(
12221173
name="inputnode",
12231174
mandatory_inputs=True,
12241175
)
1225-
12261176
inputnode.inputs.orig_nu = orig_nu
12271177
inputnode.inputs.pet = pet
12281178
inputnode.inputs.psf = pvc_psf_tsv

0 commit comments

Comments
 (0)