9#include "tpcclibConfig.h"
26static char *info[] = {
27 "Estimate the binding potential (BPnd) from simplified reference tissue",
28 "model (SRTM) (Lammertsma & Hume, 1996). The model is solved using",
29 "the basis function method (BFM; Gunn et al., 1997).",
31 "Radioactivity concentration in region(s)-of-interest is given in TTAC file;",
32 "data must be corrected for physical decay.",
33 "Radioactivity concentration in reference region (RTAC) is given in separate",
34 "file, or as name or number of the reference region inside the TTAC file.",
35 "Sample times must be in minutes in all data files, unless specified inside",
36 "the files. TTAC file should include weights.",
38 "Usage: @P [options] TTAC RTAC results",
42 " To apply BFM to non-decay-corrected data, as proposed in the original",
43 " method publication, enter the isotope with this option, using codes",
44 " C-11, F-18, Ga-68, Cu-64, ...",
45 " -t3min=<value (1/min)>",
46 " Set minimum value for theta3; it must be >= k2min/(1+BPmax)+lambda;",
47 " it has to be > lambda. Default is lambda+0.001 min-1.",
48 " Lambda for F-18 is 0.0063 and for C-11 0.034;",
49 " If isotope is not given, then lambda=0.",
50 " -t3max=<value (1/min)>",
51 " Set maximum value for theta3; it must be <= k2max+lambda.",
52 " Default is 0.60 min-1.",
54 " Set number of basis functions; default is 5000.",
55 " -end=<Fit end time (min)>",
56 " Use data from 0 to end time; by default, model is fitted to all frames.",
58 " Instead of BPnd, program saves the DVR (=BPnd+1) values.",
60 " All weights are set to 1.0 (no weighting); by default, weights in",
61 " data file are used, if available.",
63 " Weight by sampling interval.",
65 " Plots of original and fitted TTACs are written in specified file in",
68 " Fitted regional TTACs are written in specified file.",
70 " Basis functions are written in specified DFT file.",
74 " @P a4567.tac cer C-11 a4567bp.res",
77 "1. Lammertsma AA, Hume SP. Simplified reference tissue model for PET",
78 " receptor studies. NeuroImage 1996;4:153-158.",
79 "2. Gunn RN, Lammertsma AA, Hume SP, Cunningham VJ. Parametric imaging of",
80 " ligand-receptor binding in PET using a simplified reference region",
81 " model. NeuroImage 1997;6:279-287.",
83 "See also: tacweigh, tacdecay, logan, imgbfbp, fit_srtm, lhsrtm, fitk2",
85 "Keywords: TAC, modelling, binding potential, SRTM, reference input",
104int main(
int argc,
char **argv)
106 int ai, help=0, version=0, verbose=1;
107 char rtacfile[FILENAME_MAX], ttacfile[FILENAME_MAX], resfile[FILENAME_MAX],
108 fitfile[FILENAME_MAX], svgfile[FILENAME_MAX], bffile[FILENAME_MAX];
111 double t3min=nan(
""), t3max=nan(
"");
113 double fitdur=nan(
"");
123 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
124 rtacfile[0]=ttacfile[0]=resfile[0]=fitfile[0]=svgfile[0]=bffile[0]=(char)0;
126 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
128 cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
129 if(strcasecmp(cptr,
"DVR")==0) {
130 bp_plus_one=1;
continue;
131 }
else if(strncasecmp(cptr,
"BPND", 2)==0) {
132 bp_plus_one=0;
continue;
133 }
else if(strncasecmp(cptr,
"SVG=", 4)==0) {
134 strlcpy(svgfile, cptr+4, FILENAME_MAX);
if(strlen(svgfile)>0)
continue;
135 }
else if(strncasecmp(cptr,
"FIT=", 4)==0) {
136 strlcpy(fitfile, cptr+4, FILENAME_MAX);
if(strlen(fitfile)>0)
continue;
137 }
else if(strncasecmp(cptr,
"NR=", 3)==0) {
138 if(
atoiCheck(cptr+3, &bfNr)==0 && bfNr>5)
continue;
139 }
else if(strncasecmp(cptr,
"BF=", 3)==0) {
140 strlcpy(bffile, cptr+3, FILENAME_MAX);
if(strlen(bffile)>0)
continue;
141 }
else if(strncasecmp(cptr,
"min=", 4)==0) {
142 if(
atofCheck(cptr+4, &t3min)==0 && t3min>=0.0)
continue;
143 }
else if(strncasecmp(cptr,
"max=", 4)==0) {
144 if(
atofCheck(cptr+4, &t3max)==0 && t3max>=0.0)
continue;
145 }
else if(strncasecmp(cptr,
"t3min=", 6)==0) {
146 if(
atofCheck(cptr+6, &t3min)==0 && t3min>=0.0)
continue;
147 }
else if(strncasecmp(cptr,
"t3max=", 6)==0) {
148 if(
atofCheck(cptr+6, &t3max)==0 && t3max>=0.0)
continue;
149 }
else if(strncasecmp(cptr,
"end=", 4)==0) {
151 if(fitdur<=0.0) fitdur=1.0E+99;
154 fprintf(stderr,
"Error: invalid fit time '%s'.\n", argv[ai]);
157 }
else if(strcasecmp(cptr,
"W1")==0) {
159 }
else if(strcasecmp(cptr,
"WF")==0) {
161 }
else if(strncasecmp(cptr,
"I=", 2)==0) {
164 fprintf(stderr,
"Error: invalid isotope code '%s'\n", cptr+2);
169 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
178 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
183 if(ai<argc)
strlcpy(ttacfile, argv[ai++], FILENAME_MAX);
184 if(ai<argc)
strlcpy(rtacfile, argv[ai++], FILENAME_MAX);
185 if(ai<argc)
strlcpy(resfile, argv[ai++], FILENAME_MAX);
187 fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
192 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
195 if(isnan(fitdur)) fitdur=1.0E+99;
200 if(verbose>2) printf(
"lambda := %g\n", lambda);
201 if(isnan(t3min)) t3min=lambda+0.001;
202 if(isnan(t3max)) t3max=0.60;
203 if(t3min>=t3max || t3min<=lambda) {
204 fprintf(stderr,
"Error: invalid theta3 bounds (%g - %g).\n", t3min, t3max);
210 printf(
"ttacfile := %s\n", ttacfile);
211 printf(
"rtacfile := %s\n", rtacfile);
212 printf(
"resfile := %s\n", resfile);
213 printf(
"fitfile := %s\n", fitfile);
214 printf(
"svgfile := %s\n", svgfile);
215 printf(
"bffile := %s\n", bffile);
216 printf(
"bfNr := %d\n", bfNr);
217 printf(
"bp_plus_one := %d\n", bp_plus_one);
218 printf(
"t3min := %g min-1\n", t3min);
219 printf(
"t3max := %g min-1\n", t3max);
222 printf(
"required_fittime := %g min\n", fitdur);
223 printf(
"weights := %d\n",
weights);
233 if(verbose>1) printf(
"reading %s\n", ttacfile);
234 ret=
tacRead(&ttac, ttacfile, &status);
241 printf(
"tacNr := %d\n", ttac.
tacNr);
242 printf(
"sampleNr := %d\n", ttac.
sampleNr);
250 if(verbose>1) printf(
"reading %s\n", rtacfile);
258 fprintf(stderr,
"Warning: several reference regions found: %s selected.\n",
259 rtac.
c[refindex].
name);
260 }
else if(verbose>1) {
261 printf(
"reference_region := %s\n", rtac.
c[refindex].
name);
265 printf(
"ref_tacNr := %d\n", rtac.
tacNr);
266 printf(
"ref_sampleNr := %d\n", rtac.
sampleNr);
273 fprintf(stderr,
"Error: missing sample(s) in data.\n");
294 for(
int i=0; i<ttac.
sampleNr; i++) ttac.
w[i]=1.0;
301 fprintf(stderr,
"Warning: data is not weighted.\n");
305 double starttime=0.0;
306 double endtime=fitdur;
308 fitSampleNr=
tacFittime(&ttac, &starttime, &endtime, NULL, NULL, &status);
312 }
else if(fitSampleNr<4) {
313 fprintf(stderr,
"Error: too few data points for a decent fit.\n");
317 printf(
"starttime := %g\n", starttime);
318 printf(
"endtime := %g\n", endtime);
321 printf(
"fitSampleNr := %d\n", fitSampleNr);
330 if(verbose>1) printf(
"removing decay correction.\n");
342 if(verbose>1) printf(
"integrate reference region TAC for BF calculation\n");
350 if(verbose>1) printf(
"calculating basis functions\n");
352 ret=
bfmSRTM(ttac.
x, irtac.
c[refindex].
y, fitSampleNr, bfNr,
353 t3min, t3max, &bf, &status);
356 fprintf(stderr,
"Error: cannot calculate basis functions.\n");
365 if(verbose>1) printf(
"writing %s\n", bffile);
366 FILE *fp; fp=fopen(bffile,
"w");
368 fprintf(stderr,
"Error: cannot open file for writing (%s)\n", bffile);
379 if(verbose>0) printf(
"basis functions saved in %s.\n", bffile);
387 if(verbose>1) printf(
"initializing result data\n");
403 iftPut(&par.
h,
"program", buf, 0, NULL);
405 iftPut(&par.
h,
"datafile", ttacfile, 0, NULL);
406 iftPut(&par.
h,
"refname", rtac.
c[refindex].
name, 0, NULL);
411 iftPut(&par.
h,
"fitmethod",
"BFM", 0, NULL);
415 for(i=0; i<par.
tacNr; i++) {
426 if(bp_plus_one==0) strcpy(par.
n[i].
name,
"BPnd");
427 else strcpy(par.
n[i].
name,
"DVR");
440 if(fitfile[0] || svgfile[0]) {
441 if(verbose>1) printf(
"allocating space for fitted TTACs\n");
444 fprintf(stderr,
"Error: cannot allocate space for fitted TACs.\n");
456 if(verbose>1) printf(
"allocating memory for QR\n");
461 printf(
"QR_colNr := %d\n", colNr);
462 printf(
"QR_rowNr := %d\n", rowNr);
464 double *buf, **mat, *rhs, *sol, r2;
465 buf=(
double*)calloc(colNr*rowNr+rowNr+colNr,
sizeof(
double));
466 mat=(
double**)calloc(rowNr,
sizeof(
double*));
467 if(buf==NULL || mat==NULL) {
468 fprintf(stderr,
"Error: cannot allocate memory for QR\n");
473 for(
int i=0; i<rowNr; i++) mat[i]=buf+(i*colNr);
474 rhs=buf+(rowNr*colNr); sol=buf+(rowNr*colNr+rowNr);
480 if(verbose>1) printf(
"BFM fitting to TACs.\n");
483 for(
int i=0; i<ttac.
tacNr; i++) {
485 if(verbose>1 && ttac.
tacNr>1)
486 printf(
"Region %d %s\n", i+1, ttac.
c[i].
name);
489 bi_min=-1; r2_min=nan(
"");
490 for(bi=0; bi<bf.
tacNr; bi++) {
492 if(verbose>5) printf(
"bi=%d\n", bi);
495 for(
int j=0; j<rowNr; j++) {
496 mat[j][0]=rtac.
c[refindex].
y[j];
497 mat[j][1]=bf.
c[bi].
y[j];
498 rhs[j]=ttac.
c[i].
y[j];
501 if(verbose>5) printf(
" weighting\n");
506 if(verbose>5) printf(
" QR\n");
507 ret=
qrLSQ(mat, rhs, sol, rowNr, colNr, &r2);
509 fprintf(stderr,
"Error: no QR solution for BFM\n");
512 free(buf); free(mat);
516 printf(
"solution (%d):", bi);
517 for(
int j=0; j<colNr; j++) printf(
" %g", sol[j]);
518 printf(
"\nR^2= %g R=%g\n", r2, sqrt(r2));
521 if((isnan(r2_min) || r2_min>r2) && bf.
c[bi].
size!=lambda) {
522 r2_min=r2; bi_min=bi;
526 printf(
"Min basis function nr %d with R2=%g\n", bi_min+1, r2_min);
531 for(
int j=0; j<rowNr; j++) {
532 mat[j][0]=rtac.
c[refindex].
y[j];
533 mat[j][1]=bf.
c[bi].
y[j];
534 rhs[j]=ttac.
c[i].
y[j];
537 if(verbose>5) printf(
" weighting\n");
541 if(verbose>5) printf(
" QR\n");
542 ret=
qrLSQ(mat, rhs, sol, rowNr, colNr, &r2);
544 fprintf(stderr,
"Error: no QR solution for BFM\n");
547 free(buf); free(mat);
552 printf(
"best_solution (%d):", bi);
553 for(
int j=0; j<colNr; j++) printf(
" %g", sol[j]);
554 printf(
"\nR^2= %g R=%g\n", r2, sqrt(r2));
559 if(fitfile[0] || svgfile[0]) {
560 for(
int j=0; j<rowNr; j++) ftac.
c[i].
y[j]=rhs[j];
564 double R1, k2, BPnd, DVR, theta2, theta3;
565 R1=sol[0]; theta2=sol[1]; theta3=bf.
c[bi_min].
size;
566 k2=R1*(theta3-lambda) + theta2;
567 DVR= R1 + theta2/(theta3-lambda); BPnd=DVR-1.0;
572 if(bp_plus_one==0) par.
r[i].
p[2]=BPnd;
else par.
r[i].
p[2]=DVR;
574 par.
r[i].
p[3]=theta2;
575 par.
r[i].
p[4]=theta3;
581 free(buf); free(mat);
587 if(verbose>0 && par.
tacNr<50)
594 if(verbose>1) printf(
"writing %s\n", resfile);
599 FILE *fp; fp=fopen(resfile,
"w");
601 fprintf(stderr,
"Error: cannot open file for writing (%s)\n", resfile);
615 if(verbose>0) printf(
"Results saved in %s.\n", resfile);
626 if(verbose>1) printf(
"saving SVG plot\n");
629 sprintf(buf,
"SRTM-BFM fit");
632 if(i>=0) {strcat(buf,
": "); strcat(buf, ttac.
h.
item[i].
value);}
633 ret=
tacPlotFitSVG(&ttac, &ftac, buf, 0.0, nan(
""), 0.0, nan(
""),
641 if(verbose>0) printf(
"Plots written in %s.\n", svgfile);
652 if(verbose>1) printf(
"decay correction for fitted TTACs.\n");
660 if(verbose>1) printf(
"writing %s\n", fitfile);
661 FILE *fp; fp=fopen(fitfile,
"w");
663 fprintf(stderr,
"Error: cannot open file for writing (%s)\n", fitfile);
676 if(verbose>0) printf(
"fitted TACs saved in %s.\n", fitfile);
int bfmSRTM(double *t, double *cri, const int n, const int bfNr, const double t3min, const double t3max, TAC *bf, 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,...
double lambdaFromHalflife(double halflife)
int atofCheck(const char *s, double *v)
int iftPut(IFT *ift, const char *key, const char *value, char comment, TPCSTATUS *status)
int iftFindKey(IFT *ift, const char *key, int start_index)
int atoiCheck(const char *s, int *v)
char * isotopeName(int isotope_code)
double isotopeHalflife(int isotope_code)
int isotopeIdentify(const char *isotope)
int tacInterpolate(TAC *inp, TAC *xinp, TAC *tac, TAC *itac, TAC *iitac, TPCSTATUS *status)
Interpolate and/or integrate TACs from one TAC structure into a new TAC structure,...
char * parFormattxt(parformat c)
int parWrite(PAR *par, FILE *fp, parformat format, int extra, TPCSTATUS *status)
int parFormatFromExtension(const char *s)
int parAllocateWithTAC(PAR *par, TAC *tac, int parNr, TPCSTATUS *status)
Allocate PAR based on data in TAC.
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)
int qrWeightRm(int N, int M, double **A, double *b, double *weight, double *ws)
int qrLSQ(double **mat, double *rhs, double *sol, const unsigned int rows, const unsigned int cols, double *r2)
QR least-squares solving routine.
int qrWeight(int N, int M, double **A, double *b, double *weight, double *ws)
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]
IFT h
Optional (but often useful) header information.
int verbose
Verbose level, used by statusPrint() etc.
tpcerror error
Error code.
int tacDuplicate(TAC *tac1, TAC *tac2)
Make a duplicate of TAC structure.
int tacDecayCorrection(TAC *tac, int isotope, int mode, TPCSTATUS *status)
void tacSetIsotope(TAC *tac, int isotope)
int tacPlotFitSVG(TAC *tac1, TAC *tac2, const char *main_title, const double x1, const double x2, const double y1, const double y2, const char *fname, TPCSTATUS *status)
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 tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
int tacIsWeighted(TAC *tac)
int tacWByFreq(TAC *tac, isotope isot, TPCSTATUS *status)
int tacCorrectFrameOverlap(TAC *d, TPCSTATUS *status)
Correct PET frame start and end times if frames are slightly overlapping or have small gaps in betwee...
Header file for libtpcbfm.
Header file for library libtpcextensions.
weights
Is data weighted, or are weight factors available with data?
@ WEIGHTING_OFF
Not weighted or weights not available (weights for all included samples are 1.0).
char * unitName(int unit_code)
Header file for library libtpcift.
@ ISOTOPE_UNKNOWN
Unknown.
Header file for libtpclinopt.
Header file for libtpcpar.
@ PAR_FORMAT_RES
Model result format of Turku PET Centre.
@ PAR_FORMAT_UNKNOWN
Unknown format.
@ PAR_FORMAT_TSV_UK
UK TSV (point as decimal separator).
Header file for library libtpctac.
@ TAC_FORMAT_UNKNOWN
Unknown format.
@ TAC_FORMAT_PMOD
PMOD TAC format.
Header file for libtpctacmod.