TPCCLIB
Loading...
Searching...
No Matches
dftweigh.c
Go to the documentation of this file.
1
8/*****************************************************************************/
9#include "tpcclibConfig.h"
10/*****************************************************************************/
11#include <stdio.h>
12#include <stdlib.h>
13#include <math.h>
14#include <string.h>
15/*****************************************************************************/
16#include "libtpcmisc.h"
17#include "libtpccurveio.h"
18#include "libtpcimgio.h"
19/*****************************************************************************/
20
21/*****************************************************************************/
22static char *info[] = {
23 "Add or remove the sample (time frame) weighting information to TAC file",
24 "for parameter estimations and curve fitting using formula (Mazoyer et al):",
25 " weight=(frame duration)^2 / (decay corrected trues in a frame)",
26 "or optionally:",
27 " weight=(frame duration)",
28 " ",
29 "TACs are assumed to be corrected for decay.",
30 "The relative weights are adjusted using a scan information file (SIF),",
31 "or TACs in the file (volume weighted average of all regions or given region);",
32 "in the latter case the units in TAC file must be set correctly.",
33 " ",
34 "Usage: @P [Options] tacfile [sif | tacname]",
35 " ",
36 "Options:",
37 " -rm",
38 " Existing weights are removed.",
39 " -L Weights are not calculated, but existing weights are printed",
40 " on screen. Return code is non-zero if data does not contain weights.",
41 " -wf | -wfm | -wfd",
42 " Weights are based only on frame length or sampling interval.",
43 " With -wfm the range of weights is reduced using value given with",
44 " option -moderate.",
45 " With -wfd the late frames are given less weight by using formula:",
46 " weight=(frame duration)*exp(-t*ln(2)/halflife) (Thiele et al., 2008).",
47 " -i=<Isotope code>",
48 " Isotope, for example C-11, in case it is not found inside SIF or TAC",
49 " file. Isotope is only needed with SIF, and with option -wfd.",
50 " -moderate=<value>",
51 " Weights are moderated by adding (1/value)*max true counts to",
52 " all counts, if (max trues)/value > (min trues). By default, value=100.",
53 " You can set value to zero to apply full range of weights.",
54 " -sif=<Filename>",
55 " SIF data based on TAC file is written in given file; cannot be used",
56 " if SIF is given as argument.",
57 " -stdoptions", // List standard options like --help, -v, etc
58 " ",
59 "Note that absolute weights cannot be calculated. Relative weights are",
60 "scaled so that average weight is 1.0.",
61 " ",
62 "This program is deprecated: for new projects use tacweigh!",
63 " ",
64 "Reference:",
65 "1. Mazoyer BM, Huesman RH, Budinger TF, Knittel BL. Dynamic PET data",
66 " analysis. J Comput Assist Tomogr 1986; 10:645-653.",
67 "2. Thiele F, Buchert R. Evaluation of non-uniform weighting in non-linear",
68 " regression for pharmacokinetic neuroreceptor modelling.",
69 " Nucl Med Commun. 2008; 29:179-188.",
70 " ",
71 "See also: sifcat, sifisot, eframe, imgweigh, tacframe, imghead, tacdecay",
72 " ",
73 "Keywords: TAC, SIF, modelling, weighting",
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/*****************************************************************************/
92int main(int argc, char **argv)
93{
94 int ai, help=0, version=0, verbose=1;
95 int ret;
96 int remove_weights=0;
97 int print_weights=0;
98 int head_voi=-1;
99 int weight_method=0; // 0=Mazoyer; 1=frame duration;
100 // 2=moderate frame duration;
101 // 3=frame duration, for decay corrected TACs
102 double WCORR_LIMIT=100.0;
103 char *cptr, siffile[FILENAME_MAX], dftfile[FILENAME_MAX], tmp[FILENAME_MAX];
104 char newfile[FILENAME_MAX], isotope_name[128];
105 double halflife=-1.0; // in seconds
106 DFT dft;
107 SIF sif;
108
109
110 /*
111 * Get arguments
112 */
113 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
114 siffile[0]=dftfile[0]=newfile[0]=isotope_name[0]=(char)0;
115 dftInit(&dft); sifInit(&sif);
116 /* Options */
117 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
118 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
119 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
120 if(strncasecmp(cptr, "L", 1)==0) {
121 print_weights=1; continue;
122 } else if(strcasecmp(cptr, "RM")==0 || strcasecmp(cptr, "DEL")==0) {
123 remove_weights=1; continue;
124 } else if(strncasecmp(cptr, "I=", 2)==0) {
125 cptr=hlCorrectIsotopeCode(cptr+2);
126 if(cptr!=NULL) {
127 /* save isotope for later use; would not yet be safe in sif */
128 strcpy(isotope_name, cptr);
129 halflife=60.*hlFromIsotope(isotope_name);
130 if(halflife>0.0) continue;
131 }
132 } else if(strncasecmp(cptr, "SIF=", 4)==0) {
133 cptr+=4; strlcpy(newfile, cptr, FILENAME_MAX);
134 if(strlen(newfile)>0) continue;
135 } else if(strncasecmp(cptr, "moderate=", 9)==0) {
136 cptr+=9; WCORR_LIMIT=atof_dpi(cptr); if(cptr!=NULL) continue;
137 } else if(strcasecmp(cptr, "WF")==0) {
138 weight_method=1; continue;
139 } else if(strcasecmp(cptr, "WFM")==0) {
140 weight_method=2; continue;
141 } else if(strcasecmp(cptr, "WFD")==0) {
142 weight_method=3; continue;
143 }
144 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
145 dftEmpty(&dft); sifEmpty(&sif); return(1);
146 } else break;
147
148 /* Print help or version? */
149 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
150 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
151 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
152
153 /* Process other arguments, starting from the first non-option */
154
155 /* Get filename */
156 if(ai>=argc) {
157 fprintf(stderr, "Error: missing command-line argument.\n");
158 dftEmpty(&dft); sifEmpty(&sif); return(1);
159 }
160 strlcpy(dftfile, argv[ai], FILENAME_MAX);
161 ret=dftRead(dftfile, &dft);
162 if(ret) {
163 fprintf(stderr, "Error (%d) in reading '%s': %s\n",
164 ret, dftfile, dfterrmsg);
165 dftEmpty(&dft); sifEmpty(&sif); return(2);
166 }
167 /* Try to get halflife from TAC file, if not given by user. */
168 if(!(halflife>0.0) && strlen(dft.isotope)>0)
169 halflife=60.*hlFromIsotope(dft.isotope);
170 ai++;
171
172 if(ai<argc) { /* User gave either SIF or TAC name */
173 /* Check if this is SIF file */
174 ret=sifRead(argv[ai], &sif);
175 if(ret==0) {
176 strlcpy(siffile, argv[ai], FILENAME_MAX);
177 if(!(halflife>0.0)) {
178 /* Get halflife from SIF file, if available */
179 if(strlen(sif.isotope_name)>0)
180 halflife=60.*hlFromIsotope(sif.isotope_name);
181 }
182 } else {
183 /* If not SIF, then check if it is region name */
184 int n, ri;
185 n=dftSelectRegions(&dft, argv[ai], 1);
186 if(n==1) {
187 for(ri=0; ri<dft.voiNr; ri++) if(dft.voi[ri].sw) {head_voi=ri; break;}
188 } else {
189 if(n==0)
190 fprintf(stderr, "Error: no TAC matching '%s' was found.\n", argv[ai]);
191 else
192 fprintf(stderr, "Error: %d TACs match '%s'.\n", n, argv[ai]);
193 dftEmpty(&dft); sifEmpty(&sif); return(2);
194 }
195 }
196 ai++;
197 }
198
199 if(ai<argc) {
200 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
201 dftEmpty(&dft); sifEmpty(&sif); return(1);
202 }
203
204
205 /* Is something missing? */
206 if(!dftfile[0]) {
207 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
208 return(1);
209 }
210 if(newfile[0] && siffile[0]) {
211 fprintf(stderr, "Warning: option -SIF is ignored.\n");
212 strcpy(newfile, ""); fflush(stderr);
213 } else if(newfile[0] && halflife<=0.0) {
214 fprintf(stderr, "Error: SIF file cannot be saved without isotope.\n");
215 dftEmpty(&dft); sifEmpty(&sif); return(1);
216 }
217 if(weight_method==3 && halflife<=0.0) {
218 fprintf(stderr, "Error: option -wfd cannot be used without isotope.\n");
219 dftEmpty(&dft); sifEmpty(&sif); return(1);
220 }
221
222 /* In verbose mode print arguments and options */
223 if(verbose>1) {
224 printf("dftfile := %s\n", dftfile);
225 printf("remove_weights := %d\n", remove_weights);
226 printf("isotope_name := %s\n", isotope_name);
227 printf("halflife := %g\n", halflife);
228 printf("print_weights := %d\n", print_weights);
229 printf("sif.frameNr := %d\n", sif.frameNr);
230 printf("dft.frameNr := %d\n", dft.frameNr);
231 printf("head_voi := %d\n", head_voi);
232 printf("WCORR_LIMIT := %g\n", WCORR_LIMIT);
233 printf("weight_method := %d\n", weight_method);
234 if(newfile[0]) printf("newfile := %s\n", newfile);
235 fflush(stdout);
236 }
237 if(verbose>3) SIF_TEST=verbose-3; else SIF_TEST=0;
238
239
240 /*
241 * Print existing weights, if required, and quit
242 */
243 if(verbose>1) {
244 fprintf(stdout, "contains_weights := ");
245 if(dft.isweight==0) fprintf(stdout, "no\n"); else fprintf(stdout, "yes\n");
246 }
247 if(print_weights!=0) {
248 if(verbose>2) printf("printing existing weights\n");
249 if(dft.isweight==0) {
250 fprintf(stdout, "%s does not contain weights.\n", dftfile);
251 dftEmpty(&dft); sifEmpty(&sif);
252 return(100);
253 } else if(dft.timetype==DFT_TIME_STARTEND) {
254 fprintf(stdout, "start[]\tend[]\tweight\n");
255 for(int i=0; i<dft.frameNr; i++)
256 fprintf(stdout, "%g\t%g\t%.4e\n", dft.x1[i], dft.x2[i], dft.w[i]);
257 dftEmpty(&dft); sifEmpty(&sif);
258 return(0);
259 } else {
260 fprintf(stdout, "time[]\tweight\n");
261 for(int i=0; i<dft.frameNr; i++) fprintf(stdout, "%g\t%.4e\n", dft.x[i], dft.w[i]);
262 dftEmpty(&dft); sifEmpty(&sif);
263 return(0);
264 }
265 fflush(stdout);
266 }
267
268
269 /*
270 * Remove weights if required
271 */
272 if(remove_weights!=0) {
273 if(verbose>1) printf("removing weights\n");
274 if(dft.isweight==0) {
275 fprintf(stderr, " data does not contain weights.\n");
276 dftEmpty(&dft); sifEmpty(&sif); fflush(stderr); return(0);
277 }
278 dft.isweight=0;
279 if((ret=dftWrite(&dft, dftfile))!=0) {
280 fprintf(stderr, "Error (%d) in writing '%s': %s\n",ret,dftfile,dfterrmsg);
281 dftEmpty(&dft); sifEmpty(&sif); return(11);
282 }
283 if(verbose>0) fprintf(stdout, " weights removed.\n");
284 dftEmpty(&dft); sifEmpty(&sif); fflush(stdout);
285 return(0);
286 }
287
288
289 /*
290 * If SIF file was given,
291 * -check that halflife is known
292 * -check that frames match TAC data
293 * -check that there are no too low counts
294 * -calculate weights
295 */
296 if(siffile[0]) {
297 if(verbose>2) printf("SIF will be used; checking for necessary data\n");
298 if(halflife<=0.0 && (weight_method==0 || weight_method==3)) {
299 /* Halflife was already read from SIF if it was there */
300 fprintf(stderr, "Error: isotope is required with SIF file.\n");
301 sifEmpty(&sif); dftEmpty(&dft); return(1);
302 }
303 if(sif.frameNr!=dft.frameNr) {
304 fprintf(stderr, "Error: frames in DFT and SIF do not match.\n");
305 sifEmpty(&sif); dftEmpty(&dft); return(5);
306 }
307 /* calculate the weights */
308 if(weight_method==0) {
309 sifModerateTrues(&sif, WCORR_LIMIT);
310 sifWeight(&sif, halflife);
311 } else if(weight_method==1) {
312 sifWeightByFrames(&sif, 0.0);
313 } else if(weight_method==2) {
314 sifWeightByFrames(&sif, 0.0);
315 sifModerateWeights(&sif, WCORR_LIMIT);
316 sifWeightNorm(&sif);
317 } else if(weight_method==3) {
318 sifWeightByFrames(&sif, halflife);
319 } else {
320 fprintf(stderr, "Error: invalid weight setting.\n");
321 sifEmpty(&sif); dftEmpty(&dft); return(1);
322 }
323 }
324
325
326 /*
327 * If SIF was not given, then create SIF data from regional data.
328 * Count-related data is computed here for the SIF even if user just
329 * wants the weights to be based on frame lengths.
330 */
331 if(!siffile[0]) {
332 if(verbose>1) printf("creating SIF data from regional data\n");
333 if(verbose>10) dftPrint(&dft);
334 /* Make sure that DFT units are known */
335 if(dft.timeunit==TUNIT_UNKNOWN) {
336 /* Try to guess the unit */
337 double lf;
338 if(dft.timetype==DFT_TIME_STARTEND) lf=dft.x2[dft.frameNr-1];
339 else lf=dft.x[dft.frameNr-1];
340 if(verbose>3) printf("last_frame_time := %g\n", lf);
341 if(lf<=60.0) {
342 dft.timeunit=TUNIT_MIN;
343 fprintf(stderr, "Warning: unknown time unit; assumed min.\n");
344 } else if(lf>360.0) {
345 dft.timeunit=TUNIT_SEC;
346 fprintf(stderr, "Warning: unknown time unit; assumed sec.\n");
347 } else {
348 fprintf(stderr, "Error: unknown time unit.\n");
349 sifEmpty(&sif); dftEmpty(&dft); return(2);
350 }
351 fflush(stderr);
352 }
353 if(petCunitId(dft.unit)==CUNIT_UNKNOWN && weight_method==0) {
354 /* Try to guess the unit */
355 double f=dft_kBqMax(&dft);
356 if(f>=10000.) {
357 dftUnitToDFT(&dft, CUNIT_MBQ_PER_ML);
358 fprintf(stderr, "Warning: unknown calibration unit; assumed MBq/cc.\n");
359 fflush(stderr);
360 } else {
361 fprintf(stderr, "Error: unknown calibration unit.\n");
362 sifEmpty(&sif); dftEmpty(&dft); return(2);
363 }
364 }
365 if(verbose>10) dftPrint(&dft);
366 /* Allocate memory for sif data */
367 if(sifSetmem(&sif, dft.frameNr)) {
368 fprintf(stderr, "Error: out of memory.\n");
369 sifEmpty(&sif); dftEmpty(&dft); return(4);
370 }
371 sif.colNr=4; sif.version=1; strcpy(sif.isotope_name, isotope_name);
372 /* Set SIF frame times ; convert to sec if necessary */
373 for(int i=0; i<dft.frameNr; i++) {
374 sif.x1[i]=dft.x1[i]; sif.x2[i]=dft.x2[i];
375 sif.prompts[i]=sif.randoms[i]=0;
376 }
378 && dft.timeunit==TUNIT_MIN)
379 for(int i=0; i<dft.frameNr; i++) {sif.x1[i]*=60.; sif.x2[i]*=60.;}
380 /* Try to set SIF study number; needed if SIF is saved. */
381 if(strlen(dft.studynr)>0 && strcmp(dft.studynr, ".")!=0)
382 strcpy(sif.studynr, dft.studynr);
383 else
384 (void)studynr_from_fname(dftfile, sif.studynr);
385 /* Set SIF counts */
386 if(head_voi>=0) {
387 /* If 'head' region name was given, then copy it to sif */
388 for(int i=0; i<dft.frameNr; i++) sif.trues[i]=dft.voi[head_voi].y[i];
389 } else {
390 /* we must calculate the size weighted avg of all TACs */
391 double f, w;
392 for(int fi=0; fi<dft.frameNr; fi++) {
393 sif.trues[fi]=w=0.0;
394 for(int ri=0; ri<dft.voiNr; ri++) {
395 f=dft.voi[ri].size; if(f<=0.0) f=1.0;
396 if(!isnan(dft.voi[ri].y[fi]))
397 {w+=f; sif.trues[fi]+=f*dft.voi[ri].y[fi];}
398 }
399 if(w!=0.0) sif.trues[fi]/=w;
400 }
401 }
402 int dunit=petCunitId(dft.unit);
403 double cf;
404 switch(dunit) {
405 case CUNIT_BQ_PER_ML: cf=0.001; break;
406 case CUNIT_KBQ_PER_ML: cf=1.0; break;
407 case CUNIT_MBQ_PER_ML: cf=1000.0; break;
408 case CUNIT_NCI_PER_ML: cf=0.037; break;
409 default: cf=1.0;
410 }
411 for(int fi=0; fi<sif.frameNr; fi++) sif.trues[fi]*=cf;
412 sifModerateTrues(&sif, WCORR_LIMIT);
413 /* Multiply with frame duration and 1E5 to change to count scale */
414 for(int i=0; i<sif.frameNr; i++)
415 sif.trues[i]=sif.trues[i]*(sif.x2[i]-sif.x1[i])*1.0E5;
416
417 /* Calculate weights */
418 if(weight_method==0) {
419 sifWeight(&sif, 0.0);
420 } else if(weight_method==1) {
421 sifWeightByFrames(&sif, 0.0);
422 } else if(weight_method==2) {
423 sifWeightByFrames(&sif, 0.0);
424 sifModerateWeights(&sif, WCORR_LIMIT);
425 sifWeightNorm(&sif);
426 } else if(weight_method==3) {
427 sifWeightByFrames(&sif, halflife);
428 } else {
429 fprintf(stderr, "Error: invalid weight setting.\n");
430 sifEmpty(&sif); dftEmpty(&dft); return(1);
431 }
432
433 /* Save as SIF file, if required */
434 if(newfile[0]) {
435 /* Remove decay correction from counts */
436 double f;
437 for(int fi=0; fi<sif.frameNr; fi++) {
438 f=exp(-((sif.x1[fi]+sif.x2[fi])/2.0)*0.693147/halflife );
439 sif.trues[fi]*=f;
440 sif.randoms[fi]=1.0;
441 sif.prompts[fi]=sif.trues[fi]+sif.randoms[fi];
442 }
443 /* Rename existing file */
444 if(access(newfile, 0)!=-1 && backupExistingFile(newfile, NULL, tmp))
445 fprintf(stderr, "Error: %s\n", tmp);
446 /* Write SIF */
447 ret=sifWrite(&sif, newfile);
448 if(ret!=0)
449 fprintf(stderr, "Warning in writing %s : %s\n", newfile, siferrmsg);
450 else
451 fprintf(stdout, "SIF data written in %s\n", newfile);
452 }
453 fflush(stderr);
454 }
455
456
457
458 /*
459 * Print the calculated weights
460 */
461 if(verbose>0) sifPrint(&sif);
462
463
464 /*
465 * Add weight data to DFT
466 */
467 if(verbose>1) printf("add weights to DFT\n");
468 /* Check if weight data already exists */
469 if(dft.isweight)
470 fprintf(stderr, "Warning: existing weight was overwritten.\n");
471 /* Copy weights */
472 for(int i=0; i<dft.frameNr; i++) dft.w[i]=sif.weights[i];
473 dft.isweight=1;
474
475
476 /*
477 * Save DFT data, now including weight data
478 */
479 if(verbose>2) printf("writing DFT file\n");
480 /* weights cannot be saved in simple format */
483 if(dftWrite(&dft, dftfile)) {
484 fprintf(stderr, "Error: cannot write '%s'.\n", dftfile);
485 sifEmpty(&sif); dftEmpty(&dft); return(11);
486 }
487 if(verbose>0) fprintf(stdout, "weights added to %s\n", dftfile);
488
489
490 /*
491 * Free memory
492 */
493 dftEmpty(&dft); sifEmpty(&sif);
494
495 return(0);
496}
497/*****************************************************************************/
498
499/*****************************************************************************/
int backupExistingFile(char *filename, char *backup_ext, char *status)
Definition backup.c:14
double atof_dpi(char *str)
Definition decpoint.c:59
void dftInit(DFT *data)
Definition dft.c:38
char dfterrmsg[64]
Definition dft.c:6
void dftEmpty(DFT *data)
Definition dft.c:20
double dft_kBqMax(DFT *data)
Definition dft.c:1148
int dftSelectRegions(DFT *dft, char *region_name, int reset)
Definition dft.c:285
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
void dftUnitToDFT(DFT *dft, int dunit)
Definition dftunit.c:11
char * hlCorrectIsotopeCode(char *isocode)
Definition halflife.c:141
double hlFromIsotope(char *isocode)
Definition halflife.c:55
Header file for libtpccurveio.
#define DFT_FORMAT_STANDARD
#define DFT_FORMAT_PLAIN
#define DFT_TIME_STARTEND
#define DFT_FORMAT_UNKNOWN
Header file for libtpcimgio.
void sifModerateTrues(SIF *sif, double limit)
Definition weight.c:131
void sifModerateWeights(SIF *sif, double limit)
Definition weight.c:167
int sifWrite(SIF *data, char *filename)
Definition sifio.c:145
void sifInit(SIF *data)
Definition sif.c:17
int SIF_TEST
Definition sif.c:6
void sifWeightByFrames(SIF *data, double halflife)
Calculate weights for frames in SIF data based on frame lengths. Weights are normalized to have an av...
Definition weight.c:72
void sifPrint(SIF *data)
Definition sifio.c:234
void sifWeight(SIF *data, double halflife)
Calculate weights for frames in SIF data based on true counts. Weights are normalized to have an aver...
Definition weight.c:28
int sifSetmem(SIF *data, int frameNr)
Definition sif.c:56
void sifEmpty(SIF *data)
Definition sif.c:33
void sifWeightNorm(SIF *data)
Definition weight.c:105
int sifRead(char *filename, SIF *data)
Definition sifio.c:21
char siferrmsg[128]
Definition sif.c:7
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:40
int petCunitId(const char *unit)
Definition petunits.c:74
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
int studynr_from_fname(char *fname, char *studynr)
Definition studynr.c:119
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:383
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:158
int _type
int timetype
Voi * voi
int timeunit
char studynr[MAX_STUDYNR_LEN+1]
double * w
double * x1
int voiNr
double * x2
int frameNr
int isweight
char isotope[8]
double * x
char unit[MAX_UNITS_LEN+1]
double * x1
double * prompts
int frameNr
double * x2
int version
char studynr[MAX_STUDYNR_LEN+1]
char isotope_name[8]
double * weights
int colNr
double * randoms
double * trues
double size
char sw
double * y