10#include "tpcclibConfig.h"
28static char *info[] = {
29 "Computation of parametric images of K1 and Vb from dynamic PET image in",
30 "ECAT, NIfTI, or Analyze format, applying NNLS.",
31 "The multilinear model is either",
32 " Cpet(t) = Vb*Cb(t) + ((1-Vb)*K1+Vb*k2)*Integral[Cb(t)]",
33 " - k2*Integral[Cpet(t)]",
34 "or if plasma data is available",
35 " Cpet(t) = Vb*Cb(t) + (1-Vb)*K1*Integral[(1-HCT)*Cp(t)]",
37 "The program needs the blood curve and dynamic PET image, and optionally",
38 "plasma curve, multiplied by (1-HCT).",
39 "The name for the resulting parametric K1 image must be given.",
41 "Usage: @P [Options] btacfile imgfile k1file [vbfile]",
44 " -k2=<value> | -k2=median",
45 " Parameter k2 is fixed to a known value, or to a median k2 from",
46 " initial fitting of unconstrained model.",
48 " Use plasma curve, multiplied by (1-HCT), as input function;",
49 " the -k2 options are not available with this.",
51 " Pixels with last frame concentration less than (threshold/100 x BTAC)",
52 " are set to zero; default is 1%.",
53 " -end=<fit end time (min)>",
54 " Use data from 0 to end time; by default, model is fitted to all frames.",
56 " By default, all weights are set to 1.0 (no weighting, option -w1);",
57 " option -wf sets weights based on frame lengths, and option -wfa based",
58 " on both frame lengths and mean activity during each frame.",
60 " Image with pixel values of the second fitted parameter, (1-Vb)*K1.",
62 " Image with pixel values of the third fitted parameter, HCT*kc;",
63 " only available with the first model setting (with unknown PTAC).",
65 " Image with pixel values corrected for estimated blood volume,",
66 " Cpet(t) - (1-Vb)*Cb(t).",
69 "The units of pixel values in the parametric images are ml/(min*ml) for K1",
72 "See also: fitmtrap, b2ptrap, img2dft",
74 "Keywords: image, modelling, irreversible uptake, perfusion",
93int main(
int argc,
char **argv)
95 int ai, help=0, version=0, verbose=1;
96 char ptacfile[FILENAME_MAX], btacfile[FILENAME_MAX], petfile[FILENAME_MAX];
97 char k1file[FILENAME_MAX], vbfile[FILENAME_MAX], cbvfile[FILENAME_MAX];
98 char p2file[FILENAME_MAX], p3file[FILENAME_MAX];
99 float calcThreshold=0.01;
101 double fixedk2=nan(
"");
110 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
111 ptacfile[0]=btacfile[0]=petfile[0]=k1file[0]=vbfile[0]=p2file[0]=p3file[0]=cbvfile[0]=(char)0;
113 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
115 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
116 if(strncasecmp(cptr,
"THR=", 4)==0) {
118 if(!ret && v>=0.0 && v<=200.0) {calcThreshold=(float)(0.01*v);
continue;}
119 }
else if(strncasecmp(cptr,
"END=", 4)==0) {
120 ret=
atof_with_check(cptr+4, &fittime);
if(!ret && fittime>0.0)
continue;
121 }
else if(strcasecmp(cptr,
"WFA")==0) {
123 }
else if(strcasecmp(cptr,
"WF")==0) {
125 }
else if(strcasecmp(cptr,
"W1")==0) {
127 }
else if(strncasecmp(cptr,
"K2=MEDIAN", 4)==0) {
128 fixk2=2; fixedk2=nan(
"");
continue;
129 }
else if(strncasecmp(cptr,
"K2=", 3)==0) {
131 if(!ret && fixedk2>=0.0) {fixk2=1;
continue;}
132 }
else if(strncasecmp(cptr,
"PTAC=", 5)==0) {
133 strlcpy(ptacfile, cptr+5, FILENAME_MAX);
if(strlen(ptacfile)>0)
continue;
134 }
else if(strncasecmp(cptr,
"P2=", 3)==0) {
135 strlcpy(p2file, cptr+3, FILENAME_MAX);
if(strlen(p2file)>0)
continue;
136 }
else if(strncasecmp(cptr,
"P3=", 3)==0) {
137 strlcpy(p3file, cptr+3, FILENAME_MAX);
if(strlen(p3file)>0)
continue;
138 }
else if(strncasecmp(cptr,
"CBV=", 4)==0) {
139 strlcpy(cbvfile, cptr+4, FILENAME_MAX);
if(strlen(cbvfile)>0)
continue;
141 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
147 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
152 if(ai<argc)
strlcpy(btacfile, argv[ai++], FILENAME_MAX);
153 if(ai<argc)
strlcpy(petfile, argv[ai++], FILENAME_MAX);
154 if(ai<argc)
strlcpy(k1file, argv[ai++], FILENAME_MAX);
155 if(ai<argc)
strlcpy(vbfile, argv[ai++], FILENAME_MAX);
156 if(ai<argc) {fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
return(1);}
160 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
return(1);}
163 if(ptacfile[0] && fixk2>0) {
164 fprintf(stderr,
"Error: -k2 option not available with PTAC.\n");
169 fprintf(stderr,
"Warning: p3 not available with PTAC.\n");
173 fprintf(stderr,
"Warning: p3 not available with k2.\n");
180 printf(
"btacfile := %s\n", btacfile);
181 if(ptacfile[0]) printf(
"ptacfile := %s\n", ptacfile);
182 printf(
"petfile := %s\n", petfile);
183 printf(
"k1file := %s\n", k1file);
184 if(vbfile[0]) printf(
"vbfile := %s\n", vbfile);
185 if(p2file[0]) printf(
"p2file := %s\n", p2file);
186 if(p3file[0]) printf(
"p3file := %s\n", p3file);
187 if(cbvfile[0]) printf(
"cbvfile := %s\n", cbvfile);
188 printf(
"calcThreshold :=%g\n", calcThreshold);
189 printf(
"weights := %d\n", weights);
190 printf(
"fixk2 := %d\n", fixk2);
191 if(!isnan(fixedk2)) printf(
"fixedk2 := %g\n", fixedk2);
192 if(fittime>0.0) printf(
"required_fittime := %g min\n", fittime);
201 if(verbose>0) printf(
"reading data files\n");
207 petfile, NULL, btacfile, ptacfile, NULL, &fittime, &dataNr, &img,
208 NULL, &tac, 1, stdout, verbose-2, errmsg);
210 fprintf(stderr,
"Error: %s.\n", errmsg);
211 if(verbose>1) printf(
" ret := %d\n", ret);
215 if(verbose>0) fprintf(stderr,
"Warning: missing pixel values.\n");
216 strcpy(tac.
voi[0].
name,
"blood");
220 for(
int ti=0; ti<tac.
voiNr; ti++) {
221 for(
int fi=0; fi<tac.
frameNr; fi++) tac.
voi[ti].
y2[fi]/=60.0;
222 for(
int fi=0; fi<tac.
frameNr; fi++) tac.
voi[ti].
y3[fi]/=3600.0;
227 printf(
"fittimeFinal := %g min\n", fittime);
228 printf(
"dataNr := %d\n", dataNr);
229 printf(
"image_matrix_size := %dx%dx%d\n", img.
dimx, img.
dimy, img.
dimz);
230 printf(
"frames := %d\n", img.
dimt);
234 fprintf(stderr,
"Error: too few time frames for fitting.\n");
241 if(verbose>1) fprintf(stdout,
"allocating working memory for pixel TACs\n");
244 fprintf(stderr,
"Error: cannot allocate memory.\n");
245 if(verbose>0) printf(
"ret := %d\n", ret);
252 fprintf(stderr,
"Error: cannot calculate weights.\n");
255 for(
int i=0; i<dataNr; i++) tac.
w[i]=img.
weight[i];
258 double threshold=calcThreshold*tac.
voi[0].
y[dataNr-1];
259 if(verbose>1) printf(
"threshold_AUC := %g\n", threshold);
265 if(verbose>1) fprintf(stdout,
"allocating memory for parametric image data\n");
275 fprintf(stderr,
"Error: cannot allocate memory for result image.\n");
282 k1img.
end[0]=vbimg.
end[0]=p2img.
end[0]=p3img.
end[0]=60.*fittime;
284 k1img.
unit=CUNIT_ML_PER_ML_PER_MIN;
285 vbimg.
unit=CUNIT_ML_PER_ML;
286 p2img.
unit=p3img.
unit=CUNIT_PER_MIN;
295 int fittedNr=0, fittedokNr=0, thresholdNr=0;
298 for(
int m=0; m<dataNr; m++) {
299 if(tac.
w[m]<=1.0e-20) tac.
w[m]=0.0;
else tac.
w[m]=sqrt(tac.
w[m]);
305 if(!ptacfile[0] && fixk2!=1) {
306 if(verbose>0) fprintf(stdout,
"fitting 3-parameter model\n");
310 if(verbose>1) fprintf(stdout,
"allocating memory for NNLS\n");
312 int nnls_index[nnls_n];
313 double **nnls_a, nnls_b[nnls_m], nnls_zz[nnls_m], nnls_x[nnls_n], *nnls_mat,
314 nnls_wp[nnls_n], nnls_rnorm;
315 nnls_mat=(
double*)malloc((nnls_n*nnls_m)*
sizeof(
double));
316 nnls_a=(
double**)malloc(nnls_n*
sizeof(
double*));
317 if(nnls_mat==NULL || nnls_a==NULL) {
318 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
323 for(
int n=0; n<nnls_n; n++) nnls_a[n]=nnls_mat+n*nnls_m;
325 double *ct, *cti, *cb, *cbi;
329 double *p3list=(
double*)malloc(
sizeof(
double)*img.
dimz*img.
dimy*img.
dimx);
331 fprintf(stderr,
"Error: out of memory.\n");
338 for(
int pi=0; pi<img.
dimz; pi++) {
339 if(verbose>0) {fprintf(stdout,
"."); fflush(stdout);}
340 for(
int yi=0; yi<img.
dimy; yi++) {
341 for(
int xi=0; xi<img.
dimx; xi++) {
342 double K1=0.0, Vb=0.0, p2=0.0, p3=0.0;
344 k1img.
m[pi][yi][xi][0]=0.0;
345 vbimg.
m[pi][yi][xi][0]=0.0;
346 p2img.
m[pi][yi][xi][0]=0.0;
347 p3img.
m[pi][yi][xi][0]=0.0;
349 if(img.
m[pi][yi][xi][dataNr-1]<threshold) {thresholdNr++;
continue;}
351 for(
int fi=0; fi<tac.
frameNr; fi++) ct[fi]=img.
m[pi][yi][xi][fi];
357 for(
int m=0; m<nnls_m; m++) {
360 nnls_a[2][m]=-cti[m];
364 for(
int m=0; m<nnls_m; m++) {
366 for(
int n=0; n<nnls_n; n++) nnls_a[n][m]*=tac.
w[m];
369 ret=
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm, nnls_wp, nnls_zz, nnls_index);
371 if(verbose>3) printf(
"no solution possible (%d)\n", ret);
372 if(verbose>4) printf(
"nnls_n=%d nnls_m=%d\n", nnls_n, nnls_m);
373 for(
int n=0; n<nnls_n; n++) nnls_x[n]=0.0;
385 K1=(p2-Vb*p3)/(1.0-Vb);
386 if(K1>0.1 && Vb<0.6 && p3>0.1 && p3<1.0) p3list[p3nr++]=p3;
387 if(K1>0.0 && K1<8.0) fittedokNr++;
389 if(K1<0.0) K1=0.0;
else if(K1>8.0) K1=8.0;
392 vbimg.
m[pi][yi][xi][0]=Vb;
393 p2img.
m[pi][yi][xi][0]=p2;
394 p3img.
m[pi][yi][xi][0]=p3;
395 k1img.
m[pi][yi][xi][0]=K1;
398 if(cbvfile[0] && fixk2==0) {
399 for(
int ti=0; ti<img.
dimt; ti++) {
400 img.
m[pi][yi][xi][ti]-=nnls_x[0]*cb[ti];
401 if(img.
m[pi][yi][xi][ti]<0.0) img.
m[pi][yi][xi][ti]=0.0;
408 if(verbose>0) {fprintf(stdout,
" done.\n"); fflush(stdout);}
409 free(nnls_mat); free(nnls_a);
414 fprintf(stdout,
"%d out of %d pixels were fitted; %d pixels ok.\n", fittedNr, n, fittedokNr);
415 fprintf(stdout,
"%d pixels were thresholded.\n", thresholdNr);
419 if(p3nr>0) fixedk2=
dmedian(p3list, p3nr);
421 if(!isnan(fixedk2)) {
422 if(verbose>0) printf(
"Median k2 := %g\n", fixedk2);
423 if(verbose>1) printf(
"Median calculated from %d pixels\n", p3nr);
424 }
else if(fixk2==2) {
425 fprintf(stderr,
"Error: reasonable k2 median could not be calculated.\n");
433 fprintf(stderr,
"Error: cannot write p3 image.\n");
438 if(verbose>0) fprintf(stdout,
"P3 image saved.\n");
445 if(
imgWrite(cbvfile, &img)) fprintf(stderr,
"Error: cannot write cbv image.\n");
449 if(!ret && vbfile[0]) ret=
imgWrite(vbfile, &vbimg);
450 if(!ret && p2file[0]) ret=
imgWrite(p2file, &p2img);
453 fprintf(stderr,
"Error: cannot write parametric image.\n");
456 if(verbose>0) fprintf(stdout,
"Parametric image(s) saved.\n");
467 if(verbose>0) fprintf(stdout,
"fitting 2-parameter model\n");
471 if(verbose>1) fprintf(stdout,
"allocating memory for NNLS\n");
473 int nnls_index[nnls_n];
474 double **nnls_a, nnls_b[nnls_m], nnls_zz[nnls_m], nnls_x[nnls_n], *nnls_mat,
475 nnls_wp[nnls_n], nnls_rnorm;
476 nnls_mat=(
double*)malloc((nnls_n*nnls_m)*
sizeof(
double));
477 nnls_a=(
double**)malloc(nnls_n*
sizeof(
double*));
478 if(nnls_mat==NULL || nnls_a==NULL) {
479 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
484 for(
int n=0; n<nnls_n; n++) nnls_a[n]=nnls_mat+n*nnls_m;
486 double *ct, *cti, *cb, *cbi, *cpi=NULL;
488 if(ptacfile[0]) cpi=tac.
voi[1].
y2;
491 for(
int pi=0; pi<img.
dimz; pi++) {
492 if(verbose>0) {fprintf(stdout,
"."); fflush(stdout);}
493 for(
int yi=0; yi<img.
dimy; yi++) {
494 for(
int xi=0; xi<img.
dimx; xi++) {
495 double K1=0.0, Vb=0.0, p2=0.0;
497 k1img.
m[pi][yi][xi][0]=0.0;
498 vbimg.
m[pi][yi][xi][0]=0.0;
499 p2img.
m[pi][yi][xi][0]=0.0;
501 if(img.
m[pi][yi][xi][dataNr-1]<threshold) {thresholdNr++;
continue;}
503 for(
int fi=0; fi<tac.
frameNr; fi++) ct[fi]=img.
m[pi][yi][xi][fi];
514 for(
int m=0; m<nnls_m; m++) {
520 for(
int m=0; m<nnls_m; m++) {
521 nnls_a[0][m]=cb[m]+fixedk2*cbi[m];
523 nnls_b[m]=ct[m]+fixedk2*cti[m];
527 for(
int m=0; m<nnls_m; m++) {
529 for(
int n=0; n<nnls_n; n++) nnls_a[n][m]*=tac.
w[m];
532 ret=
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm, nnls_wp, nnls_zz, nnls_index);
534 if(verbose>3) printf(
"no solution possible (%d)\n", ret);
535 if(verbose>4) printf(
"nnls_n=%d nnls_m=%d\n", nnls_n, nnls_m);
536 for(
int n=0; n<nnls_n; n++) nnls_x[n]=0.0;
548 if(K1>0.0 && K1<8.0) fittedokNr++;
550 if(K1<0.0) K1=0.0;
else if(K1>8.0) K1=8.0;
553 vbimg.
m[pi][yi][xi][0]=Vb;
554 p2img.
m[pi][yi][xi][0]=p2;
555 k1img.
m[pi][yi][xi][0]=K1;
559 for(
int ti=0; ti<img.
dimt; ti++) {
560 img.
m[pi][yi][xi][ti]-=nnls_x[0]*cb[ti];
561 if(img.
m[pi][yi][xi][ti]<0.0) img.
m[pi][yi][xi][ti]=0.0;
568 if(verbose>0) {fprintf(stdout,
" done.\n"); fflush(stdout);}
569 free(nnls_mat); free(nnls_a);
574 fprintf(stdout,
"%d out of %d pixels were fitted; %d pixels ok.\n", fittedNr, n, fittedokNr);
575 fprintf(stdout,
"%d pixels were thresholded.\n", thresholdNr);
583 if(
imgWrite(cbvfile, &img)) fprintf(stderr,
"Error: cannot write cbv image.\n");
596 if(!ret && vbfile[0]) ret=
imgWrite(vbfile, &vbimg);
597 if(!ret && p2file[0]) ret=
imgWrite(p2file, &p2img);
600 fprintf(stderr,
"Error: cannot write parametric image.\n");
603 if(verbose>0) fprintf(stdout,
"Parametric image(s) saved.\n");
int atof_with_check(char *double_as_string, double *result_value)
int dftAddmem(DFT *dft, int voiNr)
int dftTimeunitConversion(DFT *dft, int tunit)
unsigned long long imgNaNs(IMG *img, int fix)
int imgAllocateWithHeader(IMG *image, int planes, int rows, int columns, int frames, IMG *image_from)
void imgEmpty(IMG *image)
int imgWrite(const char *fname, IMG *img)
int petintegral(double *x1, double *x2, double *y, int nr, double *ie, double *iie)
Integrate PET TAC data to frame mid times.
Header file for libtpccurveio.
Header file for libtpcimgio.
#define IMG_DC_NONCORRECTED
Header file for libtpcimgp.
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
size_t strlcpy(char *dst, const char *src, size_t dstsize)
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)
Header file for libtpcmodel.
int nnls(double **a, int m, int n, double *b, double *x, double *rnorm, double *w, double *zz, int *index)
double dmedian(double *data, int n)
Header file for libtpcmodext.
int imgSetWeights(IMG *img, int wmet, int verbose)
char name[MAX_REGIONNAME_LEN+1]