00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 #include "afni.h"
00027 
00028 #ifndef ALLOW_PLUGINS
00029 #  error "Plugins not properly set up -- see machdep.h"
00030 #endif
00031 
00032 #include "retroicor.h"
00033 
00034 static char * PRIC_main( PLUGIN_interface * ) ;
00035 
00036 static char helpstring[] =
00037   " Purpose: Perform Retrospective Image Correction for physiological\n"
00038   "   motion effects, using a slightly modified version of the RETROICOR\n"
00039   "   algorithm described in:\n"
00040   " \n"
00041   "     Glover, G. H., Li, T., & Ress, D. (2000). Image-based method for\n"
00042   "   retrospective correction of physiological motion effects in fMRI:\n"
00043   "   RETROICOR. Magnetic Resonance in Medicine, 44, 162-167.\n"
00044   " \n"
00045   " Inputs:\n"
00046   " \n"
00047   "   Datasets: Input  = 3D+time dataset to process\n"
00048   "             Output = Prefix for new, corrected dataset\n"
00049   "             Ignore = Number of initial timepoints to ignore in input\n"
00050   "                      (These points will be passed through uncorrected)\n"
00051   " \n"
00052   "   Cardiac: Input     = 1D cardiac waveform data for correction\n"
00053   "            Output    = Filename for 1D cardiac phase output\n"
00054   "                        (Leave blank if you don't want this output)\n"
00055   "            Threshold = Threshold for detection of R-wave peaks in input\n"
00056   "                        (Make sure it's above the background noise level;\n"
00057   "                        Try 3/4 or 4/5 times range plus minimum)\n"
00058   " \n"
00059   "   Resp: Input  = 1D respiratory waveform data for correction\n"
00060   "         Output = Filename for 1D resp phase output\n"
00061   "                  (Leave blank if you don't want this output)\n"
00062 
00063 
00064 
00065 
00066   " \n"
00067   "   Params: Order = The order of the correction\n"
00068   "           (2 is typical; higher-order terms yield little improvement\n"
00069   "            according to Glover et al.)\n"
00070   " \n"
00071   "   ** The input and output datasets and at least one input for correction\n"
00072   "      (cardiac and/or resp) are required.\n"
00073   " \n"
00074   " NOTES\n"
00075   " -----\n"
00076   " \n"
00077   " The durations of the physiological inputs are assumed to equal the\n"
00078   " duration of the dataset. Any constant sampling rate may be used, but\n"
00079   " 40 Hz seems to be acceptable. This plugin's cardiac peak detection\n"
00080   " algorithm is rather simplistic, so you might try using the scanner's\n"
00081   " cardiac gating output (transform it to a spike wave if necessary).\n"
00082   " \n"
00083   " This plugin uses slice timing information embedded in the dataset to\n"
00084   " estimate the proper cardiac/respiratory phase for each slice. It makes\n"
00085   " sense to run this plugin before any program that may destroy the slice\n"
00086   " timings (e.g. 3dvolreg for motion correction).\n"
00087   " \n"
00088   " Author -- Fred Tam, April 2002"
00089 ;
00090 
00091 #define PRIC_C_DEF_THRESHOLD 10
00092 #define PRIC_R_DEF_WINSIZE 20
00093 #define PRIC_M_DEF_ORDER 2
00094 #define PRIC_I_DEF_IGNORE 0
00095 
00096 
00097 
00098 
00099 
00100 PLUGIN_interface * PLUGIN_init( int ncall )
00101 {
00102    PLUGIN_interface * plint ;
00103 
00104    if( ncall > 0 ) return NULL ;  
00105 
00106    
00107 
00108    plint = PLUTO_new_interface( "RETROICOR" ,
00109                                 "Physio Correction of a 3D+time Dataset" ,
00110                                 helpstring ,
00111                                 PLUGIN_CALL_VIA_MENU , PRIC_main  ) ;
00112 
00113    PLUTO_add_hint( plint , "Physio Correction of a 3D+time Dataset" ) ;
00114 
00115    PLUTO_set_sequence( plint , "A:newdset:retroicor" ) ;
00116 
00117    
00118 
00119    PLUTO_add_option( plint , "Datasets" , "Datasets" , TRUE ) ;
00120 
00121    PLUTO_add_dataset( plint , "Input" ,
00122                                     ANAT_ALL_MASK , FUNC_FIM_MASK ,
00123                                     DIMEN_4D_MASK | BRICK_ALLREAL_MASK ) ;
00124    PLUTO_add_hint( plint , "Choose 3D+time input" ) ;
00125 
00126    PLUTO_add_string( plint , "Output" , 0, NULL , 19 ) ;
00127    PLUTO_add_hint( plint , "Prefix for corrected 3D+time output" ) ;
00128 
00129    PLUTO_add_number( plint , "Ignore" , 0, 100, 0, PRIC_I_DEF_IGNORE, TRUE ) ;
00130    PLUTO_add_hint( plint , "Number of initial input timepoints to ignore" ) ;
00131 
00132    
00133 
00134    PLUTO_add_option( plint , "Cardiac", "Cardiac" , FALSE ) ;
00135 
00136    PLUTO_add_timeseries( plint , "Input" );
00137    PLUTO_add_hint( plint , "Choose 1D cardiac waveform input");
00138 
00139    PLUTO_add_string( plint , "Output" , 0, NULL , 19 ) ;
00140    PLUTO_add_hint( plint , "Filename for 1D cardiac phase output (optional)" );
00141 
00142    PLUTO_add_number( plint , "Threshold" , -1280, 1270, 1,
00143                      PRIC_C_DEF_THRESHOLD, TRUE ) ;
00144    PLUTO_add_hint( plint , "Threshold for input R-wave peak detection" ) ;
00145 
00146    
00147 
00148    PLUTO_add_option( plint , "Resp", "Resp" , FALSE ) ;
00149 
00150    PLUTO_add_timeseries( plint , "Input" );
00151    PLUTO_add_hint( plint , "Choose 1D resp waveform input");
00152 
00153    PLUTO_add_string( plint , "Output" , 0, NULL , 19 ) ;
00154    PLUTO_add_hint( plint , "Filename for 1D resp phase output (optional)" ) ;
00155    
00156 
00157 
00158 
00159    
00160 
00161    PLUTO_add_option( plint , "Params", "Params" , FALSE ) ;
00162 
00163    PLUTO_add_number( plint , "Order" , 1, 5, 0, PRIC_M_DEF_ORDER, FALSE ) ;
00164    PLUTO_add_hint( plint , "Order of correction" ) ;
00165 
00166    return plint ;
00167 }
00168 
00169 
00170 
00171 
00172 
00173 static char * PRIC_main( PLUGIN_interface * plint )
00174 {
00175    char * tag , * new_prefix , * cphase1d = NULL, * rphase1d = NULL;
00176    MCW_idcode * idc ;
00177    THD_3dim_dataset * dset , * new_dset;
00178    double * avg = NULL;
00179    double * ca , * cb, * ra, * rb;
00180    MRI_IMAGE * card = NULL, * resp = NULL;
00181    MRI_IMAGE * cardphase = NULL, * respphase = NULL;
00182    float threshold, ignore_input, M_input;
00183    
00184 
00185 
00186    int ignore;
00187    int M = PRIC_M_DEF_ORDER;
00188    int winsize;
00189    float tr;
00190    int ival, nvals;
00191    FILE * fp;
00192    float * cpdata, * rpdata;
00193    char * histstring;
00194 
00195    
00196    
00197 
00198    if( plint == NULL )
00199       return "*********************\n"
00200              "PRIC_main: NULL input\n"
00201              "*********************"    ;
00202 
00203    
00204 
00205    tag = PLUTO_get_optiontag(plint) ;
00206    if( tag==NULL || strcmp(tag,"Datasets") != 0 )
00207       return "*******************************\n"
00208              "PRIC_main: bad Input option tag\n"
00209              "*******************************"    ;
00210 
00211    idc  = PLUTO_get_idcode(plint) ;
00212    dset = PLUTO_find_dset(idc) ;
00213    if( dset == NULL )
00214       return "****************************\n"
00215              "PRIC_main: bad input dataset\n"
00216              "****************************"   ;
00217 
00218    new_prefix = PLUTO_get_string(plint) ;
00219    if( ! PLUTO_prefix_ok(new_prefix) )
00220        return "*********************\n"
00221               "PRIC_main: bad prefix\n"
00222               "*********************"   ;
00223 
00224    ignore_input = PLUTO_get_number(plint) ;
00225    ignore = rint(ignore_input);
00226    if( ignore_input == BAD_NUMBER || ignore < 0 )
00227       return "***************************\n"
00228              "PRIC_main: bad ignore input\n"
00229              "***************************"   ;
00230 
00231    
00232 
00233    tag = PLUTO_get_optiontag(plint) ;
00234 
00235    if( tag != NULL && strcmp(tag, "Cardiac") == 0 ) {
00236 
00237        card = PLUTO_get_timeseries(plint) ;
00238        if( card == NULL )
00239            return "****************************\n"
00240                   "PRIC_main: bad cardiac input\n"
00241                   "****************************"   ;
00242 
00243        
00244        cphase1d = PLUTO_get_string(plint) ;
00245        if( cphase1d == NULL ||
00246            (strlen(cphase1d) > 0 && ! THD_filename_ok(cphase1d)) )
00247            return "*****************************\n"
00248                   "PRIC_main: bad CPhase 1D name\n"
00249                   "*****************************"   ;
00250 
00251        threshold = PLUTO_get_number(plint) ;
00252        if( threshold == BAD_NUMBER )
00253            return "******************************\n"
00254                   "PRIC_main: bad threshold input\n"
00255                   "******************************"   ;
00256 
00257        tag = PLUTO_get_optiontag(plint) ;
00258    }
00259 
00260    
00261 
00262    
00263 
00264    if( tag != NULL && strcmp(tag, "Resp") == 0 ) {
00265 
00266        resp = PLUTO_get_timeseries(plint) ;
00267        if( resp == NULL )
00268            return "**************************\n"
00269                   "PRIC_main: bad resp input\n"
00270                   "*************************"   ;
00271 
00272        
00273        rphase1d = PLUTO_get_string(plint) ;
00274        if( rphase1d == NULL ||
00275            (strlen(rphase1d) > 0 && ! THD_filename_ok(rphase1d)) )
00276            return "*****************************\n"
00277                   "PRIC_main: bad RPhase 1D name\n"
00278                   "*****************************"   ;
00279 
00280        
00281 
00282 
00283 
00284 
00285 
00286 
00287 
00288 
00289        tag = PLUTO_get_optiontag(plint) ;
00290    }
00291 
00292    
00293    if (card == NULL && resp == NULL)
00294        return "****************************************************\n"
00295               "PRIC_main: at least one of Cardiac and Resp required\n"
00296               "****************************************************"   ;
00297 
00298    
00299 
00300    
00301 
00302    if( tag != NULL && strcmp(tag, "Params") == 0 ) {
00303        M_input = PLUTO_get_number(plint) ;
00304        M = rint(M_input);
00305        if( M_input == BAD_NUMBER || M < 1)
00306            return "**************************\n"
00307                   "PRIC_main: bad Order input\n"
00308                   "**************************"   ;
00309    }
00310 
00311    
00312    
00313 
00314    PLUTO_popup_meter(plint);
00315 
00316    
00317 
00318    new_dset = PLUTO_copy_dset( dset , new_prefix );
00319    if( new_dset == NULL )
00320        return "********************************\n"
00321               "PRIC_main: error copying dataset\n"
00322               "********************************"   ;
00323    tross_Copy_History(dset, new_dset); 
00324    histstring = PLUTO_commandstring(plint);
00325    tross_Append_History(new_dset, histstring);
00326    free(histstring);
00327    DSET_unload( dset ) ;  
00328 
00329    PLUTO_set_meter(plint, 10);
00330 
00331    
00332 
00333    if (card != NULL) {
00334        
00335        cardphase = RIC_ToCardiacPhase(card, threshold) ;
00336        if (cardphase == NULL) {
00337            THD_delete_3dim_dataset( new_dset , False ) ;
00338            return "******************************************\n"
00339                   "PRIC_main: error transforming cardiac data\n"
00340                   "******************************************"   ;
00341        }
00342        PLUTO_set_meter(plint, 20);
00343 
00344        
00345        avg = RIC_CalcVoxelMeans(new_dset, ignore);
00346        if (avg == NULL) {
00347            THD_delete_3dim_dataset( new_dset , False ) ;
00348            mri_free(cardphase);
00349            return "************************************************\n"
00350                   "PRIC_main: error calculating dataset voxel means\n"
00351                   "************************************************"   ;
00352        }
00353        PLUTO_set_meter(plint, 33);
00354 
00355        
00356        if (RIC_CalcCoeffAB(new_dset, cardphase, avg, &ca, &cb, M, ignore)
00357            != 0) {
00358 
00359            THD_delete_3dim_dataset( new_dset , False ) ;
00360            mri_free(cardphase);
00361            return "******************************************************\n"
00362                   "PRIC_main: error calculating cardiac a, b coefficients\n"
00363                   "******************************************************"   ;
00364        }
00365    }
00366    PLUTO_set_meter(plint, 30);
00367 
00368    
00369 
00370    if (resp != NULL) {
00371        
00372        tr = new_dset->taxis->ttdel;
00373        switch (new_dset->taxis->units_type) {
00374        case UNITS_MSEC_TYPE: tr /= 1000; break;
00375        case UNITS_SEC_TYPE:  break;
00376        case UNITS_HZ_TYPE:   tr = 1 / tr; break;
00377        default:
00378            THD_delete_3dim_dataset( new_dset , False ) ;
00379            return "*****************************************\n"
00380                   "PRIC_main: bad time units type in dataset\n"
00381                   "*****************************************"   ;
00382        }
00383        winsize = ceil(resp->nx / (tr * DSET_NVALS(new_dset)) / 2.0);
00384 
00385        
00386        respphase = RIC_ToRespPhase(resp, winsize) ;
00387        if (respphase == NULL) {
00388            THD_delete_3dim_dataset( new_dset , False ) ;
00389            return "***************************************\n"
00390                   "PRIC_main: error transforming resp data\n"
00391                   "***************************************"   ;
00392        }
00393        PLUTO_set_meter(plint, 40);
00394 
00395        
00396        if (avg == NULL) {
00397            avg = RIC_CalcVoxelMeans(new_dset, ignore);
00398            if (avg == NULL) {
00399                THD_delete_3dim_dataset( new_dset , False ) ;
00400                mri_free(respphase);
00401                return "**************************************************\n"
00402                       "PRIC_main: error calculating dataset voxel means 2\n"
00403                       "**************************************************"   ;
00404            }
00405        }
00406        PLUTO_set_meter(plint, 43);
00407 
00408        
00409        if (RIC_CalcCoeffAB(new_dset, respphase, avg, &ra, &rb, M, ignore)
00410            != 0) {
00411 
00412            THD_delete_3dim_dataset( new_dset , False ) ;
00413            mri_free(respphase);
00414            return "***************************************************\n"
00415                   "PRIC_main: error calculating resp a, b coefficients\n"
00416                   "***************************************************"   ;
00417        }
00418    }
00419    PLUTO_set_meter(plint, 50);
00420 
00421    
00422 
00423    if (card != NULL) {
00424        
00425        if (RIC_CorrectDataset(new_dset, cardphase, ca, cb, M, ignore) != 0) {
00426            THD_delete_3dim_dataset( new_dset , False ) ;
00427            mri_free(cardphase);
00428            free(ca); free(cb);
00429            return "********************************************\n"
00430                   "PRIC_main: error applying cardiac correction\n"
00431                   "********************************************"   ;
00432        }
00433        PLUTO_set_meter(plint, 60);
00434 
00435        
00436        if ( THD_filename_ok(cphase1d) ) {
00437            
00438            fp = fopen(cphase1d, "w");
00439            nvals = cardphase->nx;
00440            cpdata = MRI_FLOAT_PTR(cardphase);
00441            for (ival = 0; ival < nvals; ival += 1) {
00442                fprintf(fp, "%f\n", cpdata[ival]);
00443            }
00444            fclose(fp);
00445 
00446            
00447            PLUTO_register_timeseries( cphase1d , cardphase ) ;
00448        }
00449 
00450        mri_free(cardphase);
00451        free(ca); free(cb); free(avg); avg = NULL;
00452    }
00453    PLUTO_set_meter(plint, 70);
00454 
00455    
00456 
00457    if (resp != NULL) {
00458        
00459        if (RIC_CorrectDataset(new_dset, respphase, ra, rb, M, ignore) != 0) {
00460            THD_delete_3dim_dataset( new_dset , False ) ;
00461            mri_free(respphase);
00462            free(ra); free(rb);
00463            return "*****************************************\n"
00464                   "PRIC_main: error applying resp correction\n"
00465                   "*****************************************"   ;
00466        }
00467        PLUTO_set_meter(plint, 80);
00468 
00469        
00470        if ( THD_filename_ok(rphase1d) ) {
00471            
00472            fp = fopen(rphase1d, "w");
00473            nvals = respphase->nx;
00474            rpdata = MRI_FLOAT_PTR(respphase);
00475            for (ival = 0; ival < nvals; ival += 1) {
00476                fprintf(fp, "%f\n", rpdata[ival]);
00477            }
00478            fclose(fp);
00479 
00480            
00481            PLUTO_register_timeseries( rphase1d , respphase ) ;
00482        }
00483 
00484        mri_free(respphase);
00485        free(ra); free(rb); if (avg != NULL) free(avg);
00486    }
00487    PLUTO_set_meter(plint, 90);
00488 
00489    
00490 
00491    if ( PLUTO_add_dset( plint , new_dset , DSET_ACTION_MAKE_CURRENT ) ) {
00492        THD_delete_3dim_dataset( new_dset , False ) ;
00493        return "*********************************************\n"
00494               "PRIC_main: failure to add new dataset to AFNI\n"
00495               "*********************************************" ;
00496    }
00497    PLUTO_set_meter(plint, 100);
00498 
00499    
00500 
00501    return NULL ;
00502 }