-
Notifications
You must be signed in to change notification settings - Fork 2
/
mknii
executable file
·188 lines (164 loc) · 6.53 KB
/
mknii
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#!/usr/bin/env bash
env | grep ^DRYRUN= && DRYRUN=echo || DRYRUN=""
# when we are give, change value to "check"
[ -v NOECHOCHECK ] && NOECHOCHECK="check" || NOECHOCHECK=""
[ ! -v VERBOSE ] && VERBOSE=""
verbose(){ [ -n "$VERBOSE" ] && echo "$*" >&2 || :; }
set_dcm2niix(){
DCM2NIIX="${DCM2NII:-dcm2niix}"
command -v "$DCM2NIIX" >/dev/null || DCM2NIIX=dcm2niix_afni
! command -v "$DCM2NIIX" >/dev/null && warn "cannot find dcm2niix or dcm2niix_afni; try export DCM2NIIX=..." && exit 2
return 0
}
#
#run dcm2niix with 3dNotes wrapper. check for existing file
#
# 20201016WF init
# 20210113WF use rename-recent. nest into "main" (source for testing)
usage() {
cat <<HEREDOC
USAGE:
$(basename "$0") output.nii.gz path/to/dcm/
output.nii.gz - 1st argument is the output nifti. must end in .nii.gz
path/to/dcm - 2nd arg must be a folder or single dicom
escape spaces in input
will
* recursively make directories. be careful.
* skip if already exists
* add annotation with source directory to nifti. careful not to spill participant information
* try to deal with gre fieldmap naming weirdness (if output matches magnitude[12] or phase)
use DRYRUN=1 $(basename $0) ... to test
HEREDOC
exit 1
}
# rename mag and phase
# dcm2niix adds e.g. _e2 or _ph
# extracted here for testing (t/mknii.bats)
# also need to check we have a working 'rename' command
# only needed for mag and phase. so only check when we need one of those
_rename-recent(){
rename-recent have? >/dev/null || exit 1 # dont convert if we have the wrong rename
rename-recent "$@"
}
rename-mag(){
local pattern='s/[12]?_e(\d.(nii.gz|json))$/$1/'
local outdir="$1";shift
# if input is magnitude.nii.gz and maginguted_e2.nii.gz
# make first look like magnitude_e1 for later rename
find $outdir -maxdepth 1 -mtime -1 -name '*magnitude*e2*.nii.gz' | grep -q magnitude &&
_rename-recent 's/magnitude(.nii.gz|.json)$/magnitude_e1$1/' $outdir "*magnitude.*"
_rename-recent "$pattern" "$outdir" "$@"
}
rename-phase(){
local pattern='s/_e\d+(_ph)?(.nii.gz|.json)$/$2/'
_rename-recent "$pattern" "$@"
}
rename-epi-multiecho(){
local outdir="$1"; shift
local outname="$1"; shift
if [ -r "$outdir/${outname}_e1.nii.gz" ]; then
echo "# trying to rename multiecho seq $outname";
_rename-recent 's/(sbref|bold)_e(\d+).(json|nii.gz)/echo-$2_$1.$3/' "$outdir" "${outname}_e*"
fi
}
add_echo_name(){ perl -pe 's/_(bold|sbref).nii.gz/_echo-1_$1.nii.gz/' <<< "$*"; }
multiecho_exists(){
# okay to flag nii_out as mutliecho even if it's not
# will have been caught earlier
local nii_out="$1"
for ME_name in "${nii_out/.nii.gz/_e1.nii.gz}" \
"${nii_out/magnitude.nii.gz/magnitude1.nii.gz}" \
"$(add_echo_name "$nii_out")"; do
verbose "# checking for multiecho like '$ME_name'"
! test -r "$ME_name" && continue
echo "# have '$ME_name', considering that the same as '$nii_out' (set export NOECHOCHECK=1 to go anyway)"
return 0
done
return 1
}
find_multiechos(){
local outdir="$1"; shift
local outname="$1"; shift
local ext="$1"; shift
me_name=$(perl -pe 's/_(bold|sbref)$/_echo-*_$1/' <<< "$outname");
find "$outdir" -maxdepth 1 -type f \
\( -name "$outname$ext" -or -name "$me_name$ext" \)
}
add_json_task(){
local outdir="$1"; shift
local outname="$1"; shift
local task
[[ $outname =~ task-([^-_.]*) ]] && task=${BASH_REMATCH[1]}
if [ -z "$task" ]; then
echo "WARNING: unknown taskname for $outdir/$outname!";
return 0
else
verbose "# adding task to json in '$outdir' for '$outname'"
find_multiechos "$outdir" "$outname" ".json" |
xargs -rI{} grep -L TaskName {} |
xargs -rI{} $DRYRUN sed -i "s/^{/{\\n \"TaskName\": \"$task\",/" {}
verbose "# finished adding task name to $outdir/$outname.json"
fi
return 0
}
main(){
set -euo pipefail
set_dcm2niix # DCM2NIIX might be dcm2niix_afni or given by user
[ $# -ne 2 ] && usage
nii_out="$1"; shift
dcm_folder="$*"; shift
[ -r "$nii_out" ] && echo "# have '$nii_out'" && exit 0
[ ${nii_out:(-7)} != ".nii.gz" ] &&
echo "output must have .nii.gz extention!" && exit 1
test -z "$NOECHOCHECK" && multiecho_exists "$nii_out" && exit 0
# should let dcm2niix just fail instead of doing the work here?
# [ $(find -L "$dcm_folder" \( -type f -or -type l \) -print -quit|wc -l) -le 0 ] && echo "# '$dcm_folder' DNE or is empty?" && exit 1
outname="$(basename "$nii_out" .nii.gz)"
outdir="$(dirname "$nii_out")"
[ ! -d "$outdir" ] && $DRYRUN mkdir -p "$outdir"
#pretimestamp=$(mktemp /tmp/dcmnii_XXXXXX) # -newercm $pretimestamp
# TODO: add trap to cleanup file?
$DRYRUN $DCM2NIIX -z y -b y -f "$outname" -o "$outdir" "$dcm_folder"
[ -n "$DRYRUN" ] && exit 0
# if above failed, script would bail (set -e)
# so if we don't have the file it's probably has multiple echos
case $outname in
*magnitude|*magnitude[12])
rename-recent have? || exit 1 # dont convert if we have the wrong rename
# 20210113
# dcm2nii v1.0.2018 (ginger) only puts _e2 on the second echo
# if we have _e2 and no _e1, the no _e* version is magnitude1
[ -r "$outdir"/*magnitude.nii.gz -a -r "$outdir"/*magnitude_e2.nii.gz ] &&
rename-recent 's/magnitude\.(nii.gz|json)$/_e1.$1/' "$outdir" "*magnitude\.*" -v
rename-recent 's/[12]?_e(\d.(nii.gz|json))$/$1/' "$outdir" "$outname*" -v
# update output for 3dNotes and final check
[[ $outname =~ magnitude$ ]] && nii_out="$outdir/${outname}1.nii.gz"
;;
*phase|*phasediff)
rename-recent have? || exit 1 # dont convert if we have the wrong rename
rename-recent 's/_e\d+(_ph)?(.nii.gz|.json)$/$2/' "$outdir" "$outname*"
;;
*bold|*sbref)
rename-epi-multiecho "$outdir" "$outname"
;;
esac
# add rest taskname to json sidecar
[[ $outname =~ task-.*_bold ]] &&
add_json_task "$outdir" "$outname"
local nii_outs
mapfile -t nii_outs < <(find_multiechos "$outdir" "$outname" .nii.gz)
if [ ${#nii_outs[@]} -le 0 ]; then
echo -e "ERROR: did not create '$nii_out'.\n\t'$dcm_folder' has a multi echo sequence??!"
echo "NOTE: $0 can deal with this if the output name ends with 'magnitude1.nii.gz' or 'phase.nii.gz'"
echo " or for _bold and _sbref files"
exit 1
fi
# 3dNotes likes to complain. silence it
export AFNI_NIFTI_TYPE_WARN=NO
export AFNI_NO_OBLIQUE_WARNING="YES"
verbose "# adding 3dNotes"
for nii_out_me in "${nii_outs[@]}"; do
$DRYRUN 3dNotes -h "$0 $dcm_folder $nii_out" "$nii_out_me"
done
}
[[ "$(caller)" != "0 "* ]] && set +u || main "$@"