5
5
import os
6
6
import time
7
7
import re
8
+ import logging
8
9
9
10
from collections import Counter
10
11
from nipype .interfaces .base import (
14
15
from smriprep .interfaces .freesurfer import ReconAll
15
16
16
17
18
+ LOGGER = logging .getLogger ('nipype.interface' )
19
+
17
20
SUBJECT_TEMPLATE = """\
18
21
\t <ul class="elem-desc">
19
22
\t \t <li>Subject ID: {subject_id}</li>
30
33
\t \t <details open>
31
34
\t \t <summary>Summary</summary>
32
35
\t \t <ul class="elem-desc">
36
+ \t \t \t <li>Original orientation: {ornt}</li>
33
37
\t \t \t <li>Repetition time (TR): {tr:.03g}s</li>
34
38
\t \t \t <li>Phase-encoding (PE) direction: {pedir}</li>
35
39
\t \t \t <li>{multiecho}</li>
@@ -158,7 +162,7 @@ class FunctionalSummaryInputSpec(BaseInterfaceInputSpec):
158
162
desc = 'Slice timing correction used' )
159
163
distortion_correction = traits .Str (desc = 'Susceptibility distortion correction method' ,
160
164
mandatory = True )
161
- pe_direction = traits .Enum (None , 'i' , 'i-' , 'j' , 'j-' , mandatory = True ,
165
+ pe_direction = traits .Enum (None , 'i' , 'i-' , 'j' , 'j-' , 'k' , 'k-' , mandatory = True ,
162
166
desc = 'Phase-encoding direction detected' )
163
167
registration = traits .Enum ('FSL' , 'FreeSurfer' , mandatory = True ,
164
168
desc = 'Functional/anatomical registration method' )
@@ -173,6 +177,7 @@ class FunctionalSummaryInputSpec(BaseInterfaceInputSpec):
173
177
dummy_scans = traits .Either (traits .Int (), None , desc = 'number of dummy scans specified by user' )
174
178
algo_dummy_scans = traits .Int (desc = 'number of dummy scans determined by algorithm' )
175
179
echo_idx = traits .List ([], usedefault = True , desc = "BIDS echo identifiers" )
180
+ orientation = traits .Str (mandatory = True , desc = 'Orientation of the voxel axes' )
176
181
177
182
178
183
class FunctionalSummary (SummaryInterface ):
@@ -194,10 +199,8 @@ def _generate_segment(self):
194
199
'(boundary-based registration, BBR) - %d dof' % dof ,
195
200
'FreeSurfer <code>mri_coreg</code> - %d dof' % dof ],
196
201
}[self .inputs .registration ][self .inputs .fallback ]
197
- if self .inputs .pe_direction is None :
198
- pedir = 'MISSING - Assuming Anterior-Posterior'
199
- else :
200
- pedir = {'i' : 'Left-Right' , 'j' : 'Anterior-Posterior' }[self .inputs .pe_direction [0 ]]
202
+
203
+ pedir = get_world_pedir (self .inputs .orientation , self .inputs .pe_direction )
201
204
202
205
if isdefined (self .inputs .confounds_file ):
203
206
with open (self .inputs .confounds_file ) as cfh :
@@ -233,7 +236,7 @@ def _generate_segment(self):
233
236
return FUNCTIONAL_TEMPLATE .format (
234
237
pedir = pedir , stc = stc , sdc = self .inputs .distortion_correction , registration = reg ,
235
238
confounds = re .sub (r'[\t ]+' , ', ' , conflist ), tr = self .inputs .tr ,
236
- dummy_scan_desc = dummy_scan_msg , multiecho = multiecho )
239
+ dummy_scan_desc = dummy_scan_msg , multiecho = multiecho , ornt = self . inputs . orientation )
237
240
238
241
239
242
class AboutSummaryInputSpec (BaseInterfaceInputSpec ):
@@ -249,3 +252,27 @@ def _generate_segment(self):
249
252
return ABOUT_TEMPLATE .format (version = self .inputs .version ,
250
253
command = self .inputs .command ,
251
254
date = time .strftime ("%Y-%m-%d %H:%M:%S %z" ))
255
+
256
+
257
+ def get_world_pedir (ornt , pe_direction ):
258
+ """Return world direction of phase encoding"""
259
+ axes = (
260
+ ("Right" , "Left" ),
261
+ ("Anterior" , "Posterior" ),
262
+ ("Superior" , "Inferior" )
263
+ )
264
+ ax_idcs = {"i" : 0 , "j" : 1 , "k" : 2 }
265
+
266
+ if pe_direction is not None :
267
+ axcode = ornt [ax_idcs [pe_direction [0 ]]]
268
+ inv = pe_direction [1 :] == "-"
269
+
270
+ for ax in axes :
271
+ for flip in (ax , ax [::- 1 ]):
272
+ if flip [not inv ].startswith (axcode ):
273
+ return "-" .join (flip )
274
+ LOGGER .warning (
275
+ "Cannot determine world direction of phase encoding. "
276
+ f"Orientation: { ornt } ; PE dir: { pe_direction } "
277
+ )
278
+ return "Could not be determined - assuming Anterior-Posterior"
0 commit comments