TPCCLIB
Loading...
Searching...
No Matches
imgbfk2.c
Go to the documentation of this file.
1
7/*****************************************************************************/
8#include "tpcclibConfig.h"
9/*****************************************************************************/
10#include <stdio.h>
11#include <stdlib.h>
12#include <unistd.h>
13#include <string.h>
14#include <math.h>
15#include <time.h>
16/*****************************************************************************/
17#include "libtpccurveio.h"
18#include "libtpcmodext.h"
19#include "libtpcmisc.h"
20#include "libtpcmodel.h"
21#include "libtpcimgio.h"
22#include "libtpcimgp.h"
23/*****************************************************************************/
24#define MAX_N 2
25/*****************************************************************************/
26
27/*****************************************************************************/
28static char *info[] = {
29 "Estimation of rate constants K1, k2, and Vb from dynamic PET image in",
30 "ECAT 6.3, ECAT 7.x, NIfTI-1, or Analyze 7.5 file format using",
31 "one-tissue compartment model, solved using the basis function approach.",
32 "The radioactivity concentration of each voxel is assumed to consist of",
33 "tissue (Ct) and blood (Cb):",
34 " Cpet(t) = Ct(t) + Vb*Cb",
35 "and differential equation for Ct is:",
36 " dCt(t)/dt = K1*Cp(t) - k2*Ct(t)",
37 ", where Cp(t) is arterial plasma concentration curve.",
38 "Arterial plasma and blood data must be corrected for decay and delay.",
39 "Dynamic PET image must be corrected for decay.",
40 "Fit time must be given in seconds.",
41 " ",
42 "Usage: @P [Options] ptacfile btacfile imgfile fittime K1file [k2file]",
43 " ",
44 "Options:",
45 " -Vt=<filename>",
46 " Parametric K1/k2 (Vt) image is saved.",
47 " -Vb=<filename>",
48 " Parametric Vb image is saved.",
49 " -k2min=<Min k2> and -k2max=<Max k2>",
50 " Enter the minimum and maximum k2 in units 1/min, applying to decay",
51 " corrected data. Defaults are k2min=0 and k2max=10, respectively.",
52 " -K1min=<Min K1> and -K1max=<Max K1>",
53 " Enter the minimum and maximum K1 value; defaults are",
54 " 0 and 10 mL/(mL*min), respectively.",
55 " -Vtmin=<Min Vt> and -Vtmax=<Max Vt>",
56 " Enter the minimum and maximum value for apparent Vt (K1/k2);",
57 " defaults are 0 and 10 mL/mL, respectively.",
58 " -Vbmin=<Min Vb> and -Vbmax=<Max Vb>",
59 " Enter the minimum and maximum value for Vb (volume fraction);",
60 " defaults are 0 and 1 mL/mL, respectively.",
61 " -nr=<value>",
62 " Set number of basis functions; default is 500, minimum 100.",
63 " -wss=<filename>",
64 " Weighted sum-of-squares are written in specified image file.",
65 " -thr=<threshold%>",
66 " Pixels with AUC less than (threshold/100 x input AUC) are set to zero;",
67 " default is 0%",
68 " -bf=<filename>",
69 " Basis function curves are written in specified file.",
70 " -err=<filename>",
71 " Excluded voxels and voxels with any of the result parameters at either",
72 " of its limits is written in the specified image file with value 1,",
73 " otherwise with value 0.",
74 " -stdoptions", // List standard options like --help, -v, etc
75 " ",
76 "Example 1. Calculation of K1 and k2 images using 0-20 min of",
77 "the dynamic image:",
78 " @P P223344apc.bld P223344ab.bld P223344.nii 20 P223344k1.nii P223344k2.nii",
79 " ",
80 "By default, the units of voxel values in the K1 image are",
81 "(mL plasma)/((mL tissue) * min), in k2 image 1/min, in Vb image",
82 "mL blood/mL tissue), and in Vt image (mL plasma)/(mL tissue),",
83 " ",
84 "See also: imgbfh2o, fitk2, imgunit, fitdelay, imgcbv",
85 " ",
86 "Keywords: image, modelling, compartmental model, basis function method",
87 0};
88/*****************************************************************************/
89
90/*****************************************************************************/
91/* Turn on the globbing of the command line, since it is disabled by default in
92 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
93 In Unix&Linux wildcard command line processing is enabled by default. */
94/*
95#undef _CRT_glob
96#define _CRT_glob -1
97*/
98int _dowildcard = -1;
99
100
101/*****************************************************************************/
102
103/*****************************************************************************/
108int bf1TCM(
110 DFT *input,
113 DFT *tissue,
115 DFT *bf,
117 int bfNr,
119 double k2min,
121 double k2max,
124 char *status,
126 int verbose
127) {
128 if(verbose>0)
129 printf("\n%s(*inp, *tis, *bf, %d, %g, %g, status, %d)\n", __func__, bfNr, k2min, k2max, verbose);
130
131 /* Check the parameters */
132 if(input==NULL || tissue==NULL || bf==NULL) {
133 if(status!=NULL) strcpy(status, "program error");
134 return(1);
135 }
136 if(input->frameNr<3 || input->voiNr<1) {
137 if(status!=NULL) strcpy(status, "no input data");
138 return(2);
139 }
140 if(tissue->frameNr<1) {
141 if(status!=NULL) strcpy(status, "no pet data");
142 return(3);
143 }
144 if(input->timeunit!=tissue->timeunit) {
145 if(status!=NULL) strcpy(status, "invalid time units");
146 return(4);
147 }
148 if(bfNr<2) {
149 if(status!=NULL) strcpy(status, "invalid nr of basis functions");
150 return(5);
151 }
152 if(k2min<1.0E-10) k2min=1.0E-10; // range calculation does not work otherwise
153 if(k2min>=k2max || k2min<0.0) {
154 if(status!=NULL) strcpy(status, "invalid k2 range");
155 return(6);
156 }
157 if(verbose>1) {
158 printf("input timerange: %g - %g\n", input->x[0], input->x[input->frameNr-1]);
159 printf("tissue timerange: %g - %g\n", tissue->x[0], tissue->x[tissue->frameNr-1]);
160 }
161
162 int bi, fi, ret;
163 double a, b, c;
164
165 /* Allocate memory for basis functions */
166 if(verbose>1) printf("allocating memory for basis functions\n");
167 ret=dftSetmem(bf, tissue->frameNr, bfNr);
168 if(ret) {
169 if(status!=NULL) strcpy(status, "out of memory");
170 return(10);
171 }
172
173 /* Copy and set information fields */
174 bf->voiNr=bfNr; bf->frameNr=tissue->frameNr;
175 bf->_type=tissue->_type;
176 dftCopymainhdr2(tissue, bf, 1);
177 for(bi=0; bi<bf->voiNr; bi++) {
178 snprintf(bf->voi[bi].voiname, 6, "B%5.5d", bi+1);
179 strcpy(bf->voi[bi].hemisphere, ".");
180 strcpy(bf->voi[bi].place, ".");
181 strcpy(bf->voi[bi].name, bf->voi[bi].voiname);
182 }
183 for(fi=0; fi<bf->frameNr; fi++) {
184 bf->x[fi]=tissue->x[fi];
185 bf->x1[fi]=tissue->x1[fi];
186 bf->x2[fi]=tissue->x2[fi];
187 }
188
189 /* Compute the range of k2 values to size fields */
190 if(verbose>1) printf("computing k2 values\n");
191 a=log10(k2min); b=log10(k2max); c=(b-a)/(double)(bfNr-1);
192 if(verbose>20) printf("a=%g b=%g, c=%g\n", a, b, c);
193 for(bi=0; bi<bf->voiNr; bi++) {
194 bf->voi[bi].size=pow(10.0, (double)bi*c+a);
195 }
196 if(verbose>2) {
197 printf("final BF k2 range: %g - %g\n", bf->voi[0].size, bf->voi[bf->voiNr-1].size);
198 }
199
200 /* Allocate memory for simulated TAC */
201 double *sim=(double*)malloc(input->frameNr*sizeof(double));
202 if(sim==NULL) {
203 if(status!=NULL) strcpy(status, "out of memory");
204 dftEmpty(bf); return(11);
205 }
206
207 /* Calculate the basis functions at input time points */
208 if(verbose>1) printf("computing basis functions at input sample times\n");
209 for(bi=0; bi<bf->voiNr; bi++) {
210 a=bf->voi[bi].size;
211 ret=simC1(input->x, input->voi[0].y, input->frameNr, 1.0, a, sim);
212 if(ret) {
213 if(status!=NULL) strcpy(status, "simulation problem");
214 free(sim); dftEmpty(bf);
215 return(20);
216 }
217 if(verbose>100) {
218 printf("\nk2 := %g\n", a);
219 printf("simulated TAC:\n");
220 for(fi=0; fi<input->frameNr; fi++)
221 printf(" %12.6f %12.3f\n", input->x[fi], sim[fi]);
222 }
223 /* interpolate to PET time frames */
224 if(tissue->timetype==DFT_TIME_STARTEND)
225 ret=interpolate4pet(input->x, sim, input->frameNr, tissue->x1, tissue->x2,
226 bf->voi[bi].y, NULL, NULL, bf->frameNr);
227 else
228 ret=interpolate(input->x, sim, input->frameNr, tissue->x,
229 bf->voi[bi].y, NULL, NULL, bf->frameNr);
230 if(ret) {
231 if(status!=NULL) strcpy(status, "simulation problem");
232 free(sim); dftEmpty(bf);
233 return(20);
234 }
235
236 } // next basis function
237
238 free(sim);
239 if(verbose>1) printf("%s() done.\n\n", __func__);
240 if(status!=NULL) strcpy(status, "ok");
241 return(0);
242}
243
244
245/*****************************************************************************/
246
247/*****************************************************************************/
251int main(int argc, char **argv)
252{
253 int ai, help=0, version=0, verbose=1;
254 int fitdimt;
255 char apfile[FILENAME_MAX], abfile[FILENAME_MAX], petfile[FILENAME_MAX];
256 char k1file[FILENAME_MAX], k2file[FILENAME_MAX], vbfile[FILENAME_MAX];
257 char vtfile[FILENAME_MAX], wssfile[FILENAME_MAX], errfile[FILENAME_MAX];
258 char bfsfile[FILENAME_MAX];
259 int bfNr=500, *bf_opt_nr;
260 float threshold, calcThreshold=0.0;
261 double fittime=0.0;
262 double k1min=0.0, k1max=10.0; // mL/(min*mL)
263 double k2min=0.0, k2max=10.0; // 1/min
264 double vtmin=0.0, vtmax=100.0; // mL/mL
265 double vbmin=0.0, vbmax=1.0; // mL/mL
266
267
268 /*
269 * Get arguments
270 */
271 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
272 apfile[0]=abfile[0]=petfile[0]=k1file[0]=k2file[0]=vbfile[0]=vtfile[0]=(char)0;
273 wssfile[0]=errfile[0]=bfsfile[0]=(char)0;
274 /* Get options */
275 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
276 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
277 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
278 if(strncasecmp(cptr, "VB=", 3)==0 || strncasecmp(cptr, "VA=", 3)==0) {
279 strlcpy(vbfile, cptr+3, FILENAME_MAX); if(strlen(vbfile)) continue;
280 } else if(strncasecmp(cptr, "VT=", 3)==0 || strncasecmp(cptr, "VD=", 3)==0) {
281 strlcpy(vtfile, cptr+3, FILENAME_MAX); if(strlen(vtfile)) continue;
282 } else if(strncasecmp(cptr, "K1min=", 6)==0 && strlen(cptr)>6) {
283 if(atof_with_check(cptr+6, &k1min)==0 && k1min>=0.0) continue;
284 } else if(strncasecmp(cptr, "K1max=", 6)==0 && strlen(cptr)>6) {
285 if(atof_with_check(cptr+6, &k1max)==0 && k1max>=0.0) continue;
286 } else if(strncasecmp(cptr, "k2min=", 6)==0) {
287 if(atof_with_check(cptr+6, &k2min)==0 && k2min>=0.0) continue;
288 } else if(strncasecmp(cptr, "k2max=", 6)==0) {
289 if(atof_with_check(cptr+6, &k2max)==0 && k2max>=0.0) continue;
290 } else if(strncasecmp(cptr, "Vtmin=", 6)==0) {
291 if(atof_with_check(cptr+6, &vtmin)==0 && vtmin>=0.0) continue;
292 } else if(strncasecmp(cptr, "Vtmax=", 6)==0) {
293 if(atof_with_check(cptr+6, &vtmax)==0 && vtmax>=0.0) continue;
294 } else if(strncasecmp(cptr, "Vbmin=", 6)==0) {
295 if(atof_with_check(cptr+6, &vbmin)==0 && vbmin>=0.0) continue;
296 } else if(strncasecmp(cptr, "Vbmax=", 6)==0) {
297 if(atof_with_check(cptr+6, &vbmax)==0 && vbmax>=0.0) continue;
298 } else if(strncasecmp(cptr, "NR=", 3)==0) {
299 bfNr=atoi(cptr+3); if(bfNr>5E+04) bfNr=5E+04;
300 if(bfNr>=100) continue;
301 } else if(strncasecmp(cptr, "BF=", 3)==0) {
302 strlcpy(bfsfile, cptr+3, FILENAME_MAX); if(strlen(bfsfile)>0) continue;
303 } else if(strncasecmp(cptr, "WSS=", 4)==0) {
304 strlcpy(wssfile, cptr+4, FILENAME_MAX); if(strlen(wssfile)>0) continue;
305 } else if(strncasecmp(cptr, "ERR=", 4)==0) {
306 strlcpy(errfile, cptr+4, FILENAME_MAX); if(strlen(errfile)>0) continue;
307 } else if(strncasecmp(cptr, "THR=", 4)==0 && strlen(cptr)>4) {
308 cptr+=4; if(isdigit(*cptr) || *cptr=='+' || *cptr=='-') {
309 calcThreshold=0.01*atof_dpi(cptr);
310 if(calcThreshold>=0.0 && calcThreshold<=2.0) continue;
311 }
312 }
313 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
314 return(1);
315 } else break;
316
317 /* Print help or version? */
318 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
319 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
320 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
321
322 /* Process other arguments, starting from the first non-option */
323 if(ai<argc) strlcpy(apfile, argv[ai++], FILENAME_MAX);
324 if(ai<argc) strlcpy(abfile, argv[ai++], FILENAME_MAX);
325 if(ai<argc) strlcpy(petfile, argv[ai++], FILENAME_MAX);
326 if(ai<argc) {
327 if(atof_with_check(argv[ai], &fittime)) {
328 fprintf(stderr, "Error: invalid fit time '%s'.\n", argv[ai]); return(1);}
329 ai++;
330 }
331 if(ai<argc) strlcpy(k1file, argv[ai++], FILENAME_MAX);
332 if(ai<argc) strlcpy(k2file, argv[ai++], FILENAME_MAX);
333 if(ai<argc) {
334 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
335 return(1);
336 }
337 /* Did we get all the information that we need? */
338 if(!k1file[0]) {
339 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
340 return(1);
341 }
342 if(fittime<1.0E-04) fittime=1.0E+020;
343 /* Check if Vb is constrained to zero; then abfile is not needed */
344 if(vbmin<1.0E-05 && vbmax<1.0E-05) {vbmin=vbmax=0.0; abfile[0]=(char)0;}
345 /* Check if Vb is constrained to a certain value; it would be stupid to save vbfile */
346 if(vbmin==vbmax && vbfile[0]) {
347 fprintf(stderr, "Warning: Vb image will not be created.\n");
348 vbfile[0]=(char)0;
349 }
350
351
352 /* In verbose mode print arguments and options */
353 if(verbose>1) {
354 printf("petfile := %s\n", petfile);
355 printf("apfile := %s\n", apfile);
356 if(abfile[0]) printf("abfile := %s\n", abfile);
357 printf("k1file := %s\n", k1file);
358 if(k2file[0]) printf("k2file := %s\n", k2file);
359 if(vbfile[0]) printf("vbfile := %s\n", vbfile);
360 if(vtfile[0]) printf("vtfile := %s\n", vtfile);
361 if(wssfile[0]) printf("wssfile := %s\n", wssfile);
362 if(errfile[0]) printf("errfile := %s\n", errfile);
363 if(bfsfile[0]) printf("bfsfile := %s\n", bfsfile);
364 printf("bfNr := %d\n", bfNr);
365 printf("calcThreshold := %g\n", calcThreshold);
366 printf("requested_fittime := %g [min]\n", fittime);
367 printf("k1min := %g\n", k1min);
368 printf("k1max := %g\n", k1max);
369 printf("k2min := %g\n", k2min);
370 printf("k2max := %g\n", k2max);
371 printf("vbmin := %g\n", vbmin);
372 printf("vbmax := %g\n", vbmax);
373 printf("vtmin := %g\n", vtmin);
374 printf("vtmax := %g\n", vtmax);
375 }
376 if(verbose>8) {IMG_TEST=verbose-8; SIF_TEST=verbose-8;} else IMG_TEST=SIF_TEST=0;
377
378 /* Check user-defined parameter ranges and calculate range of k2 */
379 if(k1min>=k1max) {
380 fprintf(stderr, "Error: invalid range for K1 (%g - %g).\n", k1min, k1max);
381 return(1);
382 }
383 if(k2min>=k2max) {
384 fprintf(stderr, "Error: invalid range for k2 (%g - %g).\n", k2min, k2max);
385 return(1);
386 }
387 if(vbmin>vbmax || vbmax>1.0) {
388 fprintf(stderr, "Error: invalid range for Vb (%g - %g).\n", vbmin, vbmax);
389 return(1);
390 }
391 if(vtmin>=vtmax) {
392 fprintf(stderr, "Error: invalid range for Vt (%g - %g).\n", vtmin, vtmax);
393 return(1);
394 }
395 if(k1min/vtmax>k2min) k2min=k1min/vtmax;
396 if(vtmin>0.0 && k1max/vtmin<k2max) k2max=k1max/vtmin;
397 if(k2max<=k2min || k2min<0.) {
398 fprintf(stderr, "Error: invalid range for K1 and Vt.\n");
399 return(1);
400 }
401
402
403 /*
404 * Read PET image and input TAC
405 */
406 if(verbose>1) printf("reading data files\n");
407 DFT input, tac; dftInit(&input); dftInit(&tac);
408 IMG img; imgInit(&img);
409 {
410 char tmp[256];
411 int ret=imgReadModelingData(
412 petfile, NULL, apfile, abfile, NULL, &fittime, &fitdimt, &img,
413 &input, &tac, 1, stdout, verbose-2, tmp);
414 if(ret!=0) {
415 fprintf(stderr, "Error: %s.\n", tmp);
416 if(verbose>1) printf(" ret := %d\n", ret);
417 return(2);
418 }
419 if(imgNaNs(&img, 1)>0)
420 if(verbose>0) fprintf(stderr, "Warning: missing pixel values.\n");
421 }
422 //printf("last x2 = %g\n", tac.x2[tac.frameNr-1]);
423 /* Set time unit to min */
424 dftTimeunitConversion(&input, TUNIT_MIN);
425 dftTimeunitConversion(&tac, TUNIT_MIN);
426 if(verbose>1) {
427 printf("fittimeFinal := %g min\n", fittime);
428 printf("fitdimt := %d\n", fitdimt);
429 fflush(stdout);
430 }
431 /* Check that the image is dynamic */
432 if(fitdimt<3) {
433 fprintf(stderr, "Error: too few time frames for fitting.\n");
434 if(verbose>2) imgInfo(&img);
435 imgEmpty(&img); dftEmpty(&input); dftEmpty(&tac); return(2);
436 }
437 /* Allocate memory for tissue data and integrals */
438 if(dftAddmem(&tac, 1)!=0) {
439 fprintf(stderr, "Error: cannot allocate memory.\n"); fflush(stderr);
440 imgEmpty(&img); dftEmpty(&input); dftEmpty(&tac); return(3);
441 }
442 /* And set indices for input and tissue TACs */
443 int iAPC=0, iAB=0, iT=1; if(tac.voiNr==2) {iAB++; iT++;}
444 strcpy(tac.voi[iAPC].voiname, "input");
445 strcpy(tac.voi[iT].voiname, "tissue");
446 if(iAB>0) strcpy(tac.voi[iAB].voiname, "blood");
447 if(verbose>50) dftPrint(&tac);
448
449 /* Determine the threshold */
450 if(verbose>2)
451 printf("input_AUC[%g] := %g\n", tac.x2[tac.frameNr-1], tac.voi[iAPC].y2[tac.frameNr-1]/60.0);
452 threshold=calcThreshold*tac.voi[iAPC].y2[tac.frameNr-1]/60.0;
453 if(verbose>2) {printf("threshold_AUC = %g\n", threshold); fflush(stdout);}
454
455
456 /*
457 * Determine the weights for the fit
458 */
459 if(verbose>1) printf("setting weights based on frame lengths\n");
460 if(imgSetWeights(&img, 1, verbose-5)!=0) {
461 fprintf(stderr, "Warning: cannot calculate weights.\n"); fflush(stderr);
462 /* set weights to 1 */
463 for(int i=0; i<img.dimt; i++) img.weight[i]=1.0;
464 img.isWeight=1;
465 }
466
467
468 /*
469 * Calculate the basis functions
470 */
471 if(verbose>1) {fprintf(stdout, "calculating basis functions\n"); fflush(stdout);}
472 DFT bf; dftInit(&bf);
473 {
474 char tmp[256];
475 int ai=tac.frameNr; tac.frameNr=fitdimt;
476 int ret=bf1TCM(&input, &tac, &bf, bfNr, k2min, k2max, tmp, verbose-2);
477 tac.frameNr=ai;
478 if(ret) {
479 fprintf(stderr, "Error: cannot calculate basis functions.\n");
480 if(verbose>1) fprintf(stderr, "bf1TCM(): %s.\n", tmp);
481 fflush(stderr);
482 imgEmpty(&img); dftEmpty(&input); dftEmpty(&tac); return(6);
483 }
484 }
485 /* Note that basis functions may need to be saved later (in bfsfile), after it is known
486 in how many image pixels each basis function was found to give the best fit */
487
488
489 /*
490 * Allocate result images and fill the header info
491 */
492 if(verbose>1) {printf("allocating memory for parametric images\n"); fflush(stdout);}
493 IMG k1img; imgInit(&k1img);
494 IMG k2img; imgInit(&k2img);
495 IMG vbimg; imgInit(&vbimg);
496 IMG vtimg; imgInit(&vtimg);
497 IMG wssimg; imgInit(&wssimg);
498 IMG errimg; imgInit(&errimg);
499 if(imgAllocateWithHeader(&k1img, img.dimz, img.dimy, img.dimx, 1, &img)!=0) {
500 fprintf(stderr, "Error: out of memory.\n");
501 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&bf);
502 return(8);
503 }
504 k1img.start[0]=0.0; k1img.end[0]=fittime*60.0;
506 k1img.isWeight=0;
507 k1img.unit=IMGUNIT_ML_PER_ML_PER_MIN;
508 {
509 int ret=0;
510 if(k2file[0]) {
511 ret=imgAllocateWithHeader(&k2img, img.dimz, img.dimy, img.dimx, 1, &k1img);
512 if(ret==0) k2img.unit=IMGUNIT_PER_MIN;
513 }
514 if(ret==0 && vtfile[0]) {
515 ret=imgAllocateWithHeader(&vtimg, img.dimz, img.dimy, img.dimx, 1, &k1img);
516 if(ret==0) vtimg.unit=IMGUNIT_UNITLESS;
517 }
518 if(ret==0 && vbfile[0]) {
519 ret=imgAllocateWithHeader(&vbimg, img.dimz, img.dimy, img.dimx, 1, &k1img);
520 if(ret==0) vbimg.unit=IMGUNIT_UNITLESS;
521 }
522 if(ret==0 && wssfile[0]) {
523 ret=imgAllocateWithHeader(&wssimg, img.dimz, img.dimy, img.dimx, 1, &k1img);
524 if(ret==0) wssimg.unit=IMGUNIT_UNITLESS;
525 }
526 if(ret==0 && errfile[0]) {
527 ret=imgAllocateWithHeader(&errimg, img.dimz, img.dimy, img.dimx, 1, &k1img);
528 if(ret==0) errimg.unit=IMGUNIT_UNITLESS;
529 }
530 if(ret) {
531 fprintf(stderr, "Error: out of memory.\n"); fflush(stderr);
532 imgEmpty(&img); dftEmpty(&input); dftEmpty(&tac); dftEmpty(&bf);
533 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vbimg); imgEmpty(&vtimg);
534 imgEmpty(&wssimg); imgEmpty(&errimg);
535 return(8);
536 }
537 }
538
539
540 /*
541 * Allocate memory for QR
542 */
543 int /*m, n,*/ M, N;
544 double **mem, **A, *B, X[MAX_N], *tau, *residual, RNORM, *chain;
545 double *qrweight, **wws, *ws, *wwschain;
546
547 if(verbose>1) {fprintf(stdout, "allocating memory for QR\n"); fflush(stdout);}
548 M=tac.frameNr; N=2; if(vbmin==vbmax) N--;
549 chain=(double*)malloc((M+1)*N*bf.voiNr * sizeof(double));
550 mem=(double**)malloc(bf.voiNr * sizeof(double*));
551 A=(double**)malloc(M * sizeof(double*));
552 B=(double*)malloc(M*sizeof(double));
553 residual=(double*)malloc(M*sizeof(double));
554 qrweight=(double*)malloc(M*sizeof(double));
555 wwschain=(double*)malloc((M*N+2*M)*sizeof(double));
556 wws=(double**)malloc(M * sizeof(double*));
557 if(chain==NULL || B==NULL || A==NULL || residual==NULL || qrweight==NULL || wwschain==NULL || wws==NULL)
558 {
559 fprintf(stderr, "Error: out of memory.\n"); fflush(stderr);
560 imgEmpty(&img); dftEmpty(&input); dftEmpty(&tac); dftEmpty(&bf);
561 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vbimg); imgEmpty(&vtimg);
562 imgEmpty(&wssimg); imgEmpty(&errimg);
563 return(8);
564 }
565 for(int bi=0; bi<bf.voiNr; bi++) mem[bi]=chain+bi*(M+1)*N;
566 for(int m=0; m<M; m++) wws[m]=wwschain+m*N;
567 ws=wwschain+M*N;
568
569 /* Pre-compute QR weights for faster execution */
570 for(int m=0; m<M; m++) {
571 if(img.weight[m]<=1.0e-20) qrweight[m]=0.0;
572 else qrweight[m]=sqrt(img.weight[m]);
573 }
574
575 /* Make A matrix, and QR decomposition for it, for all pixels
576 beforehand for faster execution */
577 if(verbose>1) {fprintf(stdout, "calculating QR decomposition\n"); fflush(stdout);}
578 for(int bi=0; bi<bf.voiNr; bi++) {
579
580 /* Define memory site for coefficient matrix and vector tau */
581 for(int m=0; m<M; m++) A[m]=mem[bi]+m*N;
582 tau=mem[bi]+M*N;
583
584 /* Initiate matrix (A = mem[bi]) */
585 for(int m=0; m<M; m++) {
586 A[m][0]=bf.voi[bi].y[m];
587 if(N>1) A[m][1]=tac.voi[iAB].y[m]; // blood TAC for Vb estimation
588 }
589
590 /* Apply data weights */
591 for(int m=0; m<M; m++) for(int n=0; n<N; n++) A[m][n]*=qrweight[m];
592
593 /* Compute QR decomposition of the coefficient matrix */
594 int ret=qr_decomp(A, M, N, tau, wws, ws);
595
596 if(ret>0) { /* Decomposition failed */
597 free(chain); free(B); free(residual);
598 free(A); free(wwschain); free(wws); free(qrweight); free(mem);
599 imgEmpty(&img); dftEmpty(&input); dftEmpty(&tac); dftEmpty(&bf);
600 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vbimg); imgEmpty(&vtimg);
601 imgEmpty(&wssimg); imgEmpty(&errimg);
602 return(9);
603 }
604 } /* next BF */
605
606
607 /*
608 * Compute pixel-by-pixel
609 */
610 if(verbose>0) {fprintf(stdout, "computing QR pixel-by-pixel\n"); fflush(stdout);}
611 int nosolution_nr=0, thresholded_nr=0;
612 double *ct=tac.voi[iT].y;
613 double *cti=tac.voi[iT].y2;
614 /* Allocate memory for BF counters on how often each BF is found to provide the optimal fit */
615 bf_opt_nr=(int*)malloc(bfNr*sizeof(int));
616 for(int bi=0; bi<bf.voiNr; bi++) bf_opt_nr[bi]=0.0;
617 /* pixel-by-pixel */
618 for(int zi=0; zi<img.dimz; zi++) {
619 if(img.dimz>1 && verbose>0) {
620 if(verbose>6) printf("computing plane %d\n", img.planeNumber[zi]); else fprintf(stdout, ".");
621 fflush(stdout);
622 }
623 for(int yi=0; yi<img.dimy; yi++) {
624 if(verbose>6 && yi==4*img.dimy/10) printf(" computing row %d\n", yi+1);
625 for(int xi=0; xi<img.dimx; xi++) {
626 //printf("%d,%d,%d\n", zi, yi, xi); fflush(stdout);
627 if(verbose>6 && yi==4*img.dimy/10 && xi==4*img.dimx/10)
628 printf(" computing column %d\n", xi+1);
629 /* To start with, set result voxel values to zero */
630 k1img.m[zi][yi][xi][0]=0.0;
631 if(k2file[0]) k2img.m[zi][yi][xi][0]=0.0;
632 if(vbfile[0]) vbimg.m[zi][yi][xi][0]=0.0;
633 if(vtfile[0]) vtimg.m[zi][yi][xi][0]=0.0;
634 if(wssfile[0]) wssimg.m[zi][yi][xi][0]=0.0;
635 if(errfile[0]) errimg.m[zi][yi][xi][0]=0.0;
636
637 /* if end AUC is less than threshold value, then let results be zero and quit this pixel */
638 /* Calculate pixel integral */
639 for(int m=0; m<M; m++) {ct[m]=img.m[zi][yi][xi][m];}
640 petintegral(tac.x1, tac.x2, ct, tac.frameNr, cti, NULL);
641 //printf("last x2 = %g\n", tac.x2[tac.frameNr-1]); fflush(stdout);
642 //printf(" Pixel (%d,%d,%d), int= %f, threshold= %g\n", zi, yi, xi, cti[M-1], threshold); fflush(stdout);
643 if(verbose>6 && yi==4*img.dimy/10 && xi==4*img.dimx/10) {
644 printf(" Pixel (%d,%d,%d), int= %f, threshold= %g\n", zi, yi, xi, cti[M-1], threshold);
645 if(verbose>7) {
646 for(int m=0; m<M; m++)
647 printf(" %02d:\t%g\t%g\n", m, img.m[zi][yi][xi][m], tac.voi[iAPC].y[m]);
648 }
649 }
650 if(!(cti[M-1]>=threshold)) {
651 if(errfile[0]) errimg.m[zi][yi][xi][0]=1.0;
652 thresholded_nr++;
653 continue;
654 }
655
656 /* If Vb is constrained to a value higher than 0, then subtract BTAC from tissue */
657 if(vbmin==vbmax && vbmin>0.0) {
658 for(int m=0; m<M; m++) {ct[m]=img.m[zi][yi][xi][m]-vbmin*tac.voi[iAB].y[m];}
659 /* re-calculate the integral */
660 petintegral(tac.x1, tac.x2, ct, tac.frameNr, cti, NULL);
661 /* if end AUC is less than threshold value, then set values to 0 */
662 if(!(cti[M-1]>0.0)) {
663 if(errfile[0]) errimg.m[zi][yi][xi][0]=1.0;
664 thresholded_nr++;
665 continue;
666 }
667 }
668
669 /* Go through all basis functions */
670 int bi_min=-1; double rnorm_min=1.0E80;
671 double p1=0.0, p2=0.0, p3=0.0;
672 for(int bi=0; bi<bf.voiNr; bi++) {
673 //printf(" bi=%d\n", bi); fflush(stdout);
674
675 /* Define memory site for present coefficient matrix and vector tau */
676 for(int m=0; m<M; m++) {A[m]=mem[bi]+ m*N;}
677 tau=mem[bi]+M*N;
678
679 /* Get data vector, and apply data weights */
680 for(int m=0; m<M; m++) {
681 B[m]=img.m[zi][yi][xi][m]*qrweight[m];
682 }
683
684 /* Compute solution */
685 int ret=qr_solve(A, M, N, tau, B, X, residual, &RNORM, wws, ws);
686 if(ret>0) { /* no solution is possible */
687 for(int n=0; n<N; n++) X[n]=0.0;
688 RNORM=1.0E80;
689 }
690 //printf(" %g,%g %g\n", X[0], X[1], RNORM); fflush(stdout);
691 /* Check if this was the best fit so far */
692 if(!(RNORM<rnorm_min)) continue; // not
693 /* Ok, fit is best so far, but accept it only if parameters stay inside limits */
694 /* Vb */
695 if(N>1 && X[1]>=1.0) { /* If Vb>1 then set Vb=1 and K1=0, and accept; will result to k2=0 */
696 X[1]=1.0; X[0]=0.0;
697 } else if(N>1 && (X[1]<vbmin || X[1]>vbmax)) continue;
698 /* K1 */ if(X[0]<0.0 || X[0]>k1max) continue;
699 /* K1/k2 */ if(!(X[0]/bf.voi[bi].size <= vtmax)) continue;
700 /* Fine, we accept it for now */
701 rnorm_min=RNORM; bi_min=bi;
702 /* K1 */ p1=X[0];
703 /* Vb */ if(N>1) p2=X[1]; else p2=vbmin;
704 /* k2 */ p3=bf.voi[bi_min].size;
705 if(p1<k1min+1.0E-05) p3=0.0; // if K1==0 then k2=0
706 } /* next basis function */
707 //printf(" %g,%g,%g %g\n", p1, p2, p3, rnorm_min); fflush(stdout);
708
709
710 /* Check that we got an acceptable result */
711 if(rnorm_min>=1.0E60) { // if not...
712 nosolution_nr++;
713 if(errfile[0]) errimg.m[zi][yi][xi][0]=2.0;
714 continue; // ... then next pixel
715 }
716
717 bf_opt_nr[bi_min]+=1;
718 //printf(" Pixel (%d,%d,%d), K1=%g Vb=%g k2=%g\n", zi, yi, xi, p1, p2, p3); fflush(stdout);
719
720 if(verbose>6 && yi==4*img.dimy/10 && xi==4*img.dimx/10) {
721 printf(" Pixel (%d,%d,%d), K1=%g Vb=%g k2=%g\n", zi, yi, xi, p1, p2, p3);
722 if(verbose>10) dftPrint(&tac);
723 }
724
725 /* Calculate WSS */
726 double wss=0.0;
727 for(int m=0; m<M; m++) {
728 double f=p1*bf.voi[bi_min].y[m];
729 if(N>1) f+=p2*tac.voi[iAB].y[m];
730 f-=img.m[zi][yi][xi][m];
731 wss+=img.weight[m]*f*f;
732 }
733 /* Put results to output images */
734 k1img.m[zi][yi][xi][0]=p1;
735 if(vbfile[0]) vbimg.m[zi][yi][xi][0]=p2;
736 if(k2file[0]) k2img.m[zi][yi][xi][0]=p3;
737 if(vtfile[0]) {double f=p1/p3; if(f>=vtmin && f<=vtmax) vtimg.m[zi][yi][xi][0]=f;}
738 if(wssfile[0]) wssimg.m[zi][yi][xi][0]=wss;
739 if(errfile[0]) {
740 if(bi_min==0 || bi_min==bf.voiNr-1) errimg.m[zi][yi][xi][0]=2.0;
741 else errimg.m[zi][yi][xi][0]=0.0;
742 }
743
744 } /* next column */
745 } /* next row */
746 } /* next plane */
747 if(verbose>0) {fprintf(stdout, "done.\n"); fflush(stdout);}
748
749 if(verbose>1 || thresholded_nr>0) {
750 double f;
751 f=(double)thresholded_nr/((double)(k1img.dimx*k1img.dimy*k1img.dimz));
752 f*=100.; if(f<3.0) printf("%g%%", f); else printf("%.0f%%", f);
753 printf(" of pixels were not fitted due to threshold.\n");
754 if(verbose>2) printf("thresholded %d pixels\n", thresholded_nr);
755 fflush(stdout);
756 }
757 if(verbose>1 || nosolution_nr>0) {
758 fprintf(stdout, "no QR solution for %d pixels.\n", nosolution_nr); fflush(stdout);}
759
760 /* No need for dynamic image or input tac anymore */
761 imgEmpty(&img); dftEmpty(&input); dftEmpty(&tac);
762 /* Free memory of QR */
763 free(chain); free(B); free(residual); free(A); free(wwschain);
764 free(wws); free(qrweight); free(mem);
765
766 /*
767 * Save basis functions if required;
768 * this is done not before, so that also the number of optimal fits
769 * achieved with each BF can be saved as the "size".
770 */
771 if(bfsfile[0]) {
772 for(int bi=0; bi<bf.voiNr; bi++) sprintf(bf.voi[bi].place, "%d", bf_opt_nr[bi]);
773 if(dftWrite(&bf, bfsfile)) {
774 fprintf(stderr, "Error in writing %s: %s\n", bfsfile, dfterrmsg); fflush(stderr);
775 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vbimg); imgEmpty(&vtimg);
776 imgEmpty(&wssimg); imgEmpty(&errimg);
777 dftEmpty(&bf); free(bf_opt_nr); return(11);
778 }
779 if(verbose>0) {fprintf(stdout, "basis functions written in %s\n", bfsfile); fflush(stdout);}
780 }
781
782 /* No need for basis functions any more */
783 dftEmpty(&bf); free(bf_opt_nr);
784
785
786 /*
787 * Save parametric image(s)
788 */
789 if(verbose>1) {printf("Saving parametric images\n"); fflush(stdout);}
790 {
791 int ret=imgWrite(k1file, &k1img);
792 if(ret) {fprintf(stderr, "Error: %s\n", k1img.statmsg); fflush(stderr);}
793 else if(verbose>0) {fprintf(stdout, "K1 image %s saved.\n", k1file); fflush(stdout);}
794
795 if(ret==0 && vbfile[0]) {
796 ret=imgWrite(vbfile, &vbimg);
797 if(ret) {fprintf(stderr, "Error: %s\n", vbimg.statmsg); fflush(stderr);}
798 else if(verbose>0) {fprintf(stdout, "Vb image %s saved.\n", vbfile); fflush(stdout);}
799 }
800 if(ret==0 && vtfile[0]) {
801 ret=imgWrite(vtfile, &vtimg);
802 if(ret) {fprintf(stderr, "Error: %s\n", vtimg.statmsg); fflush(stderr);}
803 else if(verbose>0) {fprintf(stdout, "Vt image %s saved.\n", vtfile); fflush(stdout);}
804 }
805 if(ret==0 && k2file[0]) {
806 ret=imgWrite(k2file, &k2img);
807 if(ret) {fprintf(stderr, "Error: %s\n", k2img.statmsg); fflush(stderr);}
808 else if(verbose>0) {fprintf(stdout, "k2 image %s saved.\n", k2file); fflush(stdout);}
809 }
810 if(ret==0 && wssfile[0]) {
811 ret=imgWrite(wssfile, &wssimg);
812 if(ret) {fprintf(stderr, "Error: %s\n", wssimg.statmsg); fflush(stderr);}
813 else if(verbose>0) {fprintf(stdout, "WSS image %s saved.\n", wssfile); fflush(stdout);}
814 }
815 if(ret==0 && errfile[0]) {
816 ret=imgWrite(errfile, &errimg);
817 if(ret) {fprintf(stderr, "Error: %s\n", errimg.statmsg); fflush(stderr);}
818 else if(verbose>0) {fprintf(stdout, "Error image %s saved.\n", errfile); fflush(stdout);}
819 }
820 if(ret) {
821 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vbimg); imgEmpty(&vtimg);
822 imgEmpty(&wssimg); imgEmpty(&errimg);
823 return(ret);
824 }
825 }
826 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&vbimg); imgEmpty(&vtimg);
827 imgEmpty(&wssimg); imgEmpty(&errimg);
828
829 return(0);
830}
831/*****************************************************************************/
832
833/*****************************************************************************/
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
int dftCopymainhdr2(DFT *dft1, DFT *dft2, int ow)
Definition dft.c:587
int dftSetmem(DFT *data, int frameNr, int voiNr)
Definition dft.c:57
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 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 interpolate(double *x, double *y, int nr, double *newx, double *newy, double *newyi, double *newyii, int newnr)
Linear interpolation and integration.
Definition integr.c:28
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.
#define DFT_TIME_STARTEND
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 simC1(double *t, double *ca, int nr, double k1, double k2, double *ct)
Definition simulate.c:1317
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)
int _type
int timetype
Voi * voi
int timeunit
double * x1
int voiNr
double * x2
int frameNr
double * x
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 name[MAX_REGIONNAME_LEN+1]
char hemisphere[MAX_REGIONSUBNAME_LEN+1]
char place[MAX_REGIONSUBNAME_LEN+1]