TPCCLIB
Loading...
Searching...
No Matches
logan.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 <math.h>
16#include <string.h>
17#include <time.h>
18/******************************************************************************/
19#include "libtpcmisc.h"
20#include "libtpcmodel.h"
21#include "libtpccurveio.h"
22#include "libtpcsvg.h"
23#include "libtpcmodext.h"
24/*****************************************************************************/
25
26/*****************************************************************************/
27static char *info[] = {
28 "Calculate the distribution volume (Vt) or distribution volume ratio (DVR)",
29 "using multiple-time graphical analysis (MTGA) for reversible PET",
30 "ligands (Logan plot) (1,2,3) from regional PET time-activity curves (TTACs).",
31 " ",
32 "Usage: @P [Options] ttac_file input start_time end_time result_file",
33 " ",
34 "Input can be either a TAC file containing arterial plasma PTAC or reference",
35 "region TAC, or name or number of reference region in the TTAC file.",
36 "Start and end times of the line fit to the plot must be given in minutes.",
37 " ",
38 "Options:",
39 " -k2=<reference region k2>",
40 " With reference region input, the population average of reference",
41 " region k2 (or k2/(1+k5/k6) in 3-compartment model) is set.",
42 " -BPnd",
43 " With reference input, BPnd (=DVR-1) is reported instead of DVR",
44 " -BPnd=<reference region name>",
45 " With plasma input, BPnd can be calculated as DVroi/DVref-1",
46 " -DVR=<reference region name>",
47 " With plasma input, DVR can be calculated as DVroi/DVref",
48 " -BPp=<reference region name>",
49 " With plasma input, BPp can be calculated as DVroi-DVref",
50 " -sd=<y|n>",
51 " Standard deviations are saved (y, default) or not saved (n) in results.",
52 " -mid[=<y|n>]",
53 " Mid frame times are used (y) or not used (n, default) even if frame",
54 " start and end times are available. For compatibility with old software.",
55 " -svg=<Filename>",
56 " Plots are written in specified file in Scalable Vector Graphics (SVG) 1.1",
57 " format; specification in https://www.w3.org/TR/SVG/",
58 " -bw",
59 " Black-and-white plot.",
60 " -plotdata=<Filename>",
61 " Data for plots is written in specified file in XHTML table format for",
62 " easy importing in Excel or OpenOffice spreadsheet, where the data can",
63 " be viewed; if file name extension is .dft, data is written in DFT format.",
64 " -stdoptions", // List standard options like --help, -v, etc
65 " ",
66 "Options for selecting the least-squares line fit method:",
67 " -C Traditional regression model",
68 " -M Median of two-point slopes and intercepts (Cornish-Bowden)",
69 " -P Perpendicular regression model (4)",
70 " -R Iterative method (York 1966, Lybanon 1984, Reed 1992); default",
71 " If tissue file contains weights, the iterative method (-R) is weighted.",
72 " With other fitting methods the weights are not used.",
73 " With options -C and -R program can automatically find the linear plot",
74 " range, if fit start time is set to zero.",
75 " ",
76 "Example 1: tissue curves are in ut2345.tac and plasma curve in ut2345ap.dat;",
77 "fitted data range is from 10 to 60 min; standard deviations are not needed,",
78 "plot is saved in file ut2345logan.svg",
79 " @P -sd=n -svg=ut2345logan.svg ut2345.tac ut2345ap.dat 10 60 ut2345.res",
80 " ",
81 "Example 2: tissue curves in ut1234.tac, including reference region 'cer';",
82 "reference tissue k2 is assumed to equal 0.163",
83 " @P -k2=0.163 ut2345.tac cer 20 60 ut2345.res",
84 " ",
85 "References:",
86 "1. Logan J, Fowler JS, Volkow ND, Wolf AP, Dewey SL, Schlyer DJ,",
87 " MacGregor RR, Hitzemann R, Bendriem B, Gatley SJ, Christman DR.",
88 " Graphical analysis of reversible radioligand binding from time-activity",
89 " measurements applied to [N-11C-methyl]-(-)-cocaine PET studies in human",
90 " subjects. J Cereb Blood Flow Metab 1990; 10: 740-747.",
91 "2. Logan J, Fowler JS, Volkow ND, Wang GJ, Ding YS, Alexoff DL.",
92 " Distribution volume ratios without blood sampling from graphical",
93 " analysis of PET data. J Cereb Blood Flow Metab. 1996; 16: 834-840.",
94 "3. Logan J. Graphical analysis of PET data applied to reversible and",
95 " irreversible tracers. Nucl Med Biol 2000; 27:661-670.",
96 "4. Varga J & Szabo Z. Modified regression model for the Logan plot.",
97 " J Cereb Blood Flow Metab 2002; 22:240-244.",
98 " ",
99 "See also: imgdv, fitk2, fitk4, patlak, rescoll",
100 " ",
101 "Keywords: TAC, MTGA, Logan plot, modelling, distribution volume, DVR",
102 0};
103/******************************************************************************/
104
105/******************************************************************************/
106/* Local functions */
108 double *x, double *y, double *wx, double *wy, int nr, int min_nr,
109 double *slope, double *ic, double *nwss, double *sslope, double *sic,
110 double *cx, double *cy, int *bnr, int verbose
111);
113 double *x, double *y, double *wx, double *wy, int nr, int min_nr,
114 double *slope, double *ic, double *r, double *sslope, double *sic, int *bnr,
115 int verbose
116);
117/******************************************************************************/
118
119/******************************************************************************/
120/*
121 * Main
122 */
123int main(int argc, char **argv)
124{
125 int ai, help=0, version=0, verbose=1;
126
127 int ri, fi, pi, ret;
128 int inputtype=0, dataNr=0, first, last, llsq_model=1;
129 int save_stat=1, dvr_roi=-1, n, always_mid=0;
130 int bp_type=0; // 0=no, 1=DVR, 2=BPnd, 3=BPp
131 int dvr_minus_one=0; // 0=DVR reported, 1=BPnd reported with ref input
132 int color_scale=0; // 0=colour, 2=black-and-white.
133 char dfile[FILENAME_MAX], ifile[FILENAME_MAX], rfile[FILENAME_MAX],
134 pfile[FILENAME_MAX], sfile[FILENAME_MAX],
135 dvrname[FILENAME_MAX], tmp[1024], *cptr;
136 double tstart, tstop, DV, DVSD, Ic, IcSD, SWSS;
137 double istart;
138 double f, k2=-1.0;
139 double *theta, *dv;
140//double *w, *wx, *wy, *cx, *cy;
141 double *ci, *ici, *ct, *ict;
142 DFT data, input;
143 RES res;
144
145
146
147 /*
148 * Get arguments
149 */
150 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
151 dfile[0]=ifile[0]=rfile[0]=pfile[0]=sfile[0]=dvrname[0]=(char)0;
152 tstart=tstop=-1;
153 dftInit(&data); dftInit(&input); resInit(&res);
154 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') { /* option */
155 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
156 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
157 cptr=argv[ai]+1;
158 if(strncasecmp(cptr, "K2=", 3)==0) {
159 k2=atof_dpi(cptr+3); if(k2>0.0) continue;
160 } else if(strncasecmp(cptr, "DVR=", 4)==0) {
161 bp_type=1; strcpy(dvrname, cptr+4); if(strlen(dvrname)>0.0) continue;
162 } else if(strcasecmp(cptr, "BPnd")==0) {
163 dvr_minus_one=1; continue;
164 } else if(strncasecmp(cptr, "BPnd=", 5)==0) {
165 bp_type=2; strcpy(dvrname, cptr+5); if(strlen(dvrname)>0.0) continue;
166 } else if(strncasecmp(cptr, "BPp=", 4)==0||strncasecmp(cptr, "DV3=", 4)==0){
167 bp_type=3; strcpy(dvrname, cptr+4); if(strlen(dvrname)>0.0) continue;
168 } else if(strncasecmp(cptr, "SD=", 3)==0) {
169 save_stat=1; cptr+=3; if(strlen(cptr)<1) continue;
170 if(strncasecmp(cptr, "yes", 1)==0) {save_stat=1; continue;}
171 else if(strncasecmp(cptr, "no", 1)==0) {save_stat=0; continue;}
172 } else if(strncasecmp(cptr, "MID", 3)==0) {
173 always_mid=1; cptr+=3; if(strlen(cptr)<2) continue;
174 if(*cptr=='=') cptr++;
175 if(strncasecmp(cptr, "yes", 1)==0) {
176 always_mid=1; continue;
177 } else if(strncasecmp(cptr, "no", 1)==0) {
178 always_mid=0; continue;
179 }
180 } else if(strncasecmp(cptr, "SVG=", 4)==0) {
181 strlcpy(sfile, cptr+4, FILENAME_MAX); if(strlen(sfile)>0) continue;
182 } else if(strncasecmp(cptr, "PLOTDATA=", 9)==0) {
183 strlcpy(pfile, cptr+9, FILENAME_MAX); if(strlen(pfile)>0) continue;
184 /* Check if regression method was specified */
185 } else if(strcasecmp(cptr, "C")==0) {
186 llsq_model=0; continue;
187 } else if(strcasecmp(cptr, "R")==0) {
188 llsq_model=1; continue;
189 } else if(strcasecmp(cptr, "P")==0) {
190 llsq_model=2; continue;
191 } else if(strcasecmp(cptr, "M")==0) {
192 llsq_model=0; continue;
193 } else if(strcasecmp(cptr, "BW")==0) {
194 color_scale=2; continue;
195 }
196 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
197 return(1);
198 } else break;
199
200 /* Print help or version? */
201 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
202 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
203 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
204
205 /* Process other arguments, starting from the first non-option */
206 for(; ai<argc; ai++) {
207 if(!dfile[0]) {strlcpy(dfile, argv[ai], FILENAME_MAX); continue;}
208 if(!ifile[0]) {strlcpy(ifile, argv[ai], FILENAME_MAX); continue;}
209 if(tstart<0) {tstart=atof_dpi(argv[ai]); continue;}
210 if(tstop<0) {tstop=atof_dpi(argv[ai]); continue;}
211 if(!rfile[0]) {strlcpy(rfile, argv[ai], FILENAME_MAX); continue;}
212 if(!pfile[0]) {strlcpy(pfile, argv[ai], FILENAME_MAX); continue;}
213 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
214 return(1);
215 }
216
217 /* Is something missing? */
218 if(!rfile[0]) {
219 fprintf(stderr, "Error: missing result file name.\n");
220 return(1);
221 }
222 if(tstop<=tstart) {
223 fprintf(stderr, "Error: illegal time range %g-%g\n", tstart, tstop);
224 return(1);
225 }
226 /* Ref name for DVR or BP calculation not allowed with ref input */
227 if(dvrname[0] && k2>0.0) {
228 fprintf(stderr, "Error: use options -DVR, -BPnd and -BPp only with plasma input.\n");
229 return(1);
230 }
231
232 /* In verbose mode print arguments and options */
233 if(verbose>1) {
234 printf("%s", argv[0]); for(ai=1; ai<argc; ai++) printf(" %s", argv[ai]);
235 printf("\n");
236 }
237 if(verbose>2) {
238 printf("llsq_model := %d\n", llsq_model);
239 printf("dfile := %s\n", dfile);
240 printf("pfile := %s\n", pfile);
241 printf("rfile := %s\n", rfile);
242 printf("ifile := %s\n", ifile);
243 printf("sfile := %s\n", sfile);
244 printf("tstart := %g\ntstop := %g\n", tstart, tstop);
245 printf("always_mid := %d\n", always_mid);
246 printf("k2 := %g\n", k2);
247 printf("bp_type := %d\n", bp_type);
248 printf("dvr_minus_one := %d\n", dvr_minus_one);
249 printf("dvrname := %s\n", dvrname);
250 if(color_scale==2) printf("color_scale := black-and-white\n");
251 fflush(stdout);
252 }
253
254
255 /*
256 * Read tissue data
257 */
258 if(verbose>1) printf("reading %s\n", dfile);
259 if(dftRead(dfile, &data)) {
260 fprintf(stderr, "Error in reading '%s': %s\n", dfile, dfterrmsg);
261 return(2);
262 }
263 //for(int i=0; i<data.voiNr; i++) printf(" name='%s'\n", data.voi[i].name);
264 //printf(" studynr='%s'\n", data.studynr);
265
266 if(dft_nr_of_NA(&data)>0) { // check if file contains NAs (missing values)
267 fprintf(stderr, "Error: missing values in %s\n", dfile);
268 dftEmpty(&data); return(2);
269 }
270 // like if we had only frame mid times
271 if(always_mid!=0) data.timetype=DFT_TIME_MIDDLE;
272 /* Make sure that there is no overlap in frame times */
273 if(data.timetype==DFT_TIME_STARTEND) {
274 if(verbose>1) fprintf(stdout, "checking frame overlap in %s\n", dfile);
275 ret=dftDeleteFrameOverlap(&data);
276 if(ret) {
277 fprintf(stderr, "Error: %s has overlapping frame times.\n", dfile);
278 dftEmpty(&data); return(2);
279 }
280 }
281 /* Set time unit to min */
282 ret=dftTimeunitConversion(&data, TUNIT_MIN);
283 if(ret) fprintf(stderr,
284 "Warning: check that regional data times are in minutes.\n");
285 /* Get and check fit time range */
286 dataNr=fittime_from_dft(&data, &tstart, &tstop, &first, &last, verbose-8);
287 if(verbose>2) {
288 printf("dataNr_in_range := %d\n", dataNr);
289 printf("first_in_range := %d\n", first);
290 printf("last_in_range := %d\n", last);
291 }
292 if(dataNr<1) {
293 fprintf(stderr, "Error: data does not contain the specified time range.\n");
294 dftEmpty(&data); return(2);
295 } else if(dataNr<2) {
296 fprintf(stderr, "Error: cannot make plot from less than 2 points.\n");
297 dftEmpty(&data); return(2);
298 } else if(dataNr==2) {
299 fprintf(stderr, "Warning: only two samples in the time range.\n");
300 }
301 if(verbose>2) {
302 printf("dataNr := %d\n", dataNr);
303 printf("tstart := %g\ntstop := %g\n", tstart, tstop);
304 printf("first := %d\nlast := %d\n", first, last);
305 }
306 //for(int i=0; i<data.voiNr; i++) printf(" name='%s'\n", data.voi[i].name);
307 //printf(" studynr='%s'\n", data.studynr);
308
309
310 /*
311 * Read input data
312 */
313 if(verbose>1) printf("reading input\n");
314 if((ret=dftReadinput(&input, &data, ifile, &inputtype, &istart,
315 NULL, 1, tmp, verbose-6))) {
316 if(ret==101) {
317 if(inputtype==5) fprintf(stderr, "Error: %s.\n", tmp);
318 else fprintf(stderr, "Error in reading '%s': %s\n", ifile, tmp);
319 dftEmpty(&data); fflush(stderr); return(3);
320 }
321 /* Other errors may be caused by much longer tissue data than plasma data.
322 Try to correct that by setting tissue frame nr to match the last
323 fitted frame */
324 if(verbose>1) printf("trying to fix tissue frame nr.\n");
325 data.frameNr=last+1;
326 if(((ret=dftReadinput(&input, &data, ifile, &inputtype, &istart, NULL, 1, tmp, verbose-6)))!=0)
327 {
328 fprintf(stderr, "Error in reading '%s': %s\n", ifile, tmp);
329 if(verbose>0) printf("dftReadinput() := %d\n", ret);
330 //if(TEST) dftPrint(&data);
331 dftEmpty(&data); return(3);
332 }
333 }
334 if(verbose>9) {
335 printf("\nInput data:\n");
336 dftPrint(&input);
337 printf("\nTissue data:\n");
338 dftPrint(&data);
339 }
340 if(inputtype==5) { // Reference region name was given
341 if(verbose>0) fprintf(stdout, "selected reference region := %s\n", input.voi[0].name);
342 for(ri=1; ri<input.voiNr; ri++)
343 fprintf(stderr, "Warning: reference region %s unused.\n", input.voi[ri].name);
344 } else {
345 if(input.voiNr>1) fprintf(stderr, "Warning: only the first of input curves is used.\n");
346 }
347 if(inputtype!=5 && k2>0.0) {
348 fprintf(stderr, "Error: reference region k2 must be used only with reference tissue.\n");
349 dftEmpty(&input); dftEmpty(&data); return(3);
350 }
351
352 /* Check that original input data started early enough, otherwise AUC(0-T)
353 might be wrong */
354 if(istart>0.3) {
355 fprintf(stderr, "Warning: input TAC should start at time zero.\n");
356 fflush(stderr);
357 }
358 //for(int i=0; i<data.voiNr; i++) printf(" name='%s'\n", data.voi[i].name);
359 //printf(" studynr='%s'\n", data.studynr);
360
361
362 /* Integrate tissue data */
363 if(verbose>1) {printf("integrating tissue data\n"); fflush(stdout);}
364 for(ri=0; ri<data.voiNr; ri++) {
365 if(data.timetype==DFT_TIME_STARTEND && always_mid==0)
366 ret=petintegral(data.x1, data.x2, data.voi[ri].y, data.frameNr, data.voi[ri].y2, NULL);
367 else
368 ret=integrate(data.x, data.voi[ri].y, data.frameNr, data.voi[ri].y2);
369 if(ret) {
370 fprintf(stderr, "Error in integration of tissue data.\n");
371 dftEmpty(&data); dftEmpty(&input); return(2);
372 }
373 }
374 //for(int i=0; i<data.voiNr; i++) printf(" name='%s'\n", data.voi[i].name);
375 //printf(" studynr='%s'\n", data.studynr);
376
377
378 /*
379 * Find the reference ROI for DVR calculation with plasma input
380 */
381 if(dvrname[0]) {
382 if(verbose>1) printf("selecting reference curve\n");
383 n=dftSelectRegions(&data, dvrname, 1);
384 if(n<=0) { /* no vois are matching */
385 fprintf(stderr, "Error: Cannot find ref voi '%s'.\n", dvrname);
386 dftEmpty(&input); dftEmpty(&data); return(1);
387 }
388 if(n>1) {
389 n=dftSelectBestReference(&data);
390 if(n<0) { /* no vois are matching */
391 fprintf(stderr, "Error: Cannot find ref voi '%s'.\n", dvrname);
392 dftEmpty(&input); dftEmpty(&data); return(1);
393 }
394 dvr_roi=n;
395 fprintf(stderr,"Warning: several ref regions match; %s is selected.\n",
396 data.voi[dvr_roi].name );
397 } else {
398 for(ri=0; ri<data.voiNr; ri++) if(data.voi[ri].sw) {dvr_roi=ri; break;}
399 }
400 if(verbose>0)
401 printf("Reference region: %s (voi=%d)\n", data.voi[dvr_roi].name, dvr_roi);
402 }
403 //for(int i=0; i<data.voiNr; i++) printf(" name='%s'\n", data.voi[i].name);
404 //printf(" studynr='%s'\n", data.studynr);
405
406
407 /*
408 * Prepare the room for results
409 */
410 if(verbose>1) printf("initializing result data\n");
411 ret=res_allocate_with_dft(&res, &data); if(ret!=0) {
412 fprintf(stderr, "Error: cannot setup memory for results.\n");
413 dftEmpty(&input); dftEmpty(&data); return(4);
414 }
415 /* Copy titles & filenames */
416 tpcProgramName(argv[0], 1, 1, res.program, 128);
417 strcpy(res.datafile, dfile);
418 //if(verbose>4) printf("dfile := '%s'\n", dfile);
419 //if(verbose>4) printf("res.datafile := '%s'\n", res.datafile);
420 if(inputtype==5) {
421 strcpy(res.refroi, input.voi[0].name);
422 strcpy(res.plasmafile, "");
423 } else {
424 strcpy(res.refroi, "");
425 strcpy(res.plasmafile, ifile);
426 }
427 //if(verbose>4) printf(" studynr := '%s'\n", res.studynr);
428 if(strlen(res.studynr)==0 || strcmp(res.studynr, ".")==0)
429 studynr_from_fname2(dfile, res.studynr, 1);
430 //if(verbose>4) printf(" studynr := '%s'\n", res.studynr);
431 /* Set data range, add ref k2 there since no space elsewhere */
432 sprintf(res.datarange, "%g - %g %s", tstart, tstop, petTunit(data.timeunit));
433 if(k2>0) {sprintf(tmp, " refk2=%g", k2); strcat(res.datarange, tmp);}
434 res.datanr=dataNr;
435 if(llsq_model==0) strcpy(res.fitmethod, "Traditional regression model");
436 else if(llsq_model==1) strcpy(res.fitmethod, "Iterative method");
437 else if(llsq_model==2) strcpy(res.fitmethod, "Perpendicular regression model");
438 else if(llsq_model==3) strcpy(res.fitmethod, "Median of two-point slopes");
439 /* Set current time to results */
440 res.time=time(NULL);
441 res.isweight=0; if(llsq_model==1) res.isweight=data.isweight;
442 /* Set parameter number, including also the extra "parameters" */
443 /* Set the string for parameter names */
444 res.parNr=3;
445 pi=0;
446 if(inputtype==5) {
447 if(dvr_minus_one==0) strcpy(res.parname[pi], "DVR");
448 else strcpy(res.parname[pi], "BPnd");
449 strcpy(res.parunit[pi], petCunit(CUNIT_UNITLESS));
450 } else {
451 strcpy(res.parname[pi], "DV");
452 strcpy(res.parunit[pi], petCunit(CUNIT_ML_PER_ML));
453 }
454 pi++;
455 strcpy(res.parname[pi], "Ic");
456 strcpy(res.parunit[pi], petCunit(CUNIT_PER_MIN));
457 pi++;
458 if(llsq_model==1 || llsq_model==3) {
459 strcpy(res.parname[pi], "SqrtWSS");
460 strcpy(res.parunit[pi], petCunit(CUNIT_UNITLESS));
461 } else if(llsq_model==0) {
462 strcpy(res.parname[pi], "r");
463 strcpy(res.parunit[pi], petCunit(CUNIT_UNITLESS));
464 } else if(llsq_model==2) {
465 strcpy(res.parname[pi], "SSD");
466 strcpy(res.parunit[pi], petCunit(CUNIT_UNITLESS));
467 }
468 pi++;
469 if(dvrname[0] && bp_type>0) {
470 res.parNr++;
471 if(bp_type==1) {
472 strcpy(res.parname[pi], "DVR");
473 strcpy(res.parunit[pi], petCunit(CUNIT_UNITLESS));
474 } else if(bp_type==2) {
475 strcpy(res.parname[pi], "BPnd");
476 strcpy(res.parunit[pi], petCunit(CUNIT_UNITLESS));
477 } else {
478 strcpy(res.parname[pi], "BPp");
479 strcpy(res.parunit[pi], petCunit(CUNIT_ML_PER_ML));
480 }
481 pi++;
482 sprintf(res.refroi, "%s", data.voi[dvr_roi].name);
483 }
484 //for(int i=0; i<data.voiNr; i++) printf(" name='%s'\n", data.voi[i].name);
485 //printf(" studynr='%s'\n", data.studynr);
486
487 /*
488 * Allocate memory for llsqwt() and weights
489 */
490 double w[data.frameNr];
491 double wx[data.frameNr];
492 double wy[data.frameNr];
493 double cx[data.frameNr];
494 double cy[data.frameNr];
495
496 /*
497 * Calculate DV for each region
498 */
499
500 /* One region at a time */
501 for(ri=0; ri<data.voiNr; ri++) {
502 if(verbose>2) printf("calculating %s\n", data.voi[ri].name);
503
504 /*
505 * Set axis weights;
506 * do this again for each region, because these can be set to zero below
507 */
508 for(fi=0; fi<data.frameNr; fi++) {
509 if(data.isweight) wx[fi]=wy[fi]=data.w[fi]; else wx[fi]=wy[fi]=1.0;
510 if(data.timetype==DFT_TIME_STARTEND)
511 wx[fi]*=data.x2[fi]; else wx[fi]*=data.x[fi];
512 }
513
514 /* Set data pointers */
515 ci=input.voi[0].y; ici=input.voi[0].y2;
516 ct=data.voi[ri].y; ict=data.voi[ri].y2;
517 theta=data.voi[ri].y2; /* Note that this is the same where integral is! */
518 dv=data.voi[ri].y3;
519
520 /* Calculate Logan plot data */
521 for(fi=data.frameNr-1; fi>=0; fi--) if(ct[fi]!=0.0) {
522 if(verbose>8) {
523 printf("%03d %8.3f : ici=%g ci=%g ict=%g ct=%g\n",
524 fi+1, data.x[fi], ici[fi], ci[fi], ict[fi], ct[fi] );
525 }
526 /* dv (y axis) */
527 dv[fi]=ict[fi]/ct[fi]; /*if(dv[fi]<0.0) wy[fi]=0;*/
528 /* theta (x axis) */
529 if(k2>0) theta[fi]=(ici[fi]+ci[fi]/k2)/ct[fi];
530 else theta[fi]=ici[fi]/ct[fi];
531 /* check the close-to-zeroes in the first frames */
532 if(data.x[fi]<0.1*data.x[data.frameNr-1]) {
533 if(theta[fi]>theta[data.frameNr-1] || dv[fi]>dv[data.frameNr-1]) {
534 if(verbose>2)
535 printf("Possible close-to-zero plot point at %g -> set to zero.\n", data.x[fi]);
536 theta[fi]=dv[fi]=wx[fi]=wy[fi]=0.0;
537 }
538 }
539 } else theta[fi]=dv[fi]=wx[fi]=wy[fi]=0.0;
540
541 if(verbose>6) {
542 for(fi=first; fi<=last; fi++)
543 printf("%03d %8.3f : %g %g (%g %g)\n",
544 fi+1, data.x[fi], theta[fi], dv[fi], wx[fi], wy[fi] );
545 }
546
547 /* Linear fit */
548 DVSD=DV=Ic=IcSD=SWSS=0.0; ret=0;
549 if(llsq_model==1) {
550 if(first==0) {
551 /* Calculation of best LLSQ fit with errors in both coordinates */
552 ret=best_logan_reed(
553 &theta[first], &dv[first], &wx[first], &wy[first], dataNr, 5,
554 &DV, &Ic, &SWSS, &DVSD, &IcSD, cx, cy, &fi, verbose-4
555 );
556 if(verbose>7) printf("Min NWSS with %d data points.\n", fi);
557 res.voi[ri].sw=fi;
558 } else {
559 /* Calculation of LLSQ fit with errors in both coordinates */
560 ret=llsqwt(
561 /* input */
562 &theta[first], &dv[first], dataNr, &wx[first], &wy[first],
563 1.0e-10, /* allowed tolerance in slope estimation */
564 /* input/output */
565 &w[first], /* work vector; effective weights w[i] are returned in it */
566 /* output ; SWSS is already normalized */
567 &Ic, &DV, &SWSS, &IcSD, &DVSD, cx, cy
568 );
569 }
570 if(verbose>6) {
571 printf("%s:\n", data.voi[ri].name);
572 for(fi=first; fi<=last; fi++)
573 printf("%03d %8.3f : %g %g (%g %g -> %g)\n",
574 fi+1, data.x[fi], theta[fi], dv[fi], wx[fi], wy[fi], w[fi] );
575 }
576 } else if(llsq_model==2) {
577 /* Check that theta is not negative */
578 for(fi=first; fi<=last; fi++) if(theta[fi]<0.0) theta[fi]=nan("");
579 /* Calculation of perpendicular line fit */
580 ret=llsqperp3(
581 /* input */
582 &theta[first], &dv[first], dataNr,
583 /* output ; SSD is already normalized */
584 &DV, &Ic, &SWSS
585 );
586 } else if(llsq_model==0) {
587 if(first==0) {
588 /* Calculation of best regression line */
589 ret=best_logan_regr(
590 &theta[first], &dv[first], &wx[first], &wy[first], dataNr, 5,
591 &DV, &Ic, &SWSS, &DVSD, &IcSD, &fi, verbose-4
592 );
593 res.voi[ri].sw=fi;
594 } else {
595 /* Check that theta is not negative */
596 for(fi=first; fi<=last; fi++) if(theta[fi]<0.0) theta[fi]=nan("");
597 /* Calculation of linear regression using pearson() */
598 ret=pearson3(
599 /* input */
600 &theta[first], &dv[first], dataNr,
601 /* output */
602 &DV, &DVSD, &Ic, &IcSD, &SWSS, &f
603 );
604 }
605 } else if(llsq_model==3) {
606 /* Check that theta is not negative */
607 for(fi=first; fi<=last; fi++) if(theta[fi]<0.0) theta[fi]=nan("");
608 /* Calculation of median slope and ic */
609 ret=medianline(
610 &theta[first], &dv[first], dataNr, &DV, &Ic
611 );
612 }
613 if(ret==0) {
614 res.voi[ri].parameter[0]=DV; if(save_stat) res.voi[ri].sd[0]=DVSD;
615 if(inputtype==5 && dvr_minus_one!=0) {
616 res.voi[ri].parameter[0]-=1.0;
617 }
618 res.voi[ri].parameter[1]=Ic; if(save_stat) res.voi[ri].sd[1]=IcSD;
619 res.voi[ri].parameter[2]=SWSS;
620 if(verbose>2) printf("DV := %g (%g)\n", DV, DVSD);
621 } else
622 fprintf(stderr, "Error (%d) in linear fit of %s\n", ret, data.voi[ri].name);
623 } /* next region */
624
625
626 /* Compute the DVR, BPnd or BPp if plasma input */
627 if(dvr_roi>=0) {
628 for(ri=0; ri<data.voiNr; ri++) {
629 if(bp_type==1)
630 res.voi[ri].parameter[3]=res.voi[ri].parameter[0]/res.voi[dvr_roi].parameter[0];
631 else if(bp_type==2)
632 res.voi[ri].parameter[3]=res.voi[ri].parameter[0]/res.voi[dvr_roi].parameter[0] - 1.0;
633 else
634 res.voi[ri].parameter[3]=res.voi[ri].parameter[0]-res.voi[dvr_roi].parameter[0];
635 }
636 }
637
638
639 /*
640 * Print results on screen
641 */
642 if(verbose>=0) {
643 if(res.voiNr<=128) {resPrint(&res); fprintf(stdout, "\n");}
644 else fprintf(stdout, "Logan plot calculated from %d regional TACs.\n", res.voiNr);
645 }
646
647 /* If DV was negative, print the plot data */
648 if(verbose>3) for(ri=0; ri<data.voiNr; ri++) if(res.voi[ri].parameter[0]<0) {
649 printf("%d %s: DV=%g\n", ri+1, data.voi[ri].name, res.voi[ri].parameter[0]);
650 for(fi=first; fi<=last; fi++)
651 printf("%03d %8.3f : %g %g\n", fi+1, data.x[fi], data.voi[ri].y2[fi], data.voi[ri].y3[fi] );
652 }
653
654
655 /*
656 * Save results
657 */
658 if(verbose>0) printf("saving results\n");
659 ret=resWrite(&res, rfile, verbose-3);
660 if(ret) {
661 fprintf(stderr, "Error in writing '%s': %s\n", rfile, reserrmsg);
662 dftEmpty(&data); dftEmpty(&input); resEmpty(&res);
663 return(11);
664 }
665
666 /*
667 * Plots
668 */
669 if(verbose>3) dftPrint(&data);
670 /* Convert ref input BPnd back to DVR for plotting */
671 if(inputtype==5 && dvr_minus_one!=0) {
672 for(ri=0; ri<res.voiNr; ri++) res.voi[ri].parameter[0]+=1.0;
673 }
674 /* Plot data */
675 if(pfile[0]) {
676 if(verbose>0) printf("saving plots\n");
677 sprintf(tmp, "Logan-plot ");
678 if(strcmp(res.studynr, ".")!=0) strcat(tmp, res.studynr);
679 ret=plotdata(&data, &res, first, last, tmp,
680 "Input integral / Tissue", "Tissue integral / Tissue", pfile);
681 if(ret) {
682 fprintf(stderr, "Error in writing '%s': %s\n", pfile, reserrmsg);
683 dftEmpty(&data); dftEmpty(&input); resEmpty(&res);
684 return(20+ret);
685 }
686 if(verbose>=0) printf("Plots written in %s\n", pfile);
687 }
688 /* SVG plot */
689 if(sfile[0]) {
690 if(verbose>0) printf("saving SVG plot\n");
691 sprintf(tmp, "Logan-plot ");
692 if(strcmp(res.studynr, ".")!=0) strcat(tmp, res.studynr);
693 fi=data.frameNr; data.frameNr=last+1;
694 ret=plot_svg(&data, &res, first, last, tmp,
695 "Input integral / Tissue", "Tissue integral / Tissue", color_scale, sfile, verbose-8);
696 data.frameNr=fi;
697 if(ret) {
698 fprintf(stderr, "Error (%d) in writing '%s'.\n", ret, sfile);
699 dftEmpty(&data); dftEmpty(&input); resEmpty(&res);
700 return(20+ret);
701 }
702 if(verbose>=0) printf("Plots written in %s\n", sfile);
703 }
704
705 /* Free memory */
706 dftEmpty(&data); dftEmpty(&input); resEmpty(&res);
707
708 return(0);
709}
710/******************************************************************************/
712/******************************************************************************/
719 double *x,
721 double *y,
723 double *wx,
725 double *wy,
727 int nr,
729 int min_nr,
731 double *slope,
733 double *ic,
735 double *nwss,
737 double *sslope,
739 double *sic,
741 double *cx,
743 double *cy,
745 int *bnr,
747 int verbose
748) {
749 int from, to, ret, from_min, to_min;
750 double *w, lic, lslope, lnwss, nwss_min;
751
752 if(verbose>0) printf("best_logan_reed()\n");
753 /* Check the data */
754 if(x==NULL || y==NULL || nr<min_nr || nr<2) return(1);
755 /* Check parameters */
756 if(min_nr<4) return(2);
757
758 /* Make room for work vector */
759 w=(double*)malloc(nr*sizeof(double)); if(w==NULL) return(3);
760
761 /* Search the plot range that gives the best nwss */
762 nwss_min=9.99E+99; from_min=to_min=-1;
763 for(from=0, to=nr-1; from<nr-min_nr; from++) {
764 ret=llsqwt(x+from, y+from, (to-from)+1, wx+from, wy+from, 1.0E-10, w,
765 &lic, &lslope, &lnwss, NULL, NULL, cx+from, cy+from);
766 if(verbose>1) {
767 printf(" range: %d-%d ; nwss=%g ; min=%g ; ret=%d\n",
768 from, to, lnwss, nwss_min, ret);
769 }
770 if(ret==0 && lslope>0.0 && lnwss<nwss_min) {
771 nwss_min=lnwss; from_min=from; to_min=to;}
772 }
773 if(from_min<0) {free(w); return(5);}
774
775 /* Run llsqwt() again with that range, now with better resolution */
776 /* and this time compute also SD's */
777 from=from_min; to=to_min;
778 ret=llsqwt(x+from, y+from, (to-from)+1, wx+from, wy+from, 1.0E-12, w,
779 ic, slope, nwss, sic, sslope, cx+from, cy+from);
780 free(w); if(ret) return(6);
781 *bnr=(to-from)+1;
782
783 return(0);
784}
785/******************************************************************************/
786
787/******************************************************************************/
794 double *x,
796 double *y,
798 double *wx,
800 double *wy,
802 int nr,
804 int min_nr,
806 double *slope,
808 double *ic,
810 double *r,
812 double *sslope,
814 double *sic,
816 int *bnr,
818 int verbose
819) {
820 int fi, from, to, ret, from_min, to_min, n;
821 double lic, lslope, lic_sd, lslope_sd, cv, cv_min, f;
822 double *cx, *cy;
823
824 if(verbose>0) printf("best_logan_regr()\n");
825 /* Check the data */
826 if(x==NULL || y==NULL || nr<min_nr || nr<2) return(1);
827 /* Check parameters */
828 if(min_nr<4) return(2);
829
830 /* Construct a checked data with no negative values and weights>0 */
831 cx=(double*)malloc(nr*sizeof(double));
832 cy=(double*)malloc(nr*sizeof(double));
833 if(cx==NULL || cy==NULL) return(3);
834 for(fi=n=0; fi<nr; fi++)
835 if(wx[fi]>0 && wy[fi]>0 && !isnan(x[fi]) && !isnan(y[fi])) {
836 cx[n]=x[fi]; cy[n]=y[fi]; n++;}
837 if(n<min_nr) {free(cx); free(cy); return(4);}
838
839 /* Search the plot range that gives the lowest CV for slope */
840 cv_min=9.99E+99; from_min=to_min=-1;
841 for(from=0, to=n-1; from<n-min_nr; from++) {
842 /* Calculation of linear regression using pearson() */
843 ret=pearson(
844 cx+from, cy+from, (to-from)+1,
845 &lslope, &lslope_sd, &lic, &lic_sd, r, &f
846 );
847 if(ret==0 && lslope>0) {
848 cv=lslope_sd/lslope;
849 } else cv=9.99E+99;
850 if(cv<cv_min) {
851 cv_min=cv; from_min=from; to_min=to;}
852 }
853 if(from_min<0) {free(cx); free(cy); return(5);}
854
855 /* Run pearson() again with that range */
856 from=from_min; to=to_min;
857 ret=pearson(
858 cx+from, cy+from, (to-from)+1,
859 slope, sslope, ic, sic, r, &f
860 );
861 free(cx); free(cy); if(ret) return(6);
862 *bnr=(to-from)+1;
863
864 return(0);
865}
866/******************************************************************************/
867
868/******************************************************************************/
869
double atof_dpi(char *str)
Definition decpoint.c:59
void dftInit(DFT *data)
Definition dft.c:38
char dfterrmsg[64]
Definition dft.c:6
int dftDeleteFrameOverlap(DFT *dft)
Definition dft.c:1237
int dftSelectBestReference(DFT *dft)
Definition dft.c:314
void dftEmpty(DFT *data)
Definition dft.c:20
int dft_nr_of_NA(DFT *dft)
Definition dft.c:905
int dftSelectRegions(DFT *dft, char *region_name, int reset)
Definition dft.c:285
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 dftRead(char *filename, DFT *data)
Definition dftio.c:22
void dftPrint(DFT *data)
Definition dftio.c:538
int res_allocate_with_dft(RES *res, DFT *dft)
Definition dftres.c:14
int dftTimeunitConversion(DFT *dft, int tunit)
Definition dftunit.c:119
int fittime_from_dft(DFT *dft, double *startTime, double *endTime, int *first, int *last, int verbose)
Definition fittime.c:16
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
int integrate(double *x, double *y, int nr, double *yi)
Definition integr.c:271
Header file for libtpccurveio.
char reserrmsg[64]
Definition result.c:6
void resInit(RES *res)
Definition result.c:52
#define DFT_TIME_MIDDLE
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
int studynr_from_fname2(char *fname, char *studynr, int force)
Definition studynr.c:67
char * petCunit(int cunit)
Definition petunits.c:211
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.
int llsqperp3(double *x, double *y, int nr, double *slope, double *ic, double *ssd)
Definition llsqwt.c:453
int medianline(double *x, double *y, int nr, double *slope, double *ic)
Definition llsqwt.c:533
int llsqwt(double *x, double *y, int n, double *wx, double *wy, double tol, double *w, double *ic, double *slope, double *nwss, double *sic, double *sslope, double *cx, double *cy)
Definition llsqwt.c:48
int pearson(double *x, double *y, int nr, double *k, double *kSD, double *b, double *bSD, double *r, double *ySD)
Definition pearson.c:14
int pearson3(double *x, double *y, int nr, double *k, double *kSD, double *b, double *bSD, double *r, double *ySD)
Definition pearson.c:154
Header file for libtpcmodext.
int plotdata(DFT *dft, RES *res, int first, int last, char *mtitle, char *xtitle, char *ytitle, char *fname)
Definition plotdata.c:217
int plot_svg(DFT *dft, RES *res, int first, int last, char *main_title, char *x_title, char *y_title, int color_scale, char *fname, int verbose)
Definition plotdata.c:17
Header file for libtpcsvg.
int best_logan_reed(double *x, double *y, double *wx, double *wy, int nr, int min_nr, double *slope, double *ic, double *nwss, double *sslope, double *sic, double *cx, double *cy, int *bnr, int verbose)
Definition logan.c:717
int best_logan_regr(double *x, double *y, double *wx, double *wy, int nr, int min_nr, double *slope, double *ic, double *r, double *sslope, double *sic, int *bnr, int verbose)
Definition logan.c:792
int timetype
Voi * voi
int timeunit
double * w
double * x1
int voiNr
double * x2
int frameNr
int isweight
double * x
char studynr[MAX_STUDYNR_LEN+1]
int parNr
char parname[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
int voiNr
int datanr
ResVOI * voi
char program[1024]
char plasmafile[FILENAME_MAX]
char datarange[128]
int isweight
char datafile[FILENAME_MAX]
char fitmethod[128]
char refroi[64]
char parunit[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
time_t time
double parameter[MAX_RESPARAMS]
double sd[MAX_RESPARAMS]
double * y2
char sw
double * y
char name[MAX_REGIONNAME_LEN+1]
double * y3