TPCCLIB
Loading...
Searching...
No Matches
dftratio.c
Go to the documentation of this file.
1
7/*****************************************************************************/
8#include "tpcclibConfig.h"
9/*****************************************************************************/
10#include <stdio.h>
11#include <stdlib.h>
12#include <math.h>
13#include <string.h>
14/*****************************************************************************/
15#include "libtpcmisc.h"
16#include "libtpccurveio.h"
17#include "libtpcmodel.h"
18#include "libtpcmodext.h"
19/*****************************************************************************/
20
21/*****************************************************************************/
22static char *info[] = {
23 "Calculate the regional tissue to reference tissue ratio, which is often",
24 "used as a robust and model independent index of receptor availability.",
25 "By default, ratio of AUCs is calculated as",
26 " ",
27 " Regional AUC(t1..t2) ",
28 "Ratio = --------------------- ",
29 " Reference AUC(t1..t2) ",
30 " ",
31 "Data concentration units are cancelled out from the ratio. Therefore",
32 "the result is the same if it is calculated from original radioactivity",
33 "concentrations or SUV TACs (SUV ratio, SUR).",
34 " ",
35 "Usage: @P [options] ttacfile reference t1 t2 resultfile",
36 " ",
37 "Reference can be given either as the name or number of the reference TAC",
38 "inside the TTAC file or the name of file containing the reference TAC.",
39 "Start and end times (t1 and t2) must be given in minutes, if TAC file(s)",
40 "do not contain time units. To use the whole time range of ttacfile, enter",
41 "zeroes as t1 and t2.",
42 " ",
43 "Options:",
44 " -bound",
45 " Bound/free-ratio is calculated instead of Total/free;",
46 " reference region TAC is first subtracted from other regional TACs.",
47 " -max",
48 " Bound TAC maximum value is searched between start and end times,",
49 " and Bound/free ratio is calculated at this time point; this should",
50 " not be used with noisy data.",
51 " -rat=<Filename>",
52 " Ratio curve is saved for plotting.",
53 " -stdoptions", // List standard options like --help, -v, etc
54 " ",
55 "Example:",
56 " @P ut1234.tac occip 40 60 ut1234ratio.res",
57 " ",
58 "See also: imgratio, taccalc, dftinteg, interpol, tacunit, dftsuv",
59 " ",
60 "Keywords: TAC, modelling, AUC, ratio",
61 0};
62/*****************************************************************************/
63
64/*****************************************************************************/
65/* Turn on the globbing of the command line, since it is disabled by default in
66 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
67 In Unix&Linux wildcard command line processing is enabled by default. */
68/*
69#undef _CRT_glob
70#define _CRT_glob -1
71*/
72int _dowildcard = -1;
73/*****************************************************************************/
74
75/*****************************************************************************/
79int main(int argc, char **argv)
80{
81 int ai, help=0, version=0, verbose=1;
82 char *cptr, tmp[512];
83 char tacfile[FILENAME_MAX], resfile[FILENAME_MAX], ratfile[FILENAME_MAX];
84 char refname[FILENAME_MAX], reffile[FILENAME_MAX];
85 double tstart, tstop;
86 int ratmode=0; // 0=normal, 1=bound/ref, 2=bound/ref at bound max
87
88
89
90
91 /*
92 * Get arguments
93 */
94 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
95 tacfile[0]=resfile[0]=ratfile[0]=reffile[0]=refname[0]=(char)0;
96 tstart=tstop=nan("");
97 /* Get options first, because it affects what arguments are read */
98 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
99 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
100 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
101 if(strncasecmp(cptr, "BOUND", 1)==0) {
102 ratmode=1; continue;
103 } else if(strncasecmp(cptr, "MAX", 1)==0) {
104 ratmode=2; continue;
105 } else if(strncasecmp(cptr, "RAT=", 4)==0 && strlen(cptr)>4) {
106 strlcpy(ratfile, cptr+4, FILENAME_MAX); continue;
107 }
108 fprintf(stderr, "Error: invalid option '%s'\n", argv[ai]);
109 return(1);
110 } else break; // tac name argument may start with '-'
111
112 /* Print help or version? */
113 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
114 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
115 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
116
117 /* Process other arguments, starting from the first non-option */
118 if(ai<argc) {strlcpy(tacfile, argv[ai++], FILENAME_MAX);}
119 if(ai<argc) {strlcpy(reffile, argv[ai++], FILENAME_MAX);}
120 if(ai<argc) {
121 if(atof_with_check(argv[ai], &tstart)!=0) {
122 fprintf(stderr, "Error: invalid t1 '%s'\n", argv[ai]); return(1);
123 }
124 ai++;
125 }
126 if(ai<argc) {
127 if(atof_with_check(argv[ai], &tstop)!=0) {
128 fprintf(stderr, "Error: invalid t2 '%s'\n", argv[ai]); return(1);
129 }
130 ai++;
131 }
132 if(ai<argc) {strlcpy(resfile, argv[ai++], FILENAME_MAX);}
133 if(ai<argc) {
134 fprintf(stderr, "Error: extra command-line argument.\n");
135 return(1);
136 }
137 /* Check that we got what we need */
138 if(!resfile[0]) {tpcPrintUsage(argv[0], info, stderr); return(1);}
139 if(tstop<tstart) {
140 fprintf(stderr, "Error: illegal time range %g-%g\n", tstart, tstop);
141 return(1);
142 }
143
144
145 /* In verbose mode print arguments and options */
146 if(verbose>1) {
147 printf("tacfile := %s\n", tacfile);
148 printf("reference := %s\n", reffile);
149 printf("t1 := %g\n", tstart);
150 printf("t2 := %g\n", tstop);
151 printf("resfile := %s\n", resfile);
152 printf("ratmode := %d\n", ratmode);
153 if(ratfile[0]) printf("ratfile := %s\n", ratfile);
154 }
155
156
157 /*
158 * Read tissue data
159 */
160 if(verbose>1) printf("reading %s\n", tacfile);
161 DFT dft; dftInit(&dft);
162 if(dftRead(tacfile, &dft)) {
163 fprintf(stderr, "Error in reading '%s': %s\n", tacfile, dfterrmsg);
164 return(2);
165 }
166 /* Check if file contains NAs (missing values) */
167 if(dft_nr_of_NA(&dft)>0) {
168 fprintf(stderr, "Error: missing values in %s\n", tacfile);
169 dftEmpty(&dft); return(2);
170 }
171 /* Sort data by increasing sample time */
172 dftSortByFrame(&dft);
173
174 /* Convert time range to seconds, if data is given in seconds */
175 if(dft.timeunit==TUNIT_SEC) {
176 tstart*=60.0; tstop*=60.0;
177 if(verbose) printf("time range is converted to seconds.\n");
178 } else if(dft.timeunit==TUNIT_UNKNOWN) {
179 fprintf(stderr, "Warning: unknown data time units.\n");
180 fprintf(stderr, "Assuming that data and time range are in same units.\n");
181 }
182
183 /* If user gave zeroes for the start and end times, then get the times from tissue data */
184 if(tstart==0.0 && tstop==0.0) {
185 if(dft.timetype==DFT_TIME_STARTEND) {tstart=dft.x1[0]; tstop=dft.x2[dft.frameNr-1];}
186 else {tstart=dft.x[0]; tstop=dft.x[dft.frameNr-1];}
187 }
188
189 /* Check the time range */
190 int n=0;
191 if(dft.timetype==DFT_TIME_STARTEND) {
192 if(ratmode==2 && tstop>dft.x2[dft.frameNr-1]) tstop=dft.x2[dft.frameNr-1];
193 if(dft.x2[dft.frameNr-1] < 0.5*(tstart+tstop) || dft.x1[0]> 0.5*(tstart+tstop) ) {
194 fprintf(stderr, "Error: data does not contain specified time range.\n");
195 dftEmpty(&dft); return(2);
196 }
197 if(dft.x2[dft.frameNr-1] < tstop) {
198 fprintf(stderr, "Warning: specified time range exceeds the data.\n");
199 }
200 for(int fi=0; fi<dft.frameNr; fi++)
201 if(dft.x2[fi]>tstart && dft.x1[fi]<tstop) n++;
202 } else {
203 if(ratmode==2 && tstop>dft.x[dft.frameNr-1]) tstop=dft.x[dft.frameNr-1];
204 if(dft.x[dft.frameNr-1] < 0.5*(tstart+tstop) || dft.x[0]> 0.5*(tstart+tstop) ) {
205 fprintf(stderr, "Error: data does not contain the specified time range.\n");
206 dftEmpty(&dft); return(2);
207 }
208 if(dft.x[dft.frameNr-1] < tstop) {
209 fprintf(stderr, "Warning: specified time range exceeds the data.\n");
210 }
211 for(int fi=0; fi<dft.frameNr; fi++) if(dft.x[fi]>=tstart && dft.x[fi]<=tstop) n++;
212 }
213 if(verbose>1) {
214 printf("tstart := %g min\n", tstart);
215 printf("tstop := %g\n", tstop);
216 }
217 /* Verifying how many samples are inside the time range */
218 if(n<1) {
219 fprintf(stderr, "Error: data does not contain the specified time range.\n");
220 dftEmpty(&dft); return(2);
221 } else if(n<3) {
222 fprintf(stderr, "Warning: only %d sample(s) in specified time range.\n", n);
223 }
224
225
226 /*
227 * Select reference TAC file or reference VOI in tissue file,
228 * set ref = its voi number
229 */
230 if(verbose>1) printf("reading reference %s\n", reffile);
231 int inputtype;
232 double f, g;
233 DFT rdft; dftInit(&rdft);
234 if(dftReadinput(&rdft, &dft, reffile, &inputtype, &f, &g, 0, tmp, verbose-1)) {
235 fprintf(stderr, "Error in reading '%s': %s\n", reffile, tmp);
236 dftEmpty(&dft); dftEmpty(&rdft); return(3);
237 }
238 if(verbose>9) dftPrint(&rdft);
239 if(verbose>2) printf("inputtype := %d\n", inputtype);
240 if(inputtype==5) { // Reference region name was given
241 if(verbose>1)
242 fprintf(stdout, "selected reference region := %s\n", rdft.voi[0].name);
243 if(rdft.voiNr>1)
244 fprintf(stderr, "Warning: %s selected of %d reference regions.\n",
245 rdft.voi[0].name, rdft.voiNr);
246 strcpy(refname, rdft.voi[0].name);
247 strcpy(reffile, "");
248 } else {
249 if(verbose>2) printf("Reference tissue calibration unit := %s\n", rdft.unit);
250 if(rdft.voiNr>1)
251 fprintf(stderr, "Warning: only the first of reference curves is used.\n");
252 if(f>tstart || g<tstop)
253 fprintf(stderr, "Warning: time range may not be optimal for reference data.\n");
254 }
255
256
257 /*
258 * Subtract the reference region TAC, if required
259 */
260 if(ratmode!=0) {
261 if(verbose>1) printf("Subtracting the ref TAC\n");
262 for(int ri=0; ri<dft.voiNr; ri++)
263 for(int fi=0; fi<dft.frameNr; fi++)
264 dft.voi[ri].y[fi]-=rdft.voi[0].y[fi];
265 if(verbose>8) dftPrint(&dft);
266 }
267
268
269 /*
270 * Prepare the room for results
271 */
272 if(verbose>1) printf("initializing result data\n");
273 RES res; resInit(&res);
274 if(res_allocate_with_dft(&res, &dft)!=0) {
275 fprintf(stderr, "Error: cannot setup memory for results.\n");
276 dftEmpty(&dft); dftEmpty(&rdft); return(6);
277 }
278 /* Copy titles & filenames */
279 tpcProgramName(argv[0], 1, 1, res.program, 256);
280 strcpy(res.datafile, tacfile);
281 if(refname[0]) snprintf(res.refroi, 63, "%s", refname);
282 if(reffile[0]) strcpy(res.reffile, reffile);
283 /* Set data range */
284 sprintf(res.datarange, "%g - %g %s", tstart, tstop, petTunit(dft.timeunit));
285 /* Set current time to results */
286 res.time=time(NULL);
287 res.isweight=0; res.Vb=-1.0;
288 /* Set parameter number, including also the extra "parameters" */
289 /* Set the string for parameter names */
290 res.parNr=2;
291 if(ratmode==2) {
292 int pi;
293 pi=0; strcpy(res.parname[pi], "B/F"); strcpy(res.parunit[pi], "");
294 pi++; strcpy(res.parname[pi], "tMax");
295 strcpy(res.parunit[pi], petTunit(dft.timeunit));
296 } else {
297 int pi;
298 if(ratmode==1) {
299 pi=0; strcpy(res.parname[pi], "B/F"); strcpy(res.parunit[pi], "");
300 } else {
301 pi=0; strcpy(res.parname[pi], "T/F"); strcpy(res.parunit[pi], "");
302 }
303 pi++; strcpy(res.parname[pi], "AUC"); strcpy(res.parunit[pi], "");
304 }
305
306
307 /*
308 * Compute the ratios
309 */
310 if(verbose>1) printf("computing the ratios\n");
311 if(ratmode!=2) { /* AUC ratio */
312
313
314#if(1) // 2022-05-09 (takes into account the frame lengths)
315
316 double aucref;
317 DFT auc; dftInit(&auc);
318
319 /*
320 * Compute the AUC in reference region
321 */
322 if(dftTimeIntegral(&rdft, tstart, tstop, &auc, 0, NULL, verbose-3)!=0) {
323 fprintf(stderr, "Error in integration of reference region TAC.\n");
324 dftEmpty(&dft); dftEmpty(&rdft); resEmpty(&res); dftEmpty(&auc); return(7);
325 }
326 aucref=auc.voi[0].y[0];
327 dftEmpty(&auc);
328 if(verbose>2) printf("%s AUC = %g\n", rdft.voi[0].name, aucref);
329 if(aucref<=0.0) {
330 fprintf(stderr, "Error: invalid reference region AUC; check the data.\n");
331 dftEmpty(&dft); dftEmpty(&rdft); resEmpty(&res); return(7);
332 }
333
334 /*
335 * Compute the AUC and ratio in regions of interest
336 */
337 if(dftTimeIntegral(&dft, tstart, tstop, &auc, 0, NULL, verbose-3)!=0) {
338 fprintf(stderr, "Error in integration of regional TACs.\n");
339 dftEmpty(&dft); dftEmpty(&rdft); resEmpty(&res); dftEmpty(&auc); return(8);
340 }
341 for(int ri=0; ri<dft.voiNr; ri++) {
342 if(verbose>2) printf("%s %s %s AUC = %g\n",
343 dft.voi[ri].voiname, dft.voi[ri].hemisphere, dft.voi[ri].place, auc.voi[ri].y[0]);
344 /* Calculate the ratio */
345 res.voi[ri].parameter[0]=auc.voi[ri].y[0]/aucref;
346 res.voi[ri].parameter[1]=auc.voi[ri].y[0];
347 } /* next region */
348 dftEmpty(&auc);
349
350#else // before 2022-05-09 (did not work with static data at all)
351
352 /*
353 * Compute the AUC in reference region
354 */
355 double x[2], yi[2], aucref, aucroi;
356 x[0]=tstart; x[1]=tstop;
357 if(interpolate(dft.x, rdft.voi[0].y, dft.frameNr, x, NULL, yi, NULL, 2)!=0) {
358 fprintf(stderr, "Error in integration of reference region TAC.\n");
359 dftEmpty(&dft); dftEmpty(&rdft); resEmpty(&res); return(7);
360 }
361 aucref=yi[1]-yi[0];
362 if(verbose>2) printf("%s AUC%g - AUC%g = %g\n", rdft.voi[0].name, yi[1], yi[0], aucref);
363 if(aucref<=0.0) {
364 fprintf(stderr, "Error: invalid reference region AUC; check the data.\n");
365 dftEmpty(&dft); dftEmpty(&rdft); resEmpty(&res); return(7);
366 }
367
368 /*
369 * Compute the AUC and ratio in regions of interest
370 */
371 for(int ri=0; ri<dft.voiNr; ri++) {
372 /* compute the AUC */
373 if(interpolate(dft.x, dft.voi[ri].y, dft.frameNr, x, NULL, yi, NULL, 2)!=0) {
374 fprintf(stderr, "Error in integration of region %d.\n", ri+1);
375 dftEmpty(&dft); dftEmpty(&rdft); resEmpty(&res); return(8);
376 }
377 aucroi=yi[1]-yi[0];
378 if(verbose>2) printf("%s %s %s AUC%g - AUC%g = %g\n",
379 dft.voi[ri].voiname, dft.voi[ri].hemisphere, dft.voi[ri].place, yi[1], yi[0], aucroi);
380 /* Calculate the ratio */
381 res.voi[ri].parameter[0]=aucroi/aucref;
382 res.voi[ri].parameter[1]=aucroi;
383 } /* next region */
384#endif
385
386 } else { /* Ratio at max bound */
387
388 double aucroi;
389 for(int ri=0; ri<dft.voiNr; ri++) {
390 /* Bound TAC is already computed; search its max */
391 aucroi=0.0; n=-1;
392 for(int fi=0; fi<dft.frameNr; fi++) {
393 /* search only inside time range and with sensible ref values */
394 if(dft.x[fi]>=tstart && dft.x[fi]<=tstop && rdft.voi[0].y[fi]>0.0) {
395 if(dft.voi[ri].y[fi]>aucroi) {
396 n=fi; aucroi=dft.voi[ri].y[fi];
397 }
398 }
399 }
400 /* Compute the ratio and set the tMax */
401 if(n>=0) {
402 res.voi[ri].parameter[0]=aucroi/rdft.voi[0].y[n];
403 res.voi[ri].parameter[1]=dft.x[n];
404 } else {
405 res.voi[ri].parameter[0]=0.0;
406 res.voi[ri].parameter[1]=0.0;
407 }
408 } /* next region */
409
410 }
411
412 /*
413 * Print results on screen
414 */
415 if(verbose>0) {resPrint(&res); fprintf(stdout, "\n");}
416
417
418 /*
419 * Save results
420 */
421 if(verbose>1) printf("saving results\n");
422 if(resWrite(&res, resfile, verbose-3)!=0) {
423 fprintf(stderr, "Error in writing '%s': %s\n", resfile, reserrmsg);
424 dftEmpty(&dft); dftEmpty(&rdft); resEmpty(&res);
425 return(20);
426 }
427
428
429 /*
430 * Calculate and save the ratio curves, if required
431 */
432 if(ratfile[0]) {
433 if(verbose>1) printf("Calculating ratio curves\n");
434 for(int ri=0; ri<dft.voiNr; ri++) {
435 for(int fi=0; fi<dft.frameNr; fi++) {
436 if(isnan(rdft.voi[0].y[fi]) || isnan(dft.voi[ri].y[fi]) ||
437 fabs(rdft.voi[0].y[fi])<1.0E-20) {
438 dft.voi[ri].y[fi]=0.0;
439 } else {
440 dft.voi[ri].y[fi]/=rdft.voi[0].y[fi];
441 }
442 }
443 }
444 /* check that ratio is not < 0 or -1 */
445 for(int ri=0; ri<dft.voiNr; ri++) for(int fi=0; fi<dft.frameNr; fi++) {
446 if(ratmode>0) {if(dft.voi[ri].y[fi]<-1.0) dft.voi[ri].y[fi]=-1.0;}
447 else {if(dft.voi[ri].y[fi]<0.0) dft.voi[ri].y[fi]=0.0;}
448 }
449 /* remove initial high peaks */
450 f=g=0.0;
451 for(int ri=0; ri<dft.voiNr; ri++) for(int fi=2; fi<dft.frameNr; fi++) {
452 if(dft.voi[ri].y[fi]>f) f=dft.voi[ri].y[fi];
453 else if(dft.voi[ri].y[fi]<g) g=dft.voi[ri].y[fi];
454 }
455 for(int ri=0; ri<dft.voiNr; ri++) for(int fi=0; fi<2 && fi<dft.frameNr; fi++) {
456 if(dft.voi[ri].y[fi]>f) dft.voi[ri].y[fi]=f;
457 else if(dft.voi[ri].y[fi]<g) dft.voi[ri].y[fi]=g;
458 }
459 /* Correct 'header' information */
460 dftUnitToDFT(&dft, CUNIT_UNITLESS);
461 dft.comments[0]=(char)0;
462 dft.isweight=0;
463 /* and write as ratio file */
464 if(dftWrite(&dft, ratfile)) {
465 fprintf(stderr, "Error in writing '%s': %s\n", ratfile, dfterrmsg);
466 dftEmpty(&dft); dftEmpty(&rdft); resEmpty(&res);
467 return(22);
468 }
469 }
470
471 dftEmpty(&dft); dftEmpty(&rdft); resEmpty(&res);
472 return(0);
473}
474/*****************************************************************************/
475
476/*****************************************************************************/
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 dftSortByFrame(DFT *dft)
Definition dft.c:1169
void dftEmpty(DFT *data)
Definition dft.c:20
int dft_nr_of_NA(DFT *dft)
Definition dft.c:905
int dftReadinput(DFT *input, DFT *tissue, char *filename, int *filetype, double *ti1, double *ti2, int verifypeak, char *status, int verbose)
Definition dftinput.c:25
int dftTimeIntegral(DFT *dft, double t1, double t2, DFT *idft, int calc_mode, char *status, int verbose)
Definition dftint.c:382
int dftRead(char *filename, DFT *data)
Definition dftio.c:22
void dftPrint(DFT *data)
Definition dftio.c:538
int dftWrite(DFT *data, char *filename)
Definition dftio.c:594
int res_allocate_with_dft(RES *res, DFT *dft)
Definition dftres.c:14
void dftUnitToDFT(DFT *dft, int dunit)
Definition dftunit.c:11
int interpolate(double *x, double *y, int nr, double *newx, double *newy, double *newyi, double *newyii, int newnr)
Linear interpolation and integration.
Definition integr.c:28
Header file for libtpccurveio.
char reserrmsg[64]
Definition result.c:6
void resInit(RES *res)
Definition result.c:52
void resPrint(RES *res)
Definition result.c:186
int resWrite(RES *res, char *filename, int verbose)
Definition result.c:565
#define DFT_TIME_STARTEND
void resEmpty(RES *res)
Definition result.c:22
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
void tpcProgramName(const char *program, int version, int copyright, char *prname, int n)
Definition proginfo.c:101
char * petTunit(int tunit)
Definition petunits.c:226
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.
Header file for libtpcmodext.
int timetype
Voi * voi
int timeunit
double * x1
char comments[_DFT_COMMENT_LEN+1]
int voiNr
double * x2
int frameNr
int isweight
double * x
char unit[MAX_UNITS_LEN+1]
int parNr
char parname[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
ResVOI * voi
double Vb
char program[1024]
char datarange[128]
int isweight
char datafile[FILENAME_MAX]
char reffile[FILENAME_MAX]
char refroi[64]
char parunit[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
time_t time
double parameter[MAX_RESPARAMS]
char voiname[MAX_REGIONSUBNAME_LEN+1]
double * y
char name[MAX_REGIONNAME_LEN+1]
char hemisphere[MAX_REGIONSUBNAME_LEN+1]
char place[MAX_REGIONSUBNAME_LEN+1]