TPCCLIB
Loading...
Searching...
No Matches
imgki.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 "libtpcmodel.h"
21#include "libtpccurveio.h"
22#include "libtpcimgio.h"
23#include "libtpcimgp.h"
24#include "libtpcmodext.h"
25/*****************************************************************************/
26
27/*****************************************************************************/
28#ifndef DEFAULT_LC
29#define DEFAULT_LC 1.00
30#endif
31#ifndef DEFAULT_DENSITY
32#define DEFAULT_DENSITY 1.00
33#endif
34/*****************************************************************************/
35
36/*****************************************************************************/
37static char *info[] = {
38 "Calculates the net influx (uptake) rate constant (Ki) from dynamic PET image",
39 "in ECAT, NIfTI, or Analyze format, applying the multiple time graphical",
40 "analysis (MTGA) approach for irreversible tracer uptake (Patlak plot).",
41 "Simple non-iterative perpendicular line fitting algorithm (4) is applied.",
42 " ",
43 "Usage: @P [Options] input image starttime resultimage",
44 " ",
45 "Input can be either a TAC file containing arterial plasma PTAC or reference",
46 "region TTAC. Data must be in the same concentration units as the data in",
47 "image, and times in min; however, if units are specified inside the input",
48 "and image file, the program can automatically convert units.",
49 "Start time of the linear fit must be given in minutes; set to zero to",
50 "let the program automatically determine the start time (not recommended).",
51 " ",
52 "Options:",
53 " -Ca=<value>",
54 " Concentration of native substrate in arterial plasma (mM),",
55 " for example plasma glucose in [18F]FDG studies.",
56 " With this option the metabolic rate (umol/(min*100 g)) is calculated.",
57 " -LC=<value>",
58 " Lumped Constant in MR calculation; default is 1.0.",
59 " -density=<value>",
60 " Tissue density in MR calculation; default is 1.0 g/ml.",
61 " -thr=<threshold%>",
62 " Pixels with AUC less than (threshold/100 x input AUC) are set to zero;",
63 " default is 0%.",
64// " Pixels with AUC less than (threshold/100 x max AUC) are set to zero.",
65// " Default is 0 %.",
66 " -noneg",
67 " Pixels with negative Ki are set to zero.",
68 " -max=<Max value>",
69 " Upper limit for Ki values.",
70 " -filter",
71 " Remove parametric pixel values that are over 4x higher than",
72 " their closest neighbours.",
73 " -end=<Fit end time (min)>",
74 " By default line is fitted to the end of data. Use this option to enter",
75 " the fit end time.",
76 " -v=<filename>",
77 " Y axis intercepts are written as an image in specified file.",
78 " -n=<filename>",
79 " Numbers of selected plot data points are written as an image.",
80 " -stdoptions", // List standard options like --help, -v, etc
81 " ",
82 "Example 1. Calculation of Ki image, starting linear fit from 20 minutes:",
83 " @P ua2918ap.kbq ua2918dy1.v 20 ua2918ki.v",
84 " ",
85 "Example 2. Calculation of glucose uptake image, when tissue density is 1.02,",
86 " plasma glucose concentration is 5.2 mM, lumped constant is 1.0,",
87 " starting linear fit from 10 minutes after radiotracer injection:",
88 " @P -density=1.02 -Ca=5.2 -LC=1.0 s8642ap.kbq s8642dy1.v 10 s8642ki.v",
89 " ",
90 "The unit of pixel values in the parametric (Ki) image is",
91 "(mL plasma)/(min*(mL tissue)) by default, and umol/(min*100 g) in metabolic",
92 "rate image.",
93 " ",
94 "To calculate Ki images from [18F]FDOPA studies with plasma input:",
95 "subtract reference region TAC from dynamic image before using this program.",
96 " ",
97 "References:",
98 "1. Gjedde A. Calculation of cerebral glucose phosphorylation from brain",
99 " uptake of glucose analogs in vivo: a re-examination. Brain Res. 1982;",
100 " 257:237-274.",
101 "2. Patlak CS, Blasberg RG, Fenstermacher JD. Graphical evaluation of",
102 " blood-to-brain transfer constants from multiple-time uptake data.",
103 " J Cereb Blood Flow Metab 1983;3:1-7.",
104 "3. Patlak CS, Blasberg RG. Graphical evaluation of blood-to-brain transfer",
105 " constants from multiple-time uptake data. Generalizations",
106 " J Cereb Blood Flow Metab 1985;5:584-590.",
107 "4. Varga J & Szabo Z. Modified regression model for the Logan plot.",
108 " J Cereb Blood Flow Metab 2002; 22:240-244.",
109 " ",
110 "See also: imgfur, imgbound, imgunit, imglhki, img2tif, patlak ",
111 " ",
112 "Keywords: image, MTGA, Patlak plot, modelling, Ki, metabolic rate",
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/*****************************************************************************/
131int main(int argc, char **argv)
132{
133 int ai, help=0, version=0, verbose=1;
134 int fi, ret;
135 int param_filt=0;
136 int noneg=0; // 0=negative values are kept, 1=negative values set to zero
137 char inpfile[FILENAME_MAX], petfile[FILENAME_MAX];
138 char icfile[FILENAME_MAX], nrfile[FILENAME_MAX], outfile[FILENAME_MAX];
139 char tmp[1024], *cptr;
140 DFT input;
141 IMG img, out, icimg, nrimg;
142 double startTime=-1.0, upperLimit=-1.0, fittime=-1.0;
143 float calcThreshold=0.0;
144 int startSample=0, endSample=0, fitdimt=0;
145 linefit_range fit_range;
146 double LC=-1.0, Ca=-1.0, density=-1.0;
147
148
149 /*
150 * Get arguments
151 */
152 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
153 inpfile[0]=petfile[0]=outfile[0]=icfile[0]=nrfile[0]=(char)0;
154 dftInit(&input);
155 imgInit(&img); imgInit(&out); imgInit(&icimg); imgInit(&nrimg);
156 /* Get options */
157 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
158 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
159 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
160 if(strncasecmp(cptr, "V=", 2)==0) {
161 strcpy(icfile, cptr+2); if(strlen(icfile)>1) continue;
162 } else if(strncasecmp(cptr, "N=", 2)==0) {
163 strcpy(nrfile, cptr+2); if(strlen(nrfile)>1) continue;
164 } else if(strncasecmp(cptr, "CA=", 3)==0) {
165 Ca=atof_dpi(cptr+3); if(Ca>0.0) continue;
166 } else if(strncasecmp(cptr, "LC=", 3)==0) {
167 LC=atof_dpi(cptr+3); if(LC>0.0) continue;
168 } else if(strncasecmp(cptr, "D=", 2)==0) {
169 density=atof_dpi(cptr+2); if(density>0.0) continue;
170 } else if(strncasecmp(cptr, "DENSITY=", 8)==0) {
171 density=atof_dpi(cptr+8); if(density>0.0) continue;
172 } else if(strncasecmp(cptr, "NONEGATIVES", 5)==0) {
173 noneg=1; continue;
174 } else if(strncasecmp(cptr, "MAX=", 4)==0) {
175 upperLimit=atof_dpi(cptr+4); if(upperLimit>0.0) continue;
176 } else if(strncasecmp(cptr, "E=", 2)==0) {
177 fittime=60.0*atof_dpi(cptr+2); if(fittime>0.0) continue;
178 } else if(strncasecmp(cptr, "END=", 4)==0) {
179 fittime=60.0*atof_dpi(cptr+4); if(fittime>0.0) continue;
180 } else if(strncasecmp(cptr, "FILTER", 4)==0) {
181 param_filt=1; continue;
182 } else if(strncasecmp(cptr, "THR=", 4)==0) {
183 double v; ret=atof_with_check(cptr+4, &v);
184 if(ret==0) {calcThreshold=0.01*v; continue;} // negative is ok
185 }
186 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
187 return(1);
188 } else break;
189
190 /* Print help or version? */
191 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
192 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
193 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
194
195 /* Process other arguments, starting from the first non-option */
196 if(ai<argc) strlcpy(inpfile, argv[ai++], FILENAME_MAX);
197 if(ai<argc) strlcpy(petfile, argv[ai++], FILENAME_MAX);
198 if(ai<argc) {startTime=atof_dpi(argv[ai++]); if(startTime<0.0) startTime=0.0;}
199 if(ai<argc) strlcpy(outfile, argv[ai++], FILENAME_MAX);
200 if(ai<argc) {
201 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
202 return(1);
203 }
204
205 /* Is something missing? */
206 if(!outfile[0]) {
207 fprintf(stderr, "Error: missing result file name.\n");
208 return(1);
209 }
210 /* If MR will be calculated, set defaults and give warnings as necessary */
211 if(Ca>0.0) {
212 if(LC<0.0) {
213 LC=DEFAULT_LC;
214 fprintf(stderr, "Warning: LC not set, using default %g\n", LC);
215 }
216 if(density<0.0) {
217 density=DEFAULT_DENSITY;
218 fprintf(stderr, "Warning: tissue density not set, using default %g\n", density);
219 }
220 } else { /* Warnings if density or LC set when MR will not be calculated */
221 if(LC>0.0) fprintf(stderr, "Warning: LC was set but is not used.\n");
222 if(density>0.0) fprintf(stderr, "Warning: tissue density was set but is not used.\n");
223 }
224
225
226 /* In verbose mode print arguments and options */
227 if(verbose>1) {
228 printf("inpfile := %s\n", inpfile);
229 printf("petfile := %s\n", petfile);
230 printf("outfile := %s\n", outfile);
231 printf("startTime := %g min\n", startTime);
232 printf("icfile := %s\n", icfile);
233 printf("nrfile := %s\n", nrfile);
234 printf("max := %f\n", upperLimit);
235 printf("Ca := %g\n", Ca);
236 printf("LC := %g\n", LC);
237 printf("fittime := %g\n", fittime);
238 printf("density := %g\n", density);
239 printf("param_filt := %d\n", param_filt);
240 printf("calcThreshold := %g%%\n", 100.*calcThreshold);
241 printf("noneg := %d\n", noneg);
242 fflush(stdout);
243 }
244 if(verbose>9) IMG_TEST=verbose-9; else IMG_TEST=0;
245
246
247 /*
248 * Read PET image and input TAC
249 */
250 fittime/=60.;
252 petfile, NULL, inpfile, NULL, NULL, &fittime, &fitdimt, &img,
253 &input, NULL, 1, NULL, verbose-2, tmp);
254 if(ret!=0) {
255 fprintf(stderr, "Error in reading data: %s.\n", tmp);
256 imgEmpty(&img); dftEmpty(&input);
257 return(2);
258 }
259 if(imgNaNs(&img, 1)>0)
260 if(verbose>0) fprintf(stderr, "Warning: missing pixel values.\n");
261 fittime*=60.;
262 endSample=fitdimt-1;
263 if(verbose>2) {
264 printf("fittimeFinal := %g [min]\n", fittime/60.0);
265 printf("fitdimt := %d\n", fitdimt);
266 printf("endSampleIndex := %d\n", endSample);
267 printf("endSample := %d\n", endSample+1);
268 }
269 /* Find out the first sample to use in the line fit */
270 for(fi=startSample=0; fi<fitdimt; fi++)
271 if((img.mid[fi]/60.0)>startTime) break; else startSample++;
272 if(verbose>2) {
273 printf("startSampleIndex := %d\n", startSample);
274 printf("startSample := %d\n", startSample+1);
275 printf("startTimeFinal := %g [min]\n", img.start[startSample]/60.0);
276 }
277 if((fitdimt-startSample)<2) {
278 fprintf(stderr, "Error: too few time frames to fit.\n");
279 imgEmpty(&img); dftEmpty(&input);
280 return(3);
281 }
282
283
284#if(0)
285 /*
286 * Thresholding
287 */
288 if(verbose>1) fprintf(stdout, "thresholding\n");
289 ret=imgThresholding(&img, calcThreshold, &n);
290 if(ret!=0) {
291 fprintf(stderr, "Error in thresholding the dynamic image: %s\n", img.statmsg);
292 imgEmpty(&img); dftEmpty(&input);
293 return(4);
294 }
295 if(verbose>1)
296 fprintf(stdout, "threshold_cutoff_nr := %d / %d\n", n, img.dimx*img.dimy*img.dimz);
297#endif
298
299 /*
300 * Calculate Gjedde-Patlak plot
301 */
302 /* Fixed or free time range */
303 if(startTime>0.0) fit_range=PRESET; else fit_range=EXCLUDE_BEGIN;
304 if(verbose>1) printf("fit_range := %d\n", (int)fit_range);
305 /* Set optional output IMG struct pointers to NULL, if not needed */
306 IMG *picimg=NULL, *pnrimg=NULL;
307 if(icfile[0]) picimg=&icimg;
308 if(nrfile[0]) pnrimg=&nrimg;
309 /* MTGA */
310 if(verbose>0) fprintf(stdout, "computing MTGA pixel-by-pixel\n");
311 clock_t fitStart, fitFinish;
312 fitStart=clock();
313 ret=img_patlak(&input, &img, startSample, endSample, fit_range, calcThreshold,
314 &out, picimg, pnrimg, tmp, verbose-5);
315 if(ret!=0) {
316 fprintf(stderr, "Error (%d): %s\n", ret, tmp);
317 imgEmpty(&img); dftEmpty(&input); return(5);
318 }
319 fitFinish=clock();
320 if(verbose>0) {
321 if(fitFinish>fitStart)
322 fprintf(stdout, "done in %g seconds.\n",
323 (double)(fitFinish - fitStart) / (double)CLOCKS_PER_SEC );
324 else fprintf(stdout, "done.\n");
325 }
326 /* No need for the dynamic image or input data anymore */
327 imgEmpty(&img); dftEmpty(&input);
328
329
330
331 /*
332 * Filter the parametric images if required
333 */
334 /* Remove Ki values that are higher than the user-defined limit */
335 if(upperLimit>0.0) {
336 if(verbose>0) fprintf(stdout, "filtering out Ki values exceeding %g\n", upperLimit);
337 imgCutoff(&out, (float)upperLimit, 0);
338 }
339 /* Remove negative Ki values, if requested */
340 if(noneg) {
341 if(verbose>0) fprintf(stdout, "filtering out negative Ki values\n");
342 imgCutoff(&out, 0.0, 1);
343 }
344 /*
345 * Filter out pixels that are over 4x higher than their
346 * closest neighbours
347 */
348 if(param_filt>0) {
349 if(verbose>0) printf("filtering extreme values from parametric images\n");
350 imgOutlierFilter(&out, 4.0);
351 if(icfile[0]) imgOutlierFilter(&icimg, 4.0);
352 }
353
354
355 /*
356 * Calculate metabolic rate, if necessary
357 */
358 if(Ca>0.0) {
359 double MRf;
360 MRf=100.*Ca/(density*LC);
361 if(verbose>1) fprintf(stdout, "converting Ki to metabolic rate with factor %g\n", MRf);
362 ret=imgArithmConst(&out, MRf, '*', 1.0E+06, verbose-6);
363 if(ret!=0) {
364 fprintf(stderr, "Error: cannot calculate metabolic rate.\n");
365 imgEmpty(&out); imgEmpty(&icimg); imgEmpty(&nrimg);
366 return(7);
367 }
368 out.unit=CUNIT_UMOL_PER_MIN_PER_100G;
369 }
370
371 /*
372 * Save parametric Ki image
373 */
374 ret=backupExistingFile(outfile, NULL, tmp); if(ret!=0) {
375 fprintf(stderr, "Error: %s\n", tmp);
376 imgEmpty(&out); imgEmpty(&icimg); imgEmpty(&nrimg); return(11);
377 }
378 if(verbose>1) printf("%s\n", tmp);
379 ret=imgWrite(outfile, &out);
380 if(ret) {
381 fprintf(stderr, "Error: %s\n", out.statmsg);
382 imgEmpty(&out); imgEmpty(&icimg); imgEmpty(&nrimg); return(12);
383 }
384 if(verbose>0) {
385 if(Ca<=0.0) fprintf(stdout, "Ki image %s saved.\n", outfile);
386 else fprintf(stdout, "MR image %s saved.\n", outfile);
387 }
388
389 /*
390 * Save parametric Ic image
391 */
392 if(icfile[0]) {
393 ret=backupExistingFile(icfile, NULL, tmp); if(ret!=0) {
394 fprintf(stderr, "Error: %s\n", tmp);
395 imgEmpty(&out); imgEmpty(&icimg); imgEmpty(&nrimg); return(13);
396 }
397 if(verbose>1) printf("%s\n", tmp);
398 ret=imgWrite(icfile, &icimg); if(ret) {
399 fprintf(stderr, "Error: %s\n", icimg.statmsg);
400 imgEmpty(&out); imgEmpty(&icimg); imgEmpty(&nrimg); return(14);
401 }
402 if(verbose>0) fprintf(stdout, "Intercept image %s saved.\n", icfile);
403 }
404
405 /*
406 * Save parametric nr image
407 */
408 if(nrfile[0]) {
409 ret=backupExistingFile(nrfile, NULL, tmp); if(ret!=0) {
410 fprintf(stderr, "Error: %s\n", tmp);
411 imgEmpty(&out); imgEmpty(&icimg); imgEmpty(&nrimg); return(15);
412 }
413 if(verbose>1) printf("%s\n", tmp);
414 ret=imgWrite(nrfile, &nrimg); if(ret) {
415 fprintf(stderr, "Error: %s\n", nrimg.statmsg);
416 imgEmpty(&out); imgEmpty(&icimg); imgEmpty(&nrimg); return(16);
417 }
418 if(verbose>0) fprintf(stdout, "Nr image %s saved.\n", nrfile);
419 }
420
421 /* Free memory */
422 imgEmpty(&out); imgEmpty(&icimg); imgEmpty(&nrimg);
423
424
425 return(0);
426}
427/*****************************************************************************/
428
429/*****************************************************************************/
int backupExistingFile(char *filename, char *backup_ext, char *status)
Definition backup.c:14
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
void dftEmpty(DFT *data)
Definition dft.c:20
int IMG_TEST
Definition img.c:6
unsigned long long imgNaNs(IMG *img, int fix)
Definition img.c:658
void imgEmpty(IMG *image)
Definition img.c:121
void imgInit(IMG *image)
Definition img.c:60
int img_patlak(DFT *input, IMG *dyn_img, int start, int end, linefit_range fit_range, float thrs, IMG *ki_img, IMG *ic_img, IMG *nr_img, char *status, int verbose)
Definition img_mtga.c:17
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)
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.
linefit_range
Header file for libtpcmodext.
unsigned short int dimx
char unit
float * start
unsigned short int dimz
unsigned short int dimy
const char * statmsg
float * mid