TPCCLIB
Loading...
Searching...
No Matches
imgflow.c
Go to the documentation of this file.
1
9/*****************************************************************************/
10#include "tpcclibConfig.h"
11/*****************************************************************************/
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <string.h>
16#include <math.h>
17#include <time.h>
18/*****************************************************************************/
19#include "libtpcmisc.h"
20#include "libtpccurveio.h"
21#include "libtpcimgio.h"
22#include "libtpcimgp.h"
23#include "libtpcmodel.h"
24#include "libtpcmodext.h"
25/*****************************************************************************/
26#define NNLS_N 3
27/*****************************************************************************/
28
29/*****************************************************************************/
30static char *info[] = {
31 "Estimation of rate constants K1, k2 and Va from dynamic PET image in",
32 "ECAT 6.3, ECAT 7.x, NIfTI-1, or Analyze 7.5 file format using",
33 "linearized two-compartment model [1].",
34 "Lawson-Hanson non-negative least squares (NNLS) method [2] is used to solve",
35 "general linear least squares functions.",
36 " ",
37 "When applied to dynamic [O-15]H2O studies, the resulting K1 image",
38 "equals perfusion (blood flow) image. K1 image can be divided by tissue",
39 "density (g/mL) (option -density) and multiplied by 100 (option -dL)",
40 "to achieve the blood flow image in units (mL blood)/((100 g tissue) * min).",
41 " ",
42 "When applied to dynamic [O-15]O2 brain studies, the resulting K1 image",
43 "can be converted to oxygen consumption image by multiplying it by",
44 "arterial oxygen concentration [3] (ml O2 / dL blood) to get the",
45 "parametric image in units mL O2 / ((100 ml tissue) * min).",
46 "The model assumptions hold only when oxygen consumption is 1-6.7",
47 "mL O2/(100g * min) and fit time is set to 300 s or less [3].",
48 " ",
49 "Arterial blood TAC must be corrected for decay and delay, with sample times",
50 "in seconds. Dynamic PET image must be corrected for decay.",
51 " ",
52 "Usage: @P [Options] btacfile imgfile flowfile",
53 " ",
54 "Options:",
55 " -end=<Fit end time (sec)>",
56 " Use data from 0 to end time; by default, all of it.",
57 " -k2=<filename>",
58 " Parametric k2 image is saved; in some situations perfusion calculation",
59 " from k2 can be more accurate than the default assumption of f=K1.",
60 " Perfusion can be calculated from k2 using equation f=k2*pH2O, where",
61 " pH2O is the physiological partition coefficient of water in tissue.",
62 " -Va=<filename>",
63 " Parametric Va image is saved.",
64 " Set -Va=0, if Va=0 is assumed; otherwise Va is always fitted.",
65// " -Vd=<filename>", // option to be removed
66// " Parametric K1/k2 image (apparent pH2O) is saved.",
67// " -wss=<filename>", // option to be removed
68// " Weighted sum-of-squares are written in specified image file.",
69 " -thr=<threshold%>",
70 " Pixels with AUC less than (threshold/100 x max AUC) are set to zero.",
71 " Default is 5%.",
72 " -max=<Max value>",
73 " Upper limit for flow values in final units.",
74 " -filter",
75 " Remove parametric pixel values that are over 4x higher than",
76 " their closest neighbours.",
77 " -noneg",
78 " Pixels where K1 estimates are negative are fitted again with",
79 " constraint Va=0.",
80 " -mL or -dL",
81 " Units in flow and Va images will be given per mL or per dL,",
82 " respectively. By default, units are per mL.",
83 " -density[=<value>]",
84 " With option -density the flow is calculated per gram or 100g tissue.",
85 " Tissue density can be changed from the default 1.04 g/ml.",
86 " -stdoptions", // List standard options like --help, -v, etc
87 " ",
88 "By default, the units of pixel values in the flow (K1) image is",
89 "(mL blood)/((mL tissue) * min), in DV image (mL blood)/(mL tissue),",
90 "in k2 image 1/min, and in Va image (mL blood/mL tissue),",
91 "but the flow and Va units can be changed with above listed options.",
92 " ",
93 "Example 1. Calculation of perfusion and arterial blood volume image,",
94 " stopping fit at 180 s:",
95 " @P -Va=s2345va.v -end=180 s2345abfit.kbq s2345dy1.v s2345flow.v",
96 " ",
97 "Example 2. Dynamic image is precorrected for vascular activity,",
98 " and all available data is used:",
99 " @P -Va=0 s2345abfit.kbq s2345dy1_vacorr.v s2345flow.v",
100 " ",
101 "References:",
102 "1. Blomqvist G. On the construction of functional maps in positron",
103 " emission tomography. J Cereb Blood Flow Metab. 1984;4:629-632.",
104 "2. Lawson CL & Hanson RJ. Solving least squares problems.",
105 " Prentice-Hall, 1974.",
106 "3. Ohta S, Meyer E, Thompson CJ, Gjedde A. Oxygen consumption of the",
107 " living human brain measured after a single inhalation of positron",
108 " emitting oxygen. J Cereb Blood Flow Metab. 1992;12:179-192.",
109 " ",
110 "See also: imgflowm, imgbfh2o, imgcbv, fit_h2o, b2t_h2o, imglhdv, fitdelay",
111 " ",
112 "Keywords: image, modelling, perfusion, blood flow, radiowater, NNLS",
113 0};
114/*****************************************************************************/
115
116/*****************************************************************************/
117/* Turn on the globbing of the command line, since it is disabled by default in
118 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
119 In Unix&Linux wildcard command line processing is enabled by default. */
120/*
121#undef _CRT_glob
122#define _CRT_glob -1
123*/
124int _dowildcard = -1;
125/*****************************************************************************/
126
127/*****************************************************************************/
128/*
129 * main()
130 */
131int main(int argc, char *argv[])
132{
133 int ai, help=0, version=0, verbose=1;
134 int fi, pi, yi, xi, ret;
135 int weight=0, dataNr=0, param_filt=0;
136 char inpfile[FILENAME_MAX], petfile[FILENAME_MAX], k1file[FILENAME_MAX];
137 char k2file[FILENAME_MAX], vafile[FILENAME_MAX], dvfile[FILENAME_MAX], wssfile[FILENAME_MAX];
138 char *cptr, tmp[FILENAME_MAX+1];
139 float calcThreshold=0.05;
140 double upperLimit=-1.0;
141 int per_gram=0;
142 double density=1.04;
143 double fittime=nan("");
144 int fitVa=1;
145 int k1_nn_constraint=0;
146 int per_dl=0;
147 DFT tac;
148 IMG img, k1img, k2img, vaimg, dvimg, wssimg;
149 double *cp, *cpi, *ct, *cti, Va;
150 /* nnls */
151 int nnls_n, nnls_m, nnls_index[NNLS_N];
152 double *nnls_a[NNLS_N], *nnls_b, *nnls_zz, nnls_x[NNLS_N], *nnls_mat,
153 nnls_wp[NNLS_N], *dptr, nnls_rnorm;
154
155
156
157 /*
158 * Get arguments
159 */
160 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
161 inpfile[0]=petfile[0]=k1file[0]=k2file[0]=vafile[0]=dvfile[0]=wssfile[0]=(char)0;
162 /* Get options */
163 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
164 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
165 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
166 if(strcasecmp(cptr, "W")==0) {
167 weight=1; continue;
168 } else if(strncasecmp(cptr, "FILTER", 4)==0) {
169 param_filt=1; continue;
170 } else if(strncasecmp(cptr, "THR=", 4)==0) {
171 double v; ret=atof_with_check(cptr+4, &v);
172 if(!ret && v<100.0) {calcThreshold=(float)(0.01*v); continue;}
173 } else if(strncasecmp(cptr, "DV=", 3)==0) {
174 strlcpy(dvfile, cptr+3, FILENAME_MAX); if(strlen(dvfile)) continue;
175 } else if(strncasecmp(cptr, "k2=", 3)==0) {
176 strlcpy(k2file, cptr+3, FILENAME_MAX); if(strlen(k2file)) continue;
177 } else if(strncasecmp(cptr, "VA=", 3)==0 || strncasecmp(cptr, "VB=", 3)==0) {
178 strlcpy(vafile, cptr+3, FILENAME_MAX); if(strlen(vafile)) continue;
179 } else if(strncasecmp(cptr, "DV=", 3)==0 || strncasecmp(cptr, "VD=", 3)==0) {
180 strlcpy(dvfile, cptr+3, FILENAME_MAX); if(strlen(dvfile)) continue;
181 } else if(strncasecmp(cptr, "noneg", 2)==0) {
182 k1_nn_constraint=1; continue;
183 } else if(strcasecmp(cptr, "DL")==0) {
184 per_dl=1; continue;
185 } else if(strcasecmp(cptr, "ML")==0) {
186 per_dl=0; continue;
187 } else if(strcasecmp(cptr, "DENSITY")==0) {
188 /* if plain -density, then use default density */
189 per_gram=1; continue;
190 } else if(strncasecmp(cptr, "DENSITY=", 8)==0) {
191 /* or read user-defined density */
192 per_gram=1;
193 density=atof_dpi(cptr+8); if(density>0.0) continue;
194 } else if(strncasecmp(cptr, "MAX=", 4)==0) {
195 upperLimit=atof_dpi(cptr+4); if(upperLimit>0.0) continue;
196 } else if(strncasecmp(cptr, "END=", 4)==0) {
197 fittime=atof_dpi(cptr+4)/60.; if(fittime>0.0) continue;
198 } else if(strncasecmp(cptr, "WSS=", 4)==0) {
199 strlcpy(wssfile, cptr+4, FILENAME_MAX); if(strlen(wssfile)>0) 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(inpfile, argv[ai++], FILENAME_MAX);
212 if(ai<argc) strlcpy(petfile, argv[ai++], FILENAME_MAX);
213 if(ai<argc) strlcpy(k1file, argv[ai++], FILENAME_MAX);
214 if(ai<argc) {fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]); return(1);}
215 /* Did we get all the information that we need? */
216 if(!k1file[0]) {
217 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
218 return(1);
219 }
220 if(!(fittime>0.0)) fittime=1.0E+020;
221 /* Check if Va=0 is assumed */
222 if(strcasecmp(vafile, "NONE")==0 || strcasecmp(vafile, "ZERO")==0 || strcasecmp(vafile, "0")==0)
223 {vafile[0]=(char)0; fitVa=0;}
224 /* In verbose mode print arguments and options */
225 if(verbose>1) {
226 printf("inpfile := %s\n", inpfile);
227 printf("petfile := %s\n", petfile);
228 printf("k1file := %s\n", k1file);
229 if(k2file[0]) printf("k2file := %s\n", k2file);
230 printf("fitVa := %d\n", fitVa);
231 if(vafile[0]) printf("vafile := %s\n", vafile);
232 if(dvfile[0]) printf("dvfile := %s\n", dvfile);
233 if(wssfile[0]) printf("wssfile := %s\n", wssfile);
234 printf("calcThreshold := %g\n", calcThreshold);
235 printf("max := %f\n", upperLimit);
236 printf("fittime := %g [sec]\n", 60.0*fittime);
237 printf("per_dl := %d\n", per_dl);
238 printf("per_gram := %d\n", per_gram);
239 printf("density := %g\n", density);
240 fflush(stdout);
241 }
242 if(verbose>10) IMG_TEST=verbose-10; else IMG_TEST=0;
243
244
245 /*
246 * Read PET image and input TAC
247 */
248 if(verbose>1) printf("reading data files\n");
249 dftInit(&tac); imgInit(&img);
251 petfile, NULL, inpfile, NULL, NULL, &fittime, &dataNr, &img,
252 NULL, &tac, 1, stdout, verbose-2, tmp);
253 if(ret!=0) {
254 fprintf(stderr, "Error: %s.\n", tmp);
255 if(verbose>1) printf(" ret := %d\n", ret);
256 return(2);
257 }
258 if(imgNaNs(&img, 1)>0)
259 if(verbose>0) fprintf(stderr, "Warning: missing pixel values.\n");
260 /* Set time unit to min, also for integrals in y2[] */
261 if(tac.timeunit==TUNIT_SEC) for(fi=0; fi<tac.frameNr; fi++) tac.voi[0].y2[fi]/=60.0;
262 ret=dftTimeunitConversion(&tac, TUNIT_MIN);
263 if(verbose>1) {
264 printf("fittimeFinal := %g s\n", 60.*fittime);
265 printf("dataNr := %d\n", dataNr);
266 }
267 /* Check that image is dynamic */
268 if(dataNr<3) {
269 fprintf(stderr, "Error: too few time frames for fitting.\n");
270 if(verbose>0) imgInfo(&img);
271 imgEmpty(&img); dftEmpty(&tac); return(2);
272 }
273
274 /*
275 * Thresholding dynamic image
276 */
277 if(verbose>0) fprintf(stdout, "thresholding\n");
278 long long tn;
279 ret=imgThresholding(&img, calcThreshold, &tn);
280 if(ret!=0) {
281 fprintf(stderr, "Error in thresholding the dynamic image: %s\n", img.statmsg);
282 imgEmpty(&img); dftEmpty(&tac); return(4);
283 }
284 if(verbose>1)
285 fprintf(stdout, "threshold_cutoff_nr := %lld / %lld\n", tn, (long long)img.dimx*img.dimy*img.dimz);
286
287
288 /* Allocate memory for tissue TAC and integral */
289 ret=dftAddmem(&tac, 1);
290 if(ret!=0) {
291 fprintf(stderr, "Error (%d) in allocating memory.\n", ret);
292 imgEmpty(&img); dftEmpty(&tac);
293 return(4);
294 }
295 strcpy(tac.voi[0].voiname, "input");
296 strcpy(tac.voi[1].voiname, "tissue");
297
298
299
300 /*
301 * Allocate result images and fill the header info
302 */
303 if(verbose>1) printf("allocating memory for parametric images\n");
304 imgInit(&k1img); imgInit(&k2img); imgInit(&dvimg); imgInit(&vaimg); imgInit(&wssimg);
305 /* K1 image */
306 ret=imgAllocateWithHeader(&k1img, img.dimz, img.dimy, img.dimx, 1, &img);
307 if(ret==0) {
308 k1img.unit=IMGUNIT_ML_PER_ML_PER_MIN;
310 k1img.start[0]=img.start[0]; k1img.end[0]=img.end[dataNr-1];
311 }
312 /* Optional parametric images */
313 if(ret==0 && k2file[0]) {
314 ret=imgAllocateWithHeader(&k2img, img.dimz, img.dimy, img.dimx, 1, &k1img);
315 if(ret==0) k2img.unit=IMGUNIT_PER_MIN;
316 }
317 if(ret==0 && dvfile[0]) {
318 ret=imgAllocateWithHeader(&dvimg, img.dimz, img.dimy, img.dimx, 1, &k1img);
319 if(ret==0) dvimg.unit=IMGUNIT_ML_PER_ML;
320 }
321 if(ret==0 && vafile[0]) {
322 ret=imgAllocateWithHeader(&vaimg, img.dimz, img.dimy, img.dimx, 1, &k1img);
323 if(ret==0) vaimg.unit=IMGUNIT_ML_PER_ML;
324 }
325 if(ret==0 && wssfile[0]) {
326 ret=imgAllocateWithHeader(&wssimg, img.dimz, img.dimy, img.dimx, 1, &k1img);
327 if(ret==0) wssimg.unit=IMGUNIT_UNKNOWN;
328 }
329 if(ret) {
330 fprintf(stderr, "Error: cannot setup memory for parametric image.\n");
331 imgEmpty(&img); dftEmpty(&tac);
332 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&dvimg); imgEmpty(&vaimg); imgEmpty(&wssimg);
333 return(6);
334 }
335
336 /*
337 * Allocate memory required by NNLS
338 */
339 if(verbose>1) printf("allocating memory for NNLS\n");
340 nnls_n=NNLS_N; nnls_m=dataNr;
341 nnls_mat=(double*)malloc(((nnls_n+2)*nnls_m)*sizeof(double));
342 if(nnls_mat==NULL) {
343 fprintf(stderr, "Error: cannot allocate memory for NNLS.\n");
344 imgEmpty(&img); dftEmpty(&tac);
345 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&dvimg); imgEmpty(&vaimg); imgEmpty(&wssimg);
346 return(7);
347 }
348 dptr=nnls_mat;
349 for(int n=0; n<nnls_n; n++) {nnls_a[n]=dptr; dptr+=nnls_m;}
350 nnls_b=dptr; dptr+=nnls_m; nnls_zz=dptr;
351
352 /* Copy weights if available */
353 /* or set them to frame lengths */
354 if(verbose>2) printf("working with NNLS weights\n");
355 if(weight==1 && img.isWeight==0) {
356 for(int m=0; m<nnls_m; m++) img.weight[m]=img.end[m]-img.start[m];
357 img.isWeight=1;
358 }
359 /* Compute NNLS weights */
360 if(img.isWeight) {
361 for(int m=0; m<nnls_m; m++) {
362 tac.w[m]=img.weight[m]; if(tac.w[m]<=1.0e-20) tac.w[m]=0.0;
363 }
364 }
365
366
367 /*
368 * Compute pixel-by-pixel
369 */
370 long long nr_of_negative_k1=0;
371 cp=tac.voi[0].y; cpi=tac.voi[0].y2;
372 ct=tac.voi[1].y; cti=tac.voi[1].y2;
373 if(verbose>0) fprintf(stdout, "computing pixel-by-pixel\n");
374 for(pi=0; pi<img.dimz; pi++) {
375 if(verbose>2) printf("computing plane %d\n", img.planeNumber[pi]);
376 else if(img.dimz>1 && verbose>0) {fprintf(stdout, "."); fflush(stdout);}
377 for(yi=0; yi<img.dimy; yi++) {
378 for(xi=0; xi<img.dimx; xi++) {
379
380 if(verbose>6 && pi==img.dimz/2 && yi==img.dimy/3 && xi==img.dimx/3) {
381 printf(" computing row %d, column %d\n", yi+1, xi+1);
382 }
383
384 /* Initiate pixel output values */
385 k1img.m[pi][yi][xi][0]=0.0;
386 if(k2file[0]) k2img.m[pi][yi][xi][0]=0.0;
387 if(vafile[0]) vaimg.m[pi][yi][xi][0]=0.0;
388 if(dvfile[0]) dvimg.m[pi][yi][xi][0]=0.0;
389
390 /*
391 * Estimate K1, k2 and Va
392 */
393 nnls_m=dataNr; nnls_n=NNLS_N; if(fitVa==0) nnls_n--;
394 /* Copy and integrate pixel curve */
395 for(int m=0; m<nnls_m; m++) ct[m]=img.m[pi][yi][xi][m];
396 petintegral(tac.x1, tac.x2, ct, nnls_m, cti, NULL);
397 /* if AUC at the end is <= zero, then forget this pixel */
398 if(cti[nnls_m-1]<=0.0) continue;
399 /* Fill NNLS A matrix: */
400 /* function #1: tissue integral x -1 */
401 for(int m=0; m<nnls_m; m++) nnls_a[0][m]=-cti[m];
402 /* function #2: integral of input */
403 for(int m=0; m<nnls_m; m++) nnls_a[1][m]=cpi[m];
404 /* function #3: input curve */
405 if(nnls_n>2) for(int m=0; m<nnls_m; m++) nnls_a[2][m]=cp[m];
406 /* Fill NNLS B array: tissue */
407 for(int m=0; m<nnls_m; m++) nnls_b[m]=ct[m];
408 /* Apply data weights */
409 if(img.isWeight) nnlsWght(nnls_n, nnls_m, nnls_a, nnls_b, tac.w);
410 if(verbose>6 && pi==img.dimz/2 && yi==img.dimy/3 && xi==img.dimx/3) {
411 printf("Matrix A Array B\n");
412 for(int m=0; m<nnls_m; m++) {
413 printf("%12.3f %12.3f %12.3f %12.3f\n",
414 nnls_a[0][m], nnls_a[1][m], nnls_a[2][m], nnls_b[m]);
415 }
416 }
417 /* NNLS */
418 ret=nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm,
419 nnls_wp, nnls_zz, nnls_index);
420 if(ret>1) continue; /* no solution is possible */
421 if(nnls_n>2) Va=nnls_x[2]; else Va=0.0;
422 k1img.m[pi][yi][xi][0]=nnls_x[1];
423 if(nnls_n>2) k1img.m[pi][yi][xi][0]-=Va*nnls_x[0];
424 if(k2file[0]) k2img.m[pi][yi][xi][0]=nnls_x[0];
425 if(vafile[0]) vaimg.m[pi][yi][xi][0]=Va;
426 if(verbose>6 && pi==img.dimz/2 && yi==img.dimy/3 && xi==img.dimx/3) {
427 printf(" Va=%g x[0]=%g x[1]=%g\n", Va, nnls_x[0], nnls_x[1]);
428 }
429
430 /*
431 * If k1<0, then fit again with assumption Va=0
432 */
433 if(k1_nn_constraint!=0 && k1img.m[pi][yi][xi][0]<0.0) {
434 nr_of_negative_k1++;
435 nnls_n=NNLS_N-1;
436 /* Fill NNLS A matrix: */
437 /* function #1: tissue integral x -1 */
438 for(int m=0; m<nnls_m; m++) nnls_a[0][m]=-cti[m];
439 /* function #2: integral of input */
440 for(int m=0; m<nnls_m; m++) nnls_a[1][m]=cpi[m];
441 /* Fill NNLS B array: tissue */
442 for(int m=0; m<nnls_m; m++) nnls_b[m]=ct[m];
443 /* Apply data weights */
444 if(img.isWeight) nnlsWght(nnls_n, nnls_m, nnls_a, nnls_b, tac.w);
445 if(verbose>6 && pi==img.dimz/2 && yi==img.dimy/3 && xi==img.dimx/3) {
446 printf("Matrix A Array B\n");
447 for(int m=0; m<nnls_m; m++) {
448 printf("%12.3f %12.3f %12.3f\n",
449 nnls_a[0][m], nnls_a[1][m], nnls_b[m]);
450 }
451 }
452 /* NNLS */
453 ret=nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm,
454 nnls_wp, nnls_zz, nnls_index);
455 if(ret>1) continue; /* no solution is possible */
456 Va=0.0;
457 k1img.m[pi][yi][xi][0]=nnls_x[1];
458 if(k2file[0]) k2img.m[pi][yi][xi][0]=nnls_x[0];
459 if(vafile[0]) vaimg.m[pi][yi][xi][0]=Va;
460 if(verbose>6 && pi==img.dimz/2 && yi==img.dimy/3 && xi==img.dimx/3) {
461 printf(" Va=%g x[0]=%g x[1]=%g\n", Va, nnls_x[0], nnls_x[1]);
462 }
463 } else if(k1img.m[pi][yi][xi][0]<0.0) {
464 nr_of_negative_k1++;
465 }
466
467 /* Calculate Sum-of-Squares, if user wants to save it */
468 if(wssfile[0]) {
469 double wss=0.0;
470 for(int m=0; m<nnls_m; m++) {
471 double f=0.0;
472 for(int n=0; n<nnls_n; n++) f+=nnls_x[n]*nnls_a[n][m];
473 f-=nnls_b[m]; wss+=f*f;
474 }
475 wssimg.m[pi][yi][xi][0]=wss;
476 }
477
478 /*
479 * Estimate K1/k2, if necessary
480 */
481 if(dvfile[0]) {
482 /* If Va was fitted, then subtract Va*Ca from tissue TAC */
483 if(nnls_n>2) {
484 for(int m=0; m<nnls_m; m++) ct[m]-=Va*cp[m];
485 petintegral(tac.x1, tac.x2, ct, nnls_m, cti, NULL);
486 //integrate(tac.x, ct, nnls_m, cti);
487 }
488 nnls_n=NNLS_N-1; /* Vb is already corrected */
489 /* Use previous copies of pixel curves */
490 /* Fill NNLS A matrix: */
491 /* function #1: tissue x -1 */
492 for(int m=0; m<nnls_m; m++) nnls_a[0][m]=-ct[m];
493 /* function #2: integral of input */
494 for(int m=0; m<nnls_m; m++) nnls_a[1][m]=cpi[m];
495 /* Fill NNLS B array: tissue integral */
496 for(int m=0; m<nnls_m; m++) nnls_b[m]=cti[m];
497 /* Apply data weights */
498 if(img.isWeight) nnlsWght(nnls_n, nnls_m, nnls_a, nnls_b, tac.w);
499 if(verbose>6 && pi==img.dimz/2 && yi==img.dimy/3 && xi==img.dimx/3) {
500 printf("Matrix A Array B\n");
501 for(int m=0; m<nnls_m; m++) {
502 printf("%12.3f %12.3f %12.3f\n",
503 nnls_a[0][m], nnls_a[1][m], nnls_b[m]);
504 }
505 }
506 /* NNLS */
507 ret=nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm, nnls_wp, nnls_zz, nnls_index);
508 if(ret>1) continue; /* no solution is possible */
509 dvimg.m[pi][yi][xi][0]=nnls_x[1];
510 if(verbose>6 && pi==img.dimz/2 && yi==img.dimy/3 && xi==img.dimx/3) {
511 printf(" x[0]=%g x[1]=%g\n", nnls_x[0], nnls_x[1]);
512 }
513 }
514
515 } /* next column */
516 } /* next row */
517 } /* next plane */
518 if(verbose>0) fprintf(stdout, "done.\n");
519
520 /* No need for dynamic image, NNLS matrix or curves any more */
521 free(nnls_mat); imgEmpty(&img); dftEmpty(&tac);
522
523 /* Tell user how many negative k1 values we got
524 (it is possible when Va is fitted) */
525 if(verbose>1 && nr_of_negative_k1>0) {
526 fprintf(stdout, "Negative flow in %lld pixels\n", nr_of_negative_k1);
527 if(k1_nn_constraint==1) fprintf(stdout, " corrected by assuming Va=0.\n");
528 }
529
530
531 /*
532 * Convert units to per dL and/or gram if necessary
533 */
534 if(per_dl || per_gram) {
535 /* K1 */
536 float f;
537 if(per_dl && per_gram) {
538 if(verbose>1) printf("converting to per 100g\n");
539 f=100.0/density; k1img.unit=IMGUNIT_ML_PER_DL_PER_MIN;
540 } else if(per_dl) {
541 if(verbose>1) printf("converting to per 100mL\n");
542 f=100.0; k1img.unit=IMGUNIT_ML_PER_DL_PER_MIN;
543 } else if(per_gram) {
544 if(verbose>1) printf("converting to per g\n");
545 f=1.0/density; k1img.unit=IMGUNIT_ML_PER_ML_PER_MIN;
546 } else {
547 if(verbose>2) printf("no conversion from mL/(min*mL)\n");
548 f=1.0;
549 }
550 ret=imgArithmConst(&k1img, f, '*', 1.0E+06, verbose-4);
551 }
552 if(per_dl && vafile[0]) {
553 /* Va */
554 vaimg.unit=IMGUNIT_ML_PER_DL;
555 ret=imgArithmConst(&vaimg, 100.0, '*', 1.0E+06, verbose-4);
556 }
557
558
559 /*
560 * Filter the parametric images if required
561 */
562 /* Remove flow values that are higher than user-defined limit */
563 if(upperLimit>0.0) {
564 if(verbose>0) fprintf(stdout, "filtering out flow values exceeding %g\n", upperLimit);
565 imgCutoff(&k1img, (float)upperLimit, 0);
566 }
567 /*
568 * Filter out pixels that are over 4x higher than their
569 * closest neighbours
570 */
571 if(param_filt>0) {
572 if(verbose>0) printf("filtering extreme values from parametric images\n");
573 imgOutlierFilter(&k1img, 4.0);
574 if(k2file[0]) imgOutlierFilter(&k2img, 4.0);
575 if(vafile[0]) imgOutlierFilter(&vaimg, 4.0);
576 if(dvfile[0]) imgOutlierFilter(&dvimg, 4.0);
577 }
578
579
580 /*
581 * Save parametric image(s)
582 */
583 if(verbose>0) printf("writing parametric images\n");
584 /* K1 */
585 ret=imgWrite(k1file, &k1img);
586 if(ret) {
587 fprintf(stderr, "Error: %s\n", k1img.statmsg);
588 if(verbose>1) printf("ret := %d\n", ret);
589 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&dvimg); imgEmpty(&vaimg); imgEmpty(&wssimg);
590 return(13);
591 }
592 if(verbose>0) fprintf(stdout, "Flow image %s saved.\n", k1file);
593 /* k2 */
594 if(k2file[0]) {
595 ret=imgWrite(k2file, &k2img);
596 if(ret) {
597 fprintf(stderr, "Error: %s\n", k2img.statmsg);
598 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&dvimg); imgEmpty(&vaimg); imgEmpty(&wssimg);
599 return(14);
600 }
601 if(verbose>0) fprintf(stdout, "k2 image %s saved.\n", k2file);
602 }
603 /* DV */
604 if(dvfile[0]) {
605 ret=imgWrite(dvfile, &dvimg);
606 if(ret) {
607 fprintf(stderr, "Error: %s\n", dvimg.statmsg);
608 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&dvimg); imgEmpty(&vaimg); imgEmpty(&wssimg);
609 return(15);
610 }
611 if(verbose>0) fprintf(stdout, "DV image %s saved.\n", dvfile);
612 }
613 /* Va */
614 if(vafile[0]) {
615 ret=imgWrite(vafile, &vaimg);
616 if(ret) {
617 fprintf(stderr, "Error: %s\n", vaimg.statmsg);
618 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&dvimg); imgEmpty(&vaimg); imgEmpty(&wssimg);
619 return(16);
620 }
621 if(verbose>0) fprintf(stdout, "Va image %s saved.\n", vafile);
622 }
623 /* WSS */
624 if(wssfile[0]) {
625 ret=imgWrite(wssfile, &wssimg);
626 if(ret) {
627 fprintf(stderr, "Error: %s\n", wssimg.statmsg);
628 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&dvimg); imgEmpty(&vaimg); imgEmpty(&wssimg);
629 return(16);
630 }
631 if(verbose>0) fprintf(stdout, "WSS image %s saved.\n", wssfile);
632 }
633
634 if(verbose>2) printf("before quitting, free memory\n");
635 imgEmpty(&k1img); imgEmpty(&k2img); imgEmpty(&dvimg); imgEmpty(&vaimg); imgEmpty(&wssimg);
636 fflush(stdout);
637
638 return(0);
639}
640/*****************************************************************************/
641
642/*****************************************************************************/
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
int dftAddmem(DFT *dft, int voiNr)
Definition dft.c:107
void dftEmpty(DFT *data)
Definition dft.c:20
int dftTimeunitConversion(DFT *dft, int tunit)
Definition dftunit.c:119
int IMG_TEST
Definition img.c:6
void imgInfo(IMG *image)
Definition img.c:359
unsigned long long imgNaNs(IMG *img, int fix)
Definition img.c:658
int imgAllocateWithHeader(IMG *image, int planes, int rows, int columns, int frames, IMG *image_from)
Definition img.c:279
void imgEmpty(IMG *image)
Definition img.c:121
void imgInit(IMG *image)
Definition img.c:60
int imgArithmConst(IMG *img, float operand, char operation, float ulimit, int verbose)
Definition imgarithm.c:100
int imgWrite(const char *fname, IMG *img)
Definition imgfile.c:136
int imgReadModelingData(char *petfile, char *siffile, char *inputfile1, char *inputfile2, char *inputfile3, double *fitdur, int *fitframeNr, IMG *img, DFT *inp, DFT *iinp, int verifypeak, FILE *loginfo, int verbose, char *status)
Definition imginput.c:24
void imgCutoff(IMG *image, float cutoff, int mode)
int imgThresholding(IMG *img, float threshold_level, long long *thr_nr)
int imgOutlierFilter(IMG *img, float limit)
int petintegral(double *x1, double *x2, double *y, int nr, double *ie, double *iie)
Integrate PET TAC data to frame mid times.
Definition integr.c:771
Header file for libtpccurveio.
Header file for libtpcimgio.
#define IMG_DC_NONCORRECTED
Header file for libtpcimgp.
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:40
size_t strlcpy(char *dst, const char *src, size_t dstsize)
Definition strext.c:245
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:213
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:383
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:158
Header file for libtpcmodel.
int nnls(double **a, int m, int n, double *b, double *x, double *rnorm, double *w, double *zz, int *index)
Definition nnls.c:37
int nnlsWght(int N, int M, double **A, double *b, double *weight)
Definition nnls.c:257
Header file for libtpcmodext.
Voi * voi
int timeunit
double * w
double * x1
double * x2
int frameNr
unsigned short int dimx
float **** m
char decayCorrection
char unit
int * planeNumber
float * weight
float * start
unsigned short int dimz
unsigned short int dimy
float * end
const char * statmsg
char isWeight
double * y2
char voiname[MAX_REGIONSUBNAME_LEN+1]
double * y