8#include "tpcclibConfig.h"
27static char *info[] = {
28 "Make a map of time delay between dynamic PET image and BTAC.",
29 "Reversible one-tissue compartment model with blood volume is applied.",
30 "Positive delay time means that tissue curve is delayed as compared to",
31 "the input curve, and vice versa. Thus, input curve needs to be moved",
32 "by the delay time to match the tissue curve.",
34 "Currently only NIfTI format is supported.",
36 "Usage: @P [options] imgfile btacfile delaymap",
39 " -min=<Time (sec)> and -max=<Time (sec)>",
40 " The range of time delays to be tested; by default -10 - +50 s.",
41 " Large range will increase computation time.",
42 " -end=<Fit end time (sec)>",
43 " Use data from 0 to end time; by default, 300 s. End time may need to",
44 " be reduced so that BTAC with negative delay extends to end time.",
46 " Pixels with AUC less than (threshold/100 x BTAC AUC) are set to zero.",
49 " Vb map is saved in units mL blood/mL PET volume.",
51 " K1 map is saved in units mL blood/(min*mL PET volume).",
53 " Map of k2 is saved in units 1/min.",
56 "See also: fitdelay, tactime",
58 "Keywords: image, time delay",
77int main(
int argc,
char **argv)
79 int ai, help=0, version=0, verbose=1;
81 char imgfile[FILENAME_MAX], btacfile[FILENAME_MAX], mapfile[FILENAME_MAX];
82 char vbfile[FILENAME_MAX], k1file[FILENAME_MAX], k2file[FILENAME_MAX];
84 double endtimemin=120.0;
85 int drange[2]={-10,+50};
86 float calcThreshold=0.01;
92 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
93 imgfile[0]=btacfile[0]=mapfile[0]=vbfile[0]=k1file[0]=k2file[0]=(char)0;
95 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
97 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(!*cptr)
continue;
98 if(strncasecmp(cptr,
"END=", 4)==0) {
99 ret=
atofCheck(cptr+4, &endtime);
if(!ret && endtime>0.0)
continue;
100 }
else if(strncasecmp(cptr,
"MIN=", 4)==0) {
101 if(
atoiCheck(cptr+4, &drange[0])==0)
continue;
102 }
else if(strncasecmp(cptr,
"MAX=", 4)==0) {
103 if(
atoiCheck(cptr+4, &drange[1])==0)
continue;
104 }
else if(strncasecmp(cptr,
"THR=", 4)==0) {
106 if(!ret && v<100.0) {calcThreshold=(float)(0.01*v);
continue;}
107 }
else if(strncasecmp(cptr,
"K1=", 3)==0) {
108 strlcpy(k1file, cptr+3, FILENAME_MAX);
if(strlen(k1file))
continue;
109 }
else if(strncasecmp(cptr,
"K2=", 3)==0) {
110 strlcpy(k2file, cptr+3, FILENAME_MAX);
if(strlen(k2file))
continue;
111 }
else if(strncasecmp(cptr,
"VB=", 3)==0) {
112 strlcpy(vbfile, cptr+3, FILENAME_MAX);
if(strlen(vbfile))
continue;
114 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
119 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
124 if(ai<argc)
strlcpy(imgfile, argv[ai++], FILENAME_MAX);
125 if(ai<argc)
strlcpy(btacfile, argv[ai++], FILENAME_MAX);
126 if(ai<argc)
strlcpy(mapfile, argv[ai++], FILENAME_MAX);
127 if(ai<argc) {fprintf(stderr,
"Error: too many arguments: '%s'.\n", argv[ai]);
return(1);}
129 if(!mapfile[0]) {
tpcPrintUsage(argv[0], info, stdout);
return(1);}
132 if(endtime<endtimemin) {
133 fprintf(stderr,
"Error: too short fit time set with option -end.\n");
136 if(drange[0]>drange[1]) {
int i=drange[0]; drange[0]=drange[1]; drange[1]=i;}
137 if((drange[1]-drange[0])>180) {
138 fprintf(stderr,
"Error: too wide delay range.\n");
140 }
else if((drange[1]-drange[0])>120) {
141 fprintf(stderr,
"Warning: large delay range.\n");
142 }
else if((drange[1]-drange[0])<4) {
143 fprintf(stderr,
"Error: too short delay range.\n");
149 printf(
"imgfile := %s\n", imgfile);
150 printf(
"btacfile := %s\n", btacfile);
151 printf(
"mapfile := %s\n", mapfile);
152 printf(
"endtime := %g s\n", endtime);
153 printf(
"delay_range := %d - %d s\n", drange[0], drange[1]);
154 printf(
"threshold := %g\n", calcThreshold);
155 if(vbfile[0]) printf(
"vbfile := %s\n", vbfile);
156 if(k1file[0]) printf(
"k1file := %s\n", k1file);
157 if(k2file[0]) printf(
"k2file := %s\n", k2file);
169 if(verbose>1) printf(
"reading %s\n", btacfile);
171 ret=
tacRead(&tac, btacfile, &status);
173 fprintf(stderr,
"Error: %s (%s)\n",
errorMsg(status.
error), btacfile);
178 printf(
"tacNr := %d\n", tac.
tacNr);
179 printf(
"sampleNr := %d\n", tac.
sampleNr);
182 printf(
"isframe := %d\n", tac.
isframe);
192 if(verbose>1) {printf(
"reading %s\n", imgfile); fflush(stdout);}
194 ret=
imgRead(&img, imgfile, &status);
197 fprintf(stderr,
"Error: %s (%s)\n",
errorMsg(status.
error), imgfile); fflush(stderr);
201 if(verbose>0) fprintf(stderr,
"Warning: missing pixel values.\n");
205 fprintf(stderr,
"Error: invalid sample times in %s\n", imgfile); fflush(stderr);
208 if(verbose>1) printf(
"Image range: %g - %g\n", ixmin, ixmax);
210 if(ixmax<endtimemin) {
211 fprintf(stderr,
"Error: image time range is too short.\n");
214 if(ixmax<endtime) endtime=ixmax;
221 if(verbose>0) fprintf(stderr,
"Warning: missing BTAC time units.\n");
224 fprintf(stderr,
"Error: invalid sample times in %s\n", btacfile); fflush(stderr);
230 fprintf(stderr,
"Warning: assuming BTAC sample times are in units %s.\n",
unitName(guessedUnit));
231 tac.
tunit=guessedUnit;
234 fprintf(stderr,
"Error: %s (%s)\n",
errorMsg(status.
error), btacfile); fflush(stderr);
239 fprintf(stderr,
"Error: invalid sample times in %s\n", btacfile); fflush(stderr);
242 if(verbose>1) printf(
"BTAC range: %g - %g\n", bxmin, bxmax);
248 if(verbose>1) {printf(
"checking time range\n"); fflush(stdout);}
250 if(tac.
sampleNr<5) {fprintf(stderr,
"Error: BTAC has too few samples.\n"); ret++;}
251 if(img.
dimt<5) {fprintf(stderr,
"Error: image has too few frames.\n"); ret++;}
252 if(bxmax<endtimemin) {fprintf(stderr,
"Error: BTAC time range is too short.\n"); ret++;}
253 if(ixmax<endtimemin) {fprintf(stderr,
"Error: image time range is too short.\n"); ret++;}
254 if(drange[0]<0 && bxmax<(endtime+drange[0])) {
256 if(endtime<endtimemin) {
257 fprintf(stderr,
"Error: BTAC does not extend to required end time.\n"); ret++;}
260 for(
int i=0; i<img.
dimt; i++)
if(img.
x[i]<=endtime) frameNr++;
261 if(frameNr<5) {fprintf(stderr,
"Error: image has too few frames in fit time range.\n"); ret++;}
265 double thrs=
tacAUC(&tac, 0, 0.0, img.
x[frameNr-1], NULL);
271 if(verbose>1) {printf(
"moving BTAC\n"); fflush(stdout);}
272 int moveNr=1+(drange[1]-drange[0]);
273 if(verbose>2) printf(
"frameNr := %d\nmoveNr := %d\n", frameNr, moveNr);
276 fprintf(stderr,
"Error: cannot allocate memory.\n"); fflush(stderr);
282 for(
int i=0; i<dtac.
sampleNr; i++) {
283 dtac.
x1[i]=img.
x1[i]; dtac.
x[i]=img.
x[i]; dtac.
x2[i]=img.
x2[i];}
286 fprintf(stderr,
"Error: cannot allocate memory.\n"); fflush(stderr);
290 if(verbose>1) {printf(
"integrating moved BTACs\n"); fflush(stdout);}
291 for(
int di=0; di<dtac.
tacNr; di++) {
292 if(di==0)
for(
int i=0; i<tac.
sampleNr; i++) tac.
x[i]+=drange[0];
293 else for(
int i=0; i<tac.
sampleNr; i++) tac.
x[i]+=1.0;
295 dtac.
c[di].
y, ditac.
c[di].
y, NULL, dtac.
sampleNr, 3, 1, 0);
299 fprintf(stderr,
"Error: cannot interpolate delayed BTACs.\n"); fflush(stderr);
303 char *dtacfile=
"delayed_btacs.dat";
304 printf(
"writing %s\n", dtacfile);
305 FILE *fp; fp=fopen(dtacfile,
"w");
310 char *ditacfile=
"delayed_btac_integrals.dat";
311 printf(
"writing %s\n", ditacfile);
312 fp=fopen(ditacfile,
"w");
323 if(verbose>1) {printf(
"preparing delay map\n"); fflush(stdout);}
326 fprintf(stderr,
"Error: cannot allocate memory\n"); fflush(stderr);
336 fprintf(stderr,
"Error: cannot allocate memory\n"); fflush(stderr);
345 fprintf(stderr,
"Error: cannot allocate memory\n"); fflush(stderr);
355 fprintf(stderr,
"Error: cannot allocate memory\n"); fflush(stderr);
365 if(verbose>1) {printf(
"allocating memory for NNLS matrices\n"); fflush(stdout);}
368 double *llsq_mat=(
double*)malloc((llsq_n*llsq_m)*
sizeof(
double));
370 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
374 double **llsq_a=(
double**)malloc(llsq_n*
sizeof(
double*));
379 for(
int ni=0; ni<llsq_n; ni++) llsq_a[ni]=llsq_mat+ni*llsq_m;
380 double r2, llsq_b[llsq_m], llsq_x[llsq_n], llsq_wp[llsq_n], llsq_zz[llsq_m];
388 if(verbose>0) {printf(
"processing %llu image pixels\n", pxlNr); fflush(stdout);}
389 for(
int zi=0; zi<img.
dimz; zi++) {
390 if(img.
dimz>2 && verbose>0) {fprintf(stdout,
"."); fflush(stdout);}
391 for(
int yi=0; yi<img.
dimy; yi++)
for(
int xi=0; xi<img.
dimx; xi++) {
392 map.
m[zi][yi][xi][0]=0.0;
393 if(vbfile[0]) vbimg.
m[zi][yi][xi][0]=0.0;
394 if(k1file[0]) k1img.
m[zi][yi][xi][0]=0.0;
395 double ttac[llsq_m], ittac[llsq_m];
396 for(
int mi=0; mi<llsq_m; mi++) ttac[mi]=img.
m[zi][yi][xi][mi];
398 if(ittac[llsq_m-1]<thrs)
continue;
400 double r2Best=1.0E+100;
401 double vbBest=0.0, k1Best=0.0, k2Best=0.0;
402 for(
int di=0; di<dtac.
tacNr; di++) {
403 dtac.
c[di].
size=1.0E+100;
405 for(
int mi=0; mi<llsq_m; mi++)
406 llsq_b[mi]=img.
m[zi][yi][xi][mi];
407 for(
int mi=0; mi<llsq_m; mi++) {
408 llsq_mat[mi]=dtac.
c[di].
y[mi];
409 llsq_mat[mi+llsq_m]=ditac.
c[di].
y[mi];
410 llsq_mat[mi+2*llsq_m]=-ittac[mi];
413 printf(
"\nmatrix %d\n", 1+di);
414 for(
int mi=0; mi<llsq_m; mi++) printf(
"%g %g %g %g\n",
415 llsq_b[mi], llsq_mat[mi], llsq_mat[mi+llsq_m], llsq_mat[mi+2*llsq_m]);
418 int ret=
nnls(llsq_a, llsq_m, llsq_n, llsq_b, llsq_x, &r2, llsq_wp, llsq_zz, indexp);
422 r2Best=r2; diBest=di;
424 k1Best=llsq_x[1]-llsq_x[0]*llsq_x[2];
428 if(diBest<0)
continue;
430 map.
m[zi][yi][xi][0]=(float)(drange[0]+diBest);
432 if(vbBest>1.0) vbBest=1.0;
433 vbimg.
m[zi][yi][xi][0]=(float)vbBest;
437 if(k1Best>6.0) k1Best=6.0;
else if(k1Best<0.0) k1Best=0.0;
438 k1img.
m[zi][yi][xi][0]=(float)k1Best;
442 if(k2Best>10.0) k2Best=10.0;
443 k2img.
m[zi][yi][xi][0]=(float)k2Best;
447 if(diBest==0 || diBest==dtac.
tacNr-1)
continue;
448 float w1=1.0/(1.0E-12+dtac.
c[diBest-1].
size);
449 float w2=1.0/(1.0E-12+dtac.
c[diBest].
size);
450 float w3=1.0/(1.0E-12+dtac.
c[diBest+1].
size);
451 float d=(w1*(float)(diBest-1) + w2*(float)diBest + w3*(float)(diBest+1))/(w1+w2+w3);
452 map.
m[zi][yi][xi][0]=(float)drange[0] + d;
456 if(img.
dimz>2 && verbose>0) {fprintf(stdout,
"\n"); fflush(stdout);}
458 free(llsq_a); free(llsq_mat);
470 if(verbose>0) {printf(
" %s written.\n", vbfile); fflush(stdout);}
475 if(verbose>0) {printf(
" %s written.\n", k1file); fflush(stdout);}
480 if(verbose>0) {printf(
" %s written.\n", k2file); fflush(stdout);}
488 if(verbose>1) {printf(
"writing %s\n", mapfile); fflush(stdout);}
489 ret=
imgWrite(&map, mapfile, &status);
491 fprintf(stderr,
"Error: %s\n",
errorMsg(status.
error)); fflush(stderr);
495 if(verbose>0) {printf(
" %s written.\n", mapfile); fflush(stdout);}
int atofCheck(const char *s, double *v)
void imgContents(IMG *img, FILE *fp)
unsigned long long imgNaNs(IMG *img, int fix)
int imgAllocate(IMG *img, const unsigned int dimz, const unsigned int dimy, const unsigned int dimx, const unsigned int dimt, TPCSTATUS *status)
int imgXRange(IMG *img, double *xmin, double *xmax)
int imgRead(IMG *img, const char *fname, TPCSTATUS *status)
int imgWrite(IMG *img, const char *fname, TPCSTATUS *status)
int liIntegratePET(double *x1, double *x2, double *y, int nr, double *ie, double *iie, const int verbose)
Calculate PET TAC AUC from start to each time frame, as averages during each frame.
int liInterpolateForPET(double *x, double *y, const int nr, double *newx1, double *newx2, double *newy, double *newyi, double *newyii, const int newnr, const int se, const int ee, const int verbose)
Linear TAC interpolation and/or integration to PET frames.
int atoiCheck(const char *s, int *v)
double tacAUC(TAC *tac, int ti, double t1, double t2, TPCSTATUS *status)
Calculates TAC AUC from t1 to t2.
int nnls(double **a, int m, int n, double *b, double *x, double *rnorm, double *wp, double *zzp, int *indexp)
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)
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)
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 tacAllocate(TAC *tac, int sampleNr, int tacNr)
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 tacSetX(TAC *d, TPCSTATUS *status)
Set TAC x values based on x1 and x2 values, or guess x1 and x2 values based on x values.
int tacXRange(TAC *d, double *xmin, double *xmax)
Get the range of x values (times) in TAC structure.
Header file for library libtpccsv.
Header file for library libtpcextensions.
@ UNIT_ML_PER_ML_MIN
mL/(mL*min)
@ UNIT_UNKNOWN
Unknown unit.
char * unitName(int unit_code)
Header file for library libtpcift.
Header file for libtpcimage.
Header file for libtpcli.
Header file for libtpclinopt.
Header file for library libtpctac.
@ TAC_FORMAT_PMOD
PMOD TAC format.
Header file for libtpctacimg.
int tacimgXMatch(TAC *tac, IMG *img)
int tacimgXCopy(TAC *tac, IMG *img)
Header file for libtpctacmod.