10#include "tpcclibConfig.h"
30static char *info[] = {
31 "Estimation of rate constants K1 (perfusion), k2, and Va from dynamic",
32 "radiowater PET image in ECAT 6.3, ECAT 7.x, NIfTI-1, or Analyze 7.5 file",
33 "format using the two-compartment model equations",
34 " dCt(t)/dt = K1*Ca(t) - k2*Ct(t)",
35 " Croi(t) = Va*Ca(t) + Ct(t)",
36 "are linearized (1) into equation",
37 " Croi(t) = Va*Ca(t) + (K1+Va*k2)*Integral[Ca(t)] - k2*Integral[Croi(t)]",
38 "from which the model parameters are solved using Lawson-Hanson non-negative",
39 "least squares (NNLS) method (2).",
41 "Arterial blood curve (BTAC) and dynamic PET image must be corrected for physical decay.",
42 "BTAC sample times must be in seconds.",
44 "Since time delay varies inside PET image, this program requires a delay map",
45 "which contains the time delay for each image pixel.",
47 "Usage: @P [Options] btacfile imgfile delayfile flowfile",
50 " -end=<Fit end time (sec)>",
51 " Use data from 0 to end time; by default, all of it.",
53 " Parametric k2 image is saved; in some situations perfusion calculation",
54 " from k2 can be more accurate than the default assumption of f=K1.",
55 " Perfusion can be calculated from k2 using equation f=k2*pH2O, where",
56 " pH2O is the physiological partition coefficient of water in tissue.",
57 " -Va=<filename> | -Va=0",
58 " Parametric Va image is saved, or set Va to 0 if image is pre-corrected",
59 " for arterial blood volume; by default Va is fitted.",
61 " Upper limit for blood flow (K1) values.",
63 " Pixels with negative K1 estimates are set to zero.",
66 "The units of pixel values in the blood flow (K1) image is",
67 "(mL blood)/((mL tissue) * min), in k2 image 1/min, and",
68 "in Va image (mL blood/mL tissue).",
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.",
76 "See also: imgdelay, imgflowm, imgbfh2o, imgcbv",
78 "Keywords: image, modelling, perfusion, blood flow, radiowater, NNLS",
97int main(
int argc,
char *argv[])
99 int ai, help=0, version=0, verbose=1;
100 int weight=0, dataNr=0;
101 char inpfile[FILENAME_MAX], petfile[FILENAME_MAX], delayfile[FILENAME_MAX];
102 char k2file[FILENAME_MAX], vafile[FILENAME_MAX], k1file[FILENAME_MAX];
103 double upperLimit=nan(
"");
104 double lowerLimit=nan(
"");
105 double fittime=nan(
"");
113 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
114 inpfile[0]=petfile[0]=k1file[0]=k2file[0]=vafile[0]=delayfile[0]=(char)0;
116 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
118 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
119 if(strcasecmp(cptr,
"W")==0) {
121 }
else if(strncasecmp(cptr,
"k2=", 3)==0) {
122 strlcpy(k2file, cptr+3, FILENAME_MAX);
if(strlen(k2file))
continue;
123 }
else if(strcasecmp(cptr,
"VA=0")==0) {
125 }
else if(strncasecmp(cptr,
"VA=", 3)==0 || strncasecmp(cptr,
"VB=", 3)==0) {
126 strlcpy(vafile, cptr+3, FILENAME_MAX);
if(strlen(vafile))
continue;
127 }
else if(strncasecmp(cptr,
"noneg", 2)==0) {
128 lowerLimit=0.0;
continue;
129 }
else if(strncasecmp(cptr,
"MAX=", 4)==0) {
130 upperLimit=
atof_dpi(cptr+4);
if(upperLimit>0.0)
continue;
131 }
else if(strncasecmp(cptr,
"END=", 4)==0) {
132 fittime=
atof_dpi(cptr+4)/60.;
if(fittime>0.0)
continue;
134 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
139 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
144 if(ai<argc)
strlcpy(inpfile, argv[ai++], FILENAME_MAX);
145 if(ai<argc)
strlcpy(petfile, argv[ai++], FILENAME_MAX);
146 if(ai<argc)
strlcpy(delayfile, argv[ai++], FILENAME_MAX);
147 if(ai<argc)
strlcpy(k1file, argv[ai++], FILENAME_MAX);
148 if(ai<argc) {fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
return(1);}
151 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
154 if(!(fittime>0.0)) fittime=1.0E+020;
158 printf(
"inpfile := %s\n", inpfile);
159 printf(
"petfile := %s\n", petfile);
160 printf(
"delayfile := %s\n", delayfile);
161 printf(
"k1file := %s\n", k1file);
162 if(k2file[0]) printf(
"k2file := %s\n", k2file);
163 printf(
"fitVa := %d\n", fitVa);
164 if(vafile[0]) printf(
"vafile := %s\n", vafile);
165 if(!isnan(upperLimit)) printf(
"upperLimit := %f\n", upperLimit);
166 if(!isnan(lowerLimit)) printf(
"lowerLimit := %f\n", lowerLimit);
167 if(isfinite(fittime)) printf(
"fittime := %g [sec]\n", 60.0*fittime);
168 printf(
"weight := %d\n", weight);
178 if(verbose>1) printf(
"reading data files\n");
183 char tmp[FILENAME_MAX+1];
185 petfile, NULL, inpfile, NULL, NULL, &fittime, &dataNr, &img,
186 &inp, &tac, 1, stdout, verbose-2, tmp);
188 fprintf(stderr,
"Error: %s.\n", tmp);
189 if(verbose>1) printf(
" ret := %d\n", ret);
193 if(verbose>0) fprintf(stderr,
"Warning: missing pixel values.\n");
197 printf(
"fittimeFinal := %g s\n", 60.0*fittime);
198 printf(
"dataNr := %d\n", dataNr);
202 fprintf(stderr,
"Error: too few time frames for fitting.\n");
210 if(verbose>1) printf(
"reading delay map\n");
213 int ret=
imgRead(delayfile, &dtmap);
215 fprintf(stderr,
"Error: cannot read delay map %s.\n", delayfile);
216 if(verbose>1) printf(
" ret := %d\n", ret);
220 fprintf(stderr,
"Error: bad delay map size.\n");
230 fprintf(stderr,
"Error (%d) in allocating memory.\n", ret);
243 IMG k1img, k2img, vaimg;
248 fprintf(stderr,
"Error: cannot allocate memory.\n");
250 for(
int zi=0; zi<img.
dimz; zi++)
251 for(
int yi=0; yi<img.
dimy; yi++)
252 for(
int xi=0; xi<img.
dimx; xi++)
253 k1img.
m[zi][yi][xi][0]=0.0;
254 k1img.
unit=IMGUNIT_ML_PER_ML_PER_MIN;
260 if(ret==0 && k2file[0]) {
262 fprintf(stderr,
"Error: cannot allocate memory.\n");
264 for(
int zi=0; zi<img.
dimz; zi++)
265 for(
int yi=0; yi<img.
dimy; yi++)
266 for(
int xi=0; xi<img.
dimx; xi++)
267 k2img.
m[zi][yi][xi][0]=0.0;
268 k2img.
unit=IMGUNIT_PER_MIN;
271 if(ret==0 && vafile[0]) {
273 fprintf(stderr,
"Error: cannot allocate memory.\n");
275 for(
int zi=0; zi<img.
dimz; zi++)
276 for(
int yi=0; yi<img.
dimy; yi++)
277 for(
int xi=0; xi<img.
dimx; xi++)
278 vaimg.
m[zi][yi][xi][0]=0.0;
279 vaimg.
unit=IMGUNIT_ML_PER_ML;
294 int nnls_n, nnls_m, nnls_index[NNLS_N];
295 double *nnls_a[NNLS_N], *nnls_b, *nnls_zz, nnls_x[NNLS_N], *nnls_mat,
296 nnls_wp[NNLS_N], *dptr, nnls_rnorm;
297 if(verbose>1) printf(
"allocating memory for NNLS\n");
298 nnls_n=NNLS_N; nnls_m=dataNr;
299 nnls_mat=(
double*)malloc(((nnls_n+2)*nnls_m)*
sizeof(
double));
301 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
307 for(
int n=0; n<nnls_n; n++) {nnls_a[n]=dptr; dptr+=nnls_m;}
308 nnls_b=dptr; dptr+=nnls_m; nnls_zz=dptr;
312 if(verbose>2) printf(
"working with NNLS weights\n");
314 for(
int m=0; m<nnls_m; m++) img.
weight[m]=img.
end[m]-img.
start[m];
319 for(
int m=0; m<nnls_m; m++) {
320 tac.
w[m]=img.
weight[m];
if(tac.
w[m]<=1.0e-20) tac.
w[m]=0.0;
329 double *cp, *cpi, *ct, *cti;
332 if(verbose>0) fprintf(stdout,
"computing pixel-by-pixel\n");
333 for(
int zi=0; zi<img.
dimz; zi++) {
334 if(verbose>2) printf(
"computing plane %d\n", img.
planeNumber[zi]);
335 else if(img.
dimz>1 && verbose>0) {fprintf(stdout,
"."); fflush(stdout);}
336 for(
int yi=0; yi<img.
dimy; yi++) {
337 for(
int xi=0; xi<img.
dimx; xi++) {
340 k1img.
m[zi][yi][xi][0]=0.0;
341 if(k2file[0]) k2img.
m[zi][yi][xi][0]=0.0;
342 if(vafile[0]) vaimg.
m[zi][yi][xi][0]=0.0;
345 if(!isfinite(dtmap.
m[zi][yi][xi][0]))
continue;
348 for(
int m=0; m<nnls_m; m++) ct[m]=img.
m[zi][yi][xi][m];
351 if(!(cti[nnls_m-1]>0.0))
continue;
355 for(
int i=0; i<inp.
frameNr; i++) dx[i]=inp.
x[i]+dtmap.
m[zi][yi][xi][0];
359 fprintf(stderr,
"Error: cannot interpolate/integrate input for pixel %d,%d,%d\n", zi, yi, xi);
366 nnls_m=dataNr; nnls_n=NNLS_N;
if(fitVa==0) nnls_n--;
369 for(
int m=0; m<nnls_m; m++) nnls_a[0][m]=-cti[m];
371 for(
int m=0; m<nnls_m; m++) nnls_a[1][m]=cpi[m];
373 if(nnls_n>2)
for(
int m=0; m<nnls_m; m++) nnls_a[2][m]=cp[m];
375 for(
int m=0; m<nnls_m; m++) nnls_b[m]=ct[m];
379 if(
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm,
380 nnls_wp, nnls_zz, nnls_index)>1)
continue;
383 if(nnls_n>2) Va=nnls_x[2];
384 k1img.
m[zi][yi][xi][0]=nnls_x[1];
385 if(nnls_n>2) k1img.
m[zi][yi][xi][0]-=Va*nnls_x[0];
386 if(k2file[0]) k2img.
m[zi][yi][xi][0]=nnls_x[0];
387 if(vafile[0]) vaimg.
m[zi][yi][xi][0]=Va;
389 k1img.
m[zi][yi][xi][0]*=60.0;
390 if(k2file[0]) k2img.
m[zi][yi][xi][0]*=60.0;
393 if(!isnan(upperLimit) && k1img.
m[zi][yi][xi][0]>upperLimit)
394 k1img.
m[zi][yi][xi][0]=upperLimit;
395 if(!isnan(lowerLimit) && k1img.
m[zi][yi][xi][0]<lowerLimit)
396 k1img.
m[zi][yi][xi][0]=lowerLimit;
399 if(vafile[0] && vaimg.
m[zi][yi][xi][0]>1.0) vaimg.
m[zi][yi][xi][0]=1.0;
405 if(verbose>0) fprintf(stdout,
"done.\n");
413 if(verbose>1) printf(
"calculation successful for %lld pixels.\n", okNr);
415 fprintf(stderr,
"Error: failed calculation.\n");
424 if(verbose>0) printf(
"writing parametric images\n");
429 fprintf(stderr,
"Error: %s\n", k1img.
statmsg);
430 if(verbose>1) printf(
"ret := %d\n", ret);
434 if(verbose>0) fprintf(stdout,
"Flow image %s saved.\n", k1file);
439 fprintf(stderr,
"Error: %s\n", k2img.
statmsg);
443 if(verbose>0) fprintf(stdout,
"k2 image %s saved.\n", k2file);
449 fprintf(stderr,
"Error: %s\n", vaimg.
statmsg);
453 if(verbose>0) fprintf(stdout,
"Va image %s saved.\n", vafile);
457 if(verbose>2) printf(
"before quitting, free memory\n");
double atof_dpi(char *str)
int dftAddmem(DFT *dft, int voiNr)
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 imgMatchMatrixSize(IMG *d1, IMG *d2)
int imgRead(const char *fname, IMG *img)
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.
int interpolate4pet(double *x, double *y, int nr, double *newx1, double *newx2, double *newy, double *newyi, double *newyii, int newnr)
Interpolate and integrate TAC to PET frames.
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]