#include "mrilib.h" #include "r_new_resam_dset.h" #include "r_idisp.h" #define MAIN #define VERSION "Version 1.7a " /*---------------------------------------------------------------------- * 3dresample - create a new dataset by reorienting and resampling * an existing one * * This program can be used to * - change the orientation of a dataset to one that is specified * - change the dx, dy, dz spacing, to one that is specified * - master a dataset, so that its orientation and spacing matches * * usage: 3dresample [options] -prefix OUTPUT_DSET -inset INPUT_DSET * * options: * -help : detailed program info * -debug LEVEL : spit out info * -version : print version info * * -dxyz DX DY DZ : resample to a new grid * (DX, DY, DZ are real numbers in mm) * -orient OR_CODE : reorient to new orientation code * (a three character string, each * from the set {A,P, I,S, L,R}) * * -master MAST_DSET : apply orient/dxyz from MAST_DSET * * -rmode RESAM : one of {"NN", "Li", "Cu", "Bk"} * * examples: * 3dresample -orient "asl" -rmode NN -prefix asl.dset -inset inset+orig * 3dresample -dxyz 1.0 1.0 0.9 -prefix 119.dset -inset some.input+tlrc * 3dresample -master master+orig -prefix new.copy -inset old.copy+orig *---------------------------------------------------------------------- */ static char g_history[] = "----------------------------------------------------------------------\n" " history:\n" "\n" " 1.0 May 23, 2002\n" " - initial release\n" "\n" " 1.1 July 2, 2002\n" " - modified to fully align new data set grid to that of the master\n" "\n" " 1.2 July 29, 2002\n" " - no change here, but updated r_new_resam_dset() for view type\n" "\n" " 1.3 January 14, 2003\n" " - clear warp information before writing to disk (fix uncommon problem)\n" "\n" " 1.4 July 27, 2003\n" " - wrap unknown printed strings in NULL check\n" "\n" " 1.5 January 07, 2004\n" " - added suggestion of 3dfractionize to -help output\n" " - added '-hist' option\n" "\n" " 1.6 March 04, 2004\n" " - added check for RESAM_shortstr[] (to catch NN and Bk modes)\n" " - reversed order of history: recent at the bottom\n" "\n" " 1.7 July 26, 2004\n" " - passed NULL sublist to r_new_resam_dset()\n" "\n" " 1.7a March 22, 2005\n" " - removed tabs\n" "----------------------------------------------------------------------\n"; /*--- local stuff ------------------------------------------------------*/ #define USE_LONG 1 #define USE_SHORT 2 #define USE_VERSION 3 #define USE_HISTORY 4 #define DELTA_MIN 0.1 #define DELTA_MAX 99.9 #define RL_DEBUG_OFF 0 #define RL_DEBUG_LOW 1 #define RL_DEBUG_HIGH 2 typedef struct { THD_3dim_dataset * dset; THD_3dim_dataset * mset; double dx, dy, dz; char * orient; char * prefix; int resam; int debug; } options_t; int disp_opts_data ( char * info, options_t * opts ); int init_options ( options_t * opts, int argc, char * argv [] ); int resam_str2mode ( char * mode ); int sync_master_opts ( options_t * opts ); int usage ( char * prog, int level ); int write_results ( THD_3dim_dataset * dout, options_t * opts, int argc, char * argv [] ); /*----------------------------------------------------------------------*/ int main( int argc , char * argv[] ) { THD_3dim_dataset * dout; options_t opts; int ret_val; mainENTRY("3dresample"); machdep(); AFNI_logger("3dresample",argc,argv); /* validate inputs and init options structure */ if ( (ret_val = init_options(&opts, argc, argv)) != 0 ) return ret_val; /* actually resample and/or reorient the dataset */ dout = r_new_resam_dset( opts.dset, opts.mset, opts.dx, opts.dy, opts.dz, opts.orient, opts.resam, NULL); if ( dout == NULL ) { fprintf( stderr, "failure to resample dataset, exiting...\n" ); return FAIL; } return write_results( dout, &opts, argc, argv ); } /*---------------------------------------------------------------------- * init_options - validate inputs, give help, init options struct *---------------------------------------------------------------------- */ int init_options ( options_t * opts, int argc, char * argv [] ) { int ac; /* clear out the options structure */ memset( opts, 0, sizeof(options_t) ); for ( ac = 1; ac < argc; ac++ ) { if ( ! strncmp(argv[ac], "-help", 5) ) { usage( argv[0], USE_LONG ); return FAIL; } else if ( ! strncmp(argv[ac], "-hist", 5) ) { usage( argv[0], USE_HISTORY ); return FAIL; } else if ( ! strncmp(argv[ac], "-version", 2) ) { usage( argv[0], USE_VERSION ); return FAIL; } else if ( ! strncmp(argv[ac], "-debug", 6) ) { if ( (ac+1) >= argc ) { fputs( "option usage: -debug LEVEL\n", stderr ); usage( argv[0], USE_SHORT ); return FAIL; } opts->debug = atoi(argv[++ac]); if ( opts->debug < 0 || opts->debug > RL_DEBUG_HIGH ) { fprintf( stderr, "bad debug level <%d>, should be in [%d,%d]\n", opts->debug, RL_DEBUG_OFF, RL_DEBUG_HIGH ); usage( argv[0], USE_SHORT ); return FAIL; } } else if ( ! strncmp(argv[ac], "-dxyz", 3) ) /* dxyz */ { if ( (ac+3) >= argc ) { fputs( "option usage: -dxyz DX DY DZ\n", stderr ); usage( argv[0], USE_SHORT ); return FAIL; } opts->dx = atof(argv[++ac]); opts->dy = atof(argv[++ac]); opts->dz = atof(argv[++ac]); if ( (opts->dx < DELTA_MIN || opts->dx > DELTA_MAX) || (opts->dy < DELTA_MIN || opts->dy > DELTA_MAX) || (opts->dz < DELTA_MIN || opts->dz > DELTA_MAX) ) { fprintf( stderr, "dxyz must be in [%.1f,%.1f]\n", DELTA_MIN, DELTA_MAX ); return FAIL; } } else if ( ! strncmp(argv[ac], "-or", 3) ) /* orientation */ { if ( (ac+1) >= argc ) { fputs( "option usage: -orient OR_STRING\n", stderr ); usage( argv[0], USE_SHORT ); return FAIL; } opts->orient = argv[++ac]; } else if ( ! strncmp(argv[ac], "-master", 5) ) /* master */ { if ( (ac+1) >= argc ) { fputs( "option usage: -master MAST_DSET\n", stderr ); usage( argv[0], USE_SHORT ); return FAIL; } opts->mset = THD_open_dataset( argv[++ac] ); if ( ! ISVALID_DSET(opts->mset) ) { fprintf( stderr, "invalid master dataset <%s>\n", argv[ac] ); return FAIL; } } else if ( ! strncmp(argv[ac], "-zeropad", 5) ) /* zeropad */ { fputs("warning: '-zeropad' is no longer a valid option\n", stderr); /* but still move on... */ } else if ( ! strncmp(argv[ac], "-rmode", 6) ) /* resample mode */ { if ( (ac+1) >= argc ) { fputs( "option usage: -rmode RESAMPLE_MODE\n", stderr ); usage( argv[0], USE_SHORT ); return FAIL; } if ( ( (opts->resam = resam_str2mode(argv[++ac]) ) < 0 ) || ( opts->resam > LAST_RESAM_TYPE ) ) { fprintf( stderr, "invalid resample mode <%s>\n", argv[ac] ); return FAIL; } } else if ( ! strncmp(argv[ac], "-prefix", 4) ) /* new dset prefix */ { if ( (ac+1) >= argc ) { fputs( "option usage: -prefix OUTPUT_PREFIX\n", stderr ); usage( argv[0], USE_SHORT ); return FAIL; } opts->prefix = argv[++ac]; if ( !THD_filename_ok(opts->prefix) ) { fprintf( stderr, "invalid output prefix <%s>\n", opts->prefix ); return usage( argv[0], USE_SHORT ); } } else if ( ! strncmp(argv[ac], "-inset", 3) || ! strncmp(argv[ac], "-input", 6) ) /* input dset */ { if ( (ac+1) >= argc ) { fputs( "option usage: -inset INPUT_DSET\n", stderr ); usage( argv[0], USE_SHORT ); return FAIL; } opts->dset = THD_open_dataset( argv[++ac] ); if ( ! ISVALID_DSET(opts->dset) ) { fprintf( stderr, "invalid input dataset <%s>\n", argv[ac] ); return FAIL; } } else /* invalid option */ { fprintf( stderr, "invalid option <%s>\n", argv[ac] ); usage( argv[0], USE_SHORT ); return FAIL; } } if ( !ISVALID_DSET(opts->dset) || (opts->prefix == NULL) ) { fprintf( stderr, "missing prefix or input dset, exiting...\n" ); usage( argv[0], USE_SHORT ); return FAIL; } if ( opts->debug >= RL_DEBUG_LOW ) { disp_opts_data( "++ options initialized: ", opts ); if ( opts->debug >= RL_DEBUG_HIGH ) /* dset is valid by now */ { r_idisp_thd_3dim_dataset( "inset : ", opts->dset ); r_idisp_thd_dataxes ( "inset : ", opts->dset->daxes ); r_idisp_thd_datablock ( "inset : ", opts->dset->dblk ); if ( opts->dset->dblk ) r_idisp_thd_diskptr ( "inset : ", opts->dset->dblk->diskptr ); } } if ( sync_master_opts( opts ) ) return FAIL; return 0; } #if 0 /* lose new_zeropad_dset */ /*---------------------------------------------------------------------- * new_zeropad_dset - create a new zeropadded dataset * * Replace dout with a new padded one. * * This function copies the master part of 3dZeropad.c. *---------------------------------------------------------------------- */ int new_zeropad_dset ( options_t * opts, THD_3dim_dataset ** dout ) { THD_3dim_dataset * tmp_dset; THD_dataxes * max = opts->mset->daxes, * iax = (*dout)->daxes; int nerr = 0; float mxbot,mybot,mzbot, mxtop,mytop,mztop, mdx,mdy,mdz; float ixbot,iybot,izbot, ixtop,iytop,iztop, idx,idy,idz; int mnx,mny,mnz, inx,iny,inz; int add_xb,add_xt, add_yb,add_yt, add_zb,add_zt; int add_I=0, add_S=0, add_A=0, add_P=0, add_L=0, add_R=0; /* check if datasets are oriented the same */ if( max->xxorient != iax->xxorient || max->yyorient != iax->yyorient || max->zzorient != iax->zzorient ) { fputs("error: orientation mismatch!\n", stderr ); nerr++; } /* check if datasets have same voxel dimensions */ mdx = max->xxdel; mdy = max->yydel; mdz = max->zzdel; idx = iax->xxdel; idy = iax->yydel; idz = iax->zzdel; mnx = max->nxx; mny = max->nyy; mnz = max->nzz; inx = iax->nxx; iny = iax->nyy; inz = iax->nzz; if( fabs(mdx-idx) > 0.01*fabs(mdx) || fabs(mdy-idy) > 0.01*fabs(mdy) || fabs(mdz-idz) > 0.01*fabs(mdz) ) { fputs("error: voxel size mismatch!\n", stderr); nerr++; } if ( nerr > 0 ) return FAIL; /* we have already printed the failure cause(s) */ /* the data looks okay */ /* calculate coords at top and bottom of each dataset */ mxbot = max->xxorg; mxtop = mxbot + mnx*mdx; mybot = max->yyorg; mytop = mybot + mny*mdy; mzbot = max->zzorg; mztop = mzbot + mnz*mdz; ixbot = iax->xxorg; ixtop = ixbot + inx*idx; iybot = iax->yyorg; iytop = iybot + iny*idy; izbot = iax->zzorg; iztop = izbot + inz*idz; /* calculate amount to add/trim at each face */ add_xb = (int) rint((ixbot-mxbot)/idx); add_xt = (int) rint((mxtop-ixtop)/idx); add_yb = (int) rint((iybot-mybot)/idy); add_yt = (int) rint((mytop-iytop)/idy); add_zb = (int) rint((izbot-mzbot)/idz); add_zt = (int) rint((mztop-iztop)/idz); /* map trims from x,y,z to RL,AP,IS coords */ switch( iax->xxorient ){ case ORI_R2L_TYPE: add_R = add_xb; add_L = add_xt; break; case ORI_L2R_TYPE: add_L = add_xb; add_R = add_xt; break; case ORI_I2S_TYPE: add_I = add_xb; add_S = add_xt; break; case ORI_S2I_TYPE: add_S = add_xb; add_I = add_xt; break; case ORI_A2P_TYPE: add_A = add_xb; add_P = add_xt; break; case ORI_P2A_TYPE: add_P = add_xb; add_A = add_xt; break; default : fputs("bad xxorient!\n", stderr); return FAIL; } switch( iax->yyorient ){ case ORI_R2L_TYPE: add_R = add_yb; add_L = add_yt; break; case ORI_L2R_TYPE: add_L = add_yb; add_R = add_yt; break; case ORI_I2S_TYPE: add_I = add_yb; add_S = add_yt; break; case ORI_S2I_TYPE: add_S = add_yb; add_I = add_yt; break; case ORI_A2P_TYPE: add_A = add_yb; add_P = add_yt; break; case ORI_P2A_TYPE: add_P = add_yb; add_A = add_yt; break; default : fputs("bad yyorient!\n", stderr); return FAIL; } switch( iax->zzorient ){ case ORI_R2L_TYPE: add_R = add_zb; add_L = add_zt; break; case ORI_L2R_TYPE: add_L = add_zb; add_R = add_zt; break; case ORI_I2S_TYPE: add_I = add_zb; add_S = add_zt; break; case ORI_S2I_TYPE: add_S = add_zb; add_I = add_zt; break; case ORI_A2P_TYPE: add_A = add_zb; add_P = add_zt; break; case ORI_P2A_TYPE: add_P = add_zb; add_A = add_zt; break; default : fputs("bad zzorient!\n", stderr); return FAIL; } if ( opts->debug >= RL_DEBUG_LOW ) { printf( "++ zeropad: (I,S,A,P,L,R) = (%d,%d,%d,%d,%d,%d)\n", add_I, add_S, add_A, add_P, add_L, add_R ); } /* pad if we need to */ if ( add_I || add_S || add_A || add_P || add_L || add_R ) { tmp_dset = THD_zeropad( *dout, add_I, add_S, add_A, add_P, add_L, add_R, opts->prefix, ZPAD_PURGE ); if ( !ISVALID_DSET( tmp_dset ) ) { fputs( "THD_zeropad failed!\n", stderr ); return FAIL; } DSET_delete( *dout ); *dout = tmp_dset; } return 0; } #endif /* end chop of new_zeropad_dset() */ /*----------------------------------------------------------------------*/ int usage ( char * prog, int level ) { if ( level == USE_SHORT ) { fprintf( stderr, "usage: %s [options] -prefix OUT_DSET -inset IN_DSET\n" "usage: %s -help\n", prog, prog ); return 0; } else if ( level == USE_LONG ) { printf( "\n" "%s - reorient and/or resample a dataset\n" "\n" " This program can be used to change the orientation of a\n" " dataset (via the -orient option), or the dx,dy,dz\n" " grid spacing (via the -dxyz option), or change them\n" " both to match that of a master dataset (via the -master\n" " option).\n" "\n" " ** Warning: this program is not meant to transform datasets\n" " between view types (such as '+orig' and '+tlrc').\n" "\n" " For that purpose, please see '3dfractionize -help'.\n" "\n" "------------------------------------------------------------\n" "\n" " usage: %s [options] -prefix OUT_DSET -inset IN_DSET\n" "\n" " examples:\n" "\n" " %s -orient asl -rmode NN -prefix asl.dset -inset in+orig\n" " %s -dxyz 1.0 1.0 0.9 -prefix 119.dset -inset in+tlrc\n" " %s -master master+orig -prefix new.dset -inset old+orig\n" "\n" " note:\n" "\n" " Information about a dataset's voxel size and orientation\n" " can be found in the output of program 3dinfo\n" "\n" "------------------------------------------------------------\n" "\n" " options: \n" "\n" " -help : show this help information\n" "\n" " -hist : output the history of program changes\n" "\n" " -debug LEVEL : print debug info along the way\n" " e.g. -debug 1\n" " default level is 0, max is 2\n" "\n" " -version : show version information\n" "\n" " -dxyz DX DY DZ : resample to new dx, dy and dz\n" " e.g. -dxyz 1.0 1.0 0.9\n" " default is to leave unchanged\n" "\n" " Each of DX,DY,DZ must be a positive real number,\n" " and will be used for a voxel delta in the new\n" " dataset (according to any new orientation).\n" "\n" " -orient OR_CODE : reorient to new axis order.\n" " e.g. -orient asl\n" " default is to leave unchanged\n" "\n" " The orientation code is a 3 character string,\n" " where the characters come from the respective\n" " sets {A,P}, {I,S}, {L,R}.\n" "\n" " For example OR_CODE = LPI is the standard\n" " 'neuroscience' orientation, where the x-axis is\n" " Left-to-Right, the y-axis is Posterior-to-Anterior,\n" " and the z-axis is Inferior-to-Superior.\n" "\n" " -rmode RESAM : use this resampling method\n" " e.g. -rmode Linear\n" " default is NN (nearest neighbor)\n" "\n" " The resampling method string RESAM should come\n" " from the set {'NN', 'Li', 'Cu', 'Bk'}. These\n" " are for 'Nearest Neighbor', 'Linear', 'Cubic'\n" " and 'Blocky' interpolation, respectively.\n" " See 'Anat resam mode' under the 'Define Markers'\n" " window in afni.\n" "\n" " -master MAST_DSET: align dataset grid to that of MAST_DSET\n" " e.g. -master master.dset+orig\n" "\n" " Get dxyz and orient from a master dataset. The\n" " resulting grid will match that of the master. This\n" " option cannot be used with -dxyz or -orient.\n" "\n" " -prefix OUT_DSET : required prefix for output dataset\n" " e.g. -prefix reori.asl.pickle\n" "\n" " -inset IN_DSET : required input dataset to reorient\n" " e.g. -inset old.dset+orig\n" "\n" "------------------------------------------------------------\n" "\n" " Author: R. Reynolds - %s\n" "\n", prog, prog, prog, prog, prog, VERSION ); return 0; } else if ( level == USE_HISTORY ) { fputs( g_history, stdout ); return 0; } else if ( level == USE_VERSION ) { printf( "%s %s, compile date: %s\n", prog, VERSION, __DATE__ ); return 0; } fprintf( stderr, "usage called with illegal level <%d>\n", level ); return FAIL; } /*----------------------------------------------------------------------*/ int resam_str2mode ( char * modestr ) { int mode; for (mode = FIRST_RESAM_TYPE; mode <= LAST_RESAM_TYPE; mode++ ) { if ( ! strncmp( modestr, RESAM_typestr[mode], 2 ) ) return mode; else if ( ! strncmp( modestr, RESAM_shortstr[mode], 2 ) ) return mode; } return FAIL; } /*----------------------------------------------------------------------*/ int write_results ( THD_3dim_dataset * dout, options_t * opts, int argc, char * argv [] ) { /* set filename */ EDIT_dset_items( dout, ADN_prefix, opts->prefix, ADN_none ); if ( THD_is_file(DSET_HEADNAME(dout)) ) { fprintf( stderr, "error: cannot overwrite existing dataset <%s>\n", DSET_HEADNAME(dout) ); return FAIL; } /* set number of time-axis slices to 0 */ if( DSET_NUM_TTOFF(dout) > 0 ) EDIT_dset_items( dout, ADN_nsl, 0, ADN_none ); /* since we are writing data to disk, clear warp info */ ZERO_IDCODE( dout->warp_parent_idcode ); dout->warp_parent_name[0] = '\0'; dout->warp = NULL; /* add to old history */ tross_Copy_History( opts->dset , dout ); tross_Make_History( "3dresample", argc, argv, dout ); /* write the output files */ if ( DSET_write( dout ) != True ) { fputs( "failure: cannot write dataset, exiting...\n", stderr ); return FAIL; } if ( opts->debug >= RL_DEBUG_LOW ) { printf( "dset <%s> has been written to disk\n", opts->prefix ); if ( opts->debug >= RL_DEBUG_HIGH ) { r_idisp_thd_3dim_dataset( "final dset : ", dout ); r_idisp_thd_dataxes ( "final daxes : ", dout->daxes ); r_idisp_thd_datablock ( "final dblk : ", dout->dblk ); if ( dout->dblk ) r_idisp_thd_diskptr ( "final diskp : ", dout->dblk->diskptr ); } } return 0; } /*----------------------------------------------------------------------*/ int sync_master_opts ( options_t * opts ) { THD_dataxes * dax; if ( !opts->mset ) return 0; /* OK */ if ( ! ISVALID_DSET(opts->mset) || ! ISVALID_DATAXES(opts->mset->daxes ) ) { fputs( "error: master dset or daxes not valid, exiting...\n", stderr ); return FAIL; /* non-NULL but invalid is bad */ } if ( ( opts->dx != 0.0 ) || ( opts->orient != NULL ) ) { fputs( "error: -dxyz and -orient are not valid with -master option, " "exiting...\n", stderr ); return FAIL; } /* all is okay, so fill dxyz and orientation code from master */ dax = opts->mset->daxes; opts->dx = fabs(dax->xxdel); opts->dy = fabs(dax->yydel); opts->dz = fabs(dax->zzdel); /* make space for orient string */ if ( (opts->orient = (char *)malloc(4 * sizeof(char)) ) == NULL ) { fputs( "failure: malloc failure for orient, exiting...\n", stderr ); return FAIL; } opts->orient[0] = ORIENT_typestr[dax->xxorient][0]; opts->orient[1] = ORIENT_typestr[dax->yyorient][0]; opts->orient[2] = ORIENT_typestr[dax->zzorient][0]; opts->orient[3] = '\0'; if ( opts->debug >= RL_DEBUG_LOW ) { disp_opts_data( "++ mastered options : ", opts ); if ( opts->debug >= RL_DEBUG_HIGH ) { r_idisp_thd_3dim_dataset("sync mset : ", opts->mset ); r_idisp_thd_dataxes ("sync mset : ", opts->mset->daxes ); r_idisp_thd_datablock ("sync mset : ", opts->mset->dblk ); if ( opts->mset->dblk ) r_idisp_thd_diskptr ("sync mset : ", opts->mset->dblk->diskptr); } } return 0; } /*----------------------------------------------------------------------*/ int disp_opts_data ( char * info, options_t * opts ) { if ( info ) fputs( info, stdout ); if ( opts == NULL ) { printf( "disp_opts_data: opts == NULL\n" ); return FAIL; } printf( "options struct at %p :\n" " dset = %p (%s)\n" " mset = %p (%s)\n" " (dx,dy,dz) = (%6.3f, %6.3f, %6.3f)\n" " orient = %.6s\n" " prefix = %.60s\n" " resam = %d\n" " debug = %d\n", opts, opts->dset, ISVALID_DSET(opts->dset) ? "valid" : "invalid", opts->mset, ISVALID_DSET(opts->mset) ? "valid" : "invalid", opts->dx, opts->dy, opts->dz, CHECK_NULL_STR(opts->orient), CHECK_NULL_STR(opts->prefix), opts->resam, opts->debug ); return 0; }