TPCCLIB
Loading...
Searching...
No Matches
imgbfh2om.c
Go to the documentation of this file.
1
11/*****************************************************************************/
12#include "tpcclibConfig.h"
13/*****************************************************************************/
14#include <stdio.h>
15#include <stdlib.h>
16#include <unistd.h>
17#include <string.h>
18#include <math.h>
19#include <time.h>
20/*****************************************************************************/
21#include "libtpccurveio.h"
22#include "libtpcmodext.h"
23#include "libtpcmisc.h"
24#include "libtpcmodel.h"
25#include "libtpcimgio.h"
26#include "libtpcimgp.h"
27/*****************************************************************************/
28#define MAX_N 2
29/*****************************************************************************/
30
31/*****************************************************************************/
32static char *info[] = {
33 "Estimation of rate constants K1, k2 and Va from dynamic PET image in",
34 "ECAT 6.3, ECAT 7.x, NIfTI-1, or Analyze 7.5 file format using",
35 "one-tissue compartment model (1), solved using the basis function",
36 "approach (2, 3).",
37 " ",
38 "When applied to dynamic [O-15]H2O studies, the resulting K1 image",
39 "equals perfusion (blood flow) image. K1 image can be divided by tissue",
40 "density (g/mL) (option -density) and multiplied by 100 (option -dL)",
41 "to achieve the blood flow image in units (mL blood)/((100 g tissue) * min).",
42 " ",
43 "When applied to dynamic [O-15]O2 brain studies, the resulting K1 image",
44 "can be converted to oxygen consumption image by multiplying it by",
45 "arterial oxygen concentration (4) (mL O2 / dL blood) to get the",
46 "parametric image in units mL O2 / ((100 ml tissue) * min).",
47 "The model assumptions hold only when oxygen consumption is 1-6.7",
48 "mL O2/(100g * min) and fit time is set to 300 s or less (4).",
49 " ",
50 "Arterial blood TAC must be corrected for decay and delay, with sample times",
51 "in seconds. Dynamic PET image must be corrected for decay. Fit time must",
52 "be given in seconds.",
53 " ",
54 "Usage: @P [Options] btacfile imgfile fittime flowfile",
55 " ",
56 "Options:",
57 " -mL or -dL",
58 " Units in flow and Va images will be given per mL or per dL,",
59 " respectively. By default, units are per mL.",
60 " -density[=<value>]",
61 " With option -density the flow is calculated per gram or 100g tissue.",
62 " Tissue density can be changed from the default 1.04 g/mL.",
63 " -Vd=<filename>",
64 " Parametric K1/k2 (Vd, apparent p) image is saved.",
65 " -k2=<filename>",
66 " Parametric k2 image is saved; in some situations perfusion calculation",
67 " from k2 can be more accurate than the default assumption of f=K1.",
68 " Perfusion can be calculated from k2 using equation f=k2*pH2O, where",
69 " pH2O is the physiological partition coefficient of water in tissue.",
70 " -Va=<filename>",
71 " Parametric Va image is saved.",
72 " Set -Va=0, if Va=0 is assumed (pre-corrected); otherwise Va is fitted.",
73 " -wss=<filename>",
74 " Weighted sum-of-squares are written in specified image file.",
75 " -thr=<threshold%>",
76 " Pixels with AUC less than (threshold/100 x input AUC) are set to zero;",
77 " default is 0%",
78 " -k2min=<Min k2> and -k2max=<Max k2>",
79 " Enter the minimum and maximum k2 in units 1/min, applying to decay",
80 " corrected data.",
81 " -fmin=<Min K1> and -fmax=<Max K1>",
82 " Enter the minimum and maximum perfusion value; defaults are",
83 " 0.005 and 4.0 mL/(mL*min), respectively.",
84 " -pmin=<Min p> and -pmax=<pmax>",
85 " Enter the minimum and maximum value for apparent partition coefficient",
86 " for water; defaults are 0.3 and 1.0 mL/mL, respectively.",
87 " -nr=<value>",
88 " Set number of basis functions; default is 500, minimum 100.",
89 " -bf=<filename>",
90 " Basis function curves are written in specified file.",
91 " -err=<filename>",
92 " Pixels with their k2 in its min or max value (calculated from min and",
93 " max K1 and p values) in the specified imagefile with values 1 and 2,",
94 " respectively, others with value 0.",
95 " -mask=<filename>",
96 " Only the masked pixels are processed. If output images exist, then",
97 " pixel values outside of mask are preserved.",
98 " -stdoptions", // List standard options like --help, -v, etc
99 " ",
100 "Example 1. Calculation of perfusion and arterial blood volume image,",
101 " stopping fit at 180 s:",
102 " @P -Va=s2345va.img s2345abfit.kbq s2345dy1.v 180 s2345flow.v",
103 " ",
104 "By default, the units of pixel values in the blood flow (K1) image is",
105 "(mL blood)/((mL tissue) * min), in Vd image (mL blood)/(mL tissue),",
106 "in k2 image 1/min, and in Va image (mL blood/mL tissue),",
107 "but the blood flow and Va units can be changed with above listed options.",
108 " ",
109 "References:",
110 "1. Lammertsma AA, Jones T. J Cereb Blood Flow Metab. 1983;3:416-424.",
111 "2. Koeppe RA et al. J Cereb Blood Flow metab. 1985;5:224-234.",
112 "3. Boellaard R et al. Mol Imaging Biol. 2005;7:273-285.",
113 "4. Ohta S, et al. J Cereb Blood Flow Metab. 1992;12:179-192.",
114 " ",
115 "See also: bfmh2o, imgflow, arlkup, fit_h2o, imgunit, fitdelay, imgcbv",
116 " ",
117 "Keywords: image, modelling, perfusion, radiowater, basis function method",
118 0};
119/*****************************************************************************/
120
121/*****************************************************************************/
122/* Turn on the globbing of the command line, since it is disabled by default in
123 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
124 In Unix&Linux wildcard command line processing is enabled by default. */
125/*
126#undef _CRT_glob
127#define _CRT_glob -1
128*/
129int _dowildcard = -1;
130/*****************************************************************************/
131
132/*****************************************************************************/
136int main(int argc, char **argv)
137{
138 int ai, help=0, version=0, verbose=1;
139 int fitdimt;
140 char inpfile[FILENAME_MAX], petfile[FILENAME_MAX], flowfile[FILENAME_MAX];
141 char vdfile[FILENAME_MAX], k2file[FILENAME_MAX], wssfile[FILENAME_MAX];
142 char errfile[FILENAME_MAX], bfsfile[FILENAME_MAX];
143 char vafile[FILENAME_MAX], maskfile[FILENAME_MAX], tmp[FILENAME_MAX+1], *cptr;
144 int bfNr=500, *bf_opt_nr;
145 float threshold, calcThreshold=0.0;
146 double fittime=0.0;
147 double flowMin=0.005, flowMax=4.0; // mL/(min*mL)
148 double pWaterMin=0.3, pWaterMax=1.0; // mL/mL
149 double k2min=-1.0, k2max=-1.0;
150 int ret, fi, pi, xi, yi;
151 int nosolution_nr=0, thresholded_nr=0;
152 clock_t fitStart, fitFinish;
153 int fitVa=1; // 1=fitted, 0=Vb assumed to be zero
154 int per_dl=0; // 0 or 1
155 int per_gram=0; // 0 or 1
156 double density=1.04;
157
158 DFT blood, tac, bf;
159 IMG img, flowimg, k2img, vdimg, vaimg, wssimg, errimg;
160
161
162 /*
163 * Get arguments
164 */
165 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
166 inpfile[0]=petfile[0]=flowfile[0]=vdfile[0]=k2file[0]=wssfile[0]=(char)0;
167 errfile[0]=bfsfile[0]=vafile[0]=maskfile[0]=(char)0;
168 /* Get options */
169 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
170 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
171 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
172 if(strcasecmp(cptr, "DL")==0) {
173 per_dl=1; continue;
174 } else if(strcasecmp(cptr, "ML")==0) {
175 per_dl=0; continue;
176 } else if(strcasecmp(cptr, "DENSITY")==0) {
177 per_gram=1; continue; /* if plain -density, then use default density */
178 } else if(strncasecmp(cptr, "DENSITY=", 8)==0) {
179 per_gram=1; density=atof_dpi(cptr+8); if(density>0.0) continue;
180 } else if(strncasecmp(cptr, "VA=", 3)==0 || strncasecmp(cptr, "VB=", 3)==0) {
181 strlcpy(vafile, cptr+3, FILENAME_MAX); if(strlen(vafile)) continue;
182 } else if(strncasecmp(cptr, "k2min=", 6)==0) {
183 if(atof_with_check(cptr+6, &k2min)==0 && k2min>=0.0) continue;
184 } else if(strncasecmp(cptr, "k2max=", 6)==0) {
185 if(atof_with_check(cptr+6, &k2max)==0 && k2max>=0.0) continue;
186 } else if(strncasecmp(cptr, "fmin=", 5)==0 && strlen(cptr)>5) {
187 if(atof_with_check(cptr+5, &flowMin)==0 && flowMin>=0.0) continue;
188 } else if(strncasecmp(cptr, "fmax=", 5)==0 && strlen(cptr)>5) {
189 if(atof_with_check(cptr+5, &flowMax)==0 && flowMax>=0.0) continue;
190 } else if(strncasecmp(cptr, "pmin=", 5)==0 && strlen(cptr)>5) {
191 if(atof_with_check(cptr+5, &pWaterMin)==0 && pWaterMin>0.0) continue;
192 } else if(strncasecmp(cptr, "pmax=", 5)==0 && strlen(cptr)>5) {
193 if(atof_with_check(cptr+5, &pWaterMax)==0 && pWaterMax<1.25) continue;
194 } else if(strncasecmp(cptr, "VD=", 3)==0) {
195 strlcpy(vdfile, cptr+3, FILENAME_MAX); if(strlen(vdfile)>1) continue;
196 } else if(strncasecmp(cptr, "k2=", 3)==0) {
197 strlcpy(k2file, cptr+3, FILENAME_MAX); if(strlen(k2file)>1) continue;
198 } else if(strncasecmp(cptr, "NR=", 3)==0) {
199 bfNr=atoi(cptr+3); if(bfNr>5E+04) bfNr=5E+04;
200 if(bfNr>=100) continue;
201 } else if(strncasecmp(cptr, "BF=", 3)==0) {
202 strlcpy(bfsfile, cptr+3, FILENAME_MAX); if(strlen(bfsfile)>0) continue;
203 } else if(strncasecmp(cptr, "WSS=", 4)==0) {
204 strlcpy(wssfile, cptr+4, FILENAME_MAX); if(strlen(wssfile)>0) continue;
205 } else if(strncasecmp(cptr, "ERR=", 4)==0) {
206 strlcpy(errfile, cptr+4, FILENAME_MAX); if(strlen(errfile)>0) continue;
207 } else if(strncasecmp(cptr, "THR=", 4)==0 && strlen(cptr)>4) {
208 cptr+=4; if(isdigit(*cptr) || *cptr=='+' || *cptr=='-') {
209 calcThreshold=0.01*atof_dpi(cptr);
210 if(calcThreshold>=0.0 && calcThreshold<=2.0) continue;
211 }
212 }
213 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
214 return(1);
215 } else break;
216
217 /* Print help or version? */
218 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
219 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
220 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
221
222 /* Process other arguments, starting from the first non-option */
223 if(ai<argc) strlcpy(inpfile, argv[ai++], FILENAME_MAX);
224 if(ai<argc) strlcpy(petfile, argv[ai++], FILENAME_MAX);
225 if(ai<argc) {
226 if(!atof_with_check(argv[ai], &fittime)) fittime/=60.0;
227 else {
228 fprintf(stderr, "Error: invalid fit time '%s'.\n", argv[ai]);
229 return(1);
230 }
231 ai++;
232 }
233 if(ai<argc) strlcpy(flowfile, argv[ai++], FILENAME_MAX);
234 if(ai<argc) {
235 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
236 return(1);
237 }
238 /* Did we get all the information that we need? */
239 if(!flowfile[0]) {
240 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
241 return(1);
242 }
243 if(fittime<=0.0) fittime=1.0E+020;
244 /* Check if Va=0 is assumed */
245 if(strcasecmp(vafile, "NONE")==0 || strcasecmp(vafile, "ZERO")==0 ||
246 strcasecmp(vafile, "0")==0) {vafile[0]=(char)0; fitVa=0;}
247 /* In verbose mode print arguments and options */
248 if(verbose>1) {
249 printf("petfile := %s\n", petfile);
250 printf("inpfile := %s\n", inpfile);
251 printf("flowfile := %s\n", flowfile);
252 if(vdfile[0]) printf("vdfile := %s\n", vdfile);
253 if(k2file[0]) printf("k2file := %s\n", k2file);
254 if(vafile[0]) printf("vafile := %s\n", vafile);
255 if(wssfile[0]) printf("wssfile := %s\n", wssfile);
256 if(errfile[0]) printf("errfile := %s\n", errfile);
257 if(bfsfile[0]) printf("bfsfile := %s\n", bfsfile);
258 if(maskfile[0]) printf("maskfile := %s\n", maskfile);
259 printf("fitVa := %d\n", fitVa);
260 printf("bfNr := %d\n", bfNr);
261 printf("per_dl := %d\n", per_dl);
262 printf("per_gram := %d\n", per_gram);
263 if(per_gram!=0) printf("density := %g\n", density);
264 printf("calcThreshold := %g\n", calcThreshold);
265 printf("requested_fittime := %g [min]\n", fittime);
266 if(k2min>0.0) printf("k2min := %g\n", k2min);
267 if(k2max>0.0) printf("k2max := %g\n", k2max);
268 printf("flowMax := %g\n", flowMax);
269 printf("flowMin := %g\n", flowMin);
270 printf("flowMax := %g\n", flowMax);
271 printf("pWaterMin := %g\n", pWaterMin);
272 printf("pWaterMax := %g\n", pWaterMax);
273 }
274 if(verbose>8) {IMG_TEST=verbose-8; SIF_TEST=verbose-8;} else IMG_TEST=SIF_TEST=0;
275
276 /* Check user-defined parameter ranges and calculate range of k2 */
277 if(flowMin>=flowMax) {
278 fprintf(stderr, "Error: invalid range for perfusion (%g - %g).\n",
279 flowMin, flowMax);
280 return(1);
281 }
282 if(pWaterMin>=pWaterMax) {
283 fprintf(stderr, "Error: invalid range for p (%g - %g).\n",
284 pWaterMin, pWaterMax);
285 return(1);
286 }
287 if(k2min<=0.) {
288 k2min=flowMin/pWaterMax;
289 if(verbose>1) printf("k2min := %g [1/min]\n", k2min);
290 }
291 if(k2max<=0.) {
292 k2max=flowMax/pWaterMin;
293 if(verbose>1) printf("k2max := %g [1/min]\n", k2max);
294 }
295 if(k2max<=k2min || k2min<=0.) {
296 fprintf(stderr, "Error: invalid range for k2 (%g - %g).\n", k2min, k2max);
297 return(1);
298 }
299
300
301 /*
302 * Read PET image and input TAC
303 */
304 if(verbose>1) printf("reading data files\n");
305 dftInit(&blood); dftInit(&tac); imgInit(&img);
307 petfile, NULL, inpfile, NULL, NULL, &fittime, &fitdimt, &img,
308 &blood, &tac, 1, stdout, verbose-2, tmp);
309 if(ret!=0) {
310 fprintf(stderr, "Error: %s.\n", tmp);
311 if(verbose>1) printf(" ret := %d\n", ret);
312 return(2);
313 }
314 if(imgNaNs(&img, 1)>0)
315 if(verbose>0) fprintf(stderr, "Warning: missing pixel values.\n");
316 //printf("last x2 = %g\n", tac.x2[tac.frameNr-1]);
317 /* Set time unit to min */
318 dftTimeunitConversion(&blood, TUNIT_MIN);
319 dftTimeunitConversion(&tac, TUNIT_MIN);
320 /* Check that the image is dynamic */
321 if(fitdimt<3) {
322 fprintf(stderr, "Error: too few time frames for fitting.\n");
323 if(verbose>0) imgInfo(&img);
324 imgEmpty(&img); dftEmpty(&blood); dftEmpty(&tac); return(2);
325 }
326 /* Allocate memory for tissue data and integrals */
327 ret=dftAddmem(&tac, 1);
328 if(ret!=0) {
329 fprintf(stderr, "Error (%d) in allocating memory.\n", ret); fflush(stderr);
330 imgEmpty(&img); dftEmpty(&blood); dftEmpty(&tac); return(3);
331 }
332 strcpy(tac.voi[0].voiname, "input");
333 strcpy(tac.voi[1].voiname, "tissue");
334 if(verbose>1) {
335 printf("fittimeFinal := %g min\n", fittime);
336 printf("fitdimt := %d\n", fitdimt);
337 fflush(stdout);
338 }
339
340 /* Determine the threshold */
341 if(verbose>50) dftPrint(&tac);
342 if(verbose>2) printf("input_AUC[%g] := %g\n", tac.x2[tac.frameNr-1], tac.voi[0].y2[tac.frameNr-1]);
343 threshold=calcThreshold*tac.voi[0].y2[tac.frameNr-1]/60.0;
344 if(verbose>2) {printf("threshold_AUC = %g\n", threshold); fflush(stdout);}
345
346
347 /*
348 * Determine the weights for the fit
349 */
350 if(verbose>1) printf("setting weights\n");
351 ret=imgSetWeights(&img, 0, verbose-5);
352 if(ret) {
353 fprintf(stderr, "Warning: cannot calculate weights.\n"); fflush(stderr);
354 /* set weights to 1 */
355 for(fi=0; fi<img.dimt; fi++) img.weight[fi]=1.0;
356 img.isWeight=1;
357 }
358
359
360 /*
361 * Calculate the basis functions
362 */
363 if(verbose>1) {fprintf(stdout, "calculating basis functions\n"); fflush(stdout);}
364 dftInit(&bf);
365 ai=tac.frameNr; tac.frameNr=fitdimt;
366 ret=bfRadiowater(&blood, &tac, &bf, bfNr, k2min, k2max, tmp, verbose-2);
367 //ret=bfRadiowater(&blood, &tac, &bf, bfNr, k2min, k2max, tmp, 10);
368 tac.frameNr=ai;
369 if(ret) {
370 fprintf(stderr, "Error: cannot calculate basis functions (%d).\n", ret); fflush(stderr);
371 imgEmpty(&img); dftEmpty(&blood); dftEmpty(&tac); return(6);
372 }
373 /* Original sampling blood data not needed any more */
374 dftEmpty(&blood);
375 // Note that basis functions are to be saved later (in bfsfile),
376 // after it is known in how many image pixels each basis function was found
377 // to give the best fit
378
379
380 /*
381 * Allocate result images and fill the header info
382 */
383 if(verbose>1) {printf("allocating memory for parametric images\n"); fflush(stdout);}
384 imgInit(&flowimg); imgInit(&k2img); imgInit(&vdimg); imgInit(&vaimg);
385 imgInit(&wssimg); imgInit(&errimg);
386 ret=imgAllocateWithHeader(&flowimg, img.dimz, img.dimy, img.dimx, 1, &img);
387 if(ret!=0) {
388 fprintf(stderr, "Error: out of memory.\n");
389 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&bf);
390 return(8);
391 }
392 flowimg.start[0]=0.0; flowimg.end[0]=fittime*60.0;
394 flowimg.isWeight=0;
395 flowimg.unit=IMGUNIT_ML_PER_ML_PER_MIN;
396 if(k2file[0]) {
397 ret=imgAllocateWithHeader(&k2img, img.dimz,img.dimy,img.dimx, 1, &flowimg);
398 k2img.unit=IMGUNIT_PER_MIN;
399 }
400 if(ret==0 && vdfile[0]) {
401 ret=imgAllocateWithHeader(&vdimg, img.dimz,img.dimy,img.dimx, 1, &flowimg);
402 vdimg.unit=IMGUNIT_UNITLESS;
403 }
404 if(ret==0 && vafile[0]) {
405 ret=imgAllocateWithHeader(&vaimg, img.dimz,img.dimy,img.dimx, 1, &flowimg);
406 vaimg.unit=IMGUNIT_UNITLESS;
407 }
408 if(ret==0 && wssfile[0]) {
409 ret=imgAllocateWithHeader(&wssimg, img.dimz,img.dimy,img.dimx, 1, &flowimg);
410 wssimg.unit=IMGUNIT_UNITLESS;
411 }
412 if(ret==0 && errfile[0]) {
413 ret=imgAllocateWithHeader(&errimg, img.dimz,img.dimy,img.dimx, 1, &flowimg);
414 errimg.unit=IMGUNIT_UNITLESS;
415 }
416 if(ret) {
417 fprintf(stderr, "Error: out of memory.\n"); fflush(stderr);
418 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&bf);
419 imgEmpty(&flowimg); imgEmpty(&k2img); imgEmpty(&vaimg); imgEmpty(&vdimg);
420 imgEmpty(&wssimg); imgEmpty(&errimg);
421 return(8);
422 }
423
424
425#if(0)
426 /*
427 * Allocate memory for QR
428 */
429 if(verbose>1) printf("allocating memory for QR\n");
430 int colNr, rowNr;
431 colNr=2; if(fitVa==0) colNr--;
432 rowNr=bf.frameNr;
433 if(verbose>2) {
434 printf("QR_colNr := %d\n", colNr);
435 printf("QR_rowNr := %d\n", rowNr);
436 }
437 double *buf, **mat, *rhs, *sol, r2;
438 buf=(double*)calloc(colNr*rowNr+rowNr+colNr, sizeof(double));
439 mat=(double**)calloc(rowNr, sizeof(double*));
440 if(buf==NULL || mat==NULL) {
441 fprintf(stderr, "Error: cannot allocate memory for QR\n");
442 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&bf);
443 imgEmpty(&flowimg); imgEmpty(&k2img); imgEmpty(&vaimg); imgEmpty(&vdimg);
444 imgEmpty(&wssimg); imgEmpty(&errimg);
445 return(8);
446 }
447 for(int i=0; i<rowNr; i++) mat[i]=buf+(i*colNr);
448 rhs=buf+(rowNr*colNr); sol=buf+(rowNr*colNr+rowNr);
449
450
451 /*
452 * Compute pixel-by-pixel
453 */
454 if(verbose>0) {fprintf(stdout, "computing QR pixel-by-pixel\n"); fflush(stdout);}
455 thresholded_nr=0; nosolution_nr=0;
456 double *ct, *cti;
457 ct=tac.voi[1].y; cti=tac.voi[1].y2;
458 /* Allocate memory for BF counters on how often each BF is
459 found to provide the optimal fit */
460 bf_opt_nr=(int*)malloc(bfNr*sizeof(int));
461 for(int bi=0; bi<bf.voiNr; bi++) bf_opt_nr[bi]=0.0;
462 fitStart=clock();
463 for(pi=0; pi<img.dimz; pi++) {
464 if(verbose>6) printf("computing plane %d\n", img.planeNumber[pi]);
465 else if(img.dimz>1 && verbose>0) {fprintf(stdout, "."); fflush(stdout);}
466 for(yi=0; yi<img.dimy; yi++) {
467 if(verbose>6 && yi==4*img.dimy/10) printf(" computing row %d\n", yi+1);
468 for(xi=0; xi<img.dimx; xi++) {
469 if(verbose>6 && yi==4*img.dimy/10 && xi==4*img.dimx/10)
470 printf(" computing column %d\n", xi+1);
471
472 /* if end AUC is less than threshold value, then set values to 0 */
473 /* Calculate pixel integral */
474 for(int i=0; i<rowNr; i++) {ct[i]=img.m[pi][yi][xi][i];}
475 ret=petintegral(tac.x1, tac.x2, ct, rowNr, cti, NULL);
476 if(cti[rowNr-1]<threshold) {
477 flowimg.m[pi][yi][xi][0]=0.0;
478 if(k2file[0]) k2img.m[pi][yi][xi][0]=0.0;
479 if(vafile[0]) vaimg.m[pi][yi][xi][0]=0.0;
480 if(vdfile[0]) vdimg.m[pi][yi][xi][0]=0.0;
481 if(wssfile[0]) wssimg.m[pi][yi][xi][0]=0.0;
482 if(errfile[0]) errimg.m[pi][yi][xi][0]=0.0;
483 thresholded_nr++;
484 continue;
485 }
486
487 /* Go through all basis functions */
488 int bi_min=-1; double r2_min=nan("");
489 for(int bi=0; bi<bf.voiNr; bi++) {
490
491 /* Initiate matrix */
492 for(int j=0; j<rowNr; j++) {
493 mat[j][0]=bf.voi[bi].y[j];
494 if(colNr>1) mat[j][1]=tac.voi[0].y[j];
495 rhs[j]=ct[j];
496 }
497
498 /* Compute QR */
499 if(qrLSQ(mat, rhs, sol, rowNr, colNr, &r2)!=0) continue;
500 /* Check if this was best fit for now */
501 if(isnan(r2_min) || r2_min>r2) {
502 r2_min=r2; bi_min=bi;
503 flowimg.m[pi][yi][xi][0]=sol[0];
504 if(vafile[0] && colNr>1) vaimg.m[pi][yi][xi][0]=sol[1];
505 }
506 } /* next basis function */
507 if(isnan(r2_min)) {nosolution_nr++; continue;}
508 else bf_opt_nr[bi_min]+=1;
509 /* Put results to output images */
510 if(flowimg.m[pi][yi][xi][0]>flowMax) flowimg.m[pi][yi][xi][0]=flowMax;
511 else if(flowimg.m[pi][yi][xi][0]<0.0) flowimg.m[pi][yi][xi][0]=0.0;
512 if(k2file[0]) {
513 k2img.m[pi][yi][xi][0]=bf.voi[bi_min].size;
514 if(k2img.m[pi][yi][xi][0]>k2max) k2img.m[pi][yi][xi][0]=k2max;
515 }
516 if(vdfile[0]) {
517 double f=flowimg.m[pi][yi][xi][0]/k2img.m[pi][yi][xi][0];
518 if(f>pWaterMax) f=pWaterMax;
519 vdimg.m[pi][yi][xi][0]=f;
520 }
521 if(wssfile[0]) wssimg.m[pi][yi][xi][0]=r2_min;
522 } /* next column */
523 } /* next row */
524 } /* next plane */
525 fitFinish=clock();
526 if(verbose>0) {fprintf(stdout, "done.\n"); fflush(stdout);}
527
528#else
529
530 /*
531 * Allocate memory for QR
532 */
533 if(verbose>1) {fprintf(stdout, "allocating memory for QR\n"); fflush(stdout);}
534 int m, n, M, N;
535 int bi, bi_min;
536 double rnorm_min, wss, f;
537 double p1, p2, p3;
538 double **mem, **A, *B, X[MAX_N], *tau, *residual, RNORM, *chain;
539 double *qrweight, **wws, *ws, *wwschain, *ct, *cti;
540
541 M=bf.frameNr; N=2; if(fitVa==0) N--;
542 chain=(double*)malloc((M+1)*N*bf.voiNr * sizeof(double));
543 mem=(double**)malloc(bf.voiNr * sizeof(double*));
544 A=(double**)malloc(M * sizeof(double*));
545 B=(double*)malloc(M*sizeof(double));
546 residual=(double*)malloc(M*sizeof(double));
547 qrweight=(double*)malloc(M*sizeof(double));
548 wwschain=(double*)malloc((M*N+2*M)*sizeof(double));
549 wws=(double**)malloc(M * sizeof(double*));
550 if(chain==NULL || B==NULL || A==NULL || residual==NULL || qrweight==NULL || wwschain==NULL || wws==NULL)
551 {
552 fprintf(stderr, "Error: out of memory.\n"); fflush(stderr);
553 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&bf);
554 imgEmpty(&flowimg); imgEmpty(&k2img); imgEmpty(&vaimg); imgEmpty(&vdimg);
555 imgEmpty(&wssimg); imgEmpty(&errimg);
556 return(8);
557 }
558 for(bi=0; bi<bf.voiNr; bi++) mem[bi]=chain+bi*(M+1)*N;
559 for(m=0; m<M; m++) wws[m]=wwschain+m*N;
560 ws=wwschain+M*N;
561
562 /* Pre-compute QR weights for faster execution */
563 for(m=0; m<M; m++) {
564 if(img.weight[m]<=1.0e-20) qrweight[m]=0.0;
565 else qrweight[m]=sqrt(img.weight[m]);
566 }
567
568 /* Make A matrix, and QR decomposition for it, for all pixels
569 beforehand for faster execution */
570 if(verbose>1) {fprintf(stdout, "calculating QR decomposition\n"); fflush(stdout);}
571 for(bi=0; bi<bf.voiNr; bi++) {
572
573 /* Define memory site for coefficient matrix and vector tau */
574 for(m=0; m<M; m++) A[m]=mem[bi]+m*N;
575 tau=mem[bi]+M*N;
576
577 /* Initiate matrix (A = mem[bi]) */
578 for(m=0; m<M; m++) {
579 A[m][0]=bf.voi[bi].y[m];
580 if(N>1) A[m][1]=tac.voi[0].y[m]; // blood TAC for Va estimation
581 }
582
583 /* Apply data weights */
584 for(m=0; m<M; m++) for(n=0; n<N; n++) A[m][n]*=qrweight[m];
585
586 /* Compute QR decomposition of the coefficient matrix */
587 ret=qr_decomp(A, M, N, tau, wws, ws);
588
589 if(ret>0) { /* Decomposition failed */
590 free(chain); free(B); free(residual);
591 free(A); free(wwschain); free(wws); free(qrweight); free(mem);
592 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&bf);
593 imgEmpty(&flowimg); imgEmpty(&k2img); imgEmpty(&vaimg); imgEmpty(&vdimg);
594 imgEmpty(&wssimg); imgEmpty(&errimg);
595 return (9);
596 }
597 } /* next BF */
598
599
600 /*
601 * Compute pixel-by-pixel
602 */
603 if(verbose>0) {fprintf(stdout, "computing QR pixel-by-pixel\n"); fflush(stdout);}
604 thresholded_nr=0; nosolution_nr=0;
605 ct=tac.voi[1].y; cti=tac.voi[1].y2;
606 /* Allocate memory for BF counters on how often each BF is
607 found to provide the optimal fit */
608 bf_opt_nr=(int*)malloc(bfNr*sizeof(int));
609 for(bi=0; bi<bf.voiNr; bi++) bf_opt_nr[bi]=0.0;
610 fitStart=clock();
611 for(pi=0; pi<img.dimz; pi++) {
612 if(verbose>6) printf("computing plane %d\n", img.planeNumber[pi]);
613 else if(img.dimz>1 && verbose>0) {fprintf(stdout, "."); fflush(stdout);}
614 for(yi=0; yi<img.dimy; yi++) {
615 if(verbose>6 && yi==4*img.dimy/10) printf(" computing row %d\n", yi+1);
616 for(xi=0; xi<img.dimx; xi++) {
617 if(verbose>6 && yi==4*img.dimy/10 && xi==4*img.dimx/10)
618 printf(" computing column %d\n", xi+1);
619
620 /* if end AUC is less than threshold value, then set values to 0 */
621 /* Calculate pixel integral */
622 for(m=0; m<M; m++) {ct[m]=img.m[pi][yi][xi][m];}
623 ret=petintegral(tac.x1, tac.x2, ct, tac.frameNr, cti, NULL);
624//if(verbose>6 && yi==4*img.dimy/10 && xi==4*img.dimx/10)
625//printf("last x2 = %g\n", tac.x2[tac.frameNr-1]);
626 if(verbose>6 && yi==4*img.dimy/10 && xi==4*img.dimx/10) {
627 printf(" Pixel (%d,%d,%d), int= %f, threshold= %g\n", pi,yi,xi,cti[M-1],threshold);
628 if(verbose>7) {
629 for(m=0; m<M; m++)
630 printf(" %02d:\t%g\t%g\n", m, img.m[pi][yi][xi][m], tac.voi[0].y[m]);
631 }
632 }
633 if(cti[M-1]<threshold) {
634 flowimg.m[pi][yi][xi][0]=0.0;
635 if(k2file[0]) k2img.m[pi][yi][xi][0]=0.0;
636 if(vafile[0]) vaimg.m[pi][yi][xi][0]=0.0;
637 if(vdfile[0]) vdimg.m[pi][yi][xi][0]=0.0;
638 if(wssfile[0]) wssimg.m[pi][yi][xi][0]=0.0;
639 if(errfile[0]) errimg.m[pi][yi][xi][0]=0.0;
640 thresholded_nr++;
641 continue;
642 }
643
644 /* Go through all basis functions */
645 bi_min=-1; rnorm_min=1.0E80; p1=p2=p3=0.0;
646 for(bi=0; bi<bf.voiNr; bi++) {
647
648 /* Define memory site for present coefficient matrix and vector tau */
649 for(m=0; m<M; m++) {A[m]=mem[bi]+ m*N;}
650 tau=mem[bi]+M*N;
651
652 /* Get data vector */
653 for(m=0; m<M; m++) {
654 B[m]=img.m[pi][yi][xi][m];
655 /* Apply data weights */
656 B[m]*=qrweight[m];
657 }
658
659 /* Compute solution */
660 ret=qr_solve(A, M, N, tau, B, X, residual, &RNORM, wws, ws);
661 if(ret>0) { /* no solution is possible */
662 for(n=0; n<N; n++) X[n]=0.0;
663 RNORM=1.0E80;
664 }
665 /* Check if this was best fit for now */
666 if(RNORM<rnorm_min) {
667 rnorm_min=RNORM; bi_min=bi;
668 /* K1 */ p1=X[0];
669 /* Va */ if(N>1) p2=X[1]; else p2=0.0;
670 /* k2 */ p3=bf.voi[bi_min].size;
671 }
672 } /* next basis function */
673 if(rnorm_min>=1.0E60) nosolution_nr++;
674 else bf_opt_nr[bi_min]+=1;
675
676 if(verbose>6 && yi==4*img.dimy/10 && xi==4*img.dimx/10) {
677 printf(" Pixel (%d,%d,%d), K1=%g Va=%g k2=%g\n", pi, yi, xi, p1, p2, p3);
678 if(verbose>10) dftPrint(&tac);
679 }
680
681 /* Calculate WSS */
682 for(m=0, wss=0.0; m<M; m++) {
683 f=p1*bf.voi[bi_min].y[m];
684 if(N>1) f+=p2*tac.voi[0].y[m];
685 f-=img.m[pi][yi][xi][m];
686 wss+=img.weight[m]*f*f;
687 }
688 /* Put results to output images */
689 if(p1>flowMax) flowimg.m[pi][yi][xi][0]=flowMax;
690 else if(p1<0.0) flowimg.m[pi][yi][xi][0]=0.0;
691 else flowimg.m[pi][yi][xi][0]=p1;
692 if(vafile[0]) vaimg.m[pi][yi][xi][0]=p2;
693 if(k2file[0]) {
694 if(p3>k2max) k2img.m[pi][yi][xi][0]=k2max;
695 else k2img.m[pi][yi][xi][0]=p3;
696 }
697 if(vdfile[0]) {
698 f=p1/p3; if(f>pWaterMax) f=pWaterMax;
699 vdimg.m[pi][yi][xi][0]=f;
700 }
701 if(wssfile[0]) wssimg.m[pi][yi][xi][0]=wss;
702 if(errfile[0]) {
703 if(bi_min==0) ret=1; else if(bi_min==bf.voiNr-1) ret=2; else ret=0;
704 errimg.m[pi][yi][xi][0]=(float)ret;
705 }
706 } /* next column */
707 } /* next row */
708 } /* next plane */
709 fitFinish=clock();
710 if(verbose>0) {fprintf(stdout, "done.\n"); fflush(stdout);}
711 if(verbose>1 || thresholded_nr>0) {
712 double f;
713 f=(double)thresholded_nr/((double)(flowimg.dimx*flowimg.dimy*flowimg.dimz));
714 f*=100.; if(f<3.0) printf("%g%%", f); else printf("%.0f%%", f);
715 printf(" of pixels were not fitted due to threshold.\n");
716 if(verbose>2) printf("thresholded %d pixels\n", thresholded_nr);
717 fflush(stdout);
718 }
719 if(verbose>1 || nosolution_nr>0) {
720 fprintf(stdout, "no QR solution for %d pixels.\n", nosolution_nr); fflush(stdout);}
721
722 /* No need for dynamic image or input tac anymore */
723 imgEmpty(&img); dftEmpty(&tac);
724 /* Free memory of QR */
725 free(chain); free(B); free(residual); free(A); free(wwschain);
726 free(wws); free(qrweight); free(mem);
727
728#endif
729
730
731 /*
732 * Save basis functions if required;
733 * this is done not before, so that also the number of optimal fits
734 * achieved with each BF can be saved as the "size".
735 */
736 if(bfsfile[0]) {
737 for(int bi=0; bi<bf.voiNr; bi++) sprintf(bf.voi[bi].place, "%d", bf_opt_nr[bi]);
738 if(dftWrite(&bf, bfsfile)) {
739 fprintf(stderr, "Error in writing %s: %s\n", bfsfile, dfterrmsg); fflush(stderr);
740 imgEmpty(&flowimg); imgEmpty(&k2img); imgEmpty(&vaimg); imgEmpty(&vdimg);
741 imgEmpty(&wssimg); imgEmpty(&errimg);
742 dftEmpty(&bf); free(bf_opt_nr); return(11);
743 }
744 if(verbose>0) {fprintf(stdout, "basis functions were written in %s\n", bfsfile); fflush(stdout);}
745 }
746
747 /* No need for basis functions anymore */
748 dftEmpty(&bf); free(bf_opt_nr);
749
750
751 /*
752 * Convert units to per dL and/or gram if necessary
753 */
754 if(per_dl || per_gram) {
755 /* K1 */
756 float f;
757 if(per_dl && per_gram) {
758 f=100.0/density; flowimg.unit=IMGUNIT_ML_PER_DL_PER_MIN;
759 } else if(per_dl) {
760 f=100.0; flowimg.unit=IMGUNIT_ML_PER_DL_PER_MIN;
761 } else if(per_gram) {
762 f=1.0/density; flowimg.unit=IMGUNIT_ML_PER_ML_PER_MIN;
763 } else
764 f=1.0;
765 ret=imgArithmConst(&flowimg, f, '*', 1.0E+06, verbose-6);
766 }
767 if(per_dl && vafile[0]) {
768 /* Va */
769 vaimg.unit=IMGUNIT_ML_PER_DL;
770 ret=imgArithmConst(&vaimg, 100.0, '*', 1.0E+06, verbose-6);
771 }
772
773 /*
774 * Save parametric image(s)
775 */
776 if(verbose>1) {printf("Saving parametric images\n"); fflush(stdout);}
777 {
778 ret=imgWrite(flowfile, &flowimg);
779 if(ret) {fprintf(stderr, "Error: %s\n", flowimg.statmsg); fflush(stderr);}
780 else if(verbose>0) {fprintf(stdout, "Flow image %s saved.\n", flowfile); fflush(stdout);}
781 }
782 if(ret==0 && vafile[0]) {
783 ret=imgWrite(vafile, &vaimg);
784 if(ret) {fprintf(stderr, "Error: %s\n", vaimg.statmsg); fflush(stderr);}
785 else if(verbose>0) {fprintf(stdout, "Va image %s saved.\n", vafile); fflush(stdout);}
786 }
787 if(ret==0 && vdfile[0]) {
788 ret=imgWrite(vdfile, &vdimg);
789 if(ret) {fprintf(stderr, "Error: %s\n", vdimg.statmsg); fflush(stderr);}
790 else if(verbose>0) {fprintf(stdout, "Vd image %s saved.\n", vdfile); fflush(stdout);}
791 }
792 if(ret==0 && k2file[0]) {
793 ret=imgWrite(k2file, &k2img);
794 if(ret) {fprintf(stderr, "Error: %s\n", k2img.statmsg); fflush(stderr);}
795 else if(verbose>0) {fprintf(stdout, "k2 image %s saved.\n", k2file); fflush(stdout);}
796 }
797 if(ret==0 && wssfile[0]) {
798 ret=imgWrite(wssfile, &wssimg);
799 if(ret) {fprintf(stderr, "Error: %s\n", wssimg.statmsg); fflush(stderr);}
800 else if(verbose>0) {fprintf(stdout, "WSS image %s saved.\n", wssfile); fflush(stdout);}
801 }
802 if(ret==0 && errfile[0]) {
803 ret=imgWrite(errfile, &errimg);
804 if(ret) {fprintf(stderr, "Error: %s\n", errimg.statmsg); fflush(stderr);}
805 else if(verbose>0) {fprintf(stdout, "Error image %s saved.\n", errfile); fflush(stdout);}
806 }
807
808 imgEmpty(&flowimg); imgEmpty(&k2img); imgEmpty(&vaimg); imgEmpty(&vdimg);
809 imgEmpty(&wssimg); imgEmpty(&errimg);
810
811 /* How long did the fitting take */
812 if(verbose>0) fprintf(stdout, "parameter estimation time := %.1f [s]\n",
813 (double)(fitFinish-fitStart) / CLOCKS_PER_SEC );
814
815 return(ret);
816}
817/*****************************************************************************/
818
819/*****************************************************************************/
int bfRadiowater(DFT *input, DFT *tissue, DFT *bf, int bfNr, double k2min, double k2max, char *status, int verbose)
Definition bf_model.c:77
int atof_with_check(char *double_as_string, double *result_value)
Definition decpoint.c:107
double atof_dpi(char *str)
Definition decpoint.c:59
void dftInit(DFT *data)
Definition dft.c:38
char dfterrmsg[64]
Definition dft.c:6
int dftAddmem(DFT *dft, int voiNr)
Definition dft.c:107
void dftEmpty(DFT *data)
Definition dft.c:20
void dftPrint(DFT *data)
Definition dftio.c:538
int dftWrite(DFT *data, char *filename)
Definition dftio.c:594
int dftTimeunitConversion(DFT *dft, int tunit)
Definition dftunit.c:119
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 imgArithmConst(IMG *img, float operand, char operation, float ulimit, int verbose)
Definition imgarithm.c:100
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
Header file for libtpccurveio.
Header file for libtpcimgio.
int SIF_TEST
Definition sif.c:6
#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 qrLSQ(double **mat, double *rhs, double *sol, const unsigned int rows, const unsigned int cols, double *r2)
QR least-squares solving routine.
Definition qr.c:54
int qr_solve(double **QR, int M, int N, double *tau, double *b, double *x, double *residual, double *resNorm, double **cchain, double *chain)
Definition qr.c:492
int qr_decomp(double **a, int M, int N, double *tau, double **cchain, double *chain)
Definition qr.c:427
Header file for libtpcmodext.
int imgSetWeights(IMG *img, int wmet, int verbose)
Voi * voi
double * x1
int voiNr
double * x2
int frameNr
unsigned short int dimx
float **** m
char decayCorrection
char unit
unsigned short int dimt
int * planeNumber
float * weight
float * start
unsigned short int dimz
unsigned short int dimy
float * end
const char * statmsg
char isWeight
double size
double * y2
char voiname[MAX_REGIONSUBNAME_LEN+1]
double * y
char place[MAX_REGIONSUBNAME_LEN+1]