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