11#include "tpcclibConfig.h"
31static char *info[] = {
32 "Computation of parametric images of K1 and Vb, and optionally k2, from",
33 "dynamic PET image in ECAT, NIfTI, or Analyze format applying",
34 "(ir)reversible on1-tissue compartmental model with arterial plasma input.",
35 "The compartmental models are transformed to general linear least squares",
36 "functions (1), which are solved using Lawson-Hanson non-negative least",
37 "squares (NNLS) algorithm (2).",
39 "Dynamic PET image and plasma time-activity curve (PTAC) must be corrected",
40 "for decay to the tracer injection time.",
42 "Usage: @P [Options] ptacfile imgfile k1file vbfile [k2file]",
46 " Pixels with AUC less than (threshold/100 x PTAC AUC) are set to zero;",
48 " -end=<Fit end time (min)>",
49 " Use data from 0 to end time; by default, model is fitted to all frames.",
58 "The unidirectional back-transport rate k2 is considered in the model setting",
59 "only if file name for k2 image is given.",
61 "Note that this model can correctly estimate Vb only if",
62 "1) plasma does not contain any labelled metabolites, and",
63 "2) plasma and blood curves are similar in shape.",
64 "Note also that Cpet is modelled as Cpet=Vb*Cb + Ct, thus K1 may need to",
65 "be corrected by factor 1/(1-Vb).",
67 "The units of pixel values in the parametric images are ml/(min*ml) for K1",
68 "1/min for k2, and ml/ml for Vb.",
71 "1. Blomqvist G. On the construction of functional maps in positron",
72 " emission tomography. J Cereb Blood Flow Metab 1984;4:629-632.",
73 "2. Lawson CL & Hanson RJ. Solving least squares problems.",
74 " Prentice-Hall, 1974, ISBN 0-89871-356-0.",
76 "See also: imglhk3, imgcbv, imgki, fitk3",
78 "Keywords: image, modelling, irreversible uptake, NNLS",
94enum {METHOD_UNKNOWN, METHOD_NNLS, METHOD_BVLS};
95static char *method_str[] = {
"unknown",
"NNLS",
"BVLS", 0};
102int main(
int argc,
char **argv)
104 int ai, help=0, version=0, verbose=1;
105 char inpfile[FILENAME_MAX], petfile[FILENAME_MAX], k1file[FILENAME_MAX];
106 char k2file[FILENAME_MAX], vbfile[FILENAME_MAX];
107 float calcThreshold=0.01;
110 int method=METHOD_NNLS;
116 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
117 inpfile[0]=petfile[0]=k1file[0]=k2file[0]=vbfile[0]=(char)0;
119 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
121 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
122 if(strncasecmp(cptr,
"THR=", 4)==0) {
124 if(!ret && v>=0.0 && v<=200.0) {calcThreshold=(float)(0.01*v);
continue;}
125 }
else if(strncasecmp(cptr,
"END=", 4)==0) {
126 ret=
atof_with_check(cptr+4, &fittime);
if(!ret && fittime>0.0)
continue;
127 }
else if(strcasecmp(cptr,
"WFA")==0) {
129 }
else if(strcasecmp(cptr,
"WF")==0) {
131 }
else if(strcasecmp(cptr,
"W1")==0) {
133 }
else if(strcasecmp(cptr,
"NNLS")==0) {
134 method=METHOD_NNLS;
continue;
135 }
else if(strcasecmp(cptr,
"BVLS")==0) {
136 method=METHOD_BVLS;
continue;
138 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
144 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
149 if(ai<argc)
strlcpy(inpfile, argv[ai++], FILENAME_MAX);
150 if(ai<argc)
strlcpy(petfile, argv[ai++], FILENAME_MAX);
151 if(ai<argc)
strlcpy(k1file, argv[ai++], FILENAME_MAX);
152 if(ai<argc)
strlcpy(vbfile, argv[ai++], FILENAME_MAX);
153 if(ai<argc)
strlcpy(k2file, argv[ai++], FILENAME_MAX);
154 if(ai<argc) {fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
return(1);}
157 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
return(1);}
160 printf(
"inpfile := %s\n", inpfile);
161 printf(
"petfile := %s\n", petfile);
162 printf(
"k1file := %s\n", k1file);
163 printf(
"vbfile := %s\n", vbfile);
164 if(k2file[0]) printf(
"k2file := %s\n", k2file);
165 printf(
"calcThreshold :=%g\n", calcThreshold);
166 printf(
"weights := %d\n", weights);
167 printf(
"method := %s\n", method_str[method]);
168 if(fittime>0.0) printf(
"required_fittime := %g min\n", fittime);
177 if(verbose>0) printf(
"reading data files\n");
183 petfile, NULL, inpfile, NULL, NULL, &fittime, &dataNr, &img,
184 NULL, &tac, 1, stdout, verbose-2, errmsg);
186 fprintf(stderr,
"Error: %s.\n", errmsg);
187 if(verbose>1) printf(
" ret := %d\n", ret);
191 if(verbose>0) fprintf(stderr,
"Warning: missing pixel values.\n");
194 for(
int fi=0; fi<tac.
frameNr; fi++) tac.
voi[0].
y2[fi]/=60.0;
195 for(
int fi=0; fi<tac.
frameNr; fi++) tac.
voi[0].
y3[fi]/=3600.0;
199 printf(
"fittimeFinal := %g min\n", fittime);
200 printf(
"dataNr := %d\n", dataNr);
204 fprintf(stderr,
"Error: too few time frames for fitting.\n");
211 if(verbose>1) fprintf(stdout,
"allocating working memory for pixel TACs\n");
214 fprintf(stderr,
"Error: cannot allocate memory.\n");
215 if(verbose>0) printf(
"ret := %d\n", ret);
218 strcpy(tac.
voi[0].
name,
"input");
219 strcpy(tac.
voi[1].
name,
"tissue");
223 fprintf(stderr,
"Error: cannot calculate weights.\n");
226 for(
int i=0; i<dataNr; i++) tac.
w[i]=img.
weight[i];
229 double threshold=calcThreshold*tac.
voi[0].
y2[dataNr-1];
230 if(verbose>1) printf(
"threshold_AUC := %g\n", threshold);
236 if(verbose>1) fprintf(stdout,
"allocating memory for parametric image data\n");
244 fprintf(stderr,
"Error: cannot allocate memory for result image.\n");
250 k1img.
end[0]=k2img.
end[0]=vbimg.
end[0]=60.*fittime;
252 k1img.
unit=CUNIT_ML_PER_ML_PER_MIN;
253 vbimg.
unit=CUNIT_ML_PER_ML;
254 k2img.
unit=CUNIT_PER_MIN;
262 int fittedNr=0, fittedokNr=0, thresholdNr=0;
264 if(method==METHOD_NNLS) {
267 for(
int m=0; m<dataNr; m++) {
268 if(tac.
w[m]<=1.0e-20) tac.
w[m]=0.0;
else tac.
w[m]=sqrt(tac.
w[m]);
274 if(verbose>1) fprintf(stdout,
"allocating memory for NNLS\n");
275 int nnls_n=NNLS_N;
if(!k2file[0]) nnls_n--;
277 int n, m, nnls_index[nnls_n];
278 double **nnls_a, nnls_b[nnls_m], nnls_zz[nnls_m], nnls_x[nnls_n], *nnls_mat,
279 nnls_wp[nnls_n], nnls_rnorm;
280 nnls_mat=(
double*)malloc((nnls_n*nnls_m)*
sizeof(
double));
281 nnls_a=(
double**)malloc(nnls_n*
sizeof(
double*));
282 if(nnls_mat==NULL || nnls_a==NULL) {
283 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
287 for(n=0; n<nnls_n; n++) nnls_a[n]=nnls_mat+n*nnls_m;
293 if(verbose>0) fprintf(stdout,
"computing NNLS pixel-by-pixel\n");
294 double *ct, *cti, *cp, *cpi;
297 for(
int pi=0; pi<img.
dimz; pi++) {
298 if(verbose>0) {fprintf(stdout,
"."); fflush(stdout);}
299 for(
int yi=0; yi<img.
dimy; yi++) {
300 for(
int xi=0; xi<img.
dimx; xi++) {
301 double K1=0.0, k2=0.0, Vb=0.0;
303 k1img.
m[pi][yi][xi][0]=0.0;
304 k2img.
m[pi][yi][xi][0]=0.0;
305 vbimg.
m[pi][yi][xi][0]=0.0;
307 for(
int fi=0; fi<tac.
frameNr; fi++) ct[fi]=img.
m[pi][yi][xi][fi];
311 if(cti[dataNr-1]<threshold) {thresholdNr++;
continue;}
318 for(
int m=0; m<nnls_m; m++) {
321 if(k2file[0]) nnls_a[2][m]=-cti[m];
325 for(
int m=0; m<nnls_m; m++) {
327 for(
int n=0; n<nnls_n; n++) nnls_a[n][m]*=tac.
w[m];
329 if(verbose>5 && pi==img.
dimz/2 && yi==img.
dimy/3 && xi==img.
dimx/3) {
330 printf(
"Matrix A Array B\n");
331 for(m=0; m<nnls_m; m++) {
332 printf(
"%12.3f %12.3f %12.3f %12.3f\n",
333 nnls_a[0][m], nnls_a[1][m], nnls_a[2][m], nnls_b[m]);
337 ret=
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm, nnls_wp, nnls_zz, nnls_index);
339 if(verbose>3) printf(
"no solution possible (%d)\n", ret);
340 if(verbose>4) printf(
"nnls_n=%d nnls_m=%d\n", nnls_n, nnls_m);
341 for(
int n=0; n<nnls_n; n++) nnls_x[n]=0.0;
348 if(k2file[0]) k2=nnls_x[2];
349 K1=nnls_x[0]-Vb*k2;
if(K1>=0.0) fittedokNr++;
else K1=k2=0.0;
351 k1img.
m[pi][yi][xi][0]=K1;
352 vbimg.
m[pi][yi][xi][0]=Vb;
353 k2img.
m[pi][yi][xi][0]=k2;
357 if(verbose>0) {fprintf(stdout,
" done.\n"); fflush(stdout);}
358 free(nnls_mat); free(nnls_a);
362 fprintf(stdout,
"%d out of %d pixels were fitted; %d pixels ok.\n", fittedokNr, n, fittedNr);
363 fprintf(stdout,
"%d pixels were thresholded.\n", thresholdNr);
368 fprintf(stderr,
"Error: selected method not available.");
383 if(!ret) ret=
imgWrite(vbfile, &vbimg);
384 if(!ret && k2file[0]) ret=
imgWrite(k2file, &k2img);
387 fprintf(stderr,
"Error: cannot write parametric image.\n");
390 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]