TPCCLIB
Loading...
Searching...
No Matches
imgbfk3.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 "libtpccurveio.h"
20#include "libtpcmodext.h"
21#include "libtpcmisc.h"
22#include "libtpcmodel.h"
23#include "libtpcimgio.h"
24#include "libtpcimgp.h"
25/*****************************************************************************/
26#define MAX_N 3
27/*****************************************************************************/
28
29/*****************************************************************************/
30static char *info[] = {
31 "Computation of parametric images from dynamic PET image in ECAT, NIfTI,",
32 "or Analyze format applying irreversible two-tissue compartmental model with",
33 "arterial plasma input, using the basis function method (1).",
34 " ",
35 "Dynamic PET image and plasma and blood time-activity curves (PTAC and BTAC)",
36 "must be corrected for decay to the tracer administration time.",
37 "Enter 'none' in place of the name of btacfile, if you want to assume Vb=0.",
38 " ",
39 "Usage: @P [Options] ptacfile btacfile imgfile k3file",
40 " ",
41 "Options:",
42 " -thr=<threshold%>",
43 " Pixels with AUC less than (threshold/100 x PTAC AUC) are set to zero;",
44 " default is 1%.",
45 " -end=<Fit end time (min)>",
46 " Use data from 0 to end time; by default, model is fitted to all frames.",
47 " -dv=<filename>",
48 " Parametric K1/(k2+k3) image is saved.",
49 " -K1=<filename>",
50 " Parametric K1 image is saved.",
51 " -k2=<filename>",
52 " Parametric k2 image is saved.",
53 " -Ki=<filename>",
54 " Parametric Ki image is saved.",
55 " -Vb=<filename>",
56 " Parametric Vb image is saved.",
57 " -min=<Min k2+k3> and -max=<Max k2+k3>",
58 " Enter the basis functions minimum and maximum k2+k3 (=alpha) in units 1/min;",
59 " defaults are 0.15 and 0.60, respectively.",
60// " -theta1max=<Max theta1>",
61// " Enter the maximum theta1=(1-Vb)*Ki; default is 1.0; not applied in QR method.",
62// " -theta2max=<Max theta2>",
63// " Enter the maximum theta2=(1-Vb)*(K1-Ki); default is 1.0; not applied in QR method.",
64// " -Vbmax=<Max Vb>",
65// " Enter the maximum Vb; default is 1.0; not applied in QR method.",
66 " -nr=<value>",
67 " Set number of basis functions; default is 200, minimum 50.",
68 " -k2k3=<filename>",
69 " Parametric k2+k3 (=alpha) image is saved.",
70 " -t1=<filename>",
71 " Parametric theta1 image is saved.",
72 " -t2=<filename>",
73 " Parametric theta2 image is saved.",
74 " -bf=<filename>",
75 " Basis function curves are written in specified TAC file.",
76 " -err=<filename>",
77 " Save image where the pixels that had k2+k3 at min or max value are",
78 " set to values 1 and 2, respectively, and other pixels are set to value 0.",
79 " -w1, -wf, -wfa",
80 " By default, all weights are set to 1.0 (no weighting, option -w1); option -wf",
81 " sets weights based on frame lengths, and option -wfa based on both frame lengths",
82 " and mean activity during each frame.",
83 " -stdoptions", // List standard options like --help, -v, etc
84 " ",
85 "Example 1. Calculation of K1, Ki, k3, and Vb images:",
86 " @P -k1=s2345k1.v -ki=s2345ki.v -Vb=s2345vb.v s2345ap.kbq s2345ab.kbq s2345dy.v s2345k3.v",
87 "Example 2. Calculation with assumption Vb=0:",
88 " @P -k1=s2345k1.v -ki=s2345ki.v s2345ap.kbq none s2345dy.v s2345k3.v",
89 " ",
90 "The units of pixel values in the parametric images are 1/min for k3,",
91 "ml/(min*ml) for K1 and Ki, and ml/ml for DV and Vb.",
92 " ",
93 "References:",
94 "1. Hong YT et al. J Cereb Blood Flow Metab. 2011;31:648-657.",
95 " ",
96 "See also: imglhk3, imgki, imgcbv, imgunit, fitdelay",
97 " ",
98 "Keywords: image, modelling, irreversible uptake, Ki, basis function method",
99 0};
100/*****************************************************************************/
101
102/*****************************************************************************/
103/* Turn on the globbing of the command line, since it is disabled by default in
104 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
105 In Unix&Linux wildcard command line processing is enabled by default. */
106/*
107#undef _CRT_glob
108#define _CRT_glob -1
109*/
110int _dowildcard = -1;
111/*****************************************************************************/
112
113/*****************************************************************************/
114enum {METHOD_UNKNOWN, METHOD_QR, METHOD_BVLS};
115static char *method_str[] = {"unknown", "QR", "BVLS", 0};
116/*****************************************************************************/
117
118/*****************************************************************************/
122int main(int argc, char **argv)
123{
124 int ai, help=0, version=0, verbose=1;
125 char ptacfile[FILENAME_MAX], btacfile[FILENAME_MAX], petfile[FILENAME_MAX];
126 char k1file[FILENAME_MAX], k2file[FILENAME_MAX], vbfile[FILENAME_MAX];
127 char dvfile[FILENAME_MAX], kifile[FILENAME_MAX], k3file[FILENAME_MAX];
128 char errfile[FILENAME_MAX], bfsfile[FILENAME_MAX], k2k3file[FILENAME_MAX];
129 char t1file[FILENAME_MAX], t2file[FILENAME_MAX];
130 float calcThreshold=0.01;
131 double fittime=-1.0;
132 int weights=2; // 1=frame lengths and activity, 2=frame lengths, 2=no weighting
133 int bfNr=200;
134 double alphamin=0.15, alphamax=0.60;
135 double theta1max=1.0, theta2max=1.0, Vbmax=1.0;
136 int fitVb=1; // 0=not fitted, 1=fitted
137 int method=METHOD_QR;
138 int ret;
139
140
141 /*
142 * Get arguments
143 */
144 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
145 ptacfile[0]=btacfile[0]=petfile[0]=k3file[0]=(char)0;
146 vbfile[0]=k1file[0]=k2file[0]=kifile[0]=dvfile[0]=(char)0;
147 errfile[0]=bfsfile[0]=k2k3file[0]=t1file[0]=t2file[0]=(char)0;
148 /* Get options */
149 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
150 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
151 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
152 if(strncasecmp(cptr, "K1=", 3)==0) {
153 strlcpy(k1file, cptr+3, FILENAME_MAX); continue;
154 } else if(strncasecmp(cptr, "K2=", 3)==0) {
155 strlcpy(k2file, cptr+3, FILENAME_MAX); continue;
156 } else if(strncasecmp(cptr, "KI=", 3)==0) {
157 strlcpy(kifile, cptr+3, FILENAME_MAX); continue;
158 } else if(strncasecmp(cptr, "VB=", 3)==0) {
159 strlcpy(vbfile, cptr+3, FILENAME_MAX); continue;
160 } else if(strncasecmp(cptr, "DV=", 3)==0) {
161 strlcpy(dvfile, cptr+3, FILENAME_MAX); continue;
162 } else if(strncasecmp(cptr, "K2K3=", 5)==0) {
163 strlcpy(k2k3file, cptr+5, FILENAME_MAX); continue;
164 } else if(strncasecmp(cptr, "T1=", 3)==0) {
165 strlcpy(t1file, cptr+3, FILENAME_MAX); continue;
166 } else if(strncasecmp(cptr, "T2=", 3)==0) {
167 strlcpy(t2file, cptr+3, FILENAME_MAX); continue;
168 } else if(strncasecmp(cptr, "THR=", 4)==0) {
169 double v; ret=atof_with_check(cptr+4, &v);
170 if(!ret && v>=0.0 && v<=200.0) {calcThreshold=(float)(0.01*v); continue;}
171 } else if(strncasecmp(cptr, "END=", 4)==0) {
172 ret=atof_with_check(cptr+4, &fittime); if(!ret && fittime>0.0) continue;
173 } else if(strcasecmp(cptr, "WFA")==0) {
174 weights=0; continue;
175 } else if(strcasecmp(cptr, "WF")==0) {
176 weights=1; continue;
177 } else if(strcasecmp(cptr, "W1")==0) {
178 weights=2; continue;
179 } else if(strncasecmp(cptr, "min=", 4)==0) {
180 if(atof_with_check(cptr+4, &alphamin)==0 && alphamin>=0.0) continue;
181 } else if(strncasecmp(cptr, "max=", 4)==0) {
182 if(atof_with_check(cptr+4, &alphamax)==0 && alphamax>=0.0) continue;
183 } else if(strncasecmp(cptr, "theta1max=", 10)==0) {
184 if(atof_with_check(cptr+10, &theta1max)==0 && theta1max>=0.0) continue;
185 } else if(strncasecmp(cptr, "theta2max=", 10)==0) {
186 if(atof_with_check(cptr+10, &theta2max)==0 && theta2max>=0.0) continue;
187 } else if(strncasecmp(cptr, "Vbmax=", 6)==0) {
188 if(atof_with_check(cptr+6, &Vbmax)==0 && Vbmax>=0.0 && Vbmax<=1.0) continue;
189 } else if(strncasecmp(cptr, "NR=", 3)==0) {
190 bfNr=atoi(cptr+3); if(bfNr>5E+04) bfNr=5E+04;
191 if(bfNr>=50) continue;
192 } else if(strncasecmp(cptr, "BF=", 3)==0) {
193 strlcpy(bfsfile, cptr+3, FILENAME_MAX); if(strlen(bfsfile)>0) continue;
194 } else if(strncasecmp(cptr, "ERR=", 4)==0) {
195 strlcpy(errfile, cptr+4, FILENAME_MAX); if(strlen(errfile)>0) continue;
196 } else if(strcasecmp(cptr, "QR")==0) {
197 method=METHOD_QR; continue;
198 } else if(strcasecmp(cptr, "BVLS")==0) {
199 method=METHOD_BVLS; continue;
200 }
201 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
202 return(1);
203 } else break;
204
205 /* Print help or version? */
206 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
207 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
208 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
209
210 /* Process other arguments, starting from the first non-option */
211 if(ai<argc) strlcpy(ptacfile, argv[ai++], FILENAME_MAX);
212 if(ai<argc) strlcpy(btacfile, argv[ai++], FILENAME_MAX);
213 if(ai<argc) strlcpy(petfile, argv[ai++], FILENAME_MAX);
214 if(ai<argc) strlcpy(k3file, argv[ai++], FILENAME_MAX);
215 if(ai<argc) {
216 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
217 return(1);
218 }
219 /* Did we get all the information that we need? */
220 if(!k3file[0]) {
221 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
222 return(1);
223 }
224 if(strcasecmp(btacfile, "NONE")==0 || strcasecmp(btacfile, "'NONE'")==0 || Vbmax<=0.0) {
225 fitVb=0; Vbmax=0.0; btacfile[0]=(char)0;
226 if(vbfile[0]) {
227 fprintf(stderr, "Error: Vb cannot be calculated without BTAC file.\n");
228 return(1);
229 }
230 }
231
232 /* In verbose mode print arguments and options */
233 if(verbose>1) {
234 printf("ptacfile := %s\n", ptacfile);
235 if(btacfile[0]) printf("btacfile := %s\n", btacfile);
236 printf("petfile := %s\n", petfile);
237 printf("k3file := %s\n", k3file);
238 if(vbfile[0]) printf("vbfile := %s\n", vbfile);
239 if(k1file[0]) printf("k1file := %s\n", k1file);
240 if(k2file[0]) printf("k2file := %s\n", k2file);
241 if(k2k3file[0]) printf("k2k3file := %s\n", k2k3file);
242 if(kifile[0]) printf("kifile := %s\n", kifile);
243 if(dvfile[0]) printf("dvfile := %s\n", dvfile);
244 if(errfile[0]) printf("errfile := %s\n", errfile);
245 if(t1file[0]) printf("t1file := %s\n", t1file);
246 if(t2file[0]) printf("t2file := %s\n", t2file);
247 if(bfsfile[0]) printf("bfsfile := %s\n", bfsfile);
248 printf("fitVb := %d\n", fitVb);
249 printf("method := %s\n", method_str[method]);
250 printf("calcThreshold :=%g\n", calcThreshold);
251 printf("weights := %d\n", weights);
252 if(fittime>0.0) printf("required_fittime := %g min\n", fittime);
253 printf("bfNr := %d\n", bfNr);
254 if(alphamin>0.0) printf("alpha_min := %g\n", alphamin);
255 if(alphamax>0.0) printf("alpha_max := %g\n", alphamax);
256 printf("theta1_max := %g\n", theta1max);
257 printf("theta2_max := %g\n", theta2max);
258 printf("Vb_max := %g\n", Vbmax);
259 }
260 if(verbose>8) IMG_TEST=SIF_TEST=verbose-8; else IMG_TEST=SIF_TEST=0;
261 if(verbose>20) ECAT63_TEST=ECAT7_TEST=verbose-20; else ECAT63_TEST=ECAT7_TEST=0;
262
263 /* Check user-defined alpha range */
264 if(alphamin>=alphamax) {
265 fprintf(stderr, "Error: invalid range for k2+k3 (%g - %g).\n", alphamin, alphamax);
266 return(1);
267 }
268
269
270 /*
271 * Read PET image and input TACs
272 */
273 if(verbose>0) {printf("reading data files\n"); fflush(stdout);}
274 DFT tac; dftInit(&tac);
275 DFT inp; dftInit(&inp);
276 IMG img; imgInit(&img);
277 int dataNr=0;
278 char errmsg[512];
280 petfile, NULL, ptacfile, btacfile, NULL, &fittime, &dataNr, &img,
281 &inp, &tac, 1, stdout, verbose-2, errmsg);
282 if(ret!=0) {
283 fprintf(stderr, "Error: %s.\n", errmsg);
284 if(verbose>1) printf(" ret := %d\n", ret);
285 fflush(stderr); fflush(stdout);
286 return(2);
287 }
288 int origDataNr=tac.frameNr;
289 if(imgNaNs(&img, 1)>0)
290 if(verbose>0) fprintf(stderr, "Warning: missing pixel values.\n");
291 /* Set time unit to min, also for integrals in y2[] */
292 if(tac.timeunit==TUNIT_SEC) {
293 for(int fi=0; fi<tac.frameNr; fi++) tac.voi[0].y2[fi]/=60.0;
294 for(int fi=0; fi<tac.frameNr; fi++) tac.voi[0].y3[fi]/=3600.0;
295 }
296 ret=dftTimeunitConversion(&tac, TUNIT_MIN);
297 ret=dftTimeunitConversion(&inp, TUNIT_MIN);
298 if(verbose>1) {
299 printf("fittimeFinal := %g min\n", fittime);
300 printf("dataNr := %d\n", dataNr);
301 }
302 /* Check that image is dynamic */
303 if(dataNr<4) {
304 fprintf(stderr, "Error: too few time frames for fitting.\n");
305 if(verbose>1) imgInfo(&img);
306 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&inp); return(2);
307 }
308
309 /* Add place for tissue TACs too */
310 if(verbose>1) fprintf(stdout, "allocating working memory for pixel TACs\n");
311 ret=dftAddmem(&tac, 2);
312 if(ret) {
313 fprintf(stderr, "Error: cannot allocate memory.\n");
314 if(verbose>0) printf("ret := %d\n", ret);
315 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&inp); return(2);
316 }
317 strcpy(tac.voi[0].name, "plasma");
318 strcpy(tac.voi[1].name, "blood"); // can be empty
319 strcpy(tac.voi[2].name, "tissue");
320 tac.voiNr=3;
321
322
323 /* Determine the threshold */
324 double threshold=calcThreshold*tac.voi[0].y2[dataNr-1];
325 if(verbose>1) printf("threshold_AUC := %g\n", threshold);
326
327 /* Set weights as requested */
328 if(imgSetWeights(&img, weights, verbose-5)!=0) {
329 fprintf(stderr, "Error: cannot calculate weights.\n");
330 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&inp); return(3);
331 }
332 for(int i=0; i<dataNr; i++) tac.w[i]=img.weight[i];
333
334
335 /*
336 * Calculate the basis functions
337 */
338 if(verbose>1) fprintf(stdout, "calculating basis functions\n");
339 DFT bf; dftInit(&bf);
340 tac.frameNr=dataNr; // origDataNr contains the full frame number
341 ret=bfIrr2TCM(&inp, &tac, &bf, bfNr, alphamin, alphamax, errmsg, verbose-2);
342 tac.frameNr=origDataNr;
343 if(ret) {
344 fprintf(stderr, "Error: cannot calculate basis functions (%d).\n", ret);
345 imgEmpty(&img); dftEmpty(&inp); dftEmpty(&tac); return(4);
346 }
347 /* Original sampling blood data not needed any more */
348 dftEmpty(&inp);
349 /* Note that basis functions are to be saved later (in bfsfile), after it is known
350 in how many image pixels each basis function was found to give the best fit.
351 */
352 /* Allocate memory for BF counters on how often each BF is
353 found to provide the optimal fit */
354 int *bf_opt_nr=(int*)malloc(bfNr*sizeof(int));
355 for(int bi=0; bi<bf.voiNr; bi++) bf_opt_nr[bi]=0.0;
356
357
358
359
360 /*
361 * Allocate result images (allocate all, even if user did not want to save those)
362 */
363 if(verbose>1) fprintf(stdout, "allocating memory for parametric image data\n");
364 IMG k3img; imgInit(&k3img);
365 IMG k1img; imgInit(&k1img);
366 IMG k2img; imgInit(&k2img);
367 IMG kiimg; imgInit(&kiimg);
368 IMG dvimg; imgInit(&dvimg);
369 IMG vbimg; imgInit(&vbimg);
370 IMG erimg; imgInit(&erimg);
371 IMG k2k3img; imgInit(&k2k3img);
372 IMG t1img; imgInit(&t1img);
373 IMG t2img; imgInit(&t2img);
374 ret=imgAllocateWithHeader( &k3img, img.dimz, img.dimy, img.dimx, 1, &img);
375 if(!ret) ret=imgAllocateWithHeader(&k1img, img.dimz, img.dimy, img.dimx, 1, &img);
376 if(!ret) ret=imgAllocateWithHeader(&k2img, img.dimz, img.dimy, img.dimx, 1, &img);
377 if(!ret) ret=imgAllocateWithHeader(&kiimg, img.dimz, img.dimy, img.dimx, 1, &img);
378 if(!ret) ret=imgAllocateWithHeader(&dvimg, img.dimz, img.dimy, img.dimx, 1, &img);
379 if(!ret) ret=imgAllocateWithHeader(&vbimg, img.dimz, img.dimy, img.dimx, 1, &img);
380 if(!ret) ret=imgAllocateWithHeader(&erimg, img.dimz, img.dimy, img.dimx, 1, &img);
381 if(!ret) ret=imgAllocateWithHeader(&k2k3img, img.dimz, img.dimy, img.dimx, 1, &img);
382 if(!ret) ret=imgAllocateWithHeader(&t1img, img.dimz, img.dimy, img.dimx, 1, &img);
383 if(!ret) ret=imgAllocateWithHeader(&t2img, img.dimz, img.dimy, img.dimx, 1, &img);
384 if(ret) {
385 fprintf(stderr, "Error: cannot allocate memory for result image.\n");
386 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&bf); free(bf_opt_nr);
387 imgEmpty(&k3img); imgEmpty(&kiimg); imgEmpty(&k1img); imgEmpty(&k2img);
388 imgEmpty(&dvimg); imgEmpty(&vbimg); imgEmpty(&erimg); imgEmpty(&k2k3img);
389 imgEmpty(&t1img); imgEmpty(&t2img);
390 return(5);
391 }
392 /* set 'frame time' for parametric images */
393 k3img.start[0]=k1img.start[0]=k2img.start[0]=kiimg.start[0]=vbimg.start[0]=
394 dvimg.start[0]=erimg.start[0]=k2k3img.start[0]=t1img.start[0]=t2img.start[0]=0.0;
395 k3img.end[0]=k1img.end[0]=k2img.end[0]=kiimg.end[0]=vbimg.end[0]=
396 dvimg.end[0]=erimg.end[0]=k2k3img.end[0]=t1img.end[0]=t2img.end[0]=60.*fittime;
397 /* set units in parametric images */
398 k3img.unit=k2img.unit=k2k3img.unit=CUNIT_PER_MIN;
399 dvimg.unit=vbimg.unit=CUNIT_ML_PER_ML;
400 k1img.unit=kiimg.unit=t1img.unit=t2img.unit=CUNIT_ML_PER_ML_PER_MIN;
401 erimg.unit=CUNIT_UNITLESS;
402 /* and set the more or less necessary things */
406 k3img.isWeight=k1img.isWeight=k2img.isWeight=kiimg.isWeight=vbimg.isWeight=
407 dvimg.isWeight=erimg.isWeight=k2k3img.isWeight=t1img.isWeight=t2img.isWeight=0;
408
409
410 /* Fitting */
411
412 int thresholded_nr=0;
413 int nosolution_nr=0;
414
415 if(method==METHOD_QR) {
416
417 /*
418 * Allocate memory for QR
419 */
420 int M, N;
421 M=dataNr; N=3; if(fitVb==0) N--;
422 double **mem, **A, *B, X[N], *tau, *residual, RNORM, *chain;
423 double *qrweight, **wws, *ws, *wwschain;
424 if(verbose>1) fprintf(stdout, "allocating memory for QR\n");
425 chain=(double*)malloc((M+1)*N*bf.voiNr * sizeof(double));
426 mem=(double**)malloc(bf.voiNr * sizeof(double*));
427 A=(double**)malloc(M * sizeof(double*));
428 B=(double*)malloc(M*sizeof(double));
429 residual=(double*)malloc(M*sizeof(double));
430 qrweight=(double*)malloc(M*sizeof(double));
431 wwschain=(double*)malloc((M*N+2*M)*sizeof(double));
432 wws=(double**)malloc(M * sizeof(double*));
433 if(chain==NULL || B==NULL || A==NULL || residual==NULL || qrweight==NULL ||
434 wwschain==NULL || wws==NULL)
435 {
436 fprintf(stderr, "Error: out of memory.\n");
437 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&bf); free(bf_opt_nr);
438 imgEmpty(&k3img); imgEmpty(&kiimg); imgEmpty(&k1img); imgEmpty(&k2img);
439 imgEmpty(&dvimg); imgEmpty(&vbimg); imgEmpty(&erimg); imgEmpty(&k2k3img);
440 imgEmpty(&t1img); imgEmpty(&t2img);
441 return(5);
442 }
443 for(int bi=0; bi<bf.voiNr; bi++) mem[bi]=chain+bi*(M+1)*N;
444 for(int m=0; m<M; m++) wws[m]=wwschain+m*N;
445 ws=wwschain+M*N;
446
447 /* Pre-compute QR weights for faster execution */
448 for(int m=0; m<M; m++) {
449 if(img.weight[m]<=1.0e-20) qrweight[m]=0.0;
450 else qrweight[m]=sqrt(img.weight[m]);
451 }
452
453
454 /* Make A matrix, and QR decomposition for it, for all pixels
455 beforehand for faster execution */
456 if(verbose>1) fprintf(stdout, "calculating QR decomposition\n");
457 for(int bi=0; bi<bf.voiNr; bi++) {
458
459 /* Define memory site for coefficient matrix and vector tau */
460 for(int m=0; m<M; m++) A[m]=mem[bi]+m*N;
461 tau=mem[bi]+M*N;
462
463 /* Initiate matrix (A = mem[bi]) */
464 for(int m=0; m<M; m++) {
465 A[m][0]=tac.voi[0].y2[m]; // plasma integral
466 A[m][1]=bf.voi[bi].y[m]; // basis function
467 if(N>2) A[m][2]=tac.voi[1].y[m]; // blood TAC for Vb estimation
468 }
469
470 /* Apply data weights */
471 for(int m=0; m<M; m++)
472 for(int n=0; n<N; n++)
473 A[m][n]*=qrweight[m];
474
475 /* Compute QR decomposition of the coefficient matrix */
476 ret=qr_decomp(A, M, N, tau, wws, ws);
477
478 if(ret>0) { /* Decomposition failed */
479 free(chain); free(B); free(residual);
480 free(A); free(wwschain); free(wws); free(qrweight); free(mem);
481 imgEmpty(&img); dftEmpty(&tac); dftEmpty(&bf); free(bf_opt_nr);
482 imgEmpty(&k3img); imgEmpty(&kiimg); imgEmpty(&k1img); imgEmpty(&k2img);
483 imgEmpty(&dvimg); imgEmpty(&vbimg); imgEmpty(&erimg); imgEmpty(&k2k3img);
484 imgEmpty(&t1img); imgEmpty(&t2img);
485 return (6);
486 }
487 } /* next BF */
488
489
490 /*
491 * Compute pixel-by-pixel
492 */
493 if(verbose>0) {fprintf(stdout, "computing QR pixel-by-pixel\n"); fflush(stdout);}
494 double maxk3=0.0, maxk1=0.0;
495 double maxt1=0.0, maxt2=0.0;
496 double *ct, *cti;
497 ct=tac.voi[2].y; cti=tac.voi[2].y2;
498 for(int pi=0; pi<img.dimz; pi++) {
499 if(img.dimz>1 && verbose>0) {fprintf(stdout, "."); fflush(stdout);}
500 for(int yi=0; yi<img.dimy; yi++) {
501 for(int xi=0; xi<img.dimx; xi++) {
502 /* Set pixel results to zero */
503 k3img.m[pi][yi][xi][0]=0.0;
504 k1img.m[pi][yi][xi][0]=0.0;
505 k2img.m[pi][yi][xi][0]=0.0;
506 kiimg.m[pi][yi][xi][0]=0.0;
507 dvimg.m[pi][yi][xi][0]=0.0;
508 vbimg.m[pi][yi][xi][0]=0.0;
509 erimg.m[pi][yi][xi][0]=0.0;
510 k2k3img.m[pi][yi][xi][0]=0.0;
511 t1img.m[pi][yi][xi][0]=0.0;
512 t2img.m[pi][yi][xi][0]=0.0;
513 /* Copy pixel TAC and calculate pixel integral */
514 for(int m=0; m<M; m++) {ct[m]=img.m[pi][yi][xi][m];}
515 ret=petintegral(tac.x1, tac.x2, ct, tac.frameNr, cti, NULL);
516 if(ret) continue;
517 /* if AUC at the end is less than threshold value, then do nothing more */
518 if(cti[dataNr-1]<threshold) {thresholded_nr++; continue;}
519
520 /* Go through all basis functions */
521 int bi_min=-1;
522 double rnorm_min=1.0E80;
523 double p1, p2, p3, p4; p1=p2=p3=p4=0.0;
524 for(int bi=0; bi<bf.voiNr; bi++) {
525
526 /* Define memory site for present coefficient matrix and vector tau */
527 for(int m=0; m<M; m++) {A[m]=mem[bi]+ m*N;}
528 tau=mem[bi]+M*N;
529
530 /* Get data vector */
531 for(int m=0; m<M; m++) {
532 B[m]=img.m[pi][yi][xi][m];
533 /* Apply data weights */
534 B[m]*=qrweight[m];
535 }
536
537 /* Compute solution */
538 ret=qr_solve(A, M, N, tau, B, X, residual, &RNORM, wws, ws);
539 if(ret!=0) { /* no solution is possible */
540 for(int n=0; n<N; n++) X[n]=0.0;
541 RNORM=1.0E80;
542 }
543
544 /* Check if this was best fit for now; if yes, then save the parameters */
545 if(RNORM<rnorm_min) {
546 rnorm_min=RNORM; bi_min=bi;
547 p1=X[0];
548 p2=X[1];
549 if(N>2) p3=X[2]; else p3=0.0; // Vb
550 p4=bf.voi[bi_min].size;
551 }
552 } /* next basis function */
553
554 if(verbose>6 && yi==4*img.dimy/10 && xi==4*img.dimx/10) {
555 printf(" Pixel (%d,%d,%d), P1=%g P2=%g P3=%g theta=%g\n",
556 pi, yi, xi, p1, p2, p3, p4);
557 if(verbose>10) dftPrint(&tac);
558 }
559
560 /* count the selected BFs */
561 if(!(rnorm_min<1.0E60)) {nosolution_nr++; continue;}
562 else bf_opt_nr[bi_min]+=1;
563
564 /* Put results to output images */
565 if(bi_min==0) ret=1; else if(bi_min==bf.voiNr-1) ret=2; else ret=0;
566 erimg.m[pi][yi][xi][0]=(float)ret;
567 t1img.m[pi][yi][xi][0]=p1; // theta1
568 t2img.m[pi][yi][xi][0]=p2; // theta2
569 k2k3img.m[pi][yi][xi][0]=p4; // alpha=k2+k3
570 if(p1>maxt1) maxt1=p1;
571 if(p2>maxt2) maxt2=p2;
572 if(fitVb!=0) {
573 vbimg.m[pi][yi][xi][0]=p3;
574 if(p3>0.99) continue; // if very large Vb, then all others should be zero
575 }
576 //if((p1)<1.0E-10) continue; // if very small Ki, then all others are zero, too
577 if((p1+p2)<1.0E-10) continue; // if very small K1, then all others are zero, too
578 k1img.m[pi][yi][xi][0]=p1+p2; if(p3>0.0 && p3<0.9) k1img.m[pi][yi][xi][0]/=(1.0-p3);
579 k2img.m[pi][yi][xi][0]=p2*p4/(p1+p2);
580 k3img.m[pi][yi][xi][0]=p1*p4/(p1+p2);
581 kiimg.m[pi][yi][xi][0]=p1; if(p3>0.0 && p3<0.9) kiimg.m[pi][yi][xi][0]/=(1.0-p3);
582 if(p4>0.00001) dvimg.m[pi][yi][xi][0]=k1img.m[pi][yi][xi][0]/p4;
583 if(k1img.m[pi][yi][xi][0]>maxk1) maxk1=k1img.m[pi][yi][xi][0];
584 if(k3img.m[pi][yi][xi][0]>maxk3) maxk3=k3img.m[pi][yi][xi][0];
585 } /* next column */
586 } /* next row */
587 } /* next plane */
588 if(verbose>0) {fprintf(stdout, "\ndone.\n"); fflush(stdout);}
589 if(verbose>1 || thresholded_nr>0) {
590 double f;
591 f=(double)thresholded_nr/((double)(k3img.dimx*k3img.dimy*k3img.dimz));
592 f*=100.; if(f<3.0) printf("%g%%", f); else printf("%.0f%%", f);
593 printf(" of pixels were not fitted due to threshold.\n");
594 if(verbose>2) printf("thresholded %d pixels\n", thresholded_nr);
595 }
596 if(verbose>0 || nosolution_nr>0)
597 fprintf(stdout, "no QR solution for %d pixels.\n", nosolution_nr);
598 if(verbose>1) {
599 printf("max_theta1 := %g\n", maxt1);
600 printf("max_theta2 := %g\n", maxt2);
601 printf("max_k1 := %g\n", maxk1);
602 printf("max_k3 := %g\n", maxk3);
603 }
604
605 /* Free memory of QR */
606 free(chain); free(B); free(residual); free(A); free(wwschain);
607 free(wws); free(qrweight); free(mem);
608
609
610 } else if(method==METHOD_BVLS) {
611
612 /*
613 * Compute pixel-by-pixel
614 */
615 if(verbose>0) {fprintf(stdout, "computing BF BVLS pixel-by-pixel\n"); fflush(stdout);}
616 int m=dataNr;
617 int n=3; if(fitVb==0) n--;
618 int nm=n*m;
619 double maxk3=0.0, maxk1=0.0;
620 double maxt1=0.0, maxt2=0.0;
621
622#pragma omp parallel for
623 for(int pi=0; pi<img.dimz; pi++) {
624 if(img.dimz>1 && verbose>0) {fprintf(stdout, "."); fflush(stdout);}
625 for(int yi=0; yi<img.dimy; yi++) {
626 for(int xi=0; xi<img.dimx; xi++) {
627 /* Set pixel results to zero */
628 k3img.m[pi][yi][xi][0]=0.0;
629 k1img.m[pi][yi][xi][0]=0.0;
630 k2img.m[pi][yi][xi][0]=0.0;
631 kiimg.m[pi][yi][xi][0]=0.0;
632 dvimg.m[pi][yi][xi][0]=0.0;
633 vbimg.m[pi][yi][xi][0]=0.0;
634 erimg.m[pi][yi][xi][0]=0.0;
635 k2k3img.m[pi][yi][xi][0]=0.0;
636 t1img.m[pi][yi][xi][0]=0.0;
637 t2img.m[pi][yi][xi][0]=0.0;
638 /* if AUC at the end is less than threshold value, then do nothing more */
639 float pxlint[dataNr];
640 if(fpetintegral(img.start, img.end, img.m[pi][yi][xi], dataNr, pxlint, NULL)!=0) continue;
641 if(pxlint[dataNr-1]<60.*threshold) {thresholded_nr++; continue;}
642 /* Allocate memory required by BVLS */
643 double *mat=(double*)malloc(nm*sizeof(double));
644 if(mat==NULL) continue;
645 double b[m], x[n], bl[n], bu[n], w[n], zz[m];
646 double act[m*(n+2)], r2;
647 int istate[n+1], iterNr;
648 /* Fit pixel with each basis function */
649 int bi_min=-1;
650 double r2_min=1.0E80;
651 double p1, p2, p3, p4; p1=p2=p3=p4=0.0;
652 for(int bi=0; bi<bf.voiNr; bi++) {
653 /* Setup data matrix A and vector B */
654 for(int mi=0; mi<m; mi++) b[mi]=img.m[pi][yi][xi][mi];
655 for(int mi=0; mi<m; mi++) {
656 mat[mi]=tac.voi[0].y2[mi]; // plasma integral
657 mat[mi+m]=bf.voi[bi].y[mi]; // basis function
658 if(n>2) mat[mi+(2*m)]=tac.voi[1].y[mi]; // blood TAC for Vb estimation
659 }
660 /* Apply data weights */
661 if(tac.isweight) llsqWght(n, m, NULL, mat, b, tac.w);
662 /* Set istate vector to indicate that all parameters are non-bound */
663 istate[n]=0; for(int ni=0; ni<n; ni++) istate[ni]=1+ni;
664 /* Set parameter limits */
665 bl[0]=0.0; bu[0]=theta1max;
666 bl[1]=0.0; bu[1]=theta2max;
667 if(fitVb!=0) {bl[2]=0.0; bu[2]=Vbmax;}
668 /* Set max iterations */
669 iterNr=3*n;
670 /* Compute BVLS */
671 ret=bvls(1, m, n, mat, b, bl, bu, x, w, act, zz, istate, &iterNr, verbose-30);
672 if(ret!=0) continue; /* no solution is possible */
673 r2=w[0];
674 /* Check if this was best fit for now; if yes, then save the parameters */
675 if(r2<r2_min) {
676 r2_min=r2; bi_min=bi;
677 p1=x[0];
678 p2=x[1];
679 if(n>2) p3=x[2]; else p3=0.0; // Vb
680 p4=bf.voi[bi_min].size;
681 }
682 } /* next basis function */
683 free(mat);
684
685 /* count the selected BFs */
686 if(!(r2_min<1.0E60)) {nosolution_nr++; continue;}
687 else bf_opt_nr[bi_min]+=1;
688
689 /* Put results to output images */
690 if(bi_min==0) ret=1; else if(bi_min==bf.voiNr-1) ret=2; else ret=0;
691 erimg.m[pi][yi][xi][0]=(float)ret;
692 t1img.m[pi][yi][xi][0]=p1; // theta1
693 t2img.m[pi][yi][xi][0]=p2; // theta2
694 k2k3img.m[pi][yi][xi][0]=p4; // alpha=k2+k3
695 if(p1>maxt1) maxt1=p1;
696 if(p2>maxt2) maxt2=p2;
697 if(fitVb!=0) {
698 vbimg.m[pi][yi][xi][0]=p3;
699 if(p3>0.99) continue; // if very large Vb, then all others should be zero
700 }
701 if((p1+p2)<1.0E-10) continue; // if very small K1, then all others are zero, too
702 k1img.m[pi][yi][xi][0]=p1+p2; if(p3>0.0 && p3<0.9) k1img.m[pi][yi][xi][0]/=(1.0-p3);
703 k2img.m[pi][yi][xi][0]=p2*p4/(p1+p2);
704 k3img.m[pi][yi][xi][0]=p1*p4/(p1+p2);
705 kiimg.m[pi][yi][xi][0]=p1; if(p3>0.0 && p3<0.9) kiimg.m[pi][yi][xi][0]/=(1.0-p3);
706 if(p4>0.00001) dvimg.m[pi][yi][xi][0]=k1img.m[pi][yi][xi][0]/p4;
707 if(k1img.m[pi][yi][xi][0]>maxk1) maxk1=k1img.m[pi][yi][xi][0];
708 if(k3img.m[pi][yi][xi][0]>maxk3) maxk3=k3img.m[pi][yi][xi][0];
709
710 } // next image column
711 } // next image row
712 } // next image plane
713 if(verbose>0) {fprintf(stdout, "\ndone.\n"); fflush(stdout);}
714 if(verbose>1 || thresholded_nr>0) {
715 double f;
716 f=(double)thresholded_nr/((double)(k3img.dimx*k3img.dimy*k3img.dimz));
717 f*=100.; if(f<3.0) printf("%g%%", f); else printf("%.0f%%", f);
718 printf(" of pixels were not fitted due to threshold.\n");
719 if(verbose>2) printf("thresholded %d pixels\n", thresholded_nr);
720 }
721 if(verbose>0 || nosolution_nr>0)
722 fprintf(stdout, "no solution for %d pixels.\n", nosolution_nr);
723 if(verbose>1) {
724 printf("max_theta1 := %g\n", maxt1);
725 printf("max_theta2 := %g\n", maxt2);
726 printf("max_k1 := %g\n", maxk1);
727 printf("max_k3 := %g\n", maxk3);
728 }
729
730 } else {
731
732 fprintf(stderr, "Error: selected method not available.");
733 dftEmpty(&bf); free(bf_opt_nr);
734 imgEmpty(&k3img); imgEmpty(&kiimg); imgEmpty(&k1img); imgEmpty(&k2img);
735 imgEmpty(&dvimg); imgEmpty(&vbimg); imgEmpty(&erimg); imgEmpty(&k2k3img);
736 imgEmpty(&t1img); imgEmpty(&t2img);
737 return(1);
738
739 }
740
741
742
743 /* No need for dynamic image or input tac any more */
744 imgEmpty(&img); dftEmpty(&tac);
745
746 /*
747 * Save basis functions if required;
748 * this is done not before, so that also the number of optimal fits
749 * achieved with each BF can be saved as the "size".
750 */
751 if(bfsfile[0]) {
752 for(int bi=0; bi<bf.voiNr; bi++)
753 sprintf(bf.voi[bi].place, "%d", bf_opt_nr[bi]);
754 if(dftWrite(&bf, bfsfile)) {
755 fprintf(stderr, "Error in writing %s: %s\n", bfsfile, dfterrmsg);
756 dftEmpty(&bf); free(bf_opt_nr);
757 imgEmpty(&k3img); imgEmpty(&kiimg); imgEmpty(&k1img); imgEmpty(&k2img);
758 imgEmpty(&dvimg); imgEmpty(&vbimg); imgEmpty(&erimg); imgEmpty(&k2k3img);
759 imgEmpty(&t1img); imgEmpty(&t2img);
760 return(11);
761 }
762 if(verbose>0) fprintf(stdout, "basis functions were written in %s\n", bfsfile);
763 }
764
765 /* No need for basis functions any more */
766 dftEmpty(&bf); free(bf_opt_nr);
767
768
769 /*
770 * Save parametric images
771 */
772 ret=imgWrite(k3file, &k3img);
773 if(!ret && k1file[0]) ret=imgWrite(k1file, &k1img);
774 if(!ret && k2file[0]) ret=imgWrite(k2file, &k2img);
775 if(!ret && kifile[0]) ret=imgWrite(kifile, &kiimg);
776 if(!ret && vbfile[0]) ret=imgWrite(vbfile, &vbimg);
777 if(!ret && dvfile[0]) ret=imgWrite(dvfile, &dvimg);
778 if(!ret && errfile[0]) ret=imgWrite(errfile, &erimg);
779 if(!ret && k2k3file[0]) ret=imgWrite(k2k3file, &k2k3img);
780 if(!ret && t1file[0]) ret=imgWrite(t1file, &t1img);
781 if(!ret && t2file[0]) ret=imgWrite(t2file, &t2img);
782 imgEmpty(&k3img); imgEmpty(&kiimg); imgEmpty(&k1img); imgEmpty(&k2img);
783 imgEmpty(&dvimg); imgEmpty(&vbimg); imgEmpty(&erimg); imgEmpty(&k2k3img);
784 imgEmpty(&t1img); imgEmpty(&t2img);
785 if(ret) {
786 fprintf(stderr, "Error: cannot write parametric image.\n");
787 return(11);
788 }
789 if(verbose>0) fprintf(stdout, "Parametric image(s) saved.\n");
790
791
792 return(0);
793}
794/*****************************************************************************/
795
796/*****************************************************************************/
int bfIrr2TCM(DFT *input, DFT *tissue, DFT *bf, int bfNr, double thetamin, double thetamax, char *status, int verbose)
Definition bf_model.c:217
int bvls(int key, const int m, const int n, double *a, double *b, double *bl, double *bu, double *x, double *w, double *act, double *zz, int *istate, int *iter, int verbose)
Bounded-value least-squares method to solve the linear problem A x ~ b , subject to limit1 <= x <= li...
Definition bvls.c:24
int llsqWght(int N, int M, double **A, double *a, double *b, double *weight)
Definition bvls.c:419
int atof_with_check(char *double_as_string, double *result_value)
Definition decpoint.c:107
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 ECAT63_TEST
Definition ecat63h.c:6
int ECAT7_TEST
Definition ecat7h.c:6
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 fpetintegral(float *x1, float *x2, float *y, int nr, float *ie, float *iie)
Integrate PET TAC data to frame mid times. Float version of petintegral().
Definition integr.c:838
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:244
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 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
int timeunit
double * w
double * x1
int voiNr
double * x2
int frameNr
int isweight
unsigned short int dimx
float **** m
char decayCorrection
char unit
float * weight
float * start
unsigned short int dimz
unsigned short int dimy
float * end
char isWeight
double size
double * y2
double * y
char name[MAX_REGIONNAME_LEN+1]
double * y3
char place[MAX_REGIONSUBNAME_LEN+1]