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) must be corrected for decay and time delay,",
42 "with sample times in seconds. Dynamic PET image must be corrected for decay.",
44 "Since time delay varies inside PET image, this program requires a mask image",
45 "which specifies to which pixels the model is applied. If output file(s) do",
46 "not exist, the output files are created with zero pixel values where mask",
47 "image has value 0. If output file(s) exist, the pre-existing pixel values",
48 "outside the mask are not changed.",
50 "Usage: @P [Options] btacfile imgfile maskfile flowfile",
53 " -end=<Fit end time (sec)>",
54 " Use data from 0 to end time; by default, all of it.",
56 " Parametric k2 image is saved; in some situations perfusion calculation",
57 " from k2 can be more accurate than the default assumption of f=K1.",
58 " Perfusion can be calculated from k2 using equation f=k2*pH2O, where",
59 " pH2O is the physiological partition coefficient of water in tissue.",
60 " -Va=<filename> | -Va=0",
61 " Parametric Va image is saved, or set Va to 0 if image is pre-corrected",
62 " for arterial blood volume; by default Va is fitted.",
64 " Upper limit for blood flow (K1) values.",
66 " Pixels with negative K1 estimates are set to zero.",
69 "The units of pixel values in the blood flow (K1) image is",
70 "(mL blood)/((mL tissue) * min), in k2 image 1/min, and",
71 "in Va image (mL blood/mL tissue).",
73 "Example 1. Calculation of perfusion and arterial blood volume image,",
74 " stopping fit at 180 s:",
75 " @P -Va=p234va.v -end=180 p234d1.bld p234.nii p234_mask1.nii p234flow.nii",
77 "Example 2. Dynamic image is pre-corrected for vascular activity,",
78 " and full data length is used:",
79 " @P -Va=0 p345dc2.bld p345_vacorr.nii p345flow.nii",
82 "1. Blomqvist G. On the construction of functional maps in positron",
83 " emission tomography. J Cereb Blood Flow Metab. 1984;4:629-632.",
84 "2. Lawson CL & Hanson RJ. Solving least squares problems.",
85 " Prentice-Hall, 1974.",
86 "3. Ohta S, Meyer E, Thompson CJ, Gjedde A. Oxygen consumption of the",
87 " living human brain measured after a single inhalation of positron",
88 " emitting oxygen. J Cereb Blood Flow Metab. 1992;12:179-192.",
90 "See also: imgflow, imgdelay, fitdelay, imgcbv, fit_h2o, imgcalc",
92 "Keywords: image, modelling, perfusion, blood flow, radiowater, mask",
111int main(
int argc,
char *argv[])
113 int ai, help=0, version=0, verbose=1;
114 int weight=0, dataNr=0;
115 char inpfile[FILENAME_MAX], petfile[FILENAME_MAX], maskfile[FILENAME_MAX];
116 char k2file[FILENAME_MAX], vafile[FILENAME_MAX], k1file[FILENAME_MAX];
117 double upperLimit=nan(
"");
118 double lowerLimit=nan(
"");
119 double fittime=nan(
"");
121 double *cp, *cpi, *ct, *cti, Va;
128 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
129 inpfile[0]=petfile[0]=k1file[0]=k2file[0]=vafile[0]=maskfile[0]=(char)0;
131 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
133 char *cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
134 if(strcasecmp(cptr,
"W")==0) {
136 }
else if(strncasecmp(cptr,
"k2=", 3)==0) {
137 strlcpy(k2file, cptr+3, FILENAME_MAX);
if(strlen(k2file))
continue;
138 }
else if(strcasecmp(cptr,
"VA=0")==0) {
140 }
else if(strncasecmp(cptr,
"VA=", 3)==0 || strncasecmp(cptr,
"VB=", 3)==0) {
141 strlcpy(vafile, cptr+3, FILENAME_MAX);
if(strlen(vafile))
continue;
142 }
else if(strncasecmp(cptr,
"noneg", 2)==0) {
143 lowerLimit=0.0;
continue;
144 }
else if(strncasecmp(cptr,
"MAX=", 4)==0) {
145 upperLimit=
atof_dpi(cptr+4);
if(upperLimit>0.0)
continue;
146 }
else if(strncasecmp(cptr,
"END=", 4)==0) {
147 fittime=
atof_dpi(cptr+4)/60.;
if(fittime>0.0)
continue;
149 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
154 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
159 if(ai<argc)
strlcpy(inpfile, argv[ai++], FILENAME_MAX);
160 if(ai<argc)
strlcpy(petfile, argv[ai++], FILENAME_MAX);
161 if(ai<argc)
strlcpy(maskfile, argv[ai++], FILENAME_MAX);
162 if(ai<argc)
strlcpy(k1file, argv[ai++], FILENAME_MAX);
163 if(ai<argc) {fprintf(stderr,
"Error: invalid argument '%s'.\n", argv[ai]);
return(1);}
166 fprintf(stderr,
"Error: missing command-line argument; use option --help\n");
169 if(!(fittime>0.0)) fittime=1.0E+020;
173 printf(
"inpfile := %s\n", inpfile);
174 printf(
"petfile := %s\n", petfile);
175 printf(
"maskfile := %s\n", maskfile);
176 printf(
"k1file := %s\n", k1file);
177 if(k2file[0]) printf(
"k2file := %s\n", k2file);
178 printf(
"fitVa := %d\n", fitVa);
179 if(vafile[0]) printf(
"vafile := %s\n", vafile);
180 if(!isnan(upperLimit)) printf(
"upperLimit := %f\n", upperLimit);
181 if(!isnan(lowerLimit)) printf(
"lowerLimit := %f\n", lowerLimit);
182 if(isfinite(fittime)) printf(
"fittime := %g [sec]\n", 60.0*fittime);
183 printf(
"weight := %d\n", weight);
192 if(verbose>1) printf(
"reading data files\n");
196 char tmp[FILENAME_MAX+1];
198 petfile, NULL, inpfile, NULL, NULL, &fittime, &dataNr, &img,
199 NULL, &tac, 1, stdout, verbose-2, tmp);
201 fprintf(stderr,
"Error: %s.\n", tmp);
202 if(verbose>1) printf(
" ret := %d\n", ret);
206 if(verbose>0) fprintf(stderr,
"Warning: missing pixel values.\n");
212 printf(
"fittimeFinal := %g s\n", 60.0*fittime);
213 printf(
"dataNr := %d\n", dataNr);
217 fprintf(stderr,
"Error: too few time frames for fitting.\n");
225 if(verbose>1) printf(
"reading mask image\n");
228 int ret=
imgRead(maskfile, &mask);
230 fprintf(stderr,
"Error: cannot read mask %s.\n", maskfile);
231 if(verbose>1) printf(
" ret := %d\n", ret);
235 fprintf(stderr,
"Error: bad mask for image.\n");
246 fprintf(stderr,
"Error (%d) in allocating memory.\n", ret);
260 IMG k1img, k2img, vaimg;
264 if(
imgRead(k1file, &k1img)==0) {
266 fprintf(stderr,
"Error: wrong matrix size in existing K1 map.\n");
269 fprintf(stderr,
"Error: cannot allocate memory.\n");
271 for(
int zi=0; zi<img.
dimz; zi++)
272 for(
int yi=0; yi<img.
dimy; yi++)
273 for(
int xi=0; xi<img.
dimx; xi++)
274 k1img.
m[zi][yi][xi][0]=0.0;
275 k1img.
unit=IMGUNIT_ML_PER_ML_PER_MIN;
281 if(ret==0 && k2file[0]) {
282 if(
imgRead(k2file, &k2img)==0) {
284 fprintf(stderr,
"Error: wrong matrix size in existing k2 map.\n");
287 fprintf(stderr,
"Error: cannot allocate memory.\n");
289 for(
int zi=0; zi<img.
dimz; zi++)
290 for(
int yi=0; yi<img.
dimy; yi++)
291 for(
int xi=0; xi<img.
dimx; xi++)
292 k2img.
m[zi][yi][xi][0]=0.0;
293 k2img.
unit=IMGUNIT_PER_MIN;
297 if(ret==0 && vafile[0]) {
298 if(
imgRead(vafile, &vaimg)==0) {
300 fprintf(stderr,
"Error: wrong matrix size in existing Va map.\n");
303 fprintf(stderr,
"Error: cannot allocate memory.\n");
305 for(
int zi=0; zi<img.
dimz; zi++)
306 for(
int yi=0; yi<img.
dimy; yi++)
307 for(
int xi=0; xi<img.
dimx; xi++)
308 vaimg.
m[zi][yi][xi][0]=0.0;
309 vaimg.
unit=IMGUNIT_ML_PER_ML;
325 int nnls_n, nnls_m, nnls_index[NNLS_N];
326 double *nnls_a[NNLS_N], *nnls_b, *nnls_zz, nnls_x[NNLS_N], *nnls_mat,
327 nnls_wp[NNLS_N], *dptr, nnls_rnorm;
328 if(verbose>1) printf(
"allocating memory for NNLS\n");
329 nnls_n=NNLS_N; nnls_m=dataNr;
330 nnls_mat=(
double*)malloc(((nnls_n+2)*nnls_m)*
sizeof(
double));
332 fprintf(stderr,
"Error: cannot allocate memory for NNLS.\n");
338 for(
int n=0; n<nnls_n; n++) {nnls_a[n]=dptr; dptr+=nnls_m;}
339 nnls_b=dptr; dptr+=nnls_m; nnls_zz=dptr;
343 if(verbose>2) printf(
"working with NNLS weights\n");
345 for(
int m=0; m<nnls_m; m++) img.
weight[m]=img.
end[m]-img.
start[m];
350 for(
int m=0; m<nnls_m; m++) {
351 tac.
w[m]=img.
weight[m];
if(tac.
w[m]<=1.0e-20) tac.
w[m]=0.0;
361 if(verbose>0) fprintf(stdout,
"computing pixel-by-pixel\n");
362 for(
int zi=0; zi<img.
dimz; zi++) {
363 if(verbose>2) printf(
"computing plane %d\n", img.
planeNumber[zi]);
364 else if(img.
dimz>1 && verbose>0) {fprintf(stdout,
"."); fflush(stdout);}
365 for(
int yi=0; yi<img.
dimy; yi++) {
366 for(
int xi=0; xi<img.
dimx; xi++) {
369 if(!(fabs(mask.
m[zi][yi][xi][0])>1.0E-12))
continue;
372 k1img.
m[zi][yi][xi][0]=0.0;
373 if(k2file[0]) k2img.
m[zi][yi][xi][0]=0.0;
374 if(vafile[0]) vaimg.
m[zi][yi][xi][0]=0.0;
379 nnls_m=dataNr; nnls_n=NNLS_N;
if(fitVa==0) nnls_n--;
381 for(
int m=0; m<nnls_m; m++) ct[m]=img.
m[zi][yi][xi][m];
384 if(cti[nnls_m-1]<=0.0)
continue;
387 for(
int m=0; m<nnls_m; m++) nnls_a[0][m]=-cti[m];
389 for(
int m=0; m<nnls_m; m++) nnls_a[1][m]=cpi[m];
391 if(nnls_n>2)
for(
int m=0; m<nnls_m; m++) nnls_a[2][m]=cp[m];
393 for(
int m=0; m<nnls_m; m++) nnls_b[m]=ct[m];
397 if(
nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm,
398 nnls_wp, nnls_zz, nnls_index)>1)
continue;
399 if(nnls_n>2) Va=nnls_x[2];
else Va=0.0;
400 k1img.
m[zi][yi][xi][0]=nnls_x[1];
401 if(nnls_n>2) k1img.
m[zi][yi][xi][0]-=Va*nnls_x[0];
402 if(k2file[0]) k2img.
m[zi][yi][xi][0]=nnls_x[0];
403 if(vafile[0]) vaimg.
m[zi][yi][xi][0]=Va;
406 if(!isnan(upperLimit) && k1img.
m[zi][yi][xi][0]>upperLimit)
407 k1img.
m[zi][yi][xi][0]=upperLimit;
408 if(!isnan(lowerLimit) && k1img.
m[zi][yi][xi][0]<lowerLimit)
409 k1img.
m[zi][yi][xi][0]=lowerLimit;
412 if(vafile[0] && vaimg.
m[zi][yi][xi][0]>1.0) vaimg.
m[zi][yi][xi][0]=1.0;
417 if(verbose>0) fprintf(stdout,
"done.\n");
426 if(verbose>0) printf(
"writing parametric images\n");
431 fprintf(stderr,
"Error: %s\n", k1img.
statmsg);
432 if(verbose>1) printf(
"ret := %d\n", ret);
436 if(verbose>0) fprintf(stdout,
"Flow image %s saved.\n", k1file);
441 fprintf(stderr,
"Error: %s\n", k2img.
statmsg);
445 if(verbose>0) fprintf(stdout,
"k2 image %s saved.\n", k2file);
451 fprintf(stderr,
"Error: %s\n", vaimg.
statmsg);
455 if(verbose>0) fprintf(stdout,
"Va image %s saved.\n", vafile);
459 if(verbose>2) printf(
"before quitting, free memory\n");
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 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.
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]