TPCCLIB
Loading...
Searching...
No Matches
tocr.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 <string.h>
14#include <math.h>
15/*****************************************************************************/
16#include "tpcextensions.h"
17#include "tpcift.h"
18#include "tpcisotope.h"
19#include "tpctac.h"
20/*****************************************************************************/
21
22/*****************************************************************************/
23static char *info[] = {
24 "Convert the head-curve (count-rate) data file from ECAT HR+ (*.r) or",
25 "HRRT (*.hc) to TAC file format that is suitable for correction of ",
26 "time delay between blood/plasma and tissue TACs.",
27 "Some usual errors in count-rate data is automatically corrected.",
28 "Output data is corrected for physical decay.",
29 " ",
30 "Usage: @P [Options] headcurve isotope outputfile",
31 " ",
32 "Options:",
33 " -min | -sec",
34 " Sample times are written in minutes or seconds;",
35 " by default in sec for tracers with short half-life, otherwise in min.",
36 " -copy",
37 " If file is not in HR+ or HRRT head-curve format, it is still saved",
38 " with the new name.",
39 " -f=<format-id>",
40 " -format=<format-id>",
41 " Accepted format-id's for the output count-rate file:",
42 " CSV-INT - CSV format with semicolons and decimal commas.",
43 " CSV-UK - CSV format with commas and decimal points.",
44 " TSV-INT - TSV format with tabs and decimal commas.",
45 " TSV-UK - TSV format with tabs and decimal points.",
46 " PMOD - PMOD tac and bld format.",
47 " DFT - TPC TAC format (default).",
48 " SIMPLE - txt file with tabs and decimal points.",
49 " -stdoptions", // List standard options like --help, -v, etc
50 " ",
51 "Example:",
52 " @P -format=simple is129.r C-11 is129.cr",
53 " ",
54 "See also: fitdelay, imghead, tacmean, dftscale, tac2svg, tacdecay",
55 " ",
56 "Keywords: ECAT HR+, HRRT, input, time delay, count-rate, head-curve",
57 0};
58/*****************************************************************************/
59
60/*****************************************************************************/
61/* Turn on the globbing of the command line, since it is disabled by default in
62 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
63 In Unix&Linux wildcard command line processing is enabled by default. */
64/*
65#undef _CRT_glob
66#define _CRT_glob -1
67*/
68int _dowildcard = -1;
69/*****************************************************************************/
70
71/*****************************************************************************/
75int main(int argc, char **argv)
76{
77 int ai, help=0, version=0, verbose=1;
78 int ret;
79 int time_unit=UNIT_UNKNOWN;
80 int outputformat=TAC_FORMAT_DFT;
81 int copy_cr=0; // 0=no, 1=yes
82 char *cptr, petfile[FILENAME_MAX], crfile[FILENAME_MAX];
84 TAC tac;
85
86
87 /*
88 * Get arguments
89 */
90 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
91 tacInit(&tac);
92 petfile[0]=crfile[0]=(char)0;
93 /* Options */
94 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
95 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
96 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(!*cptr) continue;
97 if(strncasecmp(cptr, "MIN", 1)==0) {
98 time_unit=UNIT_MIN; continue;
99 } else if(strncasecmp(cptr, "SEC", 1)==0) {
100 time_unit=UNIT_SEC; continue;
101 } else if(strncasecmp(cptr, "COPY", 1)==0) {
102 copy_cr=1; continue;
103 } else if(strncasecmp(cptr, "F=", 2)==0) {
104 cptr+=2;
105 outputformat=tacFormatIdentify(cptr);
106 if(outputformat!=TAC_FORMAT_UNKNOWN) continue;
107 /* for compatibility with previous versions */
108 if(strcasecmp(cptr, "CR")==0 || strcasecmp(cptr, "HEAD")==0) {
109 outputformat=TAC_FORMAT_SIMPLE; continue;}
110 } else if(strncasecmp(cptr, "FORMAT=", 7)==0) {
111 cptr+=7;
112 outputformat=tacFormatIdentify(cptr);
113 if(outputformat!=TAC_FORMAT_UNKNOWN) continue;
114 /* for compatibility with previous versions */
115 if(strcasecmp(cptr, "CR")==0 || strcasecmp(cptr, "HEAD")==0) {
116 outputformat=TAC_FORMAT_SIMPLE; continue;}
117 }
118 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
119 return(1);
120 } else break; // tac name argument may start with '-'
121
122 /* Print help or version? */
123 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
124 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
125 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
126
127 TPCSTATUS status; statusInit(&status);
128 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
129 status.verbose=verbose-1;
130
131 /* Process other arguments, starting from the first non-option */
132 for(; ai<argc; ai++) {
133 if(!petfile[0]) {
134 strcpy(petfile, argv[ai]); continue;
135 } else if(isotope==ISOTOPE_UNKNOWN) {
136 isotope=isotopeIdentify(argv[ai]);
137 if(isotope!=ISOTOPE_UNKNOWN) continue;
138 fprintf(stderr, "Error: invalid isotope '%s'\n", argv[ai]);
139 return(1);
140 } else if(!crfile[0]) {
141 strcpy(crfile, argv[ai]); continue;
142 }
143 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
144 return(1);
145 }
146
147 /* Is something missing? */
148 if(!petfile[0] || isotope==ISOTOPE_UNKNOWN) {
149 tpcPrintUsage(argv[0], info, stdout); return(1);}
150 /* Make output filename if necessary */
151 if(!crfile[0]) {
152 strcpy(crfile, petfile);
153 cptr=filenameGetExtension(crfile); if(cptr!=NULL) *cptr=(char)0;
154 if(outputformat==TAC_FORMAT_SIMPLE) {
155 strcat(crfile, ".cr"); // for compatibility with old scripts
156 } else {
157 strcat(crfile, tacDefaultExtension(outputformat));
158 }
159 }
160 /* If time unit was not set with option, then set it based on half-life */
161 if(time_unit==UNIT_UNKNOWN) {
162 if(isotopeHalflife(isotope)<3.0) time_unit=UNIT_SEC;
163 else time_unit=UNIT_MIN;
164 }
165
166 /* In verbose mode print arguments and options */
167 if(verbose>1) {
168 printf("petfile := %s\n", petfile);
169 printf("crfile := %s\n", crfile);
170 printf("isotope := %s\n", isotopeName(isotope));
171 printf("time_unit := %s\n", unitName(time_unit));
172 printf("copy_cr := %d\n", copy_cr);
173 printf("outputformat := %s\n", tacFormattxt(outputformat));
174 }
175
176 /*
177 * Read the given datafile
178 */
179 if(verbose>1) printf("reading %s\n", petfile);
180 ret=tacRead(&tac, petfile, &status);
181 if(ret) {
182 fprintf(stderr, "Error: %s (%s)\n", errorMsg(status.error), petfile);
183 tacFree(&tac);
184 return(2);
185 }
186 if(verbose>1) {
187 printf("fileformat := %s\n", tacFormattxt(tac.format));
188 printf("columnNr := %d\n", tac.tacNr);
189 printf("frameNr := %d\n", tac.sampleNr);
190 printf("time_unit := %s\n", unitName(tac.tunit));
191 printf("unit := %s\n", unitName(tac.cunit));
192 }
193
194 /*
195 * Process the data
196 */
197 if(tac.format==TAC_FORMAT_HRRT_HC) {
198
199 if(verbose>0) printf("identified as HRRT format.\n");
200 int i, n;
201 /* Calculate 'frame' start and end times */
202 for(i=0; i<tac.sampleNr; i++) {
203 if(i==0) tac.x1[i]=0.0; else tac.x1[i]=tac.x2[i-1];
204 tac.x2[i]=tac.x[i];
205 }
206 /* Reset 'frame' mid time */
207 for(i=0; i<tac.sampleNr; i++) {
208 tac.x[i]=0.5*(tac.x1[i]+tac.x2[i]);
209 }
210 /* kcps = prompts-randoms */
211 for(i=0; i<tac.sampleNr; i++) {
212 tac.c[0].y[i]=tac.c[2].y[i]-tac.c[1].y[i];
213 }
214 /* Remove negative values */
215 i=0; n=0;
216 while(i<tac.sampleNr) {
217 if(tac.c[0].y[i]<0.0) {tacDeleteSample(&tac, i); n++;} else i++;
218 }
219 if(n>0 && verbose>1) printf("%d negative point(s) deleted.\n", n);
220
221 /* Set tacNr etc */
222 tac.tacNr=1;
223 strcpy(tac.c[0].name, "CR");
224
225 } else if(tac.format==TAC_FORMAT_HRPLUS_HC) {
226
227 if(verbose>0) printf("identified as HR+ format.\n");
228 int i, n;
229 double a;
230 /* Calculate 'frame' start and end times */
231 for(i=0; i<tac.sampleNr; i++) {
232 if(i==0) tac.x1[i]=0.0; else tac.x1[i]=tac.x2[i-1];
233 tac.x2[i]=tac.x[i];
234 }
235 /* Reset 'frame' mid time */
236 for(i=0; i<tac.sampleNr; i++) {
237 tac.x[i]=0.5*(tac.x1[i]+tac.x2[i]);
238 }
239 /* Calculate count-rates as p_rate-d_rate */
240 /* Consider valid data to end when at least 5 consecutive points are zeroes */
241 for(i=n=0; i<tac.sampleNr && n<5; i++) {
242 tac.c[0].y[i]=tac.c[2].y[i]-tac.c[3].y[i];
243 if(tac.c[0].y[i]>0.0) n=0; else n++;
244 }
245 if(n==5) {
246 if(verbose>1) printf("leaving %d/%d points.\n", i-3, tac.sampleNr);
247 tac.sampleNr=i-3;
248 }
249 /* Remove negative values, if also dtime<=0 */
250 i=0; n=0;
251 while(i<tac.sampleNr) {
252 if(tac.c[0].y[i]<=0.0 && tac.c[4].y[i]<=0.0) {
253 tacDeleteSample(&tac, i); n++;
254 } else i++;
255 }
256 if(n>0 && verbose>1) printf("%d negative point(s) deleted.\n", n);
257 /* Remove values that are over 1.5 times higher than the average
258 of previous and next value, if also dtime<=0 */
259 i=1; n=0;
260 while(i<tac.sampleNr-1) {
261 if(tac.c[4].y[i]>0.0) {i++; continue;}
262 a=0.75*(tac.c[0].y[i-1]+tac.c[0].y[i+1]); // 1.5*0.5 = 0.75
263 if(tac.c[0].y[i]>a) {tacDeleteSample(&tac, i); n++;} else i++;
264 }
265 if(n>0 && verbose>1) printf("%d outlier point(s) deleted.\n", n);
266 /* If still zeroes in the end, then remove those */
267 i=tac.sampleNr-1; while(tac.c[0].y[i]<=0.0) i--;
268 if(i<tac.sampleNr-1) {
269 if(verbose>1) printf("%d trailing zeroes deleted.\n", tac.sampleNr-i+1);
270 tac.sampleNr=i+1;
271 }
272 /* Set tacNr etc */
273 tac.tacNr=1;
274 strcpy(tac.c[0].name, "CR");
275
276 } else {
277 if(copy_cr==0) {
278 fprintf(stderr, "Error: not identified as HRRT or HR+ countrate file\n");
279 tacFree(&tac); return(3);
280 }
281 if(verbose>0) printf("not identified as HRRT or HR+ countrate file.\n");
282 }
283
284
285 /*
286 * Check which samples are smaller than 50% of samples at both side of it
287 * AND smaller than the (average - difference) of these samples
288 */
290 int i, n=0;
291 double a, d;
292 for(i=1; i<tac.sampleNr-1; i++) {
293 d=2.0*tac.c[0].y[i];
294 if(d>tac.c[0].y[i-1] || d>tac.c[0].y[i+1]) {
295 tac.c[1].y[i]=tac.c[0].y[i]; continue;}
296 a=0.5*(tac.c[0].y[i-1]+tac.c[0].y[i+1]);
297 d=fabs(tac.c[0].y[i-1]-tac.c[0].y[i+1]);
298 if(tac.c[0].y[i]>(a-d)) {tac.c[1].y[i]=tac.c[0].y[i]; continue;}
299 tac.c[1].y[i]=nan(""); n++;
300 }
301 /* replace those with linear interpolation */
302 if(n>0) {
303 for(i=1; i<tac.sampleNr-1; i++) tac.c[0].y[i]=tac.c[1].y[i];
304 tacFixNaNs(&tac);
305 if(verbose>1) printf("%d outlier point(s) deleted.\n", n);
306 }
307 }
308
309
310 /* Check whether we have any data left */
311 if(tac.sampleNr<1) {
312 fprintf(stderr, "Error: invalid data in %s\n", petfile);
313 tacFree(&tac); return(3);
314 }
315
316
317 /*
318 * Correct for decay
319 */
321 if(verbose>1) printf("correcting data for physical decay\n");
322 /* Convert times to min for decay correction */
323 ret=tacXUnitConvert(&tac, UNIT_MIN, &status);
324 if(ret!=TPCERROR_OK) {
325 fprintf(stderr, "Error: %s.\n", errorMsg(status.error));
326 tacFree(&tac);
327 return(7);
328 }
329 /* Decay correction */
330 for(int i=0; i<tac.sampleNr; i++)
332 tac.x1[i], tac.x2[i]-tac.x1[i]);
333 }
334
335 /* Convert times to the final units, sec or min */
336 if(tac.tunit==UNIT_UNKNOWN) {
337 fprintf(stderr, "Warning: time unit unknown.\n");
338 } else {
339 ret=tacXUnitConvert(&tac, time_unit, &status);
340 if(ret!=TPCERROR_OK) {
341 fprintf(stderr, "Error: %s.\n", errorMsg(status.error));
342 tacFree(&tac);
343 return(7);
344 }
345 }
346
347 /* Write the last sample time in file */
349 char tmp[64];
350 sprintf(tmp, "%.1f [%s]", tac.x[tac.sampleNr-1], unitName(tac.tunit));
351 iftPut(&tac.h, "cr_duration", tmp, (char)1, NULL);
352 if(verbose>0) fprintf(stdout, "cr_duration := %s\n", tmp);
353 }
354
355 /* Add isotope code */
357 /* and that data is corrected for decay */
359
360
361 /*
362 * Save the data
363 */
364 if(verbose>1) printf("writing %s\n", crfile);
365 FILE *fp; fp=fopen(crfile, "w");
366 if(fp==NULL) {
367 fprintf(stderr, "Error: cannot open file for writing (%s)\n", crfile);
368 tacFree(&tac); return(11);
369 }
370 ret=tacWrite(&tac, fp, outputformat, 1, &status);
371 fclose(fp);
372 tacFree(&tac);
373 if(ret==TPCERROR_UNSUPPORTED) {
374 fprintf(stderr, "Error: writing format %s is not supported.\n",
375 tacFormattxt(outputformat));
376 return(6);
377 }
378 if(ret!=TPCERROR_OK) {
379 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
380 return(7);
381 }
382 if(verbose>0) printf("CR data written in %s\n", crfile);
383
384 return(0);
385}
386/*****************************************************************************/
387
388/*****************************************************************************/
double decayCorrectionFactorFromIsotope(int isotope, double starttime, double duration)
Definition decay.c:107
char * filenameGetExtension(const char *s)
Get the last extension of a file name.
Definition filename.c:178
int iftPut(IFT *ift, const char *key, const char *value, char comment, TPCSTATUS *status)
Definition ift.c:63
char * isotopeName(int isotope_code)
Definition isotope.c:101
double isotopeHalflife(int isotope_code)
Definition isotope.c:62
int isotopeIdentify(const char *isotope)
Definition isotope.c:145
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:47
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:169
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:339
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:114
void statusInit(TPCSTATUS *s)
Definition statusmsg.c:104
char * errorMsg(tpcerror e)
Definition statusmsg.c:68
void statusSet(TPCSTATUS *s, const char *func, const char *srcfile, int srcline, tpcerror error)
Definition statusmsg.c:142
char name[MAX_TACNAME_LEN+1]
Definition tpctac.h:81
double * y
Definition tpctac.h:75
Definition tpctac.h:87
double * x
Definition tpctac.h:97
unit cunit
Definition tpctac.h:105
tacformat format
Definition tpctac.h:93
int sampleNr
Definition tpctac.h:89
IFT h
Optional (but often useful) header information.
Definition tpctac.h:141
TACC * c
Definition tpctac.h:117
double * x2
Definition tpctac.h:101
unit tunit
Definition tpctac.h:109
double * x1
Definition tpctac.h:99
int tacNr
Definition tpctac.h:91
int verbose
Verbose level, used by statusPrint() etc.
tpcerror error
Error code.
void tacFree(TAC *tac)
Definition tac.c:106
void tacInit(TAC *tac)
Definition tac.c:24
int tacSetHeaderDecayCorrection(IFT *h, decaycorrection dc)
Definition tacift.c:578
int tacSetHeaderIsotope(IFT *h, const char *s)
Definition tacift.c:341
char * tacDefaultExtension(tacformat c)
Definition tacio.c:144
int tacRead(TAC *d, const char *fname, TPCSTATUS *status)
Definition tacio.c:413
int tacFormatIdentify(const char *s)
Definition tacio.c:113
char * tacFormattxt(tacformat c)
Definition tacio.c:98
int tacWrite(TAC *tac, FILE *fp, tacformat format, int extra, TPCSTATUS *status)
Definition tacio.c:332
int tacFixNaNs(TAC *tac)
Definition tacnan.c:121
int tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:23
int tacDeleteSample(TAC *d, int i)
Delete a certain sample (time frame) from TAC structure.
Definition tacx.c:426
Header file for library libtpcextensions.
@ UNIT_MIN
minutes
@ UNIT_UNKNOWN
Unknown unit.
@ UNIT_SEC
seconds
@ TPCERROR_UNSUPPORTED
Unsupported file type.
@ TPCERROR_OK
No error.
char * unitName(int unit_code)
Definition units.c:143
Header file for library libtpcift.
Header file for library libtpcisotope.
@ DECAY_CORRECTED
Data is corrected for physical decay.
Definition tpcisotope.h:81
isotope
Definition tpcisotope.h:50
@ ISOTOPE_UNKNOWN
Unknown.
Definition tpcisotope.h:51
Header file for library libtpctac.
@ TAC_FORMAT_UNKNOWN
Unknown format.
Definition tpctac.h:28
@ TAC_FORMAT_SIMPLE
x and y's with space delimiters
Definition tpctac.h:29
@ TAC_FORMAT_HRPLUS_HC
HR+ head curve format (reading supported).
Definition tpctac.h:52
@ TAC_FORMAT_DFT
Data format of Turku PET Centre.
Definition tpctac.h:30
@ TAC_FORMAT_HRRT_HC
HRRT head curve format (reading supported).
Definition tpctac.h:51