8#include "tpcclibConfig.h"
27static char *info[] = {
28 "Estimate the time delay (dT) between PET tissue and blood curves.",
29 "Initial part of tissue curve is fitted with a range of input curves moved",
30 "in time (1, 2, 3), using linearised one or two tissue compartment model",
31 "with blood volume term. The time that provides the best fit",
32 "(smallest sum-of-squares) is selected. Tissue heterogeneity, dispersion of",
33 "the blood curve, metabolites, and variable plasma-to-blood ratio may",
34 "only minimally impact the time delay estimate, if only the initial part",
35 "of the data is fitted with relatively complex model.",
37 "Usage: @P [options] btacfile ttacfile parfile [dcttacfile]",
40 " -min=<Time (sec)> and -max=<Time (sec)>",
41 " The range of time delays to be tested; by default -10 - +50 s.",
42 " -end=<Fit end time (sec)>",
43 " Use data from 0 to end time; by default, 300 s; at least 120 s.",
44 " In case of short PET scan, end time may need to be reduced so that",
45 " BTAC with negative delay extends to end time.",
46 " -step=<dT step size (sec)>",
47 " The step length for moving the input curve; by default 1 s.",
49 " -model=<R1TCM | I2TCM | R2TCM>",
50 " Reversible one-tissue (R1TCM, default), irreversible 2-tissue model",
51 " (I2TCM), or reversible 2-tissue model (R2TCM) can be selected.",
54 "Delay times for each tissue curve are reported in parameter file.",
55 "File may also contain a median time delay as 'time_difference := time',",
56 "depending on the file format.",
57 "Positive delay time means that tissue curve is delayed as compared to",
58 "the input curve, and vice versa. Thus, input curve needs to be moved",
59 "by the delay time to match the tissue curve.",
61 "Optionally, delay corrected TTACs can be written in specified file.",
64 " 1. Iida H et al. Evaluation of regional differences of tracer appearance",
65 " time in cerebral tissues using [15O]water and dynamic positron emission",
66 " tomography. J Cereb Blood Flow Metab. 1988; 8:285-288.",
67 " 2. Meyer. Simultaneous correction for tracer arrival delay and dispersion",
68 " in CBF measurements by the H215O autoradiographic method and dynamic PET.",
69 " J Nucl Med 1989; 30:1069-1078.",
70 " 3. van den Hoff et al. Accurate local blood flow measurements with",
71 " dynamic PET: fast determination of input function delay and dispersion",
72 " by multilinear minimization. J Nucl Med 1993; 34:1770-1777.",
74 "See also: fitdelay, imgdelay, tacmove, tactime, simdisp, tacframe",
76 "Keywords: TAC, modelling, time delay",
95int main(
int argc,
char **argv)
97 int ai, help=0, version=0, verbose=1;
98 char ttacfile[FILENAME_MAX], btacfile[FILENAME_MAX],
99 parfile[FILENAME_MAX], cttacfile[FILENAME_MAX];
100 double endtime=300.0;
101 double endtimemin=90.0;
102 double dtrange[2]={-10,+50};
111 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
112 ttacfile[0]=btacfile[0]=parfile[0]=cttacfile[0]=(char)0;
114 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
116 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
117 if(strncasecmp(cptr,
"END=", 4)==0) {
118 int ret=
atofCheck(cptr+4, &endtime);
if(!ret && endtime>=endtimemin)
continue;
119 }
else if(strncasecmp(cptr,
"MIN=", 4)==0) {
120 if(
atofCheck(cptr+4, &dtrange[0])==0)
continue;
121 }
else if(strncasecmp(cptr,
"MAX=", 4)==0) {
122 if(
atofCheck(cptr+4, &dtrange[1])==0)
continue;
123 }
else if(strncasecmp(cptr,
"STEP=", 5)==0) {
124 int ret=
atofCheck(cptr+5, &dtstep);
if(!ret && dtstep>0.0)
continue;
125 }
else if(strncasecmp(cptr,
"MODE=", 5)==0) {
126 if(
atoiCheck(cptr+5, &mode)==0)
continue;
127 }
else if(strncasecmp(cptr,
"MODEL=", 6)==0) {
128 if(strcasecmp(cptr+6,
"R1TCM")==0) {model=0;
continue;}
129 if(strcasecmp(cptr+6,
"I2TCM")==0) {model=1;
continue;}
130 if(strcasecmp(cptr+6,
"R2TCM")==0) {model=2;
continue;}
131 if(strcasecmp(cptr+6,
"DMS")==0) {model=3;
continue;}
133 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
142 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
147 if(ai<argc) {
strlcpy(btacfile, argv[ai], FILENAME_MAX); ai++;}
148 if(ai<argc) {
strlcpy(ttacfile, argv[ai], FILENAME_MAX); ai++;}
149 if(ai<argc) {
strlcpy(parfile, argv[ai], FILENAME_MAX); ai++;}
150 if(ai<argc) {
strlcpy(cttacfile, argv[ai], FILENAME_MAX); ai++;}
152 if(ai<argc) {fprintf(stderr,
"Error: extra command-line argument.\n");
return(1);}
154 if(!parfile[0]) {
tpcPrintUsage(argv[0], info, stdout);
return(1);}
158 printf(
"ttacfile := %s\n", ttacfile);
159 printf(
"btacfile := %s\n", btacfile);
160 printf(
"parfile := %s\n", parfile);
161 printf(
"cttacfile := %s\n", cttacfile);
162 printf(
"endtime := %g\n", endtime);
163 printf(
"dtrange := %g - %g s\n", dtrange[0], dtrange[1]);
164 printf(
"step := %g\n", dtstep);
165 printf(
"mode := %d\n", mode);
166 printf(
"model := %d\n", model);
172 fprintf(stderr,
"Error: file '%s' does not exist.\n", ttacfile);
176 fprintf(stderr,
"Error: file '%s' does not exist.\n", btacfile);
181 if(dtrange[0]>dtrange[1]) {
double f=dtrange[0]; dtrange[0]=dtrange[1]; dtrange[1]=f;}
182 int moveNr=1+(dtrange[1]-dtrange[0])/dtstep;
184 printf(
"step_number := %d\n", moveNr);
187 if(moveNr<5 || moveNr>2000) {
188 fprintf(stderr,
"Error: invalid dT range or step size.\n");
191 if((moveNr<10 || moveNr>500) && verbose>0)
192 fprintf(stderr,
"Warning: non-optimal dT range or step size.\n");
199 if(verbose>1) printf(
"reading TACs\n");
209 printf(
"ttacNr := %d\n", ttac.
tacNr);
210 printf(
"ttac.sampleNr := %d\n", ttac.
sampleNr);
220 printf(
"btacNr := %d\n", btac.
tacNr);
221 printf(
"btac.sampleNr := %d\n", btac.
sampleNr);
225 if(verbose>0) fprintf(stderr,
"Warning: BTAC file contains more than one TAC.\n");
230 fprintf(stderr,
"Error: too few samples.\n");
235 fprintf(stderr,
"Error: data contains missing values.\n");
243 fprintf(stderr,
"Error: invalid curve data.\n");
256 if(verbose>0) fprintf(stderr,
"Warning: check and set the data units.\n");
263 if(verbose>2) printf(
"allocate memory for PAR\n");
266 fprintf(stderr,
"Error: cannot allocate memory for results.\n");
274 strcpy(par.
n[0].
name,
"dT");
281 iftPut(&par.
h,
"program", buf, 0, NULL);
282 iftPut(&par.
h,
"bloodfile", btacfile, 0, NULL);
283 iftPut(&par.
h,
"datafile", ttacfile, 0, NULL);
285 for(
int i=0; i<par.
tacNr; i++) {
287 par.
r[i].
end=endtime;
295 double btacDelaytime=0.0;
297 if(verbose>1) {printf(
"BTAC delay fitting\n"); fflush(stdout);}
298 int ret=0, fitSampleNr=0;
299 for(fitSampleNr=0; fitSampleNr<btac.
sampleNr; fitSampleNr++)
if(btac.
x[fitSampleNr]>endtime)
break;
301 if(btac.
isframe) {x1=btac.
x1; x2=btac.
x2;}
else {x1=btac.
x; x2=NULL;}
303 ret=
spectralDMSurge(x1, x2, btac.
c[0].
y, NULL, fitSampleNr, 1.0E-10, 1.0E+00, 1000,
304 dtrange[0], dtrange[1], dtstep, NULL, NULL, &btacDelaytime, NULL, &status);
310 if(verbose>3) printf(
"BTAC delay := %g\n", btacDelaytime);
317 if(verbose>1) {printf(
"delay fitting\n"); fflush(stdout);}
320 double delaytime=nan(
"");
321 int ret=
tacDelayCMFit(&btac, &ttac, 0, dtrange[0], dtrange[1], endtime, dtstep, &delaytime,
322 mode, model, NULL, &status);
328 if(verbose>3) printf(
"delay := %g\n", delaytime);
329 par.
r[0].
p[0]=delaytime;
334 for(
int ci=0; ci<ttac.
tacNr; ci++) {
335 if(verbose>2) {printf(
"%s\n", ttac.
c[ci].
name); fflush(stdout);}
336 double delaytime=nan(
"");
338 ret=
tacDelayCMFit(&btac, &ttac, ci, dtrange[0], dtrange[1], endtime, dtstep, &delaytime,
339 mode, model, &ddata, &status);
341 ret=
tacDelayCMFit(NULL, &ttac, ci, 0.0, 0.0, 0.0, 0.0, &delaytime, mode, model, &ddata, &status);
348 if(verbose>3) printf(
"delay := %g\n", delaytime);
349 par.
r[ci].
p[0]=delaytime;
355 for(fitSampleNr=0; fitSampleNr<ttac.
sampleNr; fitSampleNr++)
if(ttac.
x[fitSampleNr]>endtime)
break;
357 if(ttac.
isframe) {x1=ttac.
x1; x2=ttac.
x2;}
else {x1=ttac.
x; x2=NULL;}
359 for(
int ci=0; ci<ttac.
tacNr; ci++) {
360 if(verbose>2) {printf(
"%s\n", ttac.
c[ci].
name); fflush(stdout);}
361 double delaytime=nan(
"");
362 int ret=
spectralDMSurge(x1, x2, ttac.
c[ci].
y, NULL, fitSampleNr, 1.0E-10, 1.0E+00, 1000,
363 dtrange[0], dtrange[1], dtstep, NULL, NULL, &delaytime, NULL, &status);
369 if(verbose>3) printf(
"delay := %g (%g-%g)\n", delaytime-btacDelaytime, delaytime, btacDelaytime);
370 delaytime-=btacDelaytime;
371 par.
r[ci].
p[0]=delaytime;
377 double delaytime=nan(
"");
380 for(
int i=0; i<par.
tacNr; i++)
if(isfinite(par.
r[i].
p[0])) t[n++]=par.
r[i].
p[0];
382 fprintf(stderr,
"Error: delay estimation failed.\n");
399 if(verbose>1) printf(
" saving %s\n", parfile);
400 FILE *fp=fopen(parfile,
"w");
402 fprintf(stderr,
"Error: cannot open file for writing.\n");
411 if(verbose>1) printf(
"parameters saved in %s\n", parfile);
419 if(verbose>1) {printf(
"delay correcting TTACs\n"); fflush(stdout);}
421 for(
int i=0; i<ttac.
tacNr; i++) {
428 if(verbose>1) {printf(
"writing delay correcting TTACs\n"); fflush(stdout);}
429 FILE *fp=fopen(cttacfile,
"w");
431 fprintf(stderr,
"Error: cannot open file for writing.\n");
440 if(verbose>1) printf(
"delay corrected TTACs saved in %s\n", cttacfile);
int spectralDMSurge(const double *x, const double *x2, const double *y, double *w, const int sNr, const double kMin, const double kMax, const int fNr, const double dtMin, const double dtMax, const double dtStep, double *k, double *a, double *dtEst, double *yfit, TPCSTATUS *status)
char * ctime_r_int(const time_t *t, char *buf)
Convert calendar time t into a null-terminated string of the form YYYY-MM-DD hh:mm:ss,...
int atofCheck(const char *s, double *v)
void freeDelayCMFitData(DELAYCMFITDATA *d)
After last use, free memory in the data structure for delay time estimation.
int tacDelay(TAC *tac, double dt, int ti, TPCSTATUS *status)
Move TAC y values (concentrations) in time, keeping sample times (x values) intact.
void initDelayCMFitData(DELAYCMFITDATA *d)
Before first use, initiate the data structure for delay time estimation.
int tacDelayCMFit(TAC *btac, TAC *ttac, int ci, double dtmin, double dtmax, double fitend, double dtstep, double *dt, int mode, int model, DELAYCMFITDATA *tdata, TPCSTATUS *status)
Fit time delay between PET tissue and plasma or blood curve.
int fileExist(const char *filename)
int iftPut(IFT *ift, const char *key, const char *value, char comment, TPCSTATUS *status)
int iftPutDouble(IFT *ift, const char *key, const double value, char comment, TPCSTATUS *status)
int atoiCheck(const char *s, int *v)
int parAllocate(PAR *par, int parNr, int tacNr)
char * parFormattxt(parformat c)
int parWrite(PAR *par, FILE *fp, parformat format, int extra, TPCSTATUS *status)
int parFormatFromExtension(const char *s)
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
void tpcProgramName(const char *program, int version, int copyright, char *prname, int n)
int tpcHtmlUsage(const char *program, char *text[], const char *path)
void tpcPrintBuild(const char *program, FILE *fp)
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
void statusInit(TPCSTATUS *s)
char * errorMsg(tpcerror e)
void statusSet(TPCSTATUS *s, const char *func, const char *srcfile, int srcline, tpcerror error)
size_t strlcpy(char *dst, const char *src, size_t dstsize)
IFT h
Optional (but often useful) header information.
char name[MAX_PARNAME_LEN+1]
char name[MAX_TACNAME_LEN+1]
char name[MAX_TACNAME_LEN+1]
int verbose
Verbose level, used by statusPrint() etc.
tpcerror error
Error code.
int tacRead(TAC *d, const char *fname, TPCSTATUS *status)
char * tacFormattxt(tacformat c)
int tacWrite(TAC *tac, FILE *fp, tacformat format, int extra, TPCSTATUS *status)
int tacSortByTime(TAC *d, TPCSTATUS *status)
int tacYUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
int tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
int tacSetXContiguous(TAC *d)
Set PET TAC frame times contiguous, without even tiny overlap or gaps in between.
Header file for libtpcbfm.
Header file for library libtpccsv.
Header file for library libtpcextensions.
Header file for libtpcfileutil.
Header file for library libtpcift.
Header file for library libtpcisotope.
@ PAR_FORMAT_UNKNOWN
Unknown format.
@ PAR_FORMAT_TSV_UK
UK TSV (point as decimal separator).
Header file for libtpcstatist.
Header file for library libtpctac.
@ TAC_FORMAT_UNKNOWN
Unknown format.
Header file for libtpctacmod.