#!/usr/bin/env tcsh @global_parse `basename $0` "$*" ; if ($status) exit 0 set here = "$PWD" set this_prog = "@djunct_4d_imager" # --------------------- revision history ------------------------- # #set rev_dat = "Feb 19, 2017" #set version = "1.0" # + birth # #set rev_dat = "Feb 20, 2017"; set version = "1.1" # + no need to gzip temp files # #set rev_dat = "Feb 24, 2017"; set version = "1.2" # + can output movies # #set rev_dat = "Feb 27, 2017"; set version = "1.3" # + slice number there as IJK in corner # #set set version = "1.4"; set rev_dat = "May 20, 2017" # + more unique naming of wdir, use rand id; safer for cluster # runs # #set set version = "1.5"; set rev_dat = "Sep 04, 2017" # + use the new @chauffeur*, with -prefix only # + and for this program, also use just -prefix # #set set version = "1.6"; set rev_dat = "Sep 14, 2017" # + run with subbrick selection now (didn't before...) # #set set version = "2.0"; set rev_dat = "Nov 24, 2017" # + helpify this program # #set set version = "2.1"; set rev_dat = "Nov 29, 2017" # + ~bug fix: voxel sizes got ignored, and for dsets with non-iso # voxel dims, images could look squashed, etc. Fixed now by # putting a 3drefit command in. # #set set version = "2.2"; set rev_dat = "Feb 20, 2018" # + miiinor: modify output filename postfix # #set version = "2.3"; set rev_dat = "Feb 12, 2019" # + [PT] change "checks" to use '3dinfo -prefix ...' as a better # methodology # #set version = "2.31"; set rev_dat = "Mar 16, 2021" # [PT] AFNI_COMPRESSOR -> NONE # #set version = "2.32"; set rev_dat = "Sep 27, 2021" # + [PT] chauffeur label_size 3 -> 4, bc imseq.c shifted all sizes # down one level # set version = "2.4"; set rev_dat = "Aug 22, 2024" # + [PT] new opts to turn off sepscl or onescl images # - also, can turn off various slice views # # ----------------- find AFNI and set viewer --------------------- setenv AFNI_ENVIRON_WARNINGS NO setenv AFNI_COMPRESSOR NONE # find AFNI binaries directory and viewer location set adir = "" set my_viewer = "" which afni >& /dev/null if ( $status ) then echo "** Cannot find 'afni' (?)." goto BAD_EXIT else set aa = `which afni` set adir = $aa:h endif # default location of viewer: user could modify! set my_viewer = "$adir/@chauffeur_afni" set my_calc_func = "$adir/adjunct_calc_mont_dims.py" set tmp_code = `3dnewid -fun11` # should be essentially unique hash set wdir = "__WORK_imgr_${tmp_code}" # working directory set DO_CLEAN = 1 set iset = "" set odir = "" set opref = "FOURD_IMAGES" set mfile0 = "tmp4d.nii" set svals = ( "-" "-" "-" ) # init, bc we can turn off slices # final order? set imgview = ( "axi" "cor" "sag" ) set mview = ( "axial" "coronal" "sagittal" ) set movie = "" # not on by default, at the moment # default to make all styles and slice views, but can turn off set DO_ONESCL = 1 set DO_SEPSCL = 1 set DO_AXI = 1 set DO_COR = 1 set DO_SAG = 1 # ------------------- process options, a la rr ---------------------- if ( $#argv == 0 ) goto SHOW_HELP set ac = 1 while ( $ac <= $#argv ) # terminal options if ( ("$argv[$ac]" == "-h" ) || ("$argv[$ac]" == "-help" )) then goto SHOW_HELP endif if ( "$argv[$ac]" == "-ver" ) then goto SHOW_VERSION endif # --------------- input dset(s) ---------------- if ( "$argv[$ac]" == "-inset" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set iset = "$argv[$ac]" # ----------------- outputs --------------------- # [PT: Sep 04, 2017] Update output to allow prefix to have # dirname/path, as well; no need for separate '-outdir' else if ( "$argv[$ac]" == "-prefix" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set opref = `basename "$argv[$ac]"` set odir = `dirname "$argv[$ac]"` else if ( "$argv[$ac]" == "-do_movie" ) then if ( $ac >= $#argv ) goto FAIL_MISSING_ARG @ ac += 1 set movie = "$argv[$ac]" if ( ( $movie == "MPEG" ) || ( $movie == "AGIF" ) ) then echo "++ OK, will make a movie of type $movie." else echo "** ERROR: '$movie' is NOT an allowed movie format!" echo " -> must be either 'MPEG' or 'AGIF'" goto BAD_EXIT endif else if ( "$argv[$ac]" == "-no_sepscl" ) then set DO_SEPSCL = 0 else if ( "$argv[$ac]" == "-no_onescl" ) then set DO_ONESCL = 0 else if ( "$argv[$ac]" == "-no_axi" ) then set DO_AXI = 0 else if ( "$argv[$ac]" == "-no_cor" ) then set DO_COR = 0 else if ( "$argv[$ac]" == "-no_sag" ) then set DO_SAG = 0 #else if ( "$argv[$ac]" == "-no_clean" ) then # set DO_CLEAN = 0 else echo "** unexpected option #$ac = '$argv[$ac]'" goto BAD_EXIT endif @ ac += 1 end # ======================================================================= # ============================ ** SETUP ** ============================== # ======================================================================= echo "++ Start imager script version: $version" echo "" echo "++ My command:" echo " $this_prog $argv" # ------------------------------------------------------------------- # NEED these two inputs if ( "$iset" == "" ) then echo "** ERROR: no file input??" goto BAD_EXIT endif # make sure we can read inset OK set check = `3dinfo -prefix "$iset"` if ( "$check" == "NO-DSET" ) then echo "** ERROR: can't find inset file: $iset" goto BAD_EXIT else echo "++ Found inset file: $iset" endif # For the main loop below over all_ii, we always have the following # relations bc of the resampling above: # 1 -> axi # 2 -> cor # 3 -> sag # Therefore, instead of always looping ii over (1 2 3), we can pick # and choose, based on what the users want; they might want to turn # off some slice planes, for speed set all_ii = () if ( ${DO_AXI} ) then set all_ii = ( ${all_ii} 1 ) endif if ( ${DO_COR} ) then set all_ii = ( ${all_ii} 2 ) endif if ( ${DO_SAG} ) then set all_ii = ( ${all_ii} 3 ) endif # ... and then make sure we still have work to do if ( ${#all_ii} == 0 ) then echo "+* WARNING: user deselected all slice views, no images to make" goto GOOD_EXIT endif # Also verify that we haven't turned off all images if ( ${DO_ONESCL} == 0 && ${DO_SEPSCL} == 0 ) then echo "+* WARNING: user turned off onescl and sepscl, no images to make" goto GOOD_EXIT endif # ===================== output dir + wdir ======================= # check output directory, use input one if nothing given # [PT: Sep 04, 2017] Probably never need this first condition, after # change from now if ( "$odir" == "" ) then # default output dir, if nothing input; it must exist already, # because a file is in it set odir = `dirname "$iset"` echo "\n++ No output directory specified by the user." echo "++ Using the input file's directory by default:" echo "\t$odir" else if ( ! -e $odir ) then echo "++ Making new output directory: $odir" mkdir $odir endif endif # make the working directory if ( ! -e $odir/$wdir ) then echo "++ Making working directory: $odir/$wdir" mkdir $odir/$wdir else echo "+* WARNING: Somehow found a premade working directory (?):" echo " $odir/$wdir" endif # ==================================================================== echo "\n-----> STARTING imager ---->" # --------------- copy inset, go to wdir ------------------------ 3dresample \ -overwrite \ -inset "$iset" \ -orient RPI \ -prefix $odir/$wdir/$mfile0 cd $odir/$wdir # ---------------- proc --------------------- # silly stuff to deal with orientation set ori = `3dinfo -orient "$mfile0"` set ori0 = `echo $ori | awk '{print substr($0,1,1)}'` set ori1 = `echo $ori | awk '{print substr($0,2,1)}'` set ori2 = `echo $ori | awk '{print substr($0,3,1)}'` set all_ori = ( $ori0 $ori1 $ori2 ) set dim0 = `3dinfo -n4 "$mfile0"` # **see note above about creating all_ii array foreach ii ( ${all_ii} ) if ( $ii == 1 ) then echo "++ Iteration #$ii" set mfile = "$mfile0" set mcopy = "" else echo "++ Iteration #$ii" set mcopy = "COPY.nii" if ( $ii == 2 ) then set newori = "${ori0}${ori2}${ori1}" else set newori = "${ori2}${ori0}${ori1}" endif 3dcalc \ -overwrite \ -a "$mfile0" \ -expr 'a' \ -prefix "$mcopy" 3drefit \ -orient "$newori" \ "$mcopy" 3dresample \ -overwrite \ -prefix "$mcopy" \ -orient $ori \ -inset "$mcopy" set mfile = "$mcopy" endif set slpref = "sssli" set ccpref = "padcombo" set ovol = "${ccpref}.nii" # where the calc function is/can be called set calc_pad = "${ccpref}.dat" set morient = `3dinfo -orient "$mfile"` # {float|byte|short}: assume types are same across vol! set mdtype = `3dinfo -datum "$mfile""[0]"` set mdim = `3dinfo -n4 "$mfile"` @ slnum = ( $mdim[3] / 2 ) + 3 set svals[$ii] = $slnum echo "++ Going to view slice [${slnum}]" # ----------------- set type of number --------------- # just make everything float, so no negs?? set mstr = "" if ( "$mdtype" == "float" ) then set mstr = "f" else set mstr = "f" echo "+* WARNING: dset wasn't floats, but will be converted" echo " to float for image-making purposes!" endif #else if ( "$mdtype" == "complex" ) then # set mstr = "c" #else if ( "$mdtype" == "double" ) then # set mstr = "d" #else if ( "$mdtype" == "short" ) then # set mstr = "s" #else if ( "$mdtype" == "byte" ) then # set mstr = "b" #else if ( "$mdtype" == "int" ) then # set mstr = "i" #else # echo "** Unrecognized type: $mdtype ! Exiting" # goto BAD_EXIT #endif # ---------------- calculate final dims + padding ---------- $my_calc_func $mdim[4] $calc_pad set padpars = `grep -v "#" $calc_pad` set Npad = $padpars[2] set Ncol = $padpars[3] set Nrow = $padpars[4] # ---------------- generate slices ---------------------- # make slices-- they should be float anyways, but saying so # explicitly at the moment echo "++ Slicing..." from3d \ -raw \ -float \ -prefix "$slpref" \ -input $mfile \ -zfirst $slnum -zlast $slnum # --------------- combine slices and pad, if nec ------------ echo "++ ...and gluing." # at the moment, have made eeeverything from from3d to be a float! to3d \ -orient $morient \ -prefix "$ovol" \ "3D${mstr}:0:0:${mdim[1]}:${mdim[2]}:1:${slpref}*" # [PT: Nov 29, 2017] make sure proper voxel dims are remembered set vdel = `3dinfo -ad3 $mfile` 3drefit \ -xdel $vdel[1] \ -ydel $vdel[2] \ -zdel $vdel[3] \ "$ovol" # Feb 24, 2017: lights, camera, action! if ( "$movie" != "" ) then echo "++ Making '${mview[$ii]}' movie" # movie of the unpadded file: each view is pretending to be axial if ( ${DO_ONESCL} ) then $my_viewer \ -ulay "$ovol" \ -prefix "${opref}_qc_onescl_$ii" \ -olay_off \ -save_ftype "$movie" \ -montx 1 \ -monty 1 \ -set_ijk 0 0 0 \ -ulay_range 1% 99.5% \ -label_mode 1 -label_size 4 \ -image_label_ijk endif if ( ${DO_SEPSCL} ) then $my_viewer \ -ulay "$ovol" \ -prefix "${opref}_qc_sepscl_$ii" \ -olay_off \ -save_ftype "$movie" \ -montx 1 \ -monty 1 \ -set_ijk 0 0 0 \ -globalrange "SLICE" \ -ulay_range 1% 99% \ -label_mode 1 -label_size 4 \ -image_label_ijk endif endif if ( $Npad > 0 ) then printf "++ Padding by $Npad, to make output image " printf "$Ncol x $Nrow\n" 3dZeropad \ -overwrite \ -prefix "$ovol" \ -S $Npad \ "$ovol" endif # ------------------ snapshot(s) ----------------------- # global range, almost all if ( ${DO_ONESCL} ) then $my_viewer \ -ulay "$ovol" \ -prefix "${opref}_qc_onescl_$ii" \ -olay_off \ -montx $Ncol \ -monty $Nrow \ -ulay_range 1% 99.5% \ -label_mode 1 -label_size 4 \ -image_label_ijk endif # slicewise ranges if ( ${DO_SEPSCL} ) then $my_viewer \ -ulay "$ovol" \ -prefix "${opref}_qc_sepscl_$ii" \ -olay_off \ -montx $Ncol \ -monty $Nrow \ -globalrange "SLICE" \ -ulay_range 1% 99% \ -label_mode 1 -label_size 4 \ -image_label_ijk endif # some images are not useful, so, bye bye; rename useful one # correctly, and put into odir (out of wdir) if ( 1 ) then \rm ${opref}_*_${ii}.sag.* \rm ${opref}_*_${ii}.cor.* foreach ff ( `\ls ${opref}_*_${ii}.axi.*` ) set gg = `echo $ff | sed "s/_${ii}.axi/.${imgview[$ii]}/g"` \mv $ff ../$gg end if ( 0 ) then # if ( "$movie" != "" ) then echo "++ copying up movie" if ( "$movie" == "AGIF" ) then set mext = "gif" else set mext = "mpg" endif foreach ff ( `\ls ${opref}_*_${ii}.axi."$mext"` ) set gg = `echo $ff | sed "s/_${ii}.axi/.${imgview[$ii]}/g"` \mv $ff ../$gg end endif endif # ------------------ clean, if desired ----------------------- if ( $DO_CLEAN == 1 ) then echo "\n++ Cleaning up old files... finishing iteration ${ii}/3.\n\n" \rm $ovol $calc_pad ${slpref}* if ( "$mcopy" != "" ) then \rm "$mcopy" endif endif end # exit wdir, now in odir cd ../ if ( $DO_CLEAN == 1 ) then \rm -rf $wdir endif # prepare for final output reporting for slices that were processed set vvx = "-" set vvy = "-" if ( "${svals[3]}" != "-" ) then @ vvx = $dim0[1] - ${svals[3]} endif if ( "${svals[2]}" != "-" ) then @ vvy = $dim0[2] - ${svals[2]} endif # have to put slice values in (i,j,k) order. echo "" echo "++ DONE. RPI slice values: (${svals[3]}, ${svals[2]}, ${svals[1]})." echo "++ In AFNI GUI slide vals: (${vvx}, ${vvy}, ${svals[1]})." echo "\n" goto GOOD_EXIT # ======================================================================== # ======================================================================== SHOW_HELP: cat << EOF # ------------------------------------------------------------------------ The program is useful for viewing the same slice across the 'time' dimension of a 4D data set. It is used in several of FATCAT's fat_proc* functions (e.g., viewing DWIs). But because it seemed so useful, it now has some basic help for basic usage by *expert* AFNI users! The program outputs the following in sets of three, one for each spatial axis: + an image of the same central slice across volumes along the time axis, with the brightness range constant across volume ("*onescl*" images); that is, the same grayscale in each panel corresponds to the same numerical value. + an image of the same central slice across volumes along the time axis, with the brightness range possibly *varying* for each panel across volume ("*sepscl*" images); that is, the grayscale value in each panel can (and likely will) correspond to *a different* numerical value. Useful, for example, for checking details in DWIs, where the expected scale of values can change dramatically across volumes. + (with option flag) a movie version of the "onescl" images, showing one slice at a time. + (with option flag) a movie version of the "sepscl" images, showing one slice at a time. The panel dimensionality in each of the above montage images is calculated to be approximately golden ratio-ish. (with blank panels appended as deemed desirable; blank slices are *not* appended to the dset, they are just added for the montage visualization). Slice numbers are shown in both the image panels and the movie panels. This program is a very basic wrapper around @chauffeur_afni. It is useful as a quality control (QC) generator, driving AFNI functionality to make images *and* movies of a data set while processing (working in a virtual X11 environment using xvfb, so that this generates images/movies even on a remote terminal). written by PA Taylor (NIMH, NIH, USA). # ======================================================================== -help, -h :see helpfile (here!) -ver :see version number -inset UUU :ulay dset (required). Probably 4D (hence the name of this program...). -prefix PPP :prefix for output files (required). -do_movie MTYPE :specify type of movie file. Basically, one of two options: MPEG AGIF This is optional; by default, a montage of PNG images is created: the same slice across the 4D set. -no_sepscl :do not create *sepscl* montage (or movie, if using -do_movie) -no_onescl :do not create *onescl* montage (or movie, if using -do_movie) -no_axi :do not create axial slice views -no_cor :do not create coronal slice views -no_sag :do not create sagittal slice views -no_clean :by default, the temporary directory made by this program is deleted when finishing. Use this option to keep the final intermediate files. # ======================================================================== EXAMPLE: # 1) Um, just view all the volumes in a DWI acquisition, both as a # montage saved as a PNG file and as an mpeg file. @djunct_4d_imager \ -inset MY_DWIs.nii.gz \ -prefix PRETTY_DWIS \ -do_movie AGIF # ------------------------------------------------------------------------ EOF goto GOOD_EXIT SHOW_VERSION: echo "version $version (${rev_dat})" goto GOOD_EXIT FAIL_MISSING_ARG: echo "** ERROR! Missing an argument after option flag: '$argv[$ac]'" goto BAD_EXIT BAD_EXIT: exit 1 # send everyone here, in case there is any cleanup to do GOOD_EXIT: exit 0