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 }