TPCCLIB
Loading...
Searching...
No Matches
imgflowd.c
Go to the documentation of this file.
1
9/*****************************************************************************/
10#include "tpcclibConfig.h"
11/*****************************************************************************/
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <string.h>
16#include <math.h>
17#include <time.h>
18/*****************************************************************************/
19#include "libtpcmisc.h"
20#include "libtpccurveio.h"
21#include "libtpcimgio.h"
22#include "libtpcimgp.h"
23#include "libtpcmodel.h"
24#include "libtpcmodext.h"
25/*****************************************************************************/
26#define NNLS_N 3
27/*****************************************************************************/
28
29/*****************************************************************************/
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).",
40 " ",
41 "Arterial blood curve (BTAC) and dynamic PET image must be corrected for physical decay.",
42 "BTAC sample times must be in seconds.",
43 " ",
44 "Since time delay varies inside PET image, this program requires a delay map",
45 "which contains the time delay for each image pixel.",
46 " ",
47 "Usage: @P [Options] btacfile imgfile delayfile flowfile",
48 " ",
49 "Options:",
50 " -end=<Fit end time (sec)>",
51 " Use data from 0 to end time; by default, all of it.",
52 " -k2=<filename>",
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.",
60 " -max=<Max value>",
61 " Upper limit for blood flow (K1) values.",
62 " -noneg",
63 " Pixels with negative K1 estimates are set to zero.",
64 " -stdoptions", // List standard options like --help, -v, etc
65 " ",
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).",
69 " ",
70 "References:",
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.",
75 " ",
76 "See also: imgdelay, imgflowm, imgbfh2o, imgcbv",
77 " ",
78 "Keywords: image, modelling, perfusion, blood flow, radiowater, NNLS",
79 0};
80/*****************************************************************************/
81
82/*****************************************************************************/
83/* Turn on the globbing of the command line, since it is disabled by default in
84 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
85 In Unix&Linux wildcard command line processing is enabled by default. */
86/*
87#undef _CRT_glob
88#define _CRT_glob -1
89*/
90int _dowildcard = -1;
91/*****************************************************************************/
92
93/*****************************************************************************/
94/*
95 * main()
96 */
97int main(int argc, char *argv[])
98{
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("");
106 int fitVa=1;
107
108
109
110 /*
111 * Get arguments
112 */
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;
115 /* Get options */
116 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
117 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
118 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
119 if(strcasecmp(cptr, "W")==0) {
120 weight=1; continue;
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) {
124 fitVa=0; continue;
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;
133 }
134 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
135 return(1);
136 } else break;
137
138 /* Print help or version? */
139 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
140 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
141 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
142
143 /* Process other arguments, starting from the first non-option */
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);}
149 /* Did we get all the information that we need? */
150 if(!k1file[0]) {
151 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
152 return(1);
153 }
154 if(!(fittime>0.0)) fittime=1.0E+020;
155
156 /* In verbose mode print arguments and options */
157 if(verbose>1) {
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);
169 fflush(stdout);
170 }
171 if(verbose>10) IMG_TEST=verbose-10; else IMG_TEST=0;
172
173
174
175 /*
176 * Read PET image and input TAC
177 */
178 if(verbose>1) printf("reading data files\n");
179 DFT inp; dftInit(&inp);
180 DFT tac; dftInit(&tac);
181 IMG img; imgInit(&img);
182 {
183 char tmp[FILENAME_MAX+1];
184 int ret=imgReadModelingData(
185 petfile, NULL, inpfile, NULL, NULL, &fittime, &dataNr, &img,
186 &inp, &tac, 1, stdout, verbose-2, tmp);
187 if(ret!=0) {
188 fprintf(stderr, "Error: %s.\n", tmp);
189 if(verbose>1) printf(" ret := %d\n", ret);
190 return(2);
191 }
192 if(imgNaNs(&img, 1)>0)
193 if(verbose>0) fprintf(stderr, "Warning: missing pixel values.\n");
194 }
195 /* Let time units be in sec because delay map is in sec */
196 if(verbose>1) {
197 printf("fittimeFinal := %g s\n", 60.0*fittime);
198 printf("dataNr := %d\n", dataNr);
199 }
200 /* Check that image is dynamic */
201 if(dataNr<3) {
202 fprintf(stderr, "Error: too few time frames for fitting.\n");
203 if(verbose>0) imgInfo(&img);
204 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&inp); return(2);
205 }
206
207 /*
208 * Read delay map
209 */
210 if(verbose>1) printf("reading delay map\n");
211 IMG dtmap; imgInit(&dtmap);
212 {
213 int ret=imgRead(delayfile, &dtmap);
214 if(ret!=0) {
215 fprintf(stderr, "Error: cannot read delay map %s.\n", delayfile);
216 if(verbose>1) printf(" ret := %d\n", ret);
217 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&inp); return(2);
218 }
219 if(imgMatchMatrixSize(&img, &dtmap)) {
220 fprintf(stderr, "Error: bad delay map size.\n");
221 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&inp); imgEmpty(&dtmap); return(2);
222 }
223 }
224
225
226 /* Allocate memory for tissue TAC and integral */
227 {
228 int ret=dftAddmem(&tac, 1);
229 if(ret!=0) {
230 fprintf(stderr, "Error (%d) in allocating memory.\n", ret);
231 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&inp); imgEmpty(&dtmap);
232 return(3);
233 }
234 strcpy(tac.voi[0].voiname, "input");
235 strcpy(tac.voi[1].voiname, "tissue");
236 }
237
238
239
240 /*
241 * Allocate memory for result images and fill the header info
242 */
243 IMG k1img, k2img, vaimg;
244 imgInit(&k1img); imgInit(&k2img); imgInit(&vaimg);
245 {
246 int ret=0;
247 if((ret=imgAllocateWithHeader(&k1img, img.dimz, img.dimy, img.dimx, 1, &img))) {
248 fprintf(stderr, "Error: cannot allocate memory.\n");
249 } else {
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;
256 //k1img.start[0]=img.start[0]; k1img.end[0]=img.end[dataNr-1];
257 k1img.start[0]=k1img.end[0]=0.0;
258 }
259 /* optional maps */
260 if(ret==0 && k2file[0]) {
261 if((ret=imgAllocateWithHeader(&k2img, img.dimz, img.dimy, img.dimx, 1, &k1img))) {
262 fprintf(stderr, "Error: cannot allocate memory.\n");
263 } else {
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;
269 }
270 }
271 if(ret==0 && vafile[0]) {
272 if((ret=imgAllocateWithHeader(&vaimg, img.dimz, img.dimy, img.dimx, 1, &k1img))) {
273 fprintf(stderr, "Error: cannot allocate memory.\n");
274 } else {
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;
280 }
281 }
282 /* quit if error */
283 if(ret) {
284 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&inp); imgEmpty(&dtmap);
285 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vaimg);
286 return(4);
287 }
288 }
289
290
291 /*
292 * Allocate memory required by NNLS
293 */
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));
300 if(nnls_mat==NULL) {
301 fprintf(stderr, "Error: cannot allocate memory for NNLS.\n");
302 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&inp); imgEmpty(&dtmap);
303 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vaimg);
304 return(5);
305 }
306 dptr=nnls_mat;
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;
309
310 /* Copy weights if available */
311 /* or set them to frame lengths */
312 if(verbose>2) printf("working with NNLS weights\n");
313 if(weight==1 && img.isWeight==0) {
314 for(int m=0; m<nnls_m; m++) img.weight[m]=img.end[m]-img.start[m];
315 img.isWeight=1;
316 }
317 /* Compute NNLS weights */
318 if(img.isWeight) {
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;
321 }
322 }
323
324
325 /*
326 * Compute pixel-by-pixel
327 */
328 long long okNr=0;
329 double *cp, *cpi, *ct, *cti;
330 cp=tac.voi[0].y; cpi=tac.voi[0].y2;
331 ct=tac.voi[1].y; cti=tac.voi[1].y2;
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++) {
338
339 /* Initiate pixel output values */
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;
343
344 /* Check the delay map */
345 if(!isfinite(dtmap.m[zi][yi][xi][0])) continue;
346
347 /* Copy and integrate pixel curve */
348 for(int m=0; m<nnls_m; m++) ct[m]=img.m[zi][yi][xi][m];
349 petintegral(tac.x1, tac.x2, ct, nnls_m, cti, NULL);
350 /* if AUC at the end is not above zero, then forget this pixel */
351 if(!(cti[nnls_m-1]>0.0)) continue;
352
353 /* Calculate delay-corrected BTAC and its AUC */
354 double dx[inp.frameNr];
355 for(int i=0; i<inp.frameNr; i++) dx[i]=inp.x[i]+dtmap.m[zi][yi][xi][0];
356 int ret=interpolate4pet(dx, inp.voi[0].y, inp.frameNr, tac.x1, tac.x2, cp, cpi, NULL, dataNr);
357 if(ret) {
358 if(verbose>3)
359 fprintf(stderr, "Error: cannot interpolate/integrate input for pixel %d,%d,%d\n", zi, yi, xi);
360 continue;
361 }
362
363 /*
364 * Estimate K1, k2 and Va
365 */
366 nnls_m=dataNr; nnls_n=NNLS_N; if(fitVa==0) nnls_n--;
367 /* Fill NNLS A matrix: */
368 /* function #1: tissue integral x -1 */
369 for(int m=0; m<nnls_m; m++) nnls_a[0][m]=-cti[m];
370 /* function #2: integral of input */
371 for(int m=0; m<nnls_m; m++) nnls_a[1][m]=cpi[m];
372 /* function #3: input curve */
373 if(nnls_n>2) for(int m=0; m<nnls_m; m++) nnls_a[2][m]=cp[m];
374 /* Fill NNLS B array: tissue */
375 for(int m=0; m<nnls_m; m++) nnls_b[m]=ct[m];
376 /* Apply data weights */
377 if(img.isWeight) nnlsWght(nnls_n, nnls_m, nnls_a, nnls_b, tac.w);
378 /* NNLS */
379 if(nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm,
380 nnls_wp, nnls_zz, nnls_index)>1) continue; /* no solution is possible */
381 /* Set pixel values */
382 double Va=0.0;
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;
388 /* Convert from per sec to per min */
389 k1img.m[zi][yi][xi][0]*=60.0;
390 if(k2file[0]) k2img.m[zi][yi][xi][0]*=60.0;
391
392 /* Enforce K1 limits, if requested */
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;
397
398 /* Va limit */
399 if(vafile[0] && vaimg.m[zi][yi][xi][0]>1.0) vaimg.m[zi][yi][xi][0]=1.0;
400
401 okNr++;
402 } /* next column */
403 } /* next row */
404 } /* next plane */
405 if(verbose>0) fprintf(stdout, "done.\n");
406
407 /* No need for dynamic image, NNLS matrix, delay map, or curves any more */
408 free(nnls_mat); imgEmpty(&img); imgEmpty(&dtmap); dftEmpty(&tac); dftEmpty(&inp);
409
410 /*
411 * Check that not all pixels failed
412 */
413 if(verbose>1) printf("calculation successful for %lld pixels.\n", okNr);
414 if(okNr==0) {
415 fprintf(stderr, "Error: failed calculation.\n");
416 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vaimg);
417 return(9);
418 }
419
420
421 /*
422 * Save parametric image(s)
423 */
424 if(verbose>0) printf("writing parametric images\n");
425 {
426 /* K1 */
427 int ret=imgWrite(k1file, &k1img);
428 if(ret) {
429 fprintf(stderr, "Error: %s\n", k1img.statmsg);
430 if(verbose>1) printf("ret := %d\n", ret);
431 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vaimg);
432 return(13);
433 }
434 if(verbose>0) fprintf(stdout, "Flow image %s saved.\n", k1file);
435 /* k2 */
436 if(k2file[0]) {
437 ret=imgWrite(k2file, &k2img);
438 if(ret) {
439 fprintf(stderr, "Error: %s\n", k2img.statmsg);
440 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vaimg);
441 return(14);
442 }
443 if(verbose>0) fprintf(stdout, "k2 image %s saved.\n", k2file);
444 }
445 /* Va */
446 if(vafile[0]) {
447 ret=imgWrite(vafile, &vaimg);
448 if(ret) {
449 fprintf(stderr, "Error: %s\n", vaimg.statmsg);
450 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vaimg);
451 return(16);
452 }
453 if(verbose>0) fprintf(stdout, "Va image %s saved.\n", vafile);
454 }
455 }
456
457 if(verbose>2) printf("before quitting, free memory\n");
458 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vaimg);
459 fflush(stdout);
460
461 return(0);
462}
463/*****************************************************************************/
464
465/*****************************************************************************/
double atof_dpi(char *str)
Definition decpoint.c:59
void dftInit(DFT *data)
Definition dft.c:38
int dftAddmem(DFT *dft, int voiNr)
Definition dft.c:107
void dftEmpty(DFT *data)
Definition dft.c:20
int IMG_TEST
Definition img.c:6
void imgInfo(IMG *image)
Definition img.c:359
unsigned long long imgNaNs(IMG *img, int fix)
Definition img.c:658
int imgAllocateWithHeader(IMG *image, int planes, int rows, int columns, int frames, IMG *image_from)
Definition img.c:279
void imgEmpty(IMG *image)
Definition img.c:121
void imgInit(IMG *image)
Definition img.c:60
int imgMatchMatrixSize(IMG *d1, IMG *d2)
Definition imgcomp.c:447
int imgRead(const char *fname, IMG *img)
Definition imgfile.c:26
int imgWrite(const char *fname, IMG *img)
Definition imgfile.c:136
int imgReadModelingData(char *petfile, char *siffile, char *inputfile1, char *inputfile2, char *inputfile3, double *fitdur, int *fitframeNr, IMG *img, DFT *inp, DFT *iinp, int verifypeak, FILE *loginfo, int verbose, char *status)
Definition imginput.c:24
int petintegral(double *x1, double *x2, double *y, int nr, double *ie, double *iie)
Integrate PET TAC data to frame mid times.
Definition integr.c:771
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.
Definition integr.c:510
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)
Definition proginfo.c:40
size_t strlcpy(char *dst, const char *src, size_t dstsize)
Definition strext.c:245
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:213
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:383
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:158
Header file for libtpcmodel.
int nnls(double **a, int m, int n, double *b, double *x, double *rnorm, double *w, double *zz, int *index)
Definition nnls.c:37
int nnlsWght(int N, int M, double **A, double *b, double *weight)
Definition nnls.c:257
Header file for libtpcmodext.
Voi * voi
double * w
double * x1
double * x2
int frameNr
double * x
unsigned short int dimx
float **** m
char decayCorrection
char unit
int * planeNumber
float * weight
float * start
unsigned short int dimz
unsigned short int dimy
float * end
const char * statmsg
char isWeight
double * y2
char voiname[MAX_REGIONSUBNAME_LEN+1]
double * y