2323"""
2424Estimating the susceptibility distortions without fieldmaps.
2525"""
26+ import json
2627
2728from nipype .pipeline import engine as pe
2829from nipype .interfaces import utility as niu
@@ -214,9 +215,14 @@ def init_syn_sdc_wf(
214215 find_zooms = pe .Node (niu .Function (function = _adjust_zooms ), name = "find_zooms" )
215216 zooms_epi = pe .Node (RegridToZooms (), name = "zooms_epi" )
216217
218+ syn_config = data .load (f"sd_syn{ '_sloppy' * sloppy } .json" )
219+
220+ vox_params = pe .Node (niu .Function (function = _mm2vox ), name = "vox_params" )
221+ vox_params .inputs .registration_config = json .loads (syn_config .read_text ())
222+
217223 # SyN Registration Core
218224 syn = pe .Node (
219- Registration (from_file = data . load ( f"sd_syn { '_sloppy' * sloppy } .json" ) ),
225+ Registration (from_file = syn_config ),
220226 name = "syn" ,
221227 n_procs = omp_nthreads ,
222228 )
@@ -277,6 +283,7 @@ def init_syn_sdc_wf(
277283 ("sd_prior" , "in2" )]),
278284 (inputnode , anat_dilmsk , [("anat_mask" , "in_file" )]),
279285 (inputnode , warp_dir , [("anat_ref" , "fixed_image" )]),
286+ (inputnode , vox_params , [("anat_ref" , "fixed_image" )]),
280287 (inputnode , anat_merge , [("anat_ref" , "in1" )]),
281288 (inputnode , lap_anat , [("anat_ref" , "op1" )]),
282289 (inputnode , find_zooms , [("anat_ref" , "in_anat" ),
@@ -295,11 +302,15 @@ def init_syn_sdc_wf(
295302 (anat_dilmsk , amask2epi , [("out_file" , "input_image" )]),
296303 (amask2epi , epi_umask , [("output_image" , "in2" )]),
297304 (readout_time , warp_dir , [("pe_direction" , "pe_dir" )]),
305+ (readout_time , vox_params , [("pe_direction" , "pe_dir" )]),
306+ (clip_epi , warp_dir , [("out_file" , "moving_image" )]),
307+ (clip_epi , vox_params , [("out_file" , "moving_image" )]),
298308 (warp_dir , syn , [("out" , "restrict_deformation" )]),
299309 (anat_merge , syn , [("out" , "fixed_image" )]),
300310 (fixed_masks , syn , [("out" , "fixed_image_masks" )]),
301311 (epi_merge , syn , [("out" , "moving_image" )]),
302312 (moving_masks , syn , [("out" , "moving_image_masks" )]),
313+ (vox_params , syn , [("out" , "transform_parameters" )]),
303314 (syn , extract_field , [(("forward_transforms" , _pop ), "transform" )]),
304315 (clip_epi , extract_field , [("out_file" , "epi" )]),
305316 (readout_time , extract_field , [("readout_time" , "ro_time" ),
@@ -583,25 +594,52 @@ def _remove_first_mask(in_file):
583594 return workflow
584595
585596
586- def _warp_dir (fixed_image , pe_dir , nlevels = 3 ):
597+ def _warp_dir (moving_image , fixed_image , pe_dir , nlevels = 2 ):
587598 """Extract the ``restrict_deformation`` argument from metadata."""
588599 import numpy as np
589600 import nibabel as nb
590601
591- img = nb .load (fixed_image )
602+ moving = nb .load (moving_image )
603+ fixed = nb .load (fixed_image )
592604
593- if np .any (nb .affines .obliquity (img .affine ) > 0.05 ):
605+ if np .any (nb .affines .obliquity (fixed .affine ) > 0.05 ):
594606 from nipype import logging
595607
596608 logging .getLogger ("nipype.interface" ).warn (
597609 "Running fieldmap-less registration on an oblique dataset"
598610 )
599611
600- vs = nb .affines .voxel_sizes (img .affine )
601- order = np .around (np .abs (img .affine [:3 , :3 ] / vs ))
602- retval = order @ [1 if pe_dir [0 ] == ax else 0.1 for ax in "ijk" ]
612+ moving_axcodes = nb .aff2axcodes (moving .affine , ["RR" , "AA" , "SS" ])
613+ moving_pe_axis = moving_axcodes ["ijk" .index (pe_dir [0 ])]
614+
615+ fixed_axcodes = nb .aff2axcodes (fixed .affine , ["RR" , "AA" , "SS" ])
616+
617+ deformation = [0.1 , 0.1 , 0.1 ]
618+ deformation [fixed_axcodes .index (moving_pe_axis )] = 1.0
619+
620+ return nlevels * [deformation ]
621+
622+
623+ def _mm2vox (moving_image , fixed_image , pe_dir , registration_config ):
624+ import nibabel as nb
625+
626+ params = registration_config ['transform_parameters' ]
627+
628+ moving = nb .load (moving_image )
629+ # Use duplicate axcodes to ignore sign
630+ moving_axcodes = nb .aff2axcodes (moving .affine , ["RR" , "AA" , "SS" ])
631+ moving_pe_axis = moving_axcodes ["ijk" .index (pe_dir [0 ])]
632+
633+ fixed = nb .load (fixed_image )
634+ fixed_axcodes = nb .aff2axcodes (fixed .affine , ["RR" , "AA" , "SS" ])
635+
636+ zooms = nb .affines .voxel_sizes (fixed .affine )
637+ pe_res = zooms [fixed_axcodes .index (moving_pe_axis )]
603638
604- return nlevels * [retval .tolist ()]
639+ return [
640+ [* level_params [:2 ], level_params [2 ] / pe_res ]
641+ for level_params in params
642+ ]
605643
606644
607645def _merge_meta (epi_ref , meta_list ):
0 commit comments