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), 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. Meyer. Simultaneous correction for tracer arrival delay and dispersion",
65 " in CBF measurements by the H215O autoradiographic method and dynamic PET.",
66 " J Nucl Med 1989; 30:1069-1078.",
67 " 2. van den Hoff et al. Accurate local blood flow measurements with",
68 " dynamic PET: fast determination of input function delay and dispersion",
69 " by multilinear minimization. J Nucl Med 1993; 34:1770-1777.",
71 "See also: fitdelay, imgdelay, tacmove, tactime, simdisp, tacframe",
73 "Keywords: TAC, modelling, time delay",
92int main(
int argc,
char **argv)
94 int ai, help=0, version=0, verbose=1;
95 char ttacfile[FILENAME_MAX], btacfile[FILENAME_MAX],
96 parfile[FILENAME_MAX], cttacfile[FILENAME_MAX];
98 double endtimemin=90.0;
99 double dtrange[2]={-10,+50};
108 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
109 ttacfile[0]=btacfile[0]=parfile[0]=cttacfile[0]=(char)0;
111 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
113 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
114 if(strncasecmp(cptr,
"END=", 4)==0) {
115 int ret=
atofCheck(cptr+4, &endtime);
if(!ret && endtime>=endtimemin)
continue;
116 }
else if(strncasecmp(cptr,
"MIN=", 4)==0) {
117 if(
atofCheck(cptr+4, &dtrange[0])==0)
continue;
118 }
else if(strncasecmp(cptr,
"MAX=", 4)==0) {
119 if(
atofCheck(cptr+4, &dtrange[1])==0)
continue;
120 }
else if(strncasecmp(cptr,
"STEP=", 5)==0) {
121 int ret=
atofCheck(cptr+5, &dtstep);
if(!ret && dtstep>0.0)
continue;
122 }
else if(strncasecmp(cptr,
"MODE=", 5)==0) {
123 if(
atoiCheck(cptr+5, &mode)==0)
continue;
124 }
else if(strncasecmp(cptr,
"MODEL=", 6)==0) {
125 if(strcasecmp(cptr+6,
"R1TCM")==0) {model=0;
continue;}
126 if(strcasecmp(cptr+6,
"I2TCM")==0) {model=1;
continue;}
127 if(strcasecmp(cptr+6,
"R2TCM")==0) {model=2;
continue;}
128 if(strcasecmp(cptr+6,
"DMS")==0) {model=3;
continue;}
130 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
139 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
144 if(ai<argc) {
strlcpy(btacfile, argv[ai], FILENAME_MAX); ai++;}
145 if(ai<argc) {
strlcpy(ttacfile, argv[ai], FILENAME_MAX); ai++;}
146 if(ai<argc) {
strlcpy(parfile, argv[ai], FILENAME_MAX); ai++;}
147 if(ai<argc) {
strlcpy(cttacfile, argv[ai], FILENAME_MAX); ai++;}
149 if(ai<argc) {fprintf(stderr,
"Error: extra command-line argument.\n");
return(1);}
151 if(!parfile[0]) {
tpcPrintUsage(argv[0], info, stdout);
return(1);}
155 printf(
"ttacfile := %s\n", ttacfile);
156 printf(
"btacfile := %s\n", btacfile);
157 printf(
"parfile := %s\n", parfile);
158 printf(
"cttacfile := %s\n", cttacfile);
159 printf(
"endtime := %g\n", endtime);
160 printf(
"dtrange := %g - %g s\n", dtrange[0], dtrange[1]);
161 printf(
"step := %g\n", dtstep);
162 printf(
"mode := %d\n", mode);
163 printf(
"model := %d\n", model);
169 fprintf(stderr,
"Error: file '%s' does not exist.\n", ttacfile);
173 fprintf(stderr,
"Error: file '%s' does not exist.\n", btacfile);
178 if(dtrange[0]>dtrange[1]) {
double f=dtrange[0]; dtrange[0]=dtrange[1]; dtrange[1]=f;}
179 int moveNr=1+(dtrange[1]-dtrange[0])/dtstep;
181 printf(
"step_number := %d\n", moveNr);
184 if(moveNr<5 || moveNr>2000) {
185 fprintf(stderr,
"Error: invalid dT range or step size.\n");
188 if((moveNr<10 || moveNr>500) && verbose>0)
189 fprintf(stderr,
"Warning: non-optimal dT range or step size.\n");
196 if(verbose>1) printf(
"reading TACs\n");
206 printf(
"ttacNr := %d\n", ttac.
tacNr);
207 printf(
"ttac.sampleNr := %d\n", ttac.
sampleNr);
217 printf(
"btacNr := %d\n", btac.
tacNr);
218 printf(
"btac.sampleNr := %d\n", btac.
sampleNr);
222 if(verbose>0) fprintf(stderr,
"Warning: BTAC file contains more than one TAC.\n");
227 fprintf(stderr,
"Error: too few samples.\n");
232 fprintf(stderr,
"Error: data contains missing values.\n");
240 fprintf(stderr,
"Error: invalid curve data.\n");
253 if(verbose>0) fprintf(stderr,
"Warning: check and set the data units.\n");
260 if(verbose>2) printf(
"allocate memory for PAR\n");
263 fprintf(stderr,
"Error: cannot allocate memory for results.\n");
271 strcpy(par.
n[0].
name,
"dT");
278 iftPut(&par.
h,
"program", buf, 0, NULL);
279 iftPut(&par.
h,
"bloodfile", btacfile, 0, NULL);
280 iftPut(&par.
h,
"datafile", ttacfile, 0, NULL);
282 for(
int i=0; i<par.
tacNr; i++) {
284 par.
r[i].
end=endtime;
292 double btacDelaytime=0.0;
294 if(verbose>1) {printf(
"BTAC delay fitting\n"); fflush(stdout);}
295 int ret=0, fitSampleNr=0;
296 for(fitSampleNr=0; fitSampleNr<btac.
sampleNr; fitSampleNr++)
if(btac.
x[fitSampleNr]>endtime)
break;
298 if(btac.
isframe) {x1=btac.
x1; x2=btac.
x2;}
else {x1=btac.
x; x2=NULL;}
300 ret=
spectralDMSurge(x1, x2, btac.
c[0].
y, NULL, fitSampleNr, 1.0E-10, 1.0E+00, 1000,
301 dtrange[0], dtrange[1], dtstep, NULL, NULL, &btacDelaytime, NULL, &status);
307 if(verbose>3) printf(
"BTAC delay := %g\n", btacDelaytime);
314 if(verbose>1) {printf(
"delay fitting\n"); fflush(stdout);}
317 double delaytime=nan(
"");
318 int ret=
tacDelayFit(&btac, &ttac, 0, dtrange[0], dtrange[1], endtime, dtstep, &delaytime,
319 mode, model, NULL, &status);
325 if(verbose>3) printf(
"delay := %g\n", delaytime);
326 par.
r[0].
p[0]=delaytime;
331 for(
int ci=0; ci<ttac.
tacNr; ci++) {
332 if(verbose>2) {printf(
"%s\n", ttac.
c[ci].
name); fflush(stdout);}
333 double delaytime=nan(
"");
335 ret=
tacDelayFit(&btac, &ttac, ci, dtrange[0], dtrange[1], endtime, dtstep, &delaytime,
336 mode, model, &ddata, &status);
338 ret=
tacDelayFit(NULL, &ttac, ci, 0.0, 0.0, 0.0, 0.0, &delaytime, mode, model, &ddata, &status);
345 if(verbose>3) printf(
"delay := %g\n", delaytime);
346 par.
r[ci].
p[0]=delaytime;
352 for(fitSampleNr=0; fitSampleNr<ttac.
sampleNr; fitSampleNr++)
if(ttac.
x[fitSampleNr]>endtime)
break;
354 if(ttac.
isframe) {x1=ttac.
x1; x2=ttac.
x2;}
else {x1=ttac.
x; x2=NULL;}
356 for(
int ci=0; ci<ttac.
tacNr; ci++) {
357 if(verbose>2) {printf(
"%s\n", ttac.
c[ci].
name); fflush(stdout);}
358 double delaytime=nan(
"");
359 int ret=
spectralDMSurge(x1, x2, ttac.
c[ci].
y, NULL, fitSampleNr, 1.0E-10, 1.0E+00, 1000,
360 dtrange[0], dtrange[1], dtstep, NULL, NULL, &delaytime, NULL, &status);
366 if(verbose>3) printf(
"delay := %g (%g-%g)\n", delaytime-btacDelaytime, delaytime, btacDelaytime);
367 delaytime-=btacDelaytime;
368 par.
r[ci].
p[0]=delaytime;
374 double delaytime=nan(
"");
377 for(
int i=0; i<par.
tacNr; i++)
if(isfinite(par.
r[i].
p[0])) t[n++]=par.
r[i].
p[0];
379 fprintf(stderr,
"Error: delay estimation failed.\n");
396 if(verbose>1) printf(
" saving %s\n", parfile);
397 FILE *fp=fopen(parfile,
"w");
399 fprintf(stderr,
"Error: cannot open file for writing.\n");
408 if(verbose>1) printf(
"parameters saved in %s\n", parfile);
416 if(verbose>1) {printf(
"delay correcting TTACs\n"); fflush(stdout);}
418 for(
int i=0; i<ttac.
tacNr; i++) {
425 if(verbose>1) {printf(
"writing delay correcting TTACs\n"); fflush(stdout);}
426 FILE *fp=fopen(cttacfile,
"w");
428 fprintf(stderr,
"Error: cannot open file for writing.\n");
437 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)
int tacDelayFit(TAC *btac, TAC *ttac, int ci, double dtmin, double dtmax, double fitend, double dtstep, double *dt, int mode, int model, DELAYFITDATA *tdata, TPCSTATUS *status)
Fit time delay between PET tissue and plasma or blood curve.
void initDelayFitData(DELAYFITDATA *d)
Before first use, initiate the data structure for delay time estimation.
void freeDelayFitData(DELAYFITDATA *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.
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.