8#include "tpcclibConfig.h"
27static char *info[] = {
28 "General geometrical model for cardiac spillover correction, based on",
29 "the following equations describing the regional TACs of myocardial muscle",
30 "and cavity with blood (Cb) and muscle tissue (Ct) curves:",
32 "Croi,myo(t) = fBV*Cb(t) + (1-fBV)*Ct(t) ",
33 "Croi,cav(t) = beta*Cb(t) + (1-beta)*Ct(t) ",
35 ", where fBV<beta. The spillover corrected BTAC can be calculated as",
37 " (1-fBV)*Croi,cav(t) + (1-beta)*Croi,myo(t)",
38 "Cb(t) = ------------------------------------------",
39 " beta*(1-fBV) - fBV*(1-beta)",
41 "and, optionally, the corrected tissue TAC can be calculated as",
43 " beta*Croi,myo(t) - fBV*Croi,cav(t)",
44 "Ct(t) = ----------------------------------",
45 " beta*(1-fBV) - fBV*(1-beta)",
47 "Radioactivity concentrations in regions-of-interest is given in TAC file,",
48 "including at least the myocardial muscle and cavity ROIs.",
49 "The names or numbers of these two ROIs are given next.",
51 "Usage: @P [Options] tacfile MYO CAV fBV beta cbfile [ctfile]",
55 " Instead of PVC, PVE is simulated from Ct(t) and Cb(t).",
59 " @P aim25.tac myoc cavity 0.2 0.9 aim25ab.tac",
62 "1. Feng et al. (1996), https://doi.org/10.1109/10.486290",
63 "2. Li et al. (1998), https://doi.org/10.1007/BF02522867",
64 "3. Fang et al. (2008), https://doi.org/10.2967/jnumed.107.047613",
66 "See also: heartcor, tacadd, b2plasma, tacformat",
68 "Keywords: input, blood, PVC, myocardium, cavity, spill-over",
85double func_ggm(
int parNr,
double *p,
void*);
86double func_ggm2(
int parNr,
double *p,
void*);
88typedef struct FITDATA {
136 int verbose=0;
if(status!=NULL) verbose=status->
verbose;
137 if(verbose>0) printf(
"%s(*rtac, %d, %d, *stac, %g, %d)\n", __func__, iMyo, iCav, start, method);
138 if(rtac==NULL || stac==NULL || fBV==NULL || beta==NULL) {
146 if(iMyo<0 || iMyo>=rtac->
tacNr || iCav<0 || iCav>=rtac->
tacNr) {
152 double rx1, rx2, sx1, sx2;
158 if(verbose>1) printf(
"setting start time\n");
161 if(sx1<start) start=sx1;
163 if(verbose>1) printf(
"finding peak\n");
166 if(
tacYRange(rtac, iCav, NULL, &ymax, NULL, &i, NULL, NULL)) {
171 if(verbose>1) printf(
"Cavity peak %g at %g (frame %d)\n", ymax, start, i);
175 printf(
"ROI data range: %g - %g\n", rx1, rx2);
176 printf(
"Blood sample data range: %g - %g\n", sx1, sx2);
177 printf(
"Start time: %g\n", start);
185 statusSet(status, __func__, __FILE__, __LINE__, ret);
194 fitdata.ymyo=ftac.
c[iMyo].
y;
195 fitdata.ycav=ftac.
c[iCav].
y;
198 fitdata.yb=stac->
c[0].
y;
211 statusSet(status, __func__, __FILE__, __LINE__, ret);
279 printf(
"ss=%g\n", ss);
281 *fBV=0.25; *beta=0.50;
285 printf(
"initial guess\n");
297 statusSet(status, __func__, __FILE__, __LINE__, ret);
302 if(verbose>1) {printf(
" SS := %g\n", nlo.
funval); fflush(stdout);}
316int main(
int argc,
char **argv)
318 int ai, help=0, version=0, verbose=1;
319 char tacfile[FILENAME_MAX], bfile[FILENAME_MAX], tfile[FILENAME_MAX];
320 char sfile[FILENAME_MAX];
322 double fBV=nan(
""), beta=nan(
"");
330 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
331 tacfile[0]=bfile[0]=tfile[0]=sfile[0]=(char)0;
332 myocname[0]=cavname[0]=(char)0;
335 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
337 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
338 if(strncasecmp(cptr,
"SIMULATE", 3)==0) {
340 }
else if(strncasecmp(cptr,
"FFIT", 2)==0) {
343 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
352 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
357 if(ai<argc) {
strlcpy(tacfile, argv[ai], FILENAME_MAX); ai++;}
360 if(ai>argc) {fprintf(stderr,
"Error: missing command-line argument(s).\n");
return(1);}
364 if(!(fBV>=0.0 && fBV<=1.0)) {
365 fprintf(stderr,
"Error: invalid Fbv command-line argument.\n");
return(1);}
366 if(!(beta>=0.0 && beta<=1.0)) {
367 fprintf(stderr,
"Error: invalid Beta command-line argument.\n");
return(1);}
370 fprintf(stderr,
"Error: fBV must be lower than beta.\n");
return(1);}
374 fprintf(stderr,
"Error: missing sample file %s\n", argv[ai]);
return(1);}
375 strlcpy(sfile, argv[ai], FILENAME_MAX);
380 if(ai<argc) {
strlcpy(bfile, argv[ai], FILENAME_MAX); ai++;}
381 if(ai<argc) {
strlcpy(tfile, argv[ai], FILENAME_MAX); ai++;}
383 if(ai<argc) {fprintf(stderr,
"Error: extra command-line argument.\n");
return(1);}
385 if(!bfile[0]) {
tpcPrintUsage(argv[0], info, stdout);
return(1);}
389 for(ai=0; ai<argc; ai++) printf(
"%s ", argv[ai]);
391 printf(
"tacfile := %s\n", tacfile);
392 if(sfile[0]) printf(
"sfile := %s\n", sfile);
393 if(isfinite(fBV)) printf(
"fBV := %g\n", fBV);
394 if(isfinite(beta)) printf(
"beta := %g\n", beta);
395 printf(
"mode := %d\n", mode);
396 if(mode==3) printf(
"method := %d\n", method);
397 printf(
"bfile := %s\n", bfile);
398 if(tfile[0]) printf(
"tfile := %s\n", tfile);
403 fprintf(stderr,
"Error: fBV must be lower than beta.\n");
411 if(verbose>1) printf(
"reading TACs\n");
419 fprintf(stderr,
"Error: file does not contain myocardial and cavity ROI.\n");
424 fprintf(stderr,
"Error: data contains missing values.\n");
438 fprintf(stderr,
"Error: cannot identify myocardial ROI.\n");
447 fprintf(stderr,
"Error: cannot identify cavity ROI.\n");
452 printf(
"myocardial ROI: %s\n", tac.
c[iMyoc].
name);
453 printf(
"cavity ROI: %s\n", tac.
c[iCav].
name);
461 if(verbose>1) printf(
"estimating PVC parameters\n");
469 fprintf(stderr,
"Note: using only the first concentration column in sample file.\n");
474 fprintf(stderr,
"Error: blood sample file contains missing values.\n");
482 fprintf(stderr,
"Error: missing or incompatible data units.\n");
486 if(ggmEstim(&tac, iMyoc, iCav, &bs, 0.0, method, &fBV, &beta, &status)!=
TPCERROR_OK) {
494 printf(
"fBV := %g\n", fBV);
495 printf(
"beta := %g\n", beta);
510 fprintf(stderr,
"Error: cannot set up BTAC data.\n");
516 strcpy(btac.
c[0].
name,
"Blood");
517 double d=beta*(1.0-fBV)-fBV*(1.0-beta);
if(verbose>2) printf(
"GGM-divider := %g\n", d);
518 if(!(fabs(d)>1.0E-010)) {
519 fprintf(stderr,
"Error: invalid fBV and beta.\n");
523 btac.
c[0].
y[i] = ((1.0-fBV)*tac.
c[iCav].
y[i] - (1.0-beta)*tac.
c[iMyoc].
y[i]) / d;
526 strcpy(btac.
c[0].
name,
"Cav");
528 btac.
c[0].
y[i] = beta*tac.
c[iCav].
y[i] + (1.0-beta)*tac.
c[iMyoc].
y[i];
531 FILE *fp; fp=fopen(bfile,
"w");
533 fprintf(stderr,
"Error: cannot write %s\n", bfile);
544 if(verbose>0) printf(
"saved %s\n", bfile);
557 fprintf(stderr,
"Error: cannot set up TTAC data.\n");
563 strcpy(ttac.
c[0].
name,
"Tissue");
564 double d=beta*(1.0-fBV)-fBV*(1.0-beta);
565 if(!(fabs(d)>1.0E-010)) {
566 fprintf(stderr,
"Error: invalid fBV and beta.\n");
570 ttac.
c[0].
y[i] = (beta*tac.
c[iMyoc].
y[i] - fBV*tac.
c[iCav].
y[i]) / d;
573 strcpy(ttac.
c[0].
name,
"Myo");
575 ttac.
c[0].
y[i] = fBV*tac.
c[iCav].
y[i] + (1.0-fBV)*tac.
c[iMyoc].
y[i];
578 FILE *fp; fp=fopen(tfile,
"w");
580 fprintf(stderr,
"Error: cannot write %s\n", tfile);
591 if(verbose>0) printf(
"saved %s\n", tfile);
606double func_ggm(
int parNr,
double *p,
void *fdata)
608 FITDATA *d=(FITDATA*)fdata;
610 if(d->verbose>0) {printf(
"%s()\n", __func__); fflush(stdout);}
611 if(d->verbose>2) printf(
"p[]: %g %g\n", p[0], p[1]);
619 double blood[d->nr], logblood[d->nr];
620 double v=beta*(1.0-fBV)-fBV*(1.0-beta);
if(d->verbose>3) printf(
"GGM-divider := %g\n", v);
621 if(!(fabs(v)>1.0E-010))
return(nan(
""));
622 for(
unsigned int i=0; i<d->nr; i++)
623 blood[i] = ((1.0-fBV)*d->ycav[i] - (1.0-beta)*d->ymyo[i]) / v;
625 for(
unsigned int i=0; i<d->nr; i++) logblood[i]=log(blood[i]);
627 int n=
fitLine(d->xr, logblood, d->nr, &m, &c);
630 printf(
" fitLine() -> %d\n", n);
631 for(
unsigned int i=0; i<d->nr; i++)
632 printf(
" %g %g %g\n", d->xr[i], blood[i], logblood[i]);
636 if(d->verbose>3) printf(
" fitted line f(x) = %g*x + %g, n=%d\n", m, c, n);
640 for(
unsigned int i=0; i<d->nb; i++) {
641 double b = c + m*d->xb[i];
642 b = exp(b) - d->yb[i];
643 if(isfinite(b)) {ss+=b*b; n++;}
645 if(n==0)
return(nan(
""));
647 for(
int i=0; i<parNr; i++) printf(
" %g", p[i]);
648 printf(
" -> %g\n", ss);
656double func_ggm2(
int parNr,
double *p,
void *fdata)
658 FITDATA *d=(FITDATA*)fdata;
660 if(d->verbose>0) {printf(
"%s()\n", __func__); fflush(stdout);}
666 double A1, A2, A3, L1, L2, L3;
673 double v=beta*(1.0-fBV)-fBV*(1.0-beta);
if(d->verbose>3) printf(
"GGM-divider := %g\n", v);
674 if(!(fabs(v)>1.0E-010))
return(nan(
""));
675 for(
unsigned int i=0; i<d->nr; i++)
676 blood[i] = ((1.0-fBV)*d->ycav[i] - (1.0-beta)*d->ymyo[i]) / v;
679 for(
unsigned int i=0; i<d->nr; i++) {
681 double f=(A1*x-A2-A3)*exp(L1*x)+A2*exp(L2*x)+A3*exp(L3*x);
686 for(
unsigned int i=0; i<d->nb; i++) {
688 double f=(A1*x-A2-A3)*exp(L1*x)+A2*exp(L2*x)+A3*exp(L3*x);
693 for(
int i=0; i<parNr; i++) printf(
" %g", p[i]);
694 printf(
" -> %g\n", ss);
int atofCheck(const char *s, double *v)
int fileExist(const char *filename)
void nloptInit(NLOPT *nlo)
int nloptAllocate(NLOPT *nlo, unsigned int parNr)
void nloptFree(NLOPT *nlo)
void nloptWrite(NLOPT *d, FILE *fp)
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
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 fitLine(double *x, double *y, const int n, double *m, double *c)
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)
double(* _fun)(int, double *, void *)
char name[MAX_TACNAME_LEN+1]
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 tacRead(TAC *d, const char *fname, TPCSTATUS *status)
int tacWrite(TAC *tac, FILE *fp, tacformat format, int extra, TPCSTATUS *status)
int tacSortByTime(TAC *d, TPCSTATUS *status)
int tacSelectTACs(TAC *d, const char *region_name, int reset, TPCSTATUS *status)
int tacSelectBestReference(TAC *d)
int tacYUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
int tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
int tacExtractRange(TAC *d1, TAC *d2, double startT, double endT)
Extract the specified time (x) range from TAC structure.
int tacXRange(TAC *d, double *xmin, double *xmax)
Get the range of x values (times) in TAC structure.
int tacYRange(TAC *d, int i, double *ymin, double *ymax, int *smin, int *smax, int *imin, int *imax)
Get the range of y values (concentrations) in TAC struct.
int nloptIATGO(NLOPT *nlo, const int doLocal, unsigned int maxIterNr, double neighFract, TPCSTATUS *status)
Header file for library libtpcextensions.
#define MAX_TACNAME_LEN
Max length of TAC ID name (not including trailing zero).
@ TPCERROR_INVALID_XRANGE
Invalid sample time range.
@ TPCERROR_FAIL
General error.
@ TPCERROR_NO_DATA
File contains no data.
Header file for libtpcfileutil.
Header file for library libtpcift.
Header file for libtpcli.
Header file for libtpclinopt.
Header file for library libtpcnlopt.
Header file for libtpcrand.
Header file for library libtpctac.
@ TAC_FORMAT_UNKNOWN
Unknown format.
Header file for libtpctacmod.