11#include "tpcclibConfig.h"
29enum {MODEL_SELECTION_WAIC,
30 MODEL_SELECTION_1, MODEL_SELECTION_2, MODEL_SELECTION_AIC};
34static char *info[] = {
35 "Computation of parametric image of distribution volume (DV) from dynamic",
36 "PET image in ECAT, NIfTI, or Analyze format applying one- or two-tissue",
37 "compartmental model with arterial plasma input.",
38 "The compartmental models are transformed to general linear least squares",
39 "functions, which are solved using Lawson-Hanson non-negative least squares",
40 "(NNLS) algorithm (1). DV is estimated directly without division (2, 3, 4).",
41 "Vascular volume is ignored here.",
43 "Dynamic PET image and plasma time-activity curve (PTAC) must be corrected",
44 "for decay to the tracer injection time.",
46 "Usage: @P [Options] ptacfile imgfile dvfile",
50 " With options -1 and -2 the one- or two-tissue compartment model can be",
51 " forced for all image voxels; otherwise both models are fitted, and",
52 " with option -A the model is selected based on AIC separately for each",
53 " voxel; by default (or option -0) Akaike weighted average of the model",
54 " parameters (5, 6) are reported.",
59 " Programs writes the selected model number (1 or 2, or value between",
60 " 1 and 2 as an image.",
62 " Pixels with AUC less than (threshold/100 x PTAC AUC) are set to zero",
64 " -end=<Fit end time (min)>",
65 " Use data from 0 to end time; by default, model is fitted to all frames.",
67 " Upper limit for DV values.",
70 "The unit of voxel values in the DV image is (ml blood)/(ml tissue).",
73 " @P ua3818ap.kbq ua3818dy1.v ua3818dv.v",
76 "1. Lawson CL & Hanson RJ. Solving least squares problems.",
77 " Prentice-Hall, 1974, ISBN 0-89871-356-0.",
78 "2. Zhou Y, Brasic J, Endres CJ, Kuwabara H, Kimes A, Contoreggi C, Maini A,",
79 " Ernst M, Wong DF. Binding potential image based statistical mapping for",
80 " detection of dopamine release by [11C]raclopride dynamic PET.",
81 " NeuroImage 2002;16(3):S91.",
82 "3. Zhou Y, Brasic JR, Ye W, Dogan AS, Hilton J, Singer HS, Wong DF.",
83 " Quantification of cerebral serotonin binding in normal controls and",
84 " subjects with Tourette's syndrome using [11C]MDL 100,907 and",
85 " (+)[11C]McN 5652 dynamic PET with parametric imaging approach.",
86 " NeuroImage 2004;22(Suppl 2):T98.",
87 "4. Hagelberg N, Aalto S, Kajander J, Oikonen V, Hinkka S, NĂ¥gren K,",
88 " Hietala J, Scheinin H. Alfentanil increases cortical dopamine D2/D3",
89 " receptor binding in healthy subjects. Pain 2004;109:86-93.",
90 "5. Turkheimer FE, Hinz R, Cunningham VJ. On the undecidability among",
91 " kinetic models: from model selection to model averaging. J Cereb Blood",
92 " Flow Metab 2003; 23: 490-498.",
93 "6. Sederholm K. Model averaging with Akaike weights. TPCMOD0016 2003-04-07.",
94 " https://www.turkupetcentre.net/reports/tpcmod0016.pdf",
96 "See also: imgdv, imgbfbp, imgratio, img2tif, logan",
98 "Keywords: image, modelling, distribution volume, Vt, NNLS",
117int main(
int argc,
char **argv)
119 int ai, help=0, version=0, verbose=1;
121 char inpfile[FILENAME_MAX], petfile[FILENAME_MAX], dvfile[FILENAME_MAX];
122 char k1file[FILENAME_MAX], modfile[FILENAME_MAX];
123 float calcThreshold=0.0;
124 double upperLimit=-1.0;
126 int model_selection=MODEL_SELECTION_WAIC;
135 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
136 inpfile[0]=petfile[0]=dvfile[0]=k1file[0]=modfile[0]=(char)0;
138 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
140 cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
141 if(strcasecmp(cptr,
"A")==0) {
142 model_selection=MODEL_SELECTION_AIC;
continue;
143 }
else if(*cptr==
'1') {
144 model_selection=MODEL_SELECTION_1;
continue;
145 }
else if(*cptr==
'2') {
146 model_selection=MODEL_SELECTION_2;
continue;
147 }
else if(*cptr==
'0') {
148 model_selection=MODEL_SELECTION_WAIC;
continue;
149 }
else if(strncasecmp(cptr,
"K1=", 3)==0) {
150 strlcpy(k1file, cptr+3, FILENAME_MAX);
continue;
151 }
else if(strncasecmp(cptr,
"M=", 2)==0) {
152 strlcpy(modfile, cptr+2, FILENAME_MAX);
continue;
153 }
else if(strncasecmp(cptr,
"THR=", 4)==0) {
155 if(!ret && v>=0.0 && v<=200.0) {calcThreshold=(float)(0.01*v);
continue;}
156 }
else if(strncasecmp(cptr,
"END=", 4)==0) {
157 fittime=
atof_dpi(cptr+4);
if(fittime>0.0)
continue;
158 }
else if(*cptr==
'w' || *cptr==
'W') {
160 }
else if(strncasecmp(cptr,
"MAX=", 4)==0) {
161 upperLimit=
atof_dpi(cptr+4);
if(upperLimit>0.0)
continue;
163 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
168 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
173 if(ai<argc)
strlcpy(inpfile, argv[ai++], FILENAME_MAX);
174 if(ai<argc)
strlcpy(petfile, argv[ai++], FILENAME_MAX);
175 if(ai<argc)
strlcpy(dvfile, argv[ai++], FILENAME_MAX);
176 if(ai<argc) {fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
return(1);}
179 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
182 if(strcasecmp(petfile, dvfile)==0 || strcasecmp(petfile, k1file)==0 ||
183 strcasecmp(petfile, modfile)==0 || strcasecmp(inpfile, dvfile)==0)
185 fprintf(stderr,
"Error: check the output filenames.\n");
190 printf(
"inpfile := %s\n", inpfile);
191 printf(
"petfile := %s\n", petfile);
192 printf(
"dvfile := %s\n", dvfile);
193 if(k1file[0]) printf(
"k1file := %s\n", k1file);
194 printf(
"calcThreshold :=%g\n", calcThreshold);
195 if(upperLimit>0.0) printf(
"upperLimit := %g\n", upperLimit);
196 printf(
"weight := %d\n", weight);
197 if(fittime>0.0) printf(
"required_fittime := %g min\n", fittime);
198 printf(
"model_selection := %d\n", model_selection);
207 if(verbose>0) printf(
"reading data files\n");
212 petfile, NULL, inpfile, NULL, NULL, &fittime, &dataNr, &img,
213 NULL, &tac, 1, stdout, verbose-2, tmp);
215 fprintf(stderr,
"Error: %s.\n", tmp);
216 if(verbose>1) printf(
" ret := %d\n", ret);
220 if(verbose>0) fprintf(stderr,
"Warning: missing pixel values.\n");
223 for(
int fi=0; fi<tac.
frameNr; fi++) tac.
voi[0].
y2[fi]/=60.0;
224 for(
int fi=0; fi<tac.
frameNr; fi++) tac.
voi[0].
y3[fi]/=3600.0;
228 printf(
"fittimeFinal := %g min\n", fittime);
229 printf(
"dataNr := %d\n", dataNr);
233 fprintf(stderr,
"Error: too few time frames for fitting.\n");
240 if(verbose>1) fprintf(stdout,
"allocating working memory for pixel TACs\n");
243 fprintf(stderr,
"Error: cannot allocate memory.\n");
244 if(verbose>0) printf(
"ret := %d\n", ret);
247 strcpy(tac.
voi[0].
name,
"input");
248 strcpy(tac.
voi[1].
name,
"tissue");
254 if(verbose>1) fprintf(stdout,
"allocating memory for parametric image data\n");
260 fprintf(stderr,
"Error: cannot allocate memory for result image.\n");
265 dvimg.
end[0]=mimg.
end[0]=60.*fittime;
266 dvimg.
unit=CUNIT_ML_PER_ML;
267 mimg.
unit=CUNIT_UNITLESS;
273 if(verbose>1) fprintf(stdout,
"allocating memory for NNLS\n");
274 int n, m, nnls_n, nnls_m, nnls_index[NNLS_N];
275 double *nnls_a[NNLS_N], *nnls_b, *nnls_zz, nnls_x[NNLS_N], *nnls_mat,
276 nnls_wp[NNLS_N], *dptr, nnls_rnorm;
277 nnls_n=NNLS_N; nnls_m=dataNr;
278 nnls_mat=(
double*)malloc(((nnls_n+2)*nnls_m)*
sizeof(
double));
280 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
284 for(n=0, dptr=nnls_mat; n<nnls_n; n++) {nnls_a[n]=dptr; dptr+=nnls_m;}
285 nnls_b=dptr; dptr+=nnls_m; nnls_zz=dptr;
295 for(m=0; m<nnls_m; m++) tac.
w[m]=img.
weight[m];
296 for(m=0; m<nnls_m; m++) {
297 if(tac.
w[m]<=1.0e-20) tac.
w[m]=0.0;
else tac.
w[m]=sqrt(tac.
w[m]);
306 double threshold=calcThreshold*tac.
voi[0].
y2[dataNr-1];
307 if(verbose>1) printf(
"threshold_AUC := %g\n", threshold);
308 if(verbose>0) fprintf(stdout,
"computing NNLS pixel-by-pixel\n");
310 double aic[MAX_MODEL_NR], w[MAX_MODEL_NR], ss[MAX_MODEL_NR], dv[MAX_MODEL_NR];
312 for(
int pi=0; pi<img.
dimz; pi++) {
313 if(verbose>1) printf(
"computing plane %d\n", img.
planeNumber[pi]);
314 else if(verbose>0) fprintf(stdout,
".");
315 if(verbose>0) fflush(stdout);
316 for(
int yi=0; yi<img.
dimy; yi++) {
317 for(
int xi=0; xi<img.
dimx; xi++) {
319 dvimg.
m[pi][yi][xi][0]=0.0;
320 mimg.
m[pi][yi][xi][0]=0.0;
322 for(
int fi=0; fi<tac.
frameNr; fi++)
323 tac.
voi[1].
y[fi]=img.
m[pi][yi][xi][fi];
327 if(tac.
voi[1].
y2[dataNr-1]<threshold)
continue;
330 if(model_selection==MODEL_SELECTION_AIC ||
331 model_selection==MODEL_SELECTION_2 ||
332 model_selection==MODEL_SELECTION_WAIC)
336 for(m=0; m<nnls_m; m++) {
337 nnls_a[0][m]=tac.
voi[0].
y3[m];
338 nnls_a[1][m]=-tac.
voi[1].
y2[m];
339 nnls_a[2][m]=tac.
voi[0].
y2[m];
340 nnls_a[3][m]=-tac.
voi[1].
y[m];
341 nnls_b[m]=tac.
voi[1].
y3[m];
344 for(m=0; m<nnls_m; m++) {
346 for(n=0; n<nnls_n; n++) nnls_a[n][m]*=tac.
w[m];
350 ret=
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm,
351 nnls_wp, nnls_zz, nnls_index);
353 for(n=0; n<nnls_n; n++) nnls_x[n]=0.0;
355 }
else if(ret==1) { }
359 for(m=0; m<nnls_m; m++) {
360 nnls_a[0][m]=tac.
voi[0].
y3[m];
361 nnls_a[1][m]=-tac.
voi[1].
y2[m];
362 nnls_a[2][m]=tac.
voi[0].
y2[m];
363 nnls_a[3][m]=-tac.
voi[1].
y[m];
364 nnls_b[m]=tac.
voi[1].
y3[m];
367 for(m=0; m<nnls_m; m++) {
369 for(n=0; n<nnls_n; n++) nnls_a[n][m]*=tac.
w[m];
372 for(m=0, ss[1]=0.0; m<nnls_m; m++) {
373 for(n=0, f=0.0; n<nnls_n; n++) f+=nnls_x[n]*nnls_a[n][m];
374 g=f; f-=nnls_b[m]; ss[1]+=f*f;
375 if(verbose>17 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3)
376 printf(
"%10.3f : %e %e - %e = %g\n",
377 tac.
x[m], tac.
voi[1].
y[m], tac.
voi[1].
y3[m], g, f);
380 aic[1]=
aicSS(ss[1], nnls_m, nnls_n);
381 if(verbose>15 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
382 for(n=0; n<nnls_n; n++) printf(
" p[%d]=%g", n, nnls_x[n]);
383 printf(
" -> SS=%g AIC=%g\n", ss[1], aic[1]);
386 for(n=0, m=nnls_n; n<nnls_n; n++)
if(nnls_x[n]<1.0e-10) m--;
388 if(m==nnls_n) cm2ok=1;
else cm2ok=0;
392 if(model_selection==MODEL_SELECTION_AIC ||
393 model_selection==MODEL_SELECTION_1 ||
394 model_selection==MODEL_SELECTION_WAIC)
398 for(m=0; m<nnls_m; m++) {
399 nnls_a[0][m]=tac.
voi[0].
y3[m];
400 nnls_a[1][m]=-tac.
voi[1].
y2[m];
401 nnls_b[m]=tac.
voi[1].
y3[m];
404 for(m=0; m<nnls_m; m++) {
406 for(n=0; n<nnls_n; n++) nnls_a[n][m]*=tac.
w[m];
410 ret=
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm,
411 nnls_wp, nnls_zz, nnls_index);
413 for(n=0; n<nnls_n; n++) nnls_x[n]=0.0;
415 }
else if(ret==1) { }
419 for(m=0; m<nnls_m; m++) {
420 nnls_a[0][m]=tac.
voi[0].
y3[m];
421 nnls_a[1][m]=-tac.
voi[1].
y2[m];
422 nnls_b[m]=tac.
voi[1].
y3[m];
425 for(m=0; m<nnls_m; m++) {
427 for(n=0; n<nnls_n; n++) nnls_a[n][m]*=tac.
w[m];
430 for(m=0, ss[0]=0.0; m<nnls_m; m++) {
431 for(n=0, f=0.0; n<nnls_n; n++) f+=nnls_x[n]*nnls_a[n][m];
432 g=f; f-=nnls_b[m]; ss[0]+=f*f;
433 if(verbose>17 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3)
434 printf(
"%10.3f : %e %e - %e = %g\n",
435 tac.
x[m], tac.
voi[1].
y[m], tac.
voi[1].
y3[m], g, f);
438 aic[0]=
aicSS(ss[0], nnls_m, nnls_n);
439 if(verbose>15 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
440 for(n=0; n<nnls_n; n++) printf(
" p[%d]=%g", n, nnls_x[n]);
441 printf(
" -> SS=%g AIC=%g\n", ss[0], aic[0]);
446 switch(model_selection) {
447 case MODEL_SELECTION_WAIC:
449 if(verbose>14 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3)
450 printf(
" Model weights are %g %g\n", w[0], w[1]);
453 mimg.
m[pi][yi][xi][0]=(float)(1.0*w[0]+2.0*w[1]);
454 if(verbose>13 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3)
455 printf(
" DV1=%g DV2=%g -> Weighted DV=%g\n", dv[0], dv[1], dvimg.
m[pi][yi][xi][0]);
457 case MODEL_SELECTION_1:
458 dvimg.
m[pi][yi][xi][0]=dv[0];
459 mimg.
m[pi][yi][xi][0]=(float)1.0;
461 case MODEL_SELECTION_2:
462 dvimg.
m[pi][yi][xi][0]=dv[1];
463 mimg.
m[pi][yi][xi][0]=(float)2.0;
465 case MODEL_SELECTION_AIC:
466 if(cm2ok && aic[1]<aic[0]) {
467 dvimg.
m[pi][yi][xi][0]=dv[1];
468 mimg.
m[pi][yi][xi][0]=2.0;
470 dvimg.
m[pi][yi][xi][0]=dv[0];
471 mimg.
m[pi][yi][xi][0]=1.0;
479 if(verbose>0) fprintf(stdout,
" done.\n");
492 if(verbose>0) fprintf(stdout,
"filtering out pixel values exceeding %g\n", upperLimit);
501 fprintf(stderr,
"Error: %s\n", tmp);
505 if(verbose>1) printf(
"%s\n", tmp);
508 fprintf(stderr,
"Error: %s\n", dvimg.
statmsg);
512 if(verbose>0) fprintf(stdout,
"DV image %s saved.\n", dvfile);
519 fprintf(stderr,
"Error: %s\n", tmp);
523 if(verbose>1) printf(
"%s\n", tmp);
526 fprintf(stderr,
"Error: %s\n", mimg.
statmsg);
530 if(verbose>0) fprintf(stdout,
"Model selection image %s saved.\n", modfile);
double aicWeightedAvg(double *w, double *p, int n)
int aicWeights(double *aic, double *w, int n)
double aicSS(double ss, const int n, const int k)
int backupExistingFile(char *filename, char *backup_ext, char *status)
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 imgWrite(const char *fname, IMG *img)
void imgCutoff(IMG *image, float cutoff, int mode)
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.
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.
char name[MAX_REGIONNAME_LEN+1]