TPCCLIB
Loading...
Searching...
No Matches
ecalibr.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 <unistd.h>
18#include <ctype.h>
19#include <dirent.h>
20#include <sys/stat.h>
21#include <time.h>
22/*****************************************************************************/
23#include "libtpcmisc.h"
24#include "libtpcimgio.h"
25/*****************************************************************************/
26
27/*****************************************************************************/
28/* Local functions */
30 char *foldername, struct tm *stm, char *fname, int verbose
31);
33 char *fname, double *coef, int verbose
34);
35/*****************************************************************************/
36
37/*****************************************************************************/
38static char *info[] = {
39 "Calibrate the activity units in CTI ECAT 931 image/sinogram from ECAT counts",
40 "to kBq/mL using specified calibration file containing the",
41 "MBq/PET_count or uCi/PET_count coefficients for all 15 images planes.",
42 "Images/sinograms which are scanned after Jan-01-1997 are corrected also",
43 "for branching ratio.",
44 "If you want to remove existing image calibration, enter '0' as the name of",
45 "calibration file (not applicable to sinogram).",
46 "With '1' only the branching ratio is corrected (do not apply to old scans).",
47 "If only the name of image/sinogram is given, program gives information",
48 "about current calibration status.",
49 "If the path to calibration files is given, then program will automatically",
50 "search for the last calibration prior to scan date.",
51 //"Sinograms are not corrected for frame length or DTC factor.",
52 " ",
53 "Usage: @P [Options] ecatfile [calibrationfile or path]",
54 " ",
55 "Options:",
56 " -stdoptions", // List standard options like --help, -v, etc
57 " ",
58 "Example:",
59 " @P a2345dy1.img S:\\Lab\\plasma\\ECAT931\\plane_calibration.dir",
60 " ",
61 "See also: lmhdr, egetstrt, esetstrt, eframe, ecatfbp, ecatmrp",
62 " ",
63 "Keywords: image, ECAT, reconstruction, calibration, branching ratio",
64 0};
65/*****************************************************************************/
66
67/*****************************************************************************/
68/* Turn on the globbing of the command line, since it is disabled by default in
69 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
70 In Unix&Linux wildcard command line processing is enabled by default. */
71/*
72#undef _CRT_glob
73#define _CRT_glob -1
74*/
75int _dowildcard = -1;
76/*****************************************************************************/
77
78/*****************************************************************************/
82int main(int argc, char **argv)
83{
84 int ai, help=0, version=0, verbose=1;
85 char ecatfile[FILENAME_MAX], calfile[FILENAME_MAX];
86 char *cptr, tmp[256];
87 int ret, unit=0;
88 FILE *fp=NULL;
89 ECAT63_mainheader main_header;
90 ECAT63_imageheader image_header;
91 ECAT63_scanheader scan_header;
92 static MATRIXLIST mlist;
93 Matval matval;
94
95
96
97 /*
98 * Get arguments
99 */
100 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
101 ecatfile[0]=calfile[0]=(char)0;
102 /* Options */
103 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') { /* options */
104 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
105 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
106 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
107 return(1);
108 } else break;
109
110 /* Print help or version? */
111 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
112 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
113 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
114
115 /* Process other arguments, starting from the first non-option */
116 for(; ai<argc; ai++) {
117 if(!ecatfile[0]) {
118 strlcpy(ecatfile, argv[ai], FILENAME_MAX); continue;
119 } else if(!calfile[0]) {
120 strlcpy(calfile, argv[ai], FILENAME_MAX); continue;
121 }
122 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
123 return(1);
124 }
125
126 /* Is something missing? */
127 if(!ecatfile[0]) {
128 fprintf(stderr, "Error: missing command-line argument; try %s --help\n",
129 argv[0]);
130 return(1);
131 }
132
133 /* In verbose mode print arguments and options */
134 if(verbose>1) {
135 printf("ecatfile := %s\n", ecatfile);
136 if(calfile[0]) printf("calfile := %s\n", calfile);
137 }
138
139
140 /*
141 * Open ECAT file
142 */
143 if(verbose==1) printf("reading %s\n", ecatfile);
144 if(verbose>1) printf("opening %s\n", ecatfile);
145 if((fp=fopen(ecatfile, "r+b")) == NULL) {
146 fprintf(stderr, "Error: cannot open file %s\n", ecatfile);
147 return(2);
148 }
149
150 /*
151 * Read main header
152 */
153 if(verbose>1) printf("reading main header\n");
154 if((ret=ecat63ReadMainheader(fp, &main_header))) {
155 fprintf(stderr, "Error (%d): cannot read main header.\n", ret);
156 fclose(fp); return(3);
157 }
158 if(verbose>5) ecat63PrintMainheader(&main_header, stdout);
159
160 /* Check the file_type */
161 if((main_header.file_type!=IMAGE_DATA && main_header.file_type!=RAW_DATA)
162 || main_header.num_planes>15)
163 {
164 fprintf(stderr,
165 "Error: only ECAT 931 images and sinograms can be calibrated.\n");
166 fclose(fp); return(3);
167 }
168
169 /* Get the scan_start_time */
170 struct tm stm;
171 time_t timet;
172 ecat63ScanstarttimeToTm(&main_header, &stm);
173 timet=timegm(&stm);
174 if(timet==-1 || timet<86400) {
175 fprintf(stderr, "Error: %s does not contain scan start time.\n", ecatfile);
176 fclose(fp); return(3);
177 }
178 strftime(tmp, 32, "%Y-%m-%d %H:%M:%S", &stm);
179 fprintf(stdout, "scan_start_time := %s\n", tmp);
180
181 /* Print current calibration unit */
182 printf("calibration_units := %s\n", ecat63Unit(main_header.calibration_units));
183
184 /* Print isotope */
185 double halflife;
186 int isotope_code;
187 halflife=hlFromIsotope(main_header.isotope_code);
188 if(halflife<=0.0) halflife=main_header.isotope_halflife;
189 isotope_code=hlIsotopeFromHalflife(halflife);
190 printf("isotope := %s\n", hlIsotopeCode(isotope_code));
191
192
193 /*
194 * Read matrix list
195 */
196 if(verbose>1) printf("reading matrix list\n");
197 ecat63InitMatlist(&mlist);
198 ret=ecat63ReadMatlist(fp, &mlist, verbose-1);
199 if(ret) {
200 fprintf(stderr, "Error (%d): cannot read matrix list.\n", ret);
201 fclose(fp); return(4);
202 }
203 if(mlist.matrixNr<=0) {
204 fprintf(stderr, "Error: matrix list is empty.\n");
205 fclose(fp); return(4);
206 }
207 if(ecat63CheckMatlist(&mlist)) {
208 fprintf(stderr, "Error: check the matrix list.\n");
209 fclose(fp); ecat63EmptyMatlist(&mlist); return(4);
210 }
211
212 /* Read calibration coefficients for each plane */
213 double cal_coef[15];
214 int plane_sw[15];
215 int plane;
216 for(int i=0; i<15; i++) {cal_coef[i]=1.; plane_sw[i]=0;}
217 if(main_header.file_type==IMAGE_DATA) {
218 for(int j=0; j<mlist.matrixNr; j++) {
219 mat_numdoc(mlist.matdir[j].matnum, &matval);
220 plane=matval.plane;
221 if(plane<1 || plane>15 || plane_sw[plane-1]) continue;
222 plane_sw[plane-1]=1; // mark this plane as processed
223 /* Read image subheader */
224 ret=ecat63ReadImageheader(fp, mlist.matdir[j].strtblk, &image_header, verbose-2, NULL);
225 if(ret!=0) {
226 fprintf(stderr, "Error: cannot read image subheader.\n");
227 fclose(fp); ecat63EmptyMatlist(&mlist); return(4);
228 }
229 cal_coef[plane-1]=image_header.ecat_calibration_fctr;
230 }
231 /* Print current calibration coefficients */
232 printf("Plane Calibration coefficient\n");
233 for(int i=0; i<15; i++) if(plane_sw[i]==1)
234 printf(" %2d %12.5e\n", i+1, cal_coef[i]);
235 }
236
237 /* We are ready, if calibration file or 0/1 instead of it were not given */
238 if(!calfile[0]) {
239 ecat63EmptyMatlist(&mlist);
240 fclose(fp);
241 return(0);
242 }
243
244
245 /*
246 * Read calibration file, if filename specified
247 */
248 if(strcasecmp(calfile, "0")==0) {
249 if(verbose>0) printf("removing calibration.\n");
250 if(main_header.file_type==RAW_DATA) {
251 fprintf(stderr, "Error: cannot clear sinogram calibration.\n");
252 ecat63EmptyMatlist(&mlist); fclose(fp); return(5);
253 }
254 for(int i=0; i<15; i++) cal_coef[i]=1.0;
255 unit=2; // ECAT counts
256 } else if(strcasecmp(calfile, "1")==0) {
257 if(verbose>0) printf("removing calibration.\n");
258 if(main_header.file_type==RAW_DATA)
259 fprintf(stderr, "Warning: cannot clear sinogram calibration.\n");
260 for(int i=0; i<15; i++) cal_coef[i]=1.0;
261 unit=2; // ECAT counts
262 if(verbose>0) printf("correcting for branching ratio.\n");
263 double bf=branchingFraction(isotope_code);
264 if(bf<=1.0E-06) {
265 fprintf(stderr, "Warning: unknown isotope.\n");
266 fprintf(stderr, "Warning: branching correction not possible.\n");
267 } else {
268 for(int i=0; i<15; i++) cal_coef[i]*=1.0/bf;
269 }
270 } else {
271 if(verbose>1) printf("reading calibration coefficients in %s\n", calfile);
272 /* First, try to select correct file from folder */
273 char fname[FILENAME_MAX];
274 ret=selectEcat931Calibrationfile(calfile, &stm, fname, verbose-3);
275 if(ret) {
276 /* Did not work; assume that user gave calibration filename directly */
277 strlcpy(fname, calfile, FILENAME_MAX);
278 } else {
279 if(verbose>0) printf("calibrationfile := %s\n", fname);
280 }
281 /* Try to read the directly specified or automatically selected file */
282 ret=readEcat931Calibrationfile(fname, cal_coef, verbose-3);
283 if(ret!=0) {
284 fprintf(stderr, "Error: cannot read calibrations in %s.\n", calfile);
285 fclose(fp); ecat63EmptyMatlist(&mlist); return(5);
286 }
287 /* Set units to kBq/mL or nCi/mL */
288 if((cal_coef[4]+cal_coef[10])/2. < 4.0) {
289 /* calibration coefficient is for counts to kBq/mL */
290 } else {
291 /* calibration coefficient is for counts to nCi/mL */
292 /* convert to kBq/mL */
293 for(int i=0; i<15; i++) cal_coef[i]*=0.037;
294 }
295 for(int i=0; i<15; i++) cal_coef[i]*=1000.;
296 unit=10; // kBq/mL in ECAT 6.3
297 /* If the image was scanned after Jan-01-1996, correct for branching ratio */
298 if(main_header.scan_start_year>=1997) {
299 if(verbose>0) printf("correcting for branching ratio.\n");
300 double bf=branchingFraction(isotope_code);
301 if(bf<=1.0E-06) {
302 fprintf(stderr, "Warning: unknown isotope.\n");
303 fprintf(stderr, "Warning: branching correction not possible.\n");
304 } else {
305 for(int i=0; i<15; i++) cal_coef[i]*=1.0/bf;
306 }
307 }
308 }
309 if(verbose>2) {
310 printf("\nNew calibration coefficients:\n");
311 printf("Plane Calibration coefficient\n");
312 for(int i=0; i<15; i++) printf(" %2d %12.5e\n", i+1, cal_coef[i]);
313 }
314
315 /*
316 * Change calibration factor for each matrix
317 */
318 for(int j=0; j<mlist.matrixNr; j++) {
319 mat_numdoc(mlist.matdir[j].matnum, &matval);
320 plane=matval.plane;
321 if(plane<1 || plane>15) continue;
322 /* Read image or sinogram subheader */
323 if(main_header.file_type==IMAGE_DATA)
324 ret=ecat63ReadImageheader(fp, mlist.matdir[j].strtblk, &image_header, verbose-2, NULL);
325 else
326 ret=ecat63ReadScanheader(fp, mlist.matdir[j].strtblk, &scan_header, verbose-2, NULL);
327 if(ret!=0) {
328 fprintf(stderr, "Error: cannot read subheader.\n");
329 fclose(fp); ecat63EmptyMatlist(&mlist); return(6);
330 }
331 /* Change the calibration */
332 if(main_header.file_type==IMAGE_DATA) {
333 image_header.ecat_calibration_fctr=cal_coef[plane-1];
334 image_header.quant_units=unit;
335 } else {
336 scan_header.scale_factor*=cal_coef[plane-1];
337 }
338 /* Write matrix header */
339 if(main_header.file_type==IMAGE_DATA)
340 ret=ecat63WriteImageheader(fp, mlist.matdir[j].strtblk, &image_header);
341 else
342 ret=ecat63WriteScanheader(fp, mlist.matdir[j].strtblk, &scan_header);
343 if(ret!=0) {
344 fprintf(stderr, "Error: cannot write subheader.\n");
345 fclose(fp); ecat63EmptyMatlist(&mlist); return(7);
346 }
347 } // next matrix
348
349
350 /*
351 * Write main header
352 */
353 main_header.calibration_units=unit;
354 ret=ecat63WriteMainheader(fp, &main_header);
355 ecat63EmptyMatlist(&mlist);
356 fclose(fp);
357 if(ret) {
358 fprintf(stderr, "Error: cannot write main header.\n");
359 return(8);
360 }
361 if(unit==2) {
362 printf("Calibrations removed.\n");
363 } else {
364 printf("Calibrated to kBq/mL with plane coefficients:\n");
365 for(int i=0; i<15; i++) printf(" %2d %12.5e\n", i+1, cal_coef[i]);
366 }
367
368 return(0);
369}
370/*****************************************************************************/
371
372/*****************************************************************************/
374
380 char *foldername,
382 struct tm *stm,
384 char *fname,
386 int verbose
387) {
388 if(verbose>0) printf("selectEcat931Calibrationfile('%s', ...)\n", foldername);
389 if(foldername==NULL || stm==NULL || fname==NULL) return(1);
390 strcpy(fname, "");
391
392 char buf[256];
393
394 /* Check whether foldername is a directory */
395 struct stat fst;
396 stat(foldername, &fst);
397 if(!S_ISDIR(fst.st_mode)) { /* it is not */
398 if(verbose>1) printf(" %s is not a directory\n", foldername);
399 return(1);
400 }
401
402 /* Open the directory */
403 DIR *dp;
404 dp=opendir(foldername); if(dp==NULL) {
405 if(verbose>1) printf("Error: cannot open directory %s\n", foldername);
406 return(2);
407 }
408
409 /* Go throught the directory */
410 struct dirent *de;
411 char year[10], month[10], day[10], cfname[FILENAME_MAX];
412 struct tm ftm;
413 double tdif, tdif_min=-1.0E30;
414 cfname[0]=(char)0;
415 while((de=readdir(dp))!=NULL) {
416 if(verbose>2) printf("d_name='%s'\n", de->d_name);
417 if(de->d_name[0]=='.') continue; /* Ignore hidden and 'system' dirs */
418 if(strlen(de->d_name)<6) continue;
419 /* Identify the date from filename which should be in format YYMMDD */
420 strlcpy(year, de->d_name, 3);
421 strlcpy(month, de->d_name+2, 3);
422 strlcpy(day, de->d_name+4, 3);
423 sprintf(buf, "%s.%s.%s 16:00:00", day, month, year);
424 /* Convert to struct tm */
425 if(get_datetime(buf, &ftm, 0)) continue;
426 if(verbose>3) printf(" %s\n", buf);
427 /* Calculate time difference between calibration and scan time */
428 tdif=tmDifference(&ftm, stm);
429 if(verbose>3) printf(" sec_difference := %.0f\n", tdif);
430 /* If later than scan time, then forget this */
431 if(tdif>0.0) continue;
432 /* If smaller difference than before, then save this */
433 if(tdif>tdif_min) {tdif_min=tdif; strcpy(cfname, de->d_name);}
434 }
435 closedir(dp);
436 if(tdif_min<-1.0E10 || !cfname[0]) return(3);
437 if(verbose>1) printf("selected_file := %s\n", cfname);
438
439 /* Combine path and name */
440 if(foldername[strlen(foldername)-1]!='/')
441 sprintf(fname, "%s/%s", foldername, cfname);
442 else
443 sprintf(fname, "%s%s", foldername, cfname);
444
445 return(0);
446}
447/*****************************************************************************/
448
449/*****************************************************************************/
455 char *fname,
458 double *coef,
460 int verbose
461) {
462 if(verbose>0) printf("readEcat931Calibrationfile('%s', ...)\n", fname);
463 if(fname==NULL || coef==NULL) return(1);
464
465 /* Read file into IFT struct */
466 IFT ift; iftInit(&ift);
467 if(iftRead(&ift, fname, 0, 0)!=0) return(2);
468 if(ift.keyNr<15) {iftEmpty(&ift); return(3);}
469
470 /* Jump over the first line which should contain the date, and then
471 read plane factors from the next 15 lines */
472 double v;
473 for(int i=1; i<=15; i++) {
474 if(verbose>1) printf("\t%d\t%s\n", i, ift.item[i].value);
475 if(atof_with_check(ift.item[i].value, &v)) {iftEmpty(&ift); return(4);}
476 if(v<=0.0) {iftEmpty(&ift); return(5);}
477 coef[i-1]=v;
478 }
479 iftEmpty(&ift);
480 return(0);
481}
482/*****************************************************************************/
483
484/*****************************************************************************/
float branchingFraction(int isotope)
Definition branch.c:14
int get_datetime(char *str, struct tm *date, int verbose)
Definition datetime.c:322
time_t timegm(struct tm *tm)
Inverse of gmtime, converting struct tm to time_t.
Definition datetime.c:69
double tmDifference(struct tm *tm1, struct tm *tm0)
Definition datetime.c:502
int atof_with_check(char *double_as_string, double *result_value)
Definition decpoint.c:107
int readEcat931Calibrationfile(char *fname, double *coef, int verbose)
Definition ecalibr.c:453
int selectEcat931Calibrationfile(char *foldername, struct tm *stm, char *fname, int verbose)
Definition ecalibr.c:378
int ecat63ReadMatlist(FILE *fp, MATRIXLIST *ml, int verbose)
Definition ecat63ml.c:46
void ecat63InitMatlist(MATRIXLIST *mlist)
Definition ecat63ml.c:20
void ecat63EmptyMatlist(MATRIXLIST *mlist)
Definition ecat63ml.c:31
int ecat63CheckMatlist(MATRIXLIST *ml)
Definition ecat63ml.c:324
void mat_numdoc(int matnum, Matval *matval)
Definition ecat63ml.c:254
void ecat63PrintMainheader(ECAT63_mainheader *h, FILE *fp)
Definition ecat63p.c:16
char * ecat63Unit(short int dunit)
Definition ecat63p.c:243
int ecat63ReadScanheader(FILE *fp, int blk, ECAT63_scanheader *h, int verbose, char *errmsg)
Definition ecat63r.c:377
int ecat63ReadImageheader(FILE *fp, int blk, ECAT63_imageheader *h, int verbose, char *errmsg)
Definition ecat63r.c:187
int ecat63ReadMainheader(FILE *fp, ECAT63_mainheader *h)
Definition ecat63r.c:25
int ecat63WriteImageheader(FILE *fp, int block, ECAT63_imageheader *h)
Definition ecat63w.c:106
int ecat63WriteScanheader(FILE *fp, int block, ECAT63_scanheader *h)
Definition ecat63w.c:242
struct tm * ecat63ScanstarttimeToTm(const ECAT63_mainheader *h, struct tm *tm)
Convert scan_start_time in ECAT 6.3 main header into a struct tm.
Definition ecat63w.c:900
int ecat63WriteMainheader(FILE *fp, ECAT63_mainheader *h)
Definition ecat63w.c:24
char * hlIsotopeCode(int isotope)
Definition halflife.c:36
double hlFromIsotope(char *isocode)
Definition halflife.c:55
int hlIsotopeFromHalflife(double halflife)
Definition halflife.c:195
void iftEmpty(IFT *ift)
Definition ift.c:60
void iftInit(IFT *ift)
Definition ift.c:45
int iftRead(IFT *ift, char *filename, int is_key_required, int verbose)
Definition iftfile.c:24
Header file for libtpcimgio.
#define RAW_DATA
#define IMAGE_DATA
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
short int calibration_units
short int scan_start_year
int keyNr
Definition libtpcmisc.h:270
IFT_KEY_AND_VALUE * item
Definition libtpcmisc.h:279
MatDir * matdir
int matnum
int strtblk
int plane