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