TPCCLIB
Loading...
Searching...
No Matches
imglhk2d.c
Go to the documentation of this file.
1
11/*****************************************************************************/
12#include "tpcclibConfig.h"
13/*****************************************************************************/
14#include <stdio.h>
15#include <stdlib.h>
16#include <unistd.h>
17#include <string.h>
18#include <math.h>
19#include <time.h>
20/*****************************************************************************/
21#include "libtpccurveio.h"
22#include "libtpcmodext.h"
23#include "libtpcmisc.h"
24#include "libtpcmodel.h"
25#include "libtpcimgio.h"
26#include "libtpcimgp.h"
27/*****************************************************************************/
28#define NNLS_N 5
29/*****************************************************************************/
30
31/*****************************************************************************/
32static char *info[] = {
33 "Computation of parametric images of K1A, VbA, K1B, VbB, and k2, from",
34 "dynamic PET image in ECAT, NIfTI, or Analyze format applying",
35 "reversible on1-tissue compartmental model with two input functions:",
36 " Cpet(t) = VbA*CbA(t) + VbB*CbB(t) + Ct(t)",
37 " dCt(t) = K1A*CbA(t) + K1B*CbB(t) - k2*Ct(t)",
38 " ",
39 "The compartmental models are transformed to general linear least squares",
40 "functions (1), which are solved using Lawson-Hanson non-negative least",
41 "squares (NNLS) algorithm (2).",
42 " ",
43 "Dynamic PET image and input TACs must be corrected for decay",
44 "to the tracer injection time.",
45 " ",
46 "Usage: @P [Options] iAfile iBfile imgfile k1Afile k1Bfile vbAfile vbBfile k2file",
47 " ",
48 "Options:",
49 " -thr=<threshold%>",
50 " Pixels with AUC less than (threshold/100 x PTAC AUC) are set to zero;",
51 " default is 1%.",
52 " -end=<Fit end time (min)>",
53 " Use data from 0 to end time; by default, model is fitted to all frames.",
54//" -NNLS or -BVLS",
55//" LLSQ method.",
56//" -w1, -wf, -wfa",
57//" By default, all weights are set to 1.0 (no weighting, option -w1); option -wf",
58//" sets weights based on frame lengths, and option -wfa based on both frame lengths",
59//" and mean activity during each frame.",
60 " -stdoptions", // List standard options like --help, -v, etc
61 " ",
62 "The units of pixel values in the parametric images are ml/(min*ml) for K1",
63 "1/min for k2, and ml/ml for Vb.",
64 " ",
65 "References:",
66 "1. Blomqvist G. On the construction of functional maps in positron",
67 " emission tomography. J Cereb Blood Flow Metab 1984;4:629-632.",
68 "2. Lawson CL & Hanson RJ. Solving least squares problems.",
69 " Prentice-Hall, 1974, ISBN 0-89871-356-0.",
70 " ",
71 "See also: imglhk3, imgcbv, imgki, fitk3",
72 " ",
73 "Keywords: image, modelling, irreversible uptake, NNLS",
74 0};
75/*****************************************************************************/
76
77/*****************************************************************************/
78/* Turn on the globbing of the command line, since it is disabled by default in
79 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
80 In Unix&Linux wildcard command line processing is enabled by default. */
81/*
82#undef _CRT_glob
83#define _CRT_glob -1
84*/
85int _dowildcard = -1;
86/*****************************************************************************/
87
88/*****************************************************************************/
89enum {METHOD_UNKNOWN, METHOD_NNLS, METHOD_BVLS};
90static char *method_str[] = {"unknown", "NNLS", "BVLS", 0};
91/*****************************************************************************/
92
93/*****************************************************************************/
97int main(int argc, char **argv)
98{
99 int ai, help=0, version=0, verbose=1;
100 char inpAfile[FILENAME_MAX], inpBfile[FILENAME_MAX], petfile[FILENAME_MAX];
101 char k1Afile[FILENAME_MAX], vbAfile[FILENAME_MAX];
102 char k1Bfile[FILENAME_MAX], vbBfile[FILENAME_MAX];
103 char k2file[FILENAME_MAX];
104 float calcThreshold=0.01;
105 double fittime=-1.0;
106 int weights=2; // 1=frame lengths and activity, 2=frame lengths, 2=no weighting
107 int method=METHOD_NNLS;
108 int ret;
109
110 /*
111 * Get arguments
112 */
113 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
114 inpAfile[0]=inpBfile[0]=petfile[0]=(char)0;
115 k1Afile[0]=k1Bfile[0]=k2file[0]=vbAfile[0]=vbBfile[0]=(char)0;
116 /* Get options */
117 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
118 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
119 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
120 if(strncasecmp(cptr, "THR=", 4)==0) {
121 double v; ret=atof_with_check(cptr+4, &v);
122 if(!ret && v>=0.0 && v<=200.0) {calcThreshold=(float)(0.01*v); continue;}
123 } else if(strncasecmp(cptr, "END=", 4)==0) {
124 ret=atof_with_check(cptr+4, &fittime); if(!ret && fittime>0.0) continue;
125 } else if(strcasecmp(cptr, "WFA")==0) {
126 weights=0; continue;
127 } else if(strcasecmp(cptr, "WF")==0) {
128 weights=1; continue;
129 } else if(strcasecmp(cptr, "W1")==0) {
130 weights=2; continue;
131 } else if(strcasecmp(cptr, "NNLS")==0) {
132 method=METHOD_NNLS; continue;
133 } else if(strcasecmp(cptr, "BVLS")==0) {
134 method=METHOD_BVLS; continue;
135 }
136 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
137 return(1);
138 } else break;
139
140
141 /* Print help or version? */
142 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
143 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
144 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
145
146 /* Process other arguments, starting from the first non-option */
147 if(ai<argc) strlcpy(inpAfile, argv[ai++], FILENAME_MAX);
148 if(ai<argc) strlcpy(inpBfile, argv[ai++], FILENAME_MAX);
149 if(ai<argc) strlcpy(petfile, argv[ai++], FILENAME_MAX);
150 if(ai<argc) strlcpy(k1Afile, argv[ai++], FILENAME_MAX);
151 if(ai<argc) strlcpy(k1Bfile, argv[ai++], FILENAME_MAX);
152 if(ai<argc) strlcpy(vbAfile, argv[ai++], FILENAME_MAX);
153 if(ai<argc) strlcpy(vbBfile, argv[ai++], FILENAME_MAX);
154 if(ai<argc) strlcpy(k2file, argv[ai++], FILENAME_MAX);
155 if(ai<argc) {fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]); return(1);}
156 /* Did we get all the information that we need? */
157 if(!k2file[0]) {
158 fprintf(stderr, "Error: missing command-line argument; use option --help\n"); return(1);}
159 /* In verbose mode print arguments and options */
160 if(verbose>1) {
161 printf("inpAfile := %s\n", inpAfile);
162 printf("inpBfile := %s\n", inpBfile);
163 printf("petfile := %s\n", petfile);
164 printf("k1Afile := %s\n", k1Afile);
165 printf("vbAfile := %s\n", vbAfile);
166 printf("k1Bfile := %s\n", k1Bfile);
167 printf("vbBfile := %s\n", vbBfile);
168 printf("k2file := %s\n", k2file);
169 printf("calcThreshold :=%g\n", calcThreshold);
170 printf("weights := %d\n", weights);
171 printf("method := %s\n", method_str[method]);
172 if(fittime>0.0) printf("required_fittime := %g min\n", fittime);
173 }
174 if(verbose>8) IMG_TEST=verbose-8; else IMG_TEST=0;
175 if(verbose>20) ECAT63_TEST=ECAT7_TEST=verbose-20; else ECAT63_TEST=ECAT7_TEST=0;
176
177
178 /*
179 * Read PET image and input TAC
180 */
181 if(verbose>0) printf("reading data files\n");
182 DFT tac; dftInit(&tac);
183 IMG img; imgInit(&img);
184 int dataNr=0;
185 char errmsg[512];
187 petfile, NULL, inpAfile, inpBfile, NULL, &fittime, &dataNr, &img,
188 NULL, &tac, 1, stdout, verbose-2, errmsg);
189 if(ret!=0) {
190 fprintf(stderr, "Error: %s.\n", errmsg);
191 if(verbose>1) printf(" ret := %d\n", ret);
192 return(2);
193 }
194 if(imgNaNs(&img, 1)>0)
195 if(verbose>0) fprintf(stderr, "Warning: missing pixel values.\n");
196 /* Set time unit to min, also for integrals in y2[] */
197 if(tac.timeunit==TUNIT_SEC) {
198 for(int ri=0; ri<tac.voiNr; ri++) {
199 for(int fi=0; fi<tac.frameNr; fi++) tac.voi[ri].y2[fi]/=60.0;
200 for(int fi=0; fi<tac.frameNr; fi++) tac.voi[ri].y3[fi]/=3600.0;
201 }
202 }
203 ret=dftTimeunitConversion(&tac, TUNIT_MIN);
204 if(verbose>1) {
205 printf("fittimeFinal := %g min\n", fittime);
206 printf("dataNr := %d\n", dataNr);
207 }
208 /* Check that image is dynamic */
209 if(dataNr<6) {
210 fprintf(stderr, "Error: too few time frames for fitting.\n");
211 if(verbose>1) imgInfo(&img);
212 imgEmpty(&img); dftEmpty(&tac); return(2);
213 }
214
215
216 /* Add place for tissue TACs too */
217 if(verbose>1) fprintf(stdout, "allocating working memory for pixel TACs\n");
218 ret=dftAddmem(&tac, 1);
219 if(ret) {
220 fprintf(stderr, "Error: cannot allocate memory.\n");
221 if(verbose>0) printf("ret := %d\n", ret);
222 imgEmpty(&img); dftEmpty(&tac); return(3);
223 }
224 strcpy(tac.voi[0].name, "inputA");
225 strcpy(tac.voi[1].name, "inputB");
226 strcpy(tac.voi[2].name, "tissue");
227
228 /* Set weights as requested */
229 if(imgSetWeights(&img, weights, verbose-5)!=0) {
230 fprintf(stderr, "Error: cannot calculate weights.\n");
231 imgEmpty(&img); dftEmpty(&tac); return(3);
232 }
233 for(int i=0; i<dataNr; i++) tac.w[i]=img.weight[i];
234
235 /* Determine the threshold */
236 double threshold=calcThreshold*tac.voi[0].y2[dataNr-1];
237 if(verbose>1) printf("threshold_AUC := %g\n", threshold);
238
239
240 /*
241 * Allocate result images (allocate all, even if user did not want to save those)
242 */
243 if(verbose>1) fprintf(stdout, "allocating memory for parametric image data\n");
244 IMG k1Aimg; imgInit(&k1Aimg);
245 IMG vbAimg; imgInit(&vbAimg);
246 IMG k1Bimg; imgInit(&k1Bimg);
247 IMG vbBimg; imgInit(&vbBimg);
248 IMG k2img; imgInit(&k2img);
249 ret=imgAllocateWithHeader(&k1Aimg, img.dimz, img.dimy, img.dimx, 1, &img);
250 if(!ret) ret=imgAllocateWithHeader(&vbAimg, img.dimz, img.dimy, img.dimx, 1, &img);
251 if(!ret) ret=imgAllocateWithHeader(&k1Bimg, img.dimz, img.dimy, img.dimx, 1, &img);
252 if(!ret) ret=imgAllocateWithHeader(&vbBimg, img.dimz, img.dimy, img.dimx, 1, &img);
253 if(!ret) ret=imgAllocateWithHeader(&k2img, img.dimz, img.dimy, img.dimx, 1, &img);
254 if(ret) {
255 fprintf(stderr, "Error: cannot allocate memory for result image.\n");
256 imgEmpty(&img); dftEmpty(&tac);
257 imgEmpty(&k1Aimg); imgEmpty(&vbAimg); imgEmpty(&k1Bimg); imgEmpty(&vbBimg); imgEmpty(&k2img);
258 return(4);
259 }
260 /* set 'frame time' for parametric images */
261 k1Aimg.start[0]=k1Bimg.start[0]=vbAimg.start[0]=vbBimg.start[0]=k2img.start[0]=0.0;
262 k1Aimg.end[0]=k1Bimg.end[0]=vbAimg.end[0]=vbBimg.end[0]=k2img.end[0]=60.*fittime;
263 /* set units in parametric images */
264 k1Aimg.unit=k1Bimg.unit=CUNIT_ML_PER_ML_PER_MIN;
265 vbAimg.unit=vbBimg.unit=CUNIT_ML_PER_ML;
266 k2img.unit=CUNIT_PER_MIN;
267
268
269 /* Fitting */
270
271 int fittedNr=0, fittedokNr=0, thresholdNr=0;
272
273 if(method==METHOD_NNLS) {
274
275 /* Compute weights for NNLS */
276 for(int m=0; m<dataNr; m++) {
277 if(tac.w[m]<=1.0e-20) tac.w[m]=0.0; else tac.w[m]=sqrt(tac.w[m]);
278 }
279
280 /*
281 * Allocate memory required by NNLS
282 */
283 if(verbose>1) fprintf(stdout, "allocating memory for NNLS\n");
284 int nnls_n=NNLS_N;
285 int nnls_m=dataNr;
286 int n, m, nnls_index[nnls_n];
287 double **nnls_a, nnls_b[nnls_m], nnls_zz[nnls_m], nnls_x[nnls_n], *nnls_mat,
288 nnls_wp[nnls_n], nnls_rnorm;
289 nnls_mat=(double*)malloc((nnls_n*nnls_m)*sizeof(double));
290 nnls_a=(double**)malloc(nnls_n*sizeof(double*));
291 if(nnls_mat==NULL || nnls_a==NULL) {
292 fprintf(stderr, "Error: cannot allocate memory for NNLS.\n");
293 imgEmpty(&img); dftEmpty(&tac);
294 imgEmpty(&k1Aimg); imgEmpty(&vbAimg); imgEmpty(&k1Bimg); imgEmpty(&vbBimg); imgEmpty(&k2img);
295 return(4);
296 }
297 for(n=0; n<nnls_n; n++) nnls_a[n]=nnls_mat+n*nnls_m;
298
299
300 /*
301 * Compute pixel-by-pixel
302 */
303 if(verbose>0) fprintf(stdout, "computing NNLS pixel-by-pixel\n");
304 double *ct, *cti, *cpA, *cpAi, *cpB, *cpBi;
305 cpA=tac.voi[0].y; cpAi=tac.voi[0].y2;
306 cpB=tac.voi[1].y; cpBi=tac.voi[1].y2;
307 ct=tac.voi[2].y; cti=tac.voi[2].y2;
308 for(int pi=0; pi<img.dimz; pi++) {
309 if(verbose>0) {fprintf(stdout, "."); fflush(stdout);}
310 for(int yi=0; yi<img.dimy; yi++) {
311 for(int xi=0; xi<img.dimx; xi++) {
312 double K1A=0.0, K1B=0.0, k2=0.0, VbA=0.0, VbB=0.0;
313 /* Set pixel results to zero */
314 k1Aimg.m[pi][yi][xi][0]=0.0;
315 vbAimg.m[pi][yi][xi][0]=0.0;
316 k1Bimg.m[pi][yi][xi][0]=0.0;
317 vbBimg.m[pi][yi][xi][0]=0.0;
318 k2img.m[pi][yi][xi][0]=0.0;
319 /* Copy and integrate pixel curve */
320 for(int fi=0; fi<tac.frameNr; fi++) ct[fi]=img.m[pi][yi][xi][fi];
321 ret=petintegral(tac.x1, tac.x2, ct, tac.frameNr, cti, NULL);
322 if(ret) continue;
323 /* if AUC at the end is less than threshold value, then do nothing more */
324 if(cti[dataNr-1]<threshold) {thresholdNr++; continue;}
325
326 /*
327 * Estimate parameters K1, Vb, and possibly k2
328 */
329
330 /* Fill the NNLS data matrix */
331 for(int m=0; m<nnls_m; m++) {
332 nnls_a[0][m]=cpAi[m];
333 nnls_a[1][m]=cpBi[m];
334 nnls_a[2][m]=cpA[m];
335 nnls_a[3][m]=cpB[m];
336 nnls_a[4][m]=-cti[m];
337 nnls_b[m]=ct[m];
338 }
339 if(img.isWeight)
340 for(int m=0; m<nnls_m; m++) {
341 nnls_b[m]*=tac.w[m];
342 for(int n=0; n<nnls_n; n++) nnls_a[n][m]*=tac.w[m];
343 }
344 if(verbose>5 && pi==img.dimz/2 && yi==img.dimy/3 && xi==img.dimx/3) {
345 printf("Matrix A Array B\n");
346 for(m=0; m<nnls_m; m++) {
347 printf("%12.3f %12.3f %12.3f %12.3f %12.3f %12.3f\n",
348 nnls_a[0][m], nnls_a[1][m], nnls_a[2][m], nnls_a[3][m], nnls_a[4][m], nnls_b[m]);
349 }
350 }
351 /* NNLS */
352 ret=nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm, nnls_wp, nnls_zz, nnls_index);
353 if(ret!=0) { /* no solution is possible */
354 if(verbose>3) printf("no solution possible (%d)\n", ret);
355 if(verbose>4) printf("nnls_n=%d nnls_m=%d\n", nnls_n, nnls_m);
356 for(int n=0; n<nnls_n; n++) nnls_x[n]=0.0;
357 nnls_rnorm=0.0;
358 continue;
359 }
360 fittedNr++;
361
362 VbA=nnls_x[2];
363 VbB=nnls_x[3];
364 k2=nnls_x[4];
365 K1A=nnls_x[0]; if(VbA<1.0) K1A-=VbA*k2;
366 K1B=nnls_x[1]; if(VbB<1.0) K1B-=-VbB*k2;
367 if((K1A+K1B)>=0.0 && (VbA+VbB)>=0.0 && (VbA+VbB)<=1.0) fittedokNr++;
368 /* Put results to output images */
369 k1Aimg.m[pi][yi][xi][0]=K1A;
370 vbAimg.m[pi][yi][xi][0]=VbA;
371 k1Bimg.m[pi][yi][xi][0]=K1B;
372 vbBimg.m[pi][yi][xi][0]=VbB;
373 k2img.m[pi][yi][xi][0]=k2;
374 } /* next column */
375 } /* next row */
376 } /* next plane */
377 if(verbose>0) {fprintf(stdout, " done.\n"); fflush(stdout);}
378 free(nnls_mat); free(nnls_a);
379 /* Show statistics on how we succeeded */
380 n=(int)img.dimx*(int)img.dimy*(int)img.dimz;
381 if(verbose>0) {
382 fprintf(stdout, "%d out of %d pixels were fitted; %d pixels ok.\n", fittedokNr, n, fittedNr);
383 fprintf(stdout, "%d pixels were thresholded.\n", thresholdNr);
384 }
385
386 } else {
387
388 fprintf(stderr, "Error: selected method not available.");
389 imgEmpty(&img); dftEmpty(&tac);
390 imgEmpty(&k1Aimg); imgEmpty(&vbAimg); imgEmpty(&k1Bimg); imgEmpty(&vbBimg); imgEmpty(&k2img);
391 return(1);
392
393 }
394
395 /* No need for dynamic image any more */
396 imgEmpty(&img); dftEmpty(&tac);
397
398
399 /*
400 * Save parametric images
401 */
402
403 ret=imgWrite(k1Afile, &k1Aimg);
404 if(!ret) ret=imgWrite(vbAfile, &vbAimg);
405 if(!ret) ret=imgWrite(k1Bfile, &k1Bimg);
406 if(!ret) ret=imgWrite(vbBfile, &vbBimg);
407 if(!ret) ret=imgWrite(k2file, &k2img);
408 imgEmpty(&k1Aimg); imgEmpty(&vbAimg); imgEmpty(&k1Bimg); imgEmpty(&vbBimg); imgEmpty(&k2img);
409 if(ret) {
410 fprintf(stderr, "Error: cannot write parametric image.\n");
411 return(11);
412 }
413 if(verbose>0) fprintf(stdout, "Parametric image(s) saved.\n");
414
415 return(0);
416}
417/*****************************************************************************/
418
419/*****************************************************************************/
int atof_with_check(char *double_as_string, double *result_value)
Definition decpoint.c:107
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 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
Header file for libtpccurveio.
Header file for libtpcimgio.
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
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
unsigned short int dimx
float **** m
char unit
float * weight
float * start
unsigned short int dimz
unsigned short int dimy
float * end
char isWeight
double * y2
double * y
char name[MAX_REGIONNAME_LEN+1]
double * y3