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