10#include "tpcclibConfig.h"
30static char *info[] = {
31 "Estimation of rate constants K1, k2 and Va from dynamic PET image in",
32 "ECAT 6.3, ECAT 7.x, NIfTI-1, or Analyze 7.5 file format using",
33 "linearized two-compartment model [1].",
34 "Lawson-Hanson non-negative least squares (NNLS) method [2] is used to solve",
35 "general linear least squares functions.",
37 "When applied to dynamic [O-15]H2O studies, the resulting K1 image",
38 "equals perfusion (blood flow) image. K1 image can be divided by tissue",
39 "density (g/mL) (option -density) and multiplied by 100 (option -dL)",
40 "to achieve the blood flow image in units (mL blood)/((100 g tissue) * min).",
42 "When applied to dynamic [O-15]O2 brain studies, the resulting K1 image",
43 "can be converted to oxygen consumption image by multiplying it by",
44 "arterial oxygen concentration [3] (ml O2 / dL blood) to get the",
45 "parametric image in units mL O2 / ((100 ml tissue) * min).",
46 "The model assumptions hold only when oxygen consumption is 1-6.7",
47 "mL O2/(100g * min) and fit time is set to 300 s or less [3].",
49 "Arterial blood TAC must be corrected for decay and delay, with sample times",
50 "in seconds. Dynamic PET image must be corrected for decay.",
52 "Usage: @P [Options] btacfile imgfile flowfile",
55 " -end=<Fit end time (sec)>",
56 " Use data from 0 to end time; by default, all of it.",
58 " Parametric k2 image is saved; in some situations perfusion calculation",
59 " from k2 can be more accurate than the default assumption of f=K1.",
60 " Perfusion can be calculated from k2 using equation f=k2*pH2O, where",
61 " pH2O is the physiological partition coefficient of water in tissue.",
63 " Parametric Va image is saved.",
64 " Set -Va=0, if Va=0 is assumed; otherwise Va is always fitted.",
70 " Pixels with AUC less than (threshold/100 x max AUC) are set to zero.",
73 " Upper limit for flow values in final units.",
75 " Remove parametric pixel values that are over 4x higher than",
76 " their closest neighbours.",
78 " Pixels where K1 estimates are negative are fitted again with",
81 " Units in flow and Va images will be given per mL or per dL,",
82 " respectively. By default, units are per mL.",
83 " -density[=<value>]",
84 " With option -density the flow is calculated per gram or 100g tissue.",
85 " Tissue density can be changed from the default 1.04 g/ml.",
88 "By default, the units of pixel values in the flow (K1) image is",
89 "(mL blood)/((mL tissue) * min), in DV image (mL blood)/(mL tissue),",
90 "in k2 image 1/min, and in Va image (mL blood/mL tissue),",
91 "but the flow and Va units can be changed with above listed options.",
93 "Example 1. Calculation of perfusion and arterial blood volume image,",
94 " stopping fit at 180 s:",
95 " @P -Va=s2345va.v -end=180 s2345abfit.kbq s2345dy1.v s2345flow.v",
97 "Example 2. Dynamic image is precorrected for vascular activity,",
98 " and all available data is used:",
99 " @P -Va=0 s2345abfit.kbq s2345dy1_vacorr.v s2345flow.v",
102 "1. Blomqvist G. On the construction of functional maps in positron",
103 " emission tomography. J Cereb Blood Flow Metab. 1984;4:629-632.",
104 "2. Lawson CL & Hanson RJ. Solving least squares problems.",
105 " Prentice-Hall, 1974.",
106 "3. Ohta S, Meyer E, Thompson CJ, Gjedde A. Oxygen consumption of the",
107 " living human brain measured after a single inhalation of positron",
108 " emitting oxygen. J Cereb Blood Flow Metab. 1992;12:179-192.",
110 "See also: imgflowm, imgbfh2o, imgcbv, fit_h2o, b2t_h2o, imglhdv, fitdelay",
112 "Keywords: image, modelling, perfusion, blood flow, radiowater, NNLS",
131int main(
int argc,
char *argv[])
133 int ai, help=0, version=0, verbose=1;
134 int fi, pi, yi, xi, ret;
135 int weight=0, dataNr=0, param_filt=0;
136 char inpfile[FILENAME_MAX], petfile[FILENAME_MAX], k1file[FILENAME_MAX];
137 char k2file[FILENAME_MAX], vafile[FILENAME_MAX], dvfile[FILENAME_MAX], wssfile[FILENAME_MAX];
138 char *cptr, tmp[FILENAME_MAX+1];
139 float calcThreshold=0.05;
140 double upperLimit=-1.0;
143 double fittime=nan(
"");
145 int k1_nn_constraint=0;
148 IMG img, k1img, k2img, vaimg, dvimg, wssimg;
149 double *cp, *cpi, *ct, *cti, Va;
151 int nnls_n, nnls_m, nnls_index[NNLS_N];
152 double *nnls_a[NNLS_N], *nnls_b, *nnls_zz, nnls_x[NNLS_N], *nnls_mat,
153 nnls_wp[NNLS_N], *dptr, nnls_rnorm;
160 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
161 inpfile[0]=petfile[0]=k1file[0]=k2file[0]=vafile[0]=dvfile[0]=wssfile[0]=(char)0;
163 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
165 cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
166 if(strcasecmp(cptr,
"W")==0) {
168 }
else if(strncasecmp(cptr,
"FILTER", 4)==0) {
169 param_filt=1;
continue;
170 }
else if(strncasecmp(cptr,
"THR=", 4)==0) {
172 if(!ret && v<100.0) {calcThreshold=(float)(0.01*v);
continue;}
173 }
else if(strncasecmp(cptr,
"DV=", 3)==0) {
174 strlcpy(dvfile, cptr+3, FILENAME_MAX);
if(strlen(dvfile))
continue;
175 }
else if(strncasecmp(cptr,
"k2=", 3)==0) {
176 strlcpy(k2file, cptr+3, FILENAME_MAX);
if(strlen(k2file))
continue;
177 }
else if(strncasecmp(cptr,
"VA=", 3)==0 || strncasecmp(cptr,
"VB=", 3)==0) {
178 strlcpy(vafile, cptr+3, FILENAME_MAX);
if(strlen(vafile))
continue;
179 }
else if(strncasecmp(cptr,
"DV=", 3)==0 || strncasecmp(cptr,
"VD=", 3)==0) {
180 strlcpy(dvfile, cptr+3, FILENAME_MAX);
if(strlen(dvfile))
continue;
181 }
else if(strncasecmp(cptr,
"noneg", 2)==0) {
182 k1_nn_constraint=1;
continue;
183 }
else if(strcasecmp(cptr,
"DL")==0) {
185 }
else if(strcasecmp(cptr,
"ML")==0) {
187 }
else if(strcasecmp(cptr,
"DENSITY")==0) {
189 per_gram=1;
continue;
190 }
else if(strncasecmp(cptr,
"DENSITY=", 8)==0) {
193 density=
atof_dpi(cptr+8);
if(density>0.0)
continue;
194 }
else if(strncasecmp(cptr,
"MAX=", 4)==0) {
195 upperLimit=
atof_dpi(cptr+4);
if(upperLimit>0.0)
continue;
196 }
else if(strncasecmp(cptr,
"END=", 4)==0) {
197 fittime=
atof_dpi(cptr+4)/60.;
if(fittime>0.0)
continue;
198 }
else if(strncasecmp(cptr,
"WSS=", 4)==0) {
199 strlcpy(wssfile, cptr+4, FILENAME_MAX);
if(strlen(wssfile)>0)
continue;
201 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
206 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
211 if(ai<argc)
strlcpy(inpfile, argv[ai++], FILENAME_MAX);
212 if(ai<argc)
strlcpy(petfile, argv[ai++], FILENAME_MAX);
213 if(ai<argc)
strlcpy(k1file, argv[ai++], FILENAME_MAX);
214 if(ai<argc) {fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
return(1);}
217 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
220 if(!(fittime>0.0)) fittime=1.0E+020;
222 if(strcasecmp(vafile,
"NONE")==0 || strcasecmp(vafile,
"ZERO")==0 || strcasecmp(vafile,
"0")==0)
223 {vafile[0]=(char)0; fitVa=0;}
226 printf(
"inpfile := %s\n", inpfile);
227 printf(
"petfile := %s\n", petfile);
228 printf(
"k1file := %s\n", k1file);
229 if(k2file[0]) printf(
"k2file := %s\n", k2file);
230 printf(
"fitVa := %d\n", fitVa);
231 if(vafile[0]) printf(
"vafile := %s\n", vafile);
232 if(dvfile[0]) printf(
"dvfile := %s\n", dvfile);
233 if(wssfile[0]) printf(
"wssfile := %s\n", wssfile);
234 printf(
"calcThreshold := %g\n", calcThreshold);
235 printf(
"max := %f\n", upperLimit);
236 printf(
"fittime := %g [sec]\n", 60.0*fittime);
237 printf(
"per_dl := %d\n", per_dl);
238 printf(
"per_gram := %d\n", per_gram);
239 printf(
"density := %g\n", density);
248 if(verbose>1) printf(
"reading data files\n");
251 petfile, NULL, inpfile, NULL, NULL, &fittime, &dataNr, &img,
252 NULL, &tac, 1, stdout, verbose-2, tmp);
254 fprintf(stderr,
"Error: %s.\n", tmp);
255 if(verbose>1) printf(
" ret := %d\n", ret);
259 if(verbose>0) fprintf(stderr,
"Warning: missing pixel values.\n");
264 printf(
"fittimeFinal := %g s\n", 60.*fittime);
265 printf(
"dataNr := %d\n", dataNr);
269 fprintf(stderr,
"Error: too few time frames for fitting.\n");
277 if(verbose>0) fprintf(stdout,
"thresholding\n");
281 fprintf(stderr,
"Error in thresholding the dynamic image: %s\n", img.
statmsg);
285 fprintf(stdout,
"threshold_cutoff_nr := %lld / %lld\n", tn, (
long long)img.
dimx*img.
dimy*img.
dimz);
291 fprintf(stderr,
"Error (%d) in allocating memory.\n", ret);
303 if(verbose>1) printf(
"allocating memory for parametric images\n");
308 k1img.
unit=IMGUNIT_ML_PER_ML_PER_MIN;
313 if(ret==0 && k2file[0]) {
315 if(ret==0) k2img.
unit=IMGUNIT_PER_MIN;
317 if(ret==0 && dvfile[0]) {
319 if(ret==0) dvimg.
unit=IMGUNIT_ML_PER_ML;
321 if(ret==0 && vafile[0]) {
323 if(ret==0) vaimg.
unit=IMGUNIT_ML_PER_ML;
325 if(ret==0 && wssfile[0]) {
327 if(ret==0) wssimg.
unit=IMGUNIT_UNKNOWN;
330 fprintf(stderr,
"Error: cannot setup memory for parametric image.\n");
339 if(verbose>1) printf(
"allocating memory for NNLS\n");
340 nnls_n=NNLS_N; nnls_m=dataNr;
341 nnls_mat=(
double*)malloc(((nnls_n+2)*nnls_m)*
sizeof(
double));
343 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
349 for(
int n=0; n<nnls_n; n++) {nnls_a[n]=dptr; dptr+=nnls_m;}
350 nnls_b=dptr; dptr+=nnls_m; nnls_zz=dptr;
354 if(verbose>2) printf(
"working with NNLS weights\n");
356 for(
int m=0; m<nnls_m; m++) img.
weight[m]=img.
end[m]-img.
start[m];
361 for(
int m=0; m<nnls_m; m++) {
362 tac.
w[m]=img.
weight[m];
if(tac.
w[m]<=1.0e-20) tac.
w[m]=0.0;
370 long long nr_of_negative_k1=0;
373 if(verbose>0) fprintf(stdout,
"computing pixel-by-pixel\n");
374 for(pi=0; pi<img.
dimz; pi++) {
375 if(verbose>2) printf(
"computing plane %d\n", img.
planeNumber[pi]);
376 else if(img.
dimz>1 && verbose>0) {fprintf(stdout,
"."); fflush(stdout);}
377 for(yi=0; yi<img.
dimy; yi++) {
378 for(xi=0; xi<img.
dimx; xi++) {
380 if(verbose>6 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
381 printf(
" computing row %d, column %d\n", yi+1, xi+1);
385 k1img.
m[pi][yi][xi][0]=0.0;
386 if(k2file[0]) k2img.
m[pi][yi][xi][0]=0.0;
387 if(vafile[0]) vaimg.
m[pi][yi][xi][0]=0.0;
388 if(dvfile[0]) dvimg.
m[pi][yi][xi][0]=0.0;
393 nnls_m=dataNr; nnls_n=NNLS_N;
if(fitVa==0) nnls_n--;
395 for(
int m=0; m<nnls_m; m++) ct[m]=img.
m[pi][yi][xi][m];
398 if(cti[nnls_m-1]<=0.0)
continue;
401 for(
int m=0; m<nnls_m; m++) nnls_a[0][m]=-cti[m];
403 for(
int m=0; m<nnls_m; m++) nnls_a[1][m]=cpi[m];
405 if(nnls_n>2)
for(
int m=0; m<nnls_m; m++) nnls_a[2][m]=cp[m];
407 for(
int m=0; m<nnls_m; m++) nnls_b[m]=ct[m];
410 if(verbose>6 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
411 printf(
"Matrix A Array B\n");
412 for(
int m=0; m<nnls_m; m++) {
413 printf(
"%12.3f %12.3f %12.3f %12.3f\n",
414 nnls_a[0][m], nnls_a[1][m], nnls_a[2][m], nnls_b[m]);
418 ret=
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm,
419 nnls_wp, nnls_zz, nnls_index);
421 if(nnls_n>2) Va=nnls_x[2];
else Va=0.0;
422 k1img.
m[pi][yi][xi][0]=nnls_x[1];
423 if(nnls_n>2) k1img.
m[pi][yi][xi][0]-=Va*nnls_x[0];
424 if(k2file[0]) k2img.
m[pi][yi][xi][0]=nnls_x[0];
425 if(vafile[0]) vaimg.
m[pi][yi][xi][0]=Va;
426 if(verbose>6 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
427 printf(
" Va=%g x[0]=%g x[1]=%g\n", Va, nnls_x[0], nnls_x[1]);
433 if(k1_nn_constraint!=0 && k1img.
m[pi][yi][xi][0]<0.0) {
438 for(
int m=0; m<nnls_m; m++) nnls_a[0][m]=-cti[m];
440 for(
int m=0; m<nnls_m; m++) nnls_a[1][m]=cpi[m];
442 for(
int m=0; m<nnls_m; m++) nnls_b[m]=ct[m];
445 if(verbose>6 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
446 printf(
"Matrix A Array B\n");
447 for(
int m=0; m<nnls_m; m++) {
448 printf(
"%12.3f %12.3f %12.3f\n",
449 nnls_a[0][m], nnls_a[1][m], nnls_b[m]);
453 ret=
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm,
454 nnls_wp, nnls_zz, nnls_index);
457 k1img.
m[pi][yi][xi][0]=nnls_x[1];
458 if(k2file[0]) k2img.
m[pi][yi][xi][0]=nnls_x[0];
459 if(vafile[0]) vaimg.
m[pi][yi][xi][0]=Va;
460 if(verbose>6 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
461 printf(
" Va=%g x[0]=%g x[1]=%g\n", Va, nnls_x[0], nnls_x[1]);
463 }
else if(k1img.
m[pi][yi][xi][0]<0.0) {
470 for(
int m=0; m<nnls_m; m++) {
472 for(
int n=0; n<nnls_n; n++) f+=nnls_x[n]*nnls_a[n][m];
473 f-=nnls_b[m]; wss+=f*f;
475 wssimg.
m[pi][yi][xi][0]=wss;
484 for(
int m=0; m<nnls_m; m++) ct[m]-=Va*cp[m];
492 for(
int m=0; m<nnls_m; m++) nnls_a[0][m]=-ct[m];
494 for(
int m=0; m<nnls_m; m++) nnls_a[1][m]=cpi[m];
496 for(
int m=0; m<nnls_m; m++) nnls_b[m]=cti[m];
499 if(verbose>6 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
500 printf(
"Matrix A Array B\n");
501 for(
int m=0; m<nnls_m; m++) {
502 printf(
"%12.3f %12.3f %12.3f\n",
503 nnls_a[0][m], nnls_a[1][m], nnls_b[m]);
507 ret=
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm, nnls_wp, nnls_zz, nnls_index);
509 dvimg.
m[pi][yi][xi][0]=nnls_x[1];
510 if(verbose>6 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
511 printf(
" x[0]=%g x[1]=%g\n", nnls_x[0], nnls_x[1]);
518 if(verbose>0) fprintf(stdout,
"done.\n");
525 if(verbose>1 && nr_of_negative_k1>0) {
526 fprintf(stdout,
"Negative flow in %lld pixels\n", nr_of_negative_k1);
527 if(k1_nn_constraint==1) fprintf(stdout,
" corrected by assuming Va=0.\n");
534 if(per_dl || per_gram) {
537 if(per_dl && per_gram) {
538 if(verbose>1) printf(
"converting to per 100g\n");
539 f=100.0/density; k1img.
unit=IMGUNIT_ML_PER_DL_PER_MIN;
541 if(verbose>1) printf(
"converting to per 100mL\n");
542 f=100.0; k1img.
unit=IMGUNIT_ML_PER_DL_PER_MIN;
543 }
else if(per_gram) {
544 if(verbose>1) printf(
"converting to per g\n");
545 f=1.0/density; k1img.
unit=IMGUNIT_ML_PER_ML_PER_MIN;
547 if(verbose>2) printf(
"no conversion from mL/(min*mL)\n");
552 if(per_dl && vafile[0]) {
554 vaimg.
unit=IMGUNIT_ML_PER_DL;
564 if(verbose>0) fprintf(stdout,
"filtering out flow values exceeding %g\n", upperLimit);
572 if(verbose>0) printf(
"filtering extreme values from parametric images\n");
583 if(verbose>0) printf(
"writing parametric images\n");
587 fprintf(stderr,
"Error: %s\n", k1img.
statmsg);
588 if(verbose>1) printf(
"ret := %d\n", ret);
592 if(verbose>0) fprintf(stdout,
"Flow image %s saved.\n", k1file);
597 fprintf(stderr,
"Error: %s\n", k2img.
statmsg);
601 if(verbose>0) fprintf(stdout,
"k2 image %s saved.\n", k2file);
607 fprintf(stderr,
"Error: %s\n", dvimg.
statmsg);
611 if(verbose>0) fprintf(stdout,
"DV image %s saved.\n", dvfile);
617 fprintf(stderr,
"Error: %s\n", vaimg.
statmsg);
621 if(verbose>0) fprintf(stdout,
"Va image %s saved.\n", vafile);
627 fprintf(stderr,
"Error: %s\n", wssimg.
statmsg);
631 if(verbose>0) fprintf(stdout,
"WSS image %s saved.\n", wssfile);
634 if(verbose>2) printf(
"before quitting, free memory\n");
int atof_with_check(char *double_as_string, double *result_value)
double atof_dpi(char *str)
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 imgArithmConst(IMG *img, float operand, char operation, float ulimit, int verbose)
int imgWrite(const char *fname, IMG *img)
void imgCutoff(IMG *image, float cutoff, int mode)
int imgThresholding(IMG *img, float threshold_level, long long *thr_nr)
int imgOutlierFilter(IMG *img, float limit)
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)
int nnlsWght(int N, int M, double **A, double *b, double *weight)
Header file for libtpcmodext.
char voiname[MAX_REGIONSUBNAME_LEN+1]