10#include "tpcclibConfig.h"
30static char *info[] = {
31 "Computation of parametric image of k3 from dynamic PET image in ECAT, NIfTI,",
32 "or Analyze format applying irreversible two-tissue compartmental model with",
33 "arterial plasma input.",
34 "The compartmental models are transformed to general linear least squares",
35 "functions (1), which are solved using Lawson-Hanson non-negative least",
36 "squares (NNLS) algorithm (2).",
38 "Dynamic PET image and plasma time-activity curve (PTAC) must be corrected",
39 "for decay to the tracer injection time.",
41 "Usage: @P [Options] ptacfile imgfile k3file",
45 " Pixels with AUC less than (threshold/100 x PTAC AUC) are set to zero;",
47 " -end=<Fit end time (min)>",
48 " Use data from 0 to end time; by default, model is fitted to all frames.",
50 " Parametric K1/(k2+k3) image is saved.",
52 " Parametric K1 image is saved.",
54 " Parametric k2 image is saved.",
56 " Parametric Ki image is saved.",
58 " Vb is fitted, and parametric Vb image is saved; without this,",
59 " Vb is assumed to be zero (or precorrected).",
61 " Parametric k2+k3 image is saved.",
70 "The vascular volume is considered in the model setting only if file name for",
71 "Vb image is given. Otherwise the contribution of vasculature to the total",
72 "radioactivity concentration is assumed to be negligible, or pre-corrected.",
73 "Note that this model can correctly estimate Vb only if",
74 "1) plasma does not contain any labelled metabolites, and",
75 "2) plasma and blood curves are similar in shape.",
76 "Vascular volume can be pre-corrected with imgcbv.",
78 "The units of pixel values in the parametric images are 1/min for k3,",
79 "ml/(min*ml) for K1 and Ki, and ml/ml for DV and Vb.",
81 "Example 1b: Vb is assumed negligible:",
82 " @P ua2917ap.kbq ua2917dy1.v ua2917k3.v",
83 "Example 1b: Vb is fitted as one of the model parameters:",
84 " @P -Vb=ua2917vb.v ua2917ap.kbq ua2917dy1.v ua2917k3.v",
85 "Example 1c: Vb is pre-corrected:",
86 " imgcbv ua2917dy1.v ua2917ab.kbq 0.04 ua2917dy1_cbv.v",
87 " @P ua2917ap.kbq ua2917dy1_cbv.v ua2917k3.v",
88 "Example 2: K1, DV, and k3 images are saved:",
89 " @P -K1=ua2917k1.v -DV=ua2917dv.v ua2917ap.kbq ua2917dy1.v ua2917k3.v",
92 "1. Blomqvist G. On the construction of functional maps in positron",
93 " emission tomography. J Cereb Blood Flow Metab 1984;4:629-632.",
94 "2. Lawson CL & Hanson RJ. Solving least squares problems.",
95 " Prentice-Hall, 1974, ISBN 0-89871-356-0.",
97 "See also: imgcbv, imgki, fitk3",
99 "Keywords: image, modelling, irreversible uptake, Ki, NNLS",
115enum {METHOD_UNKNOWN, METHOD_NNLS, METHOD_BVLS};
116static char *method_str[] = {
"unknown",
"NNLS",
"BVLS", 0};
123int main(
int argc,
char **argv)
125 int ai, help=0, version=0, verbose=1;
126 char inpfile[FILENAME_MAX], petfile[FILENAME_MAX], k3file[FILENAME_MAX];
127 char k1file[FILENAME_MAX], k2file[FILENAME_MAX], vbfile[FILENAME_MAX];
128 char dvfile[FILENAME_MAX], kifile[FILENAME_MAX], k2k3file[FILENAME_MAX];
129 float calcThreshold=0.01;
133 int method=METHOD_NNLS;
139 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
140 inpfile[0]=petfile[0]=k3file[0]=kifile[0]=(char)0;
141 vbfile[0]=k1file[0]=k2file[0]=k2k3file[0]=dvfile[0]=(char)0;
143 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
145 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
146 if(strncasecmp(cptr,
"K1=", 3)==0) {
147 strlcpy(k1file, cptr+3, FILENAME_MAX);
continue;
148 }
else if(strncasecmp(cptr,
"K2=", 3)==0) {
149 strlcpy(k2file, cptr+3, FILENAME_MAX);
continue;
150 }
else if(strncasecmp(cptr,
"K2K3=", 5)==0) {
151 strlcpy(k2k3file, cptr+5, FILENAME_MAX);
continue;
152 }
else if(strncasecmp(cptr,
"KI=", 3)==0) {
153 strlcpy(kifile, cptr+3, FILENAME_MAX);
continue;
154 }
else if(strncasecmp(cptr,
"VB=", 3)==0) {
155 strlcpy(vbfile, cptr+3, FILENAME_MAX); fitVb=1;
continue;
156 }
else if(strncasecmp(cptr,
"DV=", 3)==0) {
157 strlcpy(dvfile, cptr+3, FILENAME_MAX);
continue;
158 }
else if(strncasecmp(cptr,
"THR=", 4)==0) {
160 if(!ret && v>=0.0 && v<=200.0) {calcThreshold=(float)(0.01*v);
continue;}
161 }
else if(strncasecmp(cptr,
"END=", 4)==0) {
162 ret=
atof_with_check(cptr+4, &fittime);
if(!ret && fittime>0.0)
continue;
163 }
else if(strcasecmp(cptr,
"WFA")==0) {
165 }
else if(strcasecmp(cptr,
"WF")==0) {
167 }
else if(strcasecmp(cptr,
"W1")==0) {
169 }
else if(strcasecmp(cptr,
"NNLS")==0) {
170 method=METHOD_NNLS;
continue;
171 }
else if(strcasecmp(cptr,
"BVLS")==0) {
172 method=METHOD_BVLS;
continue;
174 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
180 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
185 if(ai<argc)
strlcpy(inpfile, argv[ai++], FILENAME_MAX);
186 if(ai<argc)
strlcpy(petfile, argv[ai++], FILENAME_MAX);
187 if(ai<argc)
strlcpy(k3file, argv[ai++], FILENAME_MAX);
188 if(ai<argc) {fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
return(1);}
191 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
return(1);}
192 if(strcasecmp(petfile, k3file)==0 || strcasecmp(petfile, k1file)==0 ||
193 strcasecmp(petfile, vbfile)==0 || strcasecmp(inpfile, dvfile)==0)
195 fprintf(stderr,
"Error: check the output filenames.\n");
200 printf(
"inpfile := %s\n", inpfile);
201 printf(
"petfile := %s\n", petfile);
202 printf(
"k3file := %s\n", k3file);
203 if(vbfile[0]) printf(
"vbfile := %s\n", vbfile);
204 if(k1file[0]) printf(
"k1file := %s\n", k1file);
205 if(k2file[0]) printf(
"k2file := %s\n", k2file);
206 if(k2k3file[0]) printf(
"k2k3file := %s\n", k2k3file);
207 if(kifile[0]) printf(
"kifile := %s\n", kifile);
208 if(dvfile[0]) printf(
"dvfile := %s\n", dvfile);
209 printf(
"calcThreshold :=%g\n", calcThreshold);
210 printf(
"weights := %d\n", weights);
211 printf(
"fitVb := %d\n", fitVb);
212 printf(
"method := %s\n", method_str[method]);
213 if(fittime>0.0) printf(
"required_fittime := %g min\n", fittime);
222 if(verbose>0) printf(
"reading data files\n");
228 petfile, NULL, inpfile, NULL, NULL, &fittime, &dataNr, &img,
229 NULL, &tac, 1, stdout, verbose-2, errmsg);
231 fprintf(stderr,
"Error: %s.\n", errmsg);
232 if(verbose>1) printf(
" ret := %d\n", ret);
236 if(verbose>0) fprintf(stderr,
"Warning: missing pixel values.\n");
239 for(
int fi=0; fi<tac.
frameNr; fi++) tac.
voi[0].
y2[fi]/=60.0;
240 for(
int fi=0; fi<tac.
frameNr; fi++) tac.
voi[0].
y3[fi]/=3600.0;
244 printf(
"fittimeFinal := %g min\n", fittime);
245 printf(
"dataNr := %d\n", dataNr);
249 fprintf(stderr,
"Error: too few time frames for fitting.\n");
256 if(verbose>1) fprintf(stdout,
"allocating working memory for pixel TACs\n");
259 fprintf(stderr,
"Error: cannot allocate memory.\n");
260 if(verbose>0) printf(
"ret := %d\n", ret);
263 strcpy(tac.
voi[0].
name,
"input");
264 strcpy(tac.
voi[1].
name,
"tissue");
268 fprintf(stderr,
"Error: cannot calculate weights.\n");
271 for(
int i=0; i<dataNr; i++) tac.
w[i]=img.
weight[i];
274 double threshold=calcThreshold*tac.
voi[0].
y2[dataNr-1];
275 if(verbose>1) printf(
"threshold_AUC := %g\n", threshold);
281 if(verbose>1) fprintf(stdout,
"allocating memory for parametric image data\n");
297 fprintf(stderr,
"Error: cannot allocate memory for result image.\n");
306 dvimg.
end[0]=k2k3img.
end[0]=60.*fittime;
309 dvimg.
unit=vbimg.
unit=CUNIT_ML_PER_ML;
310 k1img.
unit=kiimg.
unit=CUNIT_ML_PER_ML_PER_MIN;
320 int fittedNr=0, fittedokNr=0, thresholdNr=0;
322 if(method==METHOD_NNLS) {
325 for(
int m=0; m<dataNr; m++) {
326 if(tac.
w[m]<=1.0e-20) tac.
w[m]=0.0;
else tac.
w[m]=sqrt(tac.
w[m]);
332 if(verbose>1) fprintf(stdout,
"allocating memory for NNLS\n");
333 int nnls_n=NNLS_N;
if(fitVb==0) nnls_n--;
335 int n, m, nnls_index[nnls_n];
336 double **nnls_a, nnls_b[nnls_m], nnls_zz[nnls_m], nnls_x[nnls_n], *nnls_mat,
337 nnls_wp[nnls_n], nnls_rnorm;
338 nnls_mat=(
double*)malloc((nnls_n*nnls_m)*
sizeof(
double));
339 nnls_a=(
double**)malloc(nnls_n*
sizeof(
double*));
340 if(nnls_mat==NULL || nnls_a==NULL) {
341 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
346 for(n=0; n<nnls_n; n++) nnls_a[n]=nnls_mat+n*nnls_m;
352 if(verbose>0) fprintf(stdout,
"computing NNLS pixel-by-pixel\n");
353 double K1, k3, Vb, Ki, K1k2k3, k2k3;
354 double *ct, *cti, *cp, *cpi, *cpii;
357 for(
int pi=0; pi<img.
dimz; pi++) {
358 if(verbose>0) {fprintf(stdout,
"."); fflush(stdout);}
359 for(
int yi=0; yi<img.
dimy; yi++) {
360 for(
int xi=0; xi<img.
dimx; xi++) {
361 nnls_n=NNLS_N;
if(fitVb==0) nnls_n--;
363 k3img.
m[pi][yi][xi][0]=0.0;
364 k1img.
m[pi][yi][xi][0]=0.0;
365 k2img.
m[pi][yi][xi][0]=0.0;
366 k2k3img.
m[pi][yi][xi][0]=0.0;
367 kiimg.
m[pi][yi][xi][0]=0.0;
368 dvimg.
m[pi][yi][xi][0]=0.0;
369 vbimg.
m[pi][yi][xi][0]=0.0;
371 for(
int fi=0; fi<tac.
frameNr; fi++) ct[fi]=img.
m[pi][yi][xi][fi];
375 if(cti[dataNr-1]<threshold) {thresholdNr++;
continue;}
382 for(
int m=0; m<nnls_m; m++) {
383 nnls_a[0][m]=-cti[m];
384 nnls_a[1][m]=cpii[m];
386 if(fitVb!=0) nnls_a[3][m]=cp[m];
390 for(
int m=0; m<nnls_m; m++) {
392 for(
int n=0; n<nnls_n; n++) nnls_a[n][m]*=tac.
w[m];
394 if(verbose>5 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
395 printf(
"Matrix A Array B\n");
396 for(m=0; m<nnls_m; m++) {
397 printf(
"%12.3f %12.3f %12.3f %12.3f\n",
398 nnls_a[0][m], nnls_a[1][m], nnls_a[2][m], nnls_b[m]);
402 ret=
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm, nnls_wp, nnls_zz, nnls_index);
404 if(verbose>3) printf(
"no solution possible (%d)\n", ret);
405 if(verbose>4) printf(
"nnls_n=%d nnls_m=%d\n", nnls_n, nnls_m);
406 for(
int n=0; n<nnls_n; n++) nnls_x[n]=0.0;
411 if(fitVb!=0) Vb=nnls_x[3];
else Vb=0.0;
414 vbimg.
m[pi][yi][xi][0]=Vb;
418 K1=nnls_x[2]-Vb*k2k3;
419 if(Vb<0.99) k3=nnls_x[1]/K1;
else k3=0.0;
420 if(Vb<0.99) K1k2k3=K1/k2k3;
else K1k2k3=0.0;
421 if(Vb<0.99) Ki=nnls_x[1]/k2k3;
else Ki=0.0;
430 if(fitVb!=0 && Vb>0.0 && Vb<1.0) {
431 for(
int fi=0; fi<tac.
frameNr; fi++) ct[fi]-=Vb*cp[fi];
432 for(
int fi=0; fi<tac.
frameNr; fi++) ct[fi]/=(1.0-Vb);
442 for(
int n=0; n<nnls_n; n++) nnls_a[n]=nnls_mat+n*nnls_m;
445 for(m=0; m<nnls_m; m++) {
447 nnls_a[1][m]=cpii[m];
451 if(img.
isWeight)
for(m=0; m<nnls_m; m++) {
453 for(n=0; n<nnls_n; n++) nnls_a[n][m]*=tac.
w[m];
455 if(verbose>5 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
456 printf(
"Matrix A Array B\n");
457 for(m=0; m<nnls_m; m++) {
458 printf(
"%12.3f %12.3f %12.3f %12.3f\n",
459 nnls_a[0][m], nnls_a[1][m], nnls_a[2][m], nnls_b[m]);
463 ret=
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm, nnls_wp, nnls_zz, nnls_index);
465 if(verbose>3) printf(
" no solution possible\n");
466 for(n=0; n<nnls_n; n++) nnls_x[n]=0.0;
470 Ki=nnls_x[1]; K1k2k3=nnls_x[2];
477 for(m=0; m<nnls_m; m++) {
478 nnls_a[0][m]=-cpii[m];
483 if(img.
isWeight)
for(m=0; m<nnls_m; m++) {
485 for(n=0; n<nnls_n; n++) nnls_a[n][m]*=tac.
w[m];
487 if(verbose>5 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
488 printf(
"Matrix A Array B\n");
489 for(m=0; m<nnls_m; m++) {
490 printf(
"%12.3f %12.3f %12.3f %12.3f\n",
491 nnls_a[0][m], nnls_a[1][m], nnls_a[2][m], nnls_b[m]);
495 ret=
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm, nnls_wp, nnls_zz, nnls_index);
497 if(verbose>3) printf(
" no solution possible\n");
498 for(n=0; n<nnls_n; n++) nnls_x[n]=0.0;
502 k3=nnls_x[0]; fittedNr++;
505 k3img.
m[pi][yi][xi][0]=k3;
if(k3>0.0) fittedokNr++;
506 k1img.
m[pi][yi][xi][0]=K1;
507 vbimg.
m[pi][yi][xi][0]=Vb;
508 dvimg.
m[pi][yi][xi][0]=K1k2k3;
509 kiimg.
m[pi][yi][xi][0]=Ki;
510 k2k3img.
m[pi][yi][xi][0]=k2k3;
511 if(Ki>0.0) k2img.
m[pi][yi][xi][0]=k3*(K1/Ki-1.0);
512 else if(K1k2k3>0.0) k2img.
m[pi][yi][xi][0]=K1/K1k2k3;
516 if(verbose>0) {fprintf(stdout,
" done.\n"); fflush(stdout);}
517 free(nnls_mat); free(nnls_a);
521 fprintf(stdout,
"%d out of %d pixels were fitted; %d pixels ok.\n", fittedokNr, n, fittedNr);
522 fprintf(stdout,
"%d pixels were thresholded.\n", thresholdNr);
527 fprintf(stderr,
"Error: selected method not available.");
543 if(!ret && k1file[0]) ret=
imgWrite(k1file, &k1img);
544 if(!ret && k2file[0]) ret=
imgWrite(k2file, &k2img);
545 if(!ret && k2k3file[0]) ret=
imgWrite(k2k3file, &k2k3img);
546 if(!ret && kifile[0]) ret=
imgWrite(kifile, &kiimg);
547 if(!ret && vbfile[0]) ret=
imgWrite(vbfile, &vbimg);
548 if(!ret && dvfile[0]) ret=
imgWrite(dvfile, &dvimg);
552 fprintf(stderr,
"Error: cannot write parametric image.\n");
555 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)
Header file for libtpcmodext.
int imgSetWeights(IMG *img, int wmet, int verbose)
char name[MAX_REGIONNAME_LEN+1]