-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmknii
More file actions
executable file
·249 lines (215 loc) · 9.1 KB
/
mknii
File metadata and controls
executable file
·249 lines (215 loc) · 9.1 KB
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
#!/usr/bin/env bash
#
#run dcm2niix with 3dNotes wrapper. check for existing file
#
# 20201016WF init
# 20210113WF use rename-recent. nest into "main" (source for testing)
# 2026 support .zip
[ -n "${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 || :; }
noquiet() { [ -z "${MKNII_QUIET:-}" ] && echo "$*"; return 0; }
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
}
usage() {
cat <<HEREDOC
USAGE:
$(basename "$0") output.nii.gz path/to/dcm/
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)
remove dcm2niix output name's '_ph' if name also has part-phase
use DRYRUN=1 $(basename $0) ... to test
ARGUMENTS:
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
ENVIRONMENT:
DRYRUN=1 - show commands but dont create (testing command)
NOECHOCHECK=1 - write new files even if there is a '_e1.nii.gz' version
MEPATT=mGRE - additional output file name pattern that will have mutliecho output
DCM2NIIX=/p/dcm - alternative path to dcm2niix binary
VERBOSE=1 - show more of the process
MKNII_QUIET=1 - be quiet. Don't report "# have ..."
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 outdir="$1"; shift
local fileslike="$1"; shift
local pattern
# if only one phase output, remve the echo '_e1' part
# but if many, hope that MEPATT will catch
[ $(ls "$outdir"/$fileslike | wc -l) -le 1 ] &&
pattern='s/_e\d+(_ph)?(.nii.gz|.json)$/$2/' ||
pattern='s/(_ph)?(.nii.gz|.json)$/$2/'
_rename-recent "$pattern" "$outdir" "$fileslike"
}
rename-epi-multiecho(){
local outdir="$1"; shift
local outname="$1"; shift
local rename_patt='s/([^_-]+)_[ie](\d+).(json|nii.gz)/echo-$2_$1.$3/'
if [ -r "$outdir/${outname}_e1.nii.gz" -o -r "$outdir/${outname}_i00001.nii.gz" ]; then
echo "# trying to rename multiecho seq $outname";
_rename-recent "$rename_patt" "$outdir" "${outname}_e*"
fi
}
add_echo_name(){ perl -pe 's/_([^_-]+).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/.nii.gz/_i00001.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
noquiet "# have '$ME_name', considering that the same as '$nii_out' (set export NOECHOCHECK=1 to go anyway)"
return 0
done
return 1
}
# find all outputs. default '$outname', but also look also for _echo-
find_multiechos(){
local outdir="$1"; shift
local outname="$1"; shift
local ext="$1"; shift
# added 20251017
# GRE fmap magnitude mutliecho doesn't have _echo-, but the echo number appended directly to the suffix
local fmap_mag=()
[[ $outname =~ magnitude ]] && fmap_mag=(-or -name "$outname[12]$ext")
me_name=$(perl -pe 's/_([^_-]*)$/_echo-*_$1/' <<< "$outname");
find "$outdir" -maxdepth 1 -type f \
\( -name "$outname$ext" -or -name "$me_name$ext" "${fmap_mag[@]}" \)
}
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
local unzip_tmp="" # if input is zip file, track to rm tmp folder later
[ $# -ne 2 ] && echo "given $# not 2 arguments! '$*'" >&2 && usage
nii_out="$1"; shift
dcm_folder="$*"; shift
# not a definiative check. multi-echo might not catch here.
[ -r "$nii_out" ] && noquiet "# have '$nii_out'" && exit 0
[ "${nii_out:(-7)}" != ".nii.gz" ] &&
echo "ERROR: output '$nii_out' must have .nii.gz extention!" >&2 && exit 1
if [[ $dcm_folder =~ .zip$ ]]; then
unzip_tmp=$(mktemp -d ${TMPDIR:-/tmp}/dcmzip_XXXX)
zip_file=$dcm_folder
[ ! -d "$unzip_tmp" ] && echo "ERROR: failed to create temporary folder for unzipping!" && return 1
echo "# unzipping $zip_file to $unzip_tmp" >&2
$DRYRUN unzip -d "$unzip_tmp" "$zip_file"
# TODO: trap to cleanup zip file?
dcm_folder=$unzip_tmp
fi
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"
# TODO: add trap to cleanup file?
#pretimestamp=$(mktemp /tmp/dcmnii_XXXXXX) # -newercm $pretimestamp
$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
verbose "# checking '$outname' against *magnitude|*magnitude[12], *phase|*phasediff|*part-phase*, *bold|*sbref (multi-echo)"
multi_outs=0
case $outname in
*magnitude|*magnitude[12])
verbose "# mag '$outname'"
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"
multi_outs=1
;;
*phase|*phasediff|*part-phase*)
rename-phase "$outdir" "$outname*"
# 20210113 - recontwix phasediff_e2 - why no _e1?, no _ph either
[[ $outname =~ phasediff ]] &&
rename-recent 's/_e\d+(_ph)?(.nii.gz|.json)$/$2/' "$outdir" "$outname*"
;;
*bold|*sbref)
rename-epi-multiecho "$outdir" "$outname"
;;
esac
# TODO: integrate with case above for *bold|*sbref
if [[ -n ${MEPATT:-} ]] &&[[ $outname =~ ${MEPATT:-} ]]; then
echo "# '$outname' matches MEPATT '$MEPATT'" >&2
rename-epi-multiecho "$outdir" "$outname"
fi
# 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! Set mutliecho file output name pattern like 'export MEPATT=mGRE'."
echo "Or 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
# cleanup unziped file
[[ -n "$unzip_tmp" && -d "$unzip_tmp" && "$unzip_tmp" =~ dcmzip_ ]] && rm -r "$unzip_tmp"
return 0
}
[[ "$(caller)" != "0 "* ]] && set +u || main "$@"