TPCCLIB
Loading...
Searching...
No Matches
atnmake.c
Go to the documentation of this file.
1
11/*****************************************************************************/
12#include "tpcclibConfig.h"
13/*****************************************************************************/
14#include <stdio.h>
15#include <stdlib.h>
16#include <math.h>
17#include <string.h>
18#include <unistd.h>
19#include <ctype.h>
20#include <dirent.h>
21#include <sys/file.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24#include <time.h>
25/*****************************************************************************/
26#include "libtpcmisc.h"
27#include "libtpcimgio.h"
28#include "libtpcrec.h"
29/*****************************************************************************/
30#ifdef HAVE_OMP_H
31#include <omp.h>
32#endif
33/*****************************************************************************/
34#define ATN_HI_MAX 100.0
35/*****************************************************************************/
36
37/*****************************************************************************/
38static char *info[] = {
39 "Computing PET attenuation correction data from blank and transmission",
40 "sinograms (1) in ECAT 6.3 file format, to be applied to ECAT 931 and",
41 "GE Advance 2D sinograms.",
42 "Normalization file is required to reduce the noise in attenuation data,",
43 "and emission sinogram still needs to be normalized.",
44 "Transmission image can be reconstructed and saved optionally.",
45 " ",
46 "Usage: @P [Options] blankscan trscan normfile atnfile [trimage]",
47 " ",
48 "Options:",
49 " -decay=<Y|n>",
50 " Decay correction for the time difference between blank and",
51 " transmission scans is done (y, default), or not done (n).",
52 " -limit=<y|N>",
53 " Attenuation factors are set to values between 1.0 and 100.0 (y),",
54 " or not limited (n, default).",
55 " -zoom=<value>",
56 " Set zoom factor for transmission image; by default 1.0 (no zoom).",
57 " -dim=<value>",
58 " Set transmission image x and y dimensions; by default the ray number.",
59 " -rot[ation]=<value>",
60 " Set transmission image rotation in degrees, -180 - 180; by default 0.",
61 " -x=<value>",
62 " Set transmission image shift in x direction (in cm); by default 0.",
63 " -y=<value>",
64 " Set transmission image shift in y direction (in cm); by default 0.",
65 " -beta=<value>",
66 " Set beta value, usually 0.1 - 0.9; by default 0.9; affects also",
67 " attenuation factors.",
68 " -mask=<3|5>",
69 " Set mask dimension for median filter, either 3 (3x3) or 5 (5x5 without",
70 " corner pixels); by default 3. Affects also attenuation factors.",
71 " -iter=<value>",
72 " Set maximum number of iterations for transmission image reconstruction;",
73 " by default 45. For attenuation 120 iterations are always used.",
74 " -skip=<value>",
75 " Set the number of iterations without prior in transmission image",
76 " reconstruction; by default 1.",
77 " -os=<1|2|4|8|16>",
78 " Set the number of OS sets (acceleration) in transmission image",
79 " reconstruction; by default 1.",
80 " -stdoptions", // List standard options like --help, -v, etc
81 " ",
82 "Example:",
83 " @P 28oct97bl1.scn s02892tr1.scn s02892sta.nrm s02892tr1.atn s02892tr1.img",
84 " ",
85 "References:",
86 "1. Bettinardi V, Alenius S, Numminen P, Teras M, Gilardi MC, Fazio F,",
87 " Ruotsalainen U. Implementation and evaluation of an ordered subsets",
88 " reconstruction algorithm for transmission PET studies using median root",
89 " prior and inter-update median filtering.",
90 " Eur J Nucl Med. 2003; 30(2):222-231.",
91 " ",
92 "See also: ecatfbp, ecatmrp, ecatnorm, lmhdr, egetstrt, edecay",
93 " ",
94 "Keywords: ECAT, sinogram, reconstruction, attenuation",
95 0};
96/*****************************************************************************/
97
98/*****************************************************************************/
99/* Turn on the globbing of the command line, since it is disabled by default in
100 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
101 In Unix&Linux wildcard command line processing is enabled by default. */
102/*
103#undef _CRT_glob
104#define _CRT_glob -1
105*/
106int _dowildcard = -1;
107/*****************************************************************************/
109/*****************************************************************************/
117 char *trafile,
119 char *blkfile,
121 char *norfile,
123 char *atnfile,
126 char *imgfile,
128 int decay,
130 int limit,
133 int keepNegat,
135 int imgDim,
137 float zoom,
139 float shiftX,
141 float shiftY,
143 float rotation,
145 int maxIterNr,
147 int skipPriorNr,
149 float beta,
151 int maskDim,
153 int osSetNr,
156 char *status,
158 int verbose
159) {
160 int ret;
161
162 /* Check the input */
163 if(status!=NULL) sprintf(status, "invalid filename");
164 if(trafile==NULL || !trafile[0]) return(1);
165 if(blkfile==NULL || !blkfile[0]) return(1);
166 if(norfile==NULL || !blkfile[0]) return(1);
167 if(atnfile==NULL || !atnfile[0]) return(1);
168
169
170 /*
171 * Open the transmission, blank, and normalization sinograms for reading
172 */
173 FILE *fptra=NULL, *fpblk=NULL, *fpnor=NULL;
174 if(verbose>0) printf("opening %s\n", trafile);
175 if((fptra=fopen(trafile, "rb")) == NULL) {
176 if(status!=NULL) sprintf(status, "cannot open transmission sinogram");
177 return(2);
178 }
179 if(verbose>0) printf("opening %s\n", blkfile);
180 if((fpblk=fopen(blkfile, "rb")) == NULL) {
181 if(status!=NULL) sprintf(status, "cannot open blank sinogram");
182 fclose(fptra);
183 return(2);
184 }
185 if(verbose>0) printf("opening %s\n", norfile);
186 if((fpnor=fopen(norfile, "rb")) == NULL) {
187 if(status!=NULL) sprintf(status, "cannot open normalization file");
188 fclose(fptra); fclose(fpblk);
189 return(2);
190 }
191
192 /*
193 * Read main headers
194 */
195 if(verbose>1) printf("reading transmission main header\n");
196 ECAT63_mainheader tra_main_header;
197 if((ret=ecat63ReadMainheader(fptra, &tra_main_header))) {
198 if(status!=NULL) sprintf(status, "cannot read transmission main header");
199 fclose(fptra); fclose(fpblk); fclose(fpnor);
200 return(3);
201 }
202 if(verbose>100) ecat63PrintMainheader(&tra_main_header, stdout);
203 if(verbose>4) printf("file_type := %d\n", tra_main_header.file_type);
204
205 if(verbose>1) printf("reading blank main header\n");
206 ECAT63_mainheader blk_main_header;
207 if((ret=ecat63ReadMainheader(fpblk, &blk_main_header))) {
208 if(status!=NULL) sprintf(status, "cannot read blank main header");
209 fclose(fptra); fclose(fpblk); fclose(fpnor);
210 return(3);
211 }
212 if(verbose>100) ecat63PrintMainheader(&blk_main_header, stdout);
213 if(verbose>4) printf("file_type := %d\n", blk_main_header.file_type);
214
215 if(verbose>1) printf("reading normalization main header\n");
216 ECAT63_mainheader nor_main_header;
217 if((ret=ecat63ReadMainheader(fpnor, &nor_main_header))) {
218 if(status!=NULL) sprintf(status, "cannot read blank main header");
219 fclose(fptra); fclose(fpblk); fclose(fpnor);
220 return(3);
221 }
222 if(verbose>100) ecat63PrintMainheader(&nor_main_header, stdout);
223 if(verbose>4) printf("file_type := %d\n", nor_main_header.file_type);
224
225 /* Check the file_type */
226 if(tra_main_header.file_type!=RAW_DATA || blk_main_header.file_type!=RAW_DATA
227 || nor_main_header.file_type!=RAW_DATA)
228 {
229 if(status!=NULL) sprintf(status, "file is not a sinogram");
230 fclose(fptra); fclose(fpblk); fclose(fpnor);
231 return(3);
232 }
233
234 /* Read scan start times */
235 time_t tra_start, blk_start;
236 tra_start=ecat63Scanstarttime(&tra_main_header);
237 blk_start=ecat63Scanstarttime(&blk_main_header);
238 if(verbose>1) {
239 char buf[32];
240 printf("Blank scan start time := %s\n", ctime_r_int(&blk_start, buf));
241 printf("Transmission scan start time := %s\n", ctime_r_int(&tra_start, buf));
242 }
243 if(decay!=0 && (tra_start==0 || blk_start==0)) {
244 if(status!=NULL) sprintf(status, "missing scan start time");
245 fclose(fptra); fclose(fpblk); fclose(fpnor);
246 return(3);
247 }
248 double start_time_diff=difftime(tra_start, blk_start);
249 if(verbose>1) {printf("scan start time difference := %g\n", start_time_diff);}
250
251 /* Get isotope halflife */
252 double halflife=-1.0;
253 if(tra_main_header.isotope_halflife>0.0) halflife=tra_main_header.isotope_halflife;
254 else if(blk_main_header.isotope_halflife>0.0) halflife=blk_main_header.isotope_halflife;
255 if(verbose>1 && halflife>0.0) {printf("isotope halflife := %g\n", halflife);}
256 if(decay!=0 && halflife<=0.0) {
257 if(status!=NULL) sprintf(status, "missing isotope halflife");
258 fclose(fptra); fclose(fpblk); fclose(fpnor);
259 return(3);
260 }
261
262
263
264 /*
265 * Read the matrix lists
266 */
267 if(verbose>1) printf("reading transmission matrix list\n");
268 static MATRIXLIST tra_mlist; ecat63InitMatlist(&tra_mlist);
269 ret=ecat63ReadMatlist(fptra, &tra_mlist, verbose-2);
270 if(ret) {
271 if(status!=NULL) sprintf(status, "cannot read sinogram matrix list");
272 fclose(fptra); fclose(fpblk); fclose(fpnor);
273 return(3);
274 }
275
276 if(verbose>1) printf("reading blank matrix list\n");
277 static MATRIXLIST blk_mlist; ecat63InitMatlist(&blk_mlist);
278 ret=ecat63ReadMatlist(fpblk, &blk_mlist, verbose-2);
279 if(ret) {
280 if(status!=NULL) sprintf(status, "cannot read blank matrix list");
281 ecat63EmptyMatlist(&tra_mlist);
282 fclose(fptra); fclose(fpblk); fclose(fpnor);
283 return(3);
284 }
285
286 if(verbose>1) printf("reading normalization matrix list\n");
287 static MATRIXLIST nor_mlist; ecat63InitMatlist(&nor_mlist);
288 ret=ecat63ReadMatlist(fpnor, &nor_mlist, verbose-2);
289 if(ret) {
290 if(status!=NULL) sprintf(status, "cannot read normalization matrix list");
291 ecat63EmptyMatlist(&tra_mlist); ecat63EmptyMatlist(&blk_mlist);
292 fclose(fptra); fclose(fpblk); fclose(fpnor);
293 return(3);
294 }
295
296 /* Check the contents of matrix lists */
297 if(tra_mlist.matrixNr<=0 || blk_mlist.matrixNr<=0 || nor_mlist.matrixNr<=0) {
298 if(status!=NULL) sprintf(status, "empty matrix list");
299 ecat63EmptyMatlist(&tra_mlist); ecat63EmptyMatlist(&blk_mlist);
300 ecat63EmptyMatlist(&nor_mlist);
301 fclose(fptra); fclose(fpblk); fclose(fpnor);
302 return(3);
303 }
304 if(ecat63CheckMatlist(&tra_mlist) || ecat63CheckMatlist(&blk_mlist) ||
305 ecat63CheckMatlist(&nor_mlist))
306 {
307 if(status!=NULL) sprintf(status, "invalid matrix list");
308 ecat63EmptyMatlist(&tra_mlist); ecat63EmptyMatlist(&blk_mlist);
309 ecat63EmptyMatlist(&nor_mlist);
310 fclose(fptra); fclose(fpblk); fclose(fpnor);
311 return(3);
312 }
313 if(verbose>3) {
314 printf("transmission_matrixNr := %d\n", tra_mlist.matrixNr);
315 printf("blank_matrixNr := %d\n", blk_mlist.matrixNr);
316 printf("normalization_matrixNr := %d\n", nor_mlist.matrixNr);
317 }
318 if(tra_mlist.matrixNr!=blk_mlist.matrixNr ||
319 tra_mlist.matrixNr!=nor_mlist.matrixNr ||
320 tra_main_header.num_planes != blk_main_header.num_planes ||
321 tra_main_header.num_planes != nor_main_header.num_planes)
322 {
323 if(status!=NULL) sprintf(status, "different plane numbers");
324 ecat63EmptyMatlist(&tra_mlist); ecat63EmptyMatlist(&blk_mlist);
325 ecat63EmptyMatlist(&nor_mlist);
326 fclose(fptra); fclose(fpblk); fclose(fpnor);
327 return(3);
328 }
329
330 /*
331 * Open output file(s) and write main header(s)
332 */
333 if(verbose>0) printf("writing main header in %s\n", atnfile);
334 ECAT63_mainheader atn_main_header;
335 ecat63CopyMainheader(&tra_main_header, &atn_main_header);
336 atn_main_header.data_type = IEEE_R4; /* float */
337 atn_main_header.file_type = ATTN_DATA;
338 atn_main_header.calibration_units = 2;
339 strcpy(atn_main_header.user_process_code, "tMRP");
340 FILE *fpatn=NULL;
341 fpatn=ecat63Create(atnfile, &atn_main_header);
342 if(fpatn==NULL) {
343 if(status!=NULL) sprintf(status, "cannot write attenuation file\n");
344 ecat63EmptyMatlist(&tra_mlist); ecat63EmptyMatlist(&blk_mlist);
345 ecat63EmptyMatlist(&nor_mlist);
346 fclose(fptra); fclose(fpblk); fclose(fpnor);
347 return(11);
348 }
349
350 FILE *fpimg=NULL;
351 if(imgfile!=NULL && imgfile[0]) {
352 if(verbose>0) printf("writing main header in %s\n", imgfile);
353 ECAT63_mainheader img_main_header;
354 ecat63CopyMainheader(&tra_main_header, &img_main_header);
355 img_main_header.file_type = IMAGE_DATA;
356 img_main_header.data_type = SUN_I2; /* short int */
357 img_main_header.calibration_units = 2;
358 img_main_header.num_frames=1;
359 strcpy(img_main_header.user_process_code, "tMRP");
360 fpimg=ecat63Create(imgfile, &img_main_header);
361 if(fpimg==NULL) {
362 if(status!=NULL) sprintf(status, "cannot write transmission image\n");
363 ecat63EmptyMatlist(&tra_mlist); ecat63EmptyMatlist(&blk_mlist);
364 ecat63EmptyMatlist(&nor_mlist);
365 fclose(fptra); fclose(fpblk); fclose(fpnor);
366 remove(atnfile);
367 return(12);
368 }
369 }
370
371
372 /*
373 * Process one matrix at a time
374 */
375 if(verbose>0) printf("processing matrices...\n");
376 int cret=0;
377 for(int mi=0; mi<tra_mlist.matrixNr; mi++) {
378 if(verbose==1) {fprintf(stdout, "."); fflush(stdout);}
379
380 FILE *lfptra=fptra;
381 FILE *lfpblk=fpblk;
382 FILE *lfpnor=fpnor;
383 FILE *lfpatn=fpatn;
384 FILE *lfpimg=fpimg;
385
386 ECAT63_scanheader tra_header, blk_header, nor_header;
387 ECAT63_attnheader atn_header;
388 ECAT63_imageheader img_header;
389 Matval matval;
390 int dimx, dimy, scnpxlNr=0, imgpxlNr=0, matnum=0, ni=0, bi=0;
391 float *tramat, *blkmat, *normat, f;
392 int ret=0;
393
394 /* Get plane and frame nr */
395 mat_numdoc(tra_mlist.matdir[mi].matnum, &matval);
396
397 /* Read transmission sinogram subheader and scaled data */
398 if(verbose>1) printf("reading transmission matrix %d\n", 1+mi);
399 ret=ecat63ReadScanMatrix(lfptra, tra_mlist.matdir[mi].strtblk,
400 tra_mlist.matdir[mi].endblk, &tra_header, &tramat);
401 if(ret) {
402 if(status!=NULL)
403 sprintf(status, "cannot read transmission sinogram matrix %u", tra_mlist.matdir[mi].matnum);
404 cret=ret;
405 continue;
406 }
407 if(verbose>2)
408 printf("Matrix: plane %d frame %d gate %d bed %d\n",
409 matval.plane, matval.frame, matval.gate, matval.bed);
410 if(verbose>200) {
411 ecat63PrintScanheader(&tra_header, stdout);
412 } else if(verbose>1 && mi==0) {
413 printf("Transmission sinogram dimensions := %d x %d\n",
414 tra_header.dimension_1, tra_header.dimension_2);
415 printf("Transmission sinogram frame duration := %g\n",
416 0.001*(double)tra_header.frame_duration);
417 printf("Transmission sinogram dead-time correction := %g\n",
418 tra_header.loss_correction_fctr);
419 }
420 dimx=tra_header.dimension_1;
421 dimy=tra_header.dimension_2;
422 scnpxlNr=dimx*dimy;
423 if(imgDim<2) imgDim=dimx; // transmission dim to number of rays by default
424 /* Dead-time correction */
425 if(tra_header.loss_correction_fctr>0.0) {
426 for(int i=0; i<scnpxlNr; i++)
427 tramat[i]*=tra_header.loss_correction_fctr;
428 }
429 /* Divide counts by frame duration */
430 if(tra_header.frame_duration<1) {
431 if(status!=NULL) sprintf(status, "missing frame duration in transmission sinogram");
432 free(tramat);
433 cret=ret;
434 continue;
435 }
436 f=1000.0/tra_header.frame_duration;
437 for(int i=0; i<scnpxlNr; i++) tramat[i]*=f;
438
439 /* Search corresponding matrix from the normalization file */
440 matnum=mat_numcod(matval.frame, matval.plane, matval.gate, matval.data, matval.bed);
441 for(ni=0; ni<nor_mlist.matrixNr; ni++)
442 if(nor_mlist.matdir[ni].matnum==matnum)
443 break;
444 if(ni>=nor_mlist.matrixNr) {
445 if(status!=NULL)
446 sprintf(status, "cannot find matrix %u in normalization sinogram",
447 nor_mlist.matdir[ni].matnum);
448 free(tramat);
449 cret=ret;
450 continue;
451 }
452 /* Read normalization subheader and scaled data */
453 if(verbose>1) printf("reading normalization matrix %d\n", 1+ni);
454 ret=ecat63ReadScanMatrix(lfpnor, nor_mlist.matdir[ni].strtblk,
455 nor_mlist.matdir[ni].endblk, &nor_header, &normat);
456 if(ret) {
457 if(status!=NULL)
458 sprintf(status, "cannot read blank sinogram matrix %u", nor_mlist.matdir[ni].matnum);
459 free(tramat);
460 cret=ret;
461 continue;
462 }
463 if(verbose>200) {
464 ecat63PrintScanheader(&nor_header, stdout);
465 } else if(verbose>1 && mi==0) {
466 printf("Normalization sinogram dimensions := %d x %d\n",
467 nor_header.dimension_1, nor_header.dimension_2);
468 printf("Normalization sinogram frame duration := %g\n",
469 0.001*(double)nor_header.frame_duration);
470 printf("Normalization sinogram dead-time correction := %g\n",
471 nor_header.loss_correction_fctr);
472 }
473 if(tra_header.dimension_1!=nor_header.dimension_1
474 || tra_header.dimension_2!=nor_header.dimension_2)
475 {
476 if(status!=NULL) sprintf(status, "incompatible matrix dimensions");
477 free(tramat); free(normat);
478 cret=ret;
479 continue;
480 }
481
482 /* Normalization correction for transmission sinogram */
483 for(int i=0; i<scnpxlNr; i++) tramat[i]*=normat[i];
484 /* Set negatives to zero */
485 if(keepNegat==0)
486 for(int i=0; i<scnpxlNr; i++) if(tramat[i]<0.0) tramat[i]=0.0;
487
488 /* Search corresponding matrix from the blank file */
489 matnum=mat_numcod(matval.frame, matval.plane, matval.gate, matval.data,
490 matval.bed);
491 for(bi=0; bi<blk_mlist.matrixNr; bi++)
492 if(blk_mlist.matdir[bi].matnum==matnum)
493 break;
494 if(bi>=blk_mlist.matrixNr) {
495 if(status!=NULL)
496 sprintf(status, "cannot find matrix %u in blank sinogram", blk_mlist.matdir[bi].matnum);
497 free(tramat); free(normat);
498 cret=ret;
499 continue;
500 }
501 /* Read blank subheader and scaled data */
502 if(verbose>1) printf("reading blank matrix %d\n", 1+bi);
503 ret=ecat63ReadScanMatrix(lfpblk, blk_mlist.matdir[bi].strtblk,
504 blk_mlist.matdir[bi].endblk, &blk_header, &blkmat);
505 if(ret) {
506 if(status!=NULL)
507 sprintf(status, "cannot read blank sinogram matrix %u", blk_mlist.matdir[bi].matnum);
508 free(tramat); free(normat);
509 cret=ret;
510 continue;
511 }
512 if(verbose>200) {
513 ecat63PrintScanheader(&blk_header, stdout);
514 } else if(verbose>1 && mi==0) {
515 printf("Blank sinogram dimensions := %d x %d\n",
516 blk_header.dimension_1, blk_header.dimension_2);
517 printf("Blank sinogram frame duration := %g\n",
518 0.001*(double)blk_header.frame_duration);
519 printf("Blank sinogram dead-time correction := %g\n",
520 blk_header.loss_correction_fctr);
521 }
522 if(tra_header.dimension_1!=blk_header.dimension_1
523 || tra_header.dimension_2!=blk_header.dimension_2)
524 {
525 if(status!=NULL) sprintf(status, "incompatible matrix dimensions");
526 free(tramat); free(normat); free(blkmat);
527 cret=ret;
528 continue;
529 }
530 /* Dead-time correction */
531 if(blk_header.loss_correction_fctr>0.0) {
532 for(int i=0; i<scnpxlNr; i++)
533 blkmat[i]*=blk_header.loss_correction_fctr;
534 }
535 /* Divide counts by frame duration */
536 if(blk_header.frame_duration<1) {
537 if(status!=NULL) sprintf(status, "missing frame duration in blank sinogram");
538 free(tramat); free(normat); free(blkmat);
539 cret=ret;
540 continue;
541 }
542 f=1000.0/blk_header.frame_duration;
543 for(int i=0; i<scnpxlNr; i++) blkmat[i]*=f;
544
545 /* Normalization correction for blank sinogram */
546 for(int i=0; i<scnpxlNr; i++) blkmat[i]*=normat[i];
547 /* Set negatives to zero */
548 if(keepNegat==0) for(int i=0; i<scnpxlNr; i++) if(blkmat[i]<0.0) blkmat[i]=0.0;
549
550
551 /* Correct transmission scan for the isotope decay in radiation source
552 that may have happened between blank and transmission scans;
553 it is not important in patient scans, but may be very important for
554 phantom studies and calibration measurements.
555 Frame scan durations are considered to be insignificant for decay.
556 */
557 double dcf=1.0;
558 if(decay!=0) {
559 double lambda=hl2lambda(halflife);
560 double t=start_time_diff;
561 t+=0.001*(double)tra_header.frame_start_time;
562 t-=0.001*(double)blk_header.frame_start_time;
563 dcf=hlLambda2factor(lambda, t, -1.0);
564 if(verbose>1 && mi==0) {
565 printf("frame start time difference := %g\n", t);
566 printf("decay correction factor := %g\n", dcf);
567 }
568 for(int i=0; i<scnpxlNr; i++) tramat[i]*=dcf;
569 }
570
571
572 /* Set up the attenuation sub header information */
573 if(verbose>3) printf("setting attenuation sub-header\n");
574 if(mi==0) {
575 /* Set fields that are common for all matrices */
576 memset(&atn_header, 0, sizeof(ECAT63_attnheader));
577 atn_header.data_type=IEEE_R4; /* float */
578 atn_header.attenuation_type=1; /* 1 for measured attenuation */
579 atn_header.scale_factor=1.0;
580 atn_header.x_origin=0.0;
581 atn_header.y_origin=0.0;
582 atn_header.x_radius=0.0;
583 atn_header.y_radius=0.0;
584 atn_header.tilt_angle=tra_main_header.gantry_tilt;
585 atn_header.attenuation_coeff=1.0;
586 atn_header.sample_distance=tra_header.sample_distance;
587 }
588 atn_header.dimension_1 = tra_header.dimension_1;
589 atn_header.dimension_2 = tra_header.dimension_2;
590
591 /* Set up the log attenuation sub header information, if needed */
592 if(lfpimg!=NULL && mi==0) {
593 if(verbose>6) printf("setting transmission image sub-header\n");
594 /* Set fields that are common for all matrices */
595 img_header.data_type=SUN_I2; /* short int */
596 img_header.num_dimensions=2;
597 img_header.dimension_1=imgDim;
598 img_header.dimension_2=imgDim;
599 img_header.x_origin=10.0*shiftX;
600 img_header.y_origin=10.0*shiftY;
601 img_header.recon_scale=zoom;
602 img_header.quant_scale=1.0;
603 img_header.pixel_size=tra_header.sample_distance*(float)dimx/(zoom*(float)imgDim);
604 img_header.slice_width=tra_main_header.plane_separation;
605 img_header.frame_start_time=0;
606 img_header.frame_duration=0; // to prevent accidental decay correction
607 img_header.frame_duration=0;
608 img_header.filter_code=3;
609 img_header.image_rotation=rotation;
610 img_header.decay_corr_fctr=dcf;
611 img_header.loss_corr_fctr=1.0;
612 img_header.quant_units=2;
613 img_header.ecat_calibration_fctr=1.0;
614 img_header.filter_params[0]=beta;
615 img_header.filter_params[1]=(float)maskDim;
616 img_header.filter_params[2]=(float)maxIterNr;
617 img_header.filter_params[3]=(float)1;
618 img_header.filter_params[4]=(float)osSetNr;
619 img_header.filter_params[5]=(float)skipPriorNr;
620 strcpy(img_header.annotation, "tMRP");
621 }
622 if(lfpimg!=NULL) { // separately for each matrix
623 img_header.plane_eff_corr_fctr=nor_header.scale_factor;
624 img_header.scan_matrix_num=tra_mlist.matdir[mi].matnum;
625 img_header.norm_matrix_num=nor_mlist.matdir[ni].matnum;
626 img_header.atten_cor_mat_num=tra_mlist.matdir[mi].matnum;
627 }
628
629
630 /*
631 * Reconstruct image containing log-transformed attenuation correction factors.
632 * It must be log-transformed, because actual attenuation factors are >1 for the whole sinogram.
633 * At this step there must be no zoom, shifts, or rotation, and
634 * image dimension must be the same as sinogram width (nr or rays or bins).
635 */
636 if(verbose>1) {printf("matrix reconstruction\n"); fflush(stdout);}
637 imgpxlNr=dimx*dimx;
638 float imgmat[imgpxlNr];
639 ret=trmrp(blkmat, tramat, dimx, imgmat, 120, 1,
640 dimx, dimy, maskDim, 1.0, beta,
641 10.0*tra_main_header.axial_fov, 10.0*tra_header.sample_distance,
642 1, 1, 0.0, 0.0, 0.0,
643 verbose-6);
644 if(ret) {
645 if(status!=NULL) sprintf(status, "cannot calculate attenuation correction factors");
646 if(verbose>1) printf("trmrp_return_value := %d\n", ret);
647 free(tramat); free(normat); free(blkmat);
648 cret=ret;
649 continue;
650 }
651 if(verbose>1) {
652 for(int i=0; i<imgpxlNr; i++)
653 if(!isfinite(imgmat[i])) {
654 printf(" inf in the image!\n");
655 break;
656 }
657 }
658
659
660 /*
661 * If requested transmission image dimension is the same as scan ray number, and
662 * zoom, shifts or rotation were not set, then simply save the reconstructed image;
663 * otherwise we have to reconstruct it later again.
664 */
665 if(lfpimg!=NULL && imgDim==dimx && zoom==1.0 && shiftX==0.0 && shiftY==0.0 && rotation==0.0) {
666 if(verbose>1) printf("writing transmission image matrix\n");
667 matnum=mat_numcod(1, matval.plane, matval.gate, matval.data, matval.bed);
668 ret=ecat63WriteImageMatrix(lfpimg, matnum, &img_header, imgmat);
669 if(ret) {
670 if(status!=NULL) sprintf(status, "cannot write transmission image matrix");
671 if(verbose>1) printf("ret := %d\n", ret);
672 free(tramat); free(normat); free(blkmat);
673 cret=ret;
674 continue;
675 }
676 }
677
678 /*
679 * Reprojection to sinogram space
680 */
681 if(verbose>1) {printf("matrix reprojection\n"); fflush(stdout);}
682 float atnmat[scnpxlNr];
683 ret=reprojection(imgmat, dimx, dimx, dimy, 1.0, atnmat, verbose-10);
684 if(ret) {
685 if(status!=NULL) sprintf(status, "cannot reproject attenuation correction data");
686 if(verbose>1) printf("trmrp_return_value := %d\n", ret);
687 free(tramat); free(normat); free(blkmat);
688 cret=ret;
689 continue;
690 }
691 if(verbose>1) {
692 for(int i=0; i<scnpxlNr; i++)
693 if(!isfinite(atnmat[i])) {
694 printf(" inf in the log attenuation data!\n");
695 break;
696 }
697 }
698
699 /*
700 * Convert from log values to correction factors for attenuation file
701 */
702 if(verbose>2) {printf("conversion from logarithms\n"); fflush(stdout);}
703#if(0)
704 if(tra_header.sample_distance>0.001) f=10.0*tra_header.sample_distance;
705 else f=1.0;
706 for(int i=0; i<scnpxlNr; i++) atnmat[i]=expf(atnmat[i]*f);
707#endif
708 for(int i=0; i<scnpxlNr; i++) atnmat[i]=expf(atnmat[i]);
709 if(verbose>1) {
710 for(int i=0; i<scnpxlNr; i++)
711 if(!isfinite(atnmat[i])) {
712 printf(" inf in the attenuation data!\n");
713 break;
714 }
715 }
716 for(int i=0; i<scnpxlNr; i++) if(!isfinite(atnmat[i])) atnmat[i]=1.0;
717
718 /* Fix too low and high values, if requested */
719 if(limit>0) {
720 for(int i=0; i<scnpxlNr; i++) {
721 if(!(atnmat[i]>=1.0)) atnmat[i]=1.0;
722 else if(atnmat[i]>ATN_HI_MAX) atnmat[i]=ATN_HI_MAX;
723 }
724 }
725
726
727 /* Write the attenuation matrix */
728 if(verbose>1) printf("writing attenuation matrix\n");
729 matnum=mat_numcod(1, matval.plane, matval.gate, matval.data, matval.bed);
730 ret=ecat63WriteAttn(lfpatn, matnum, &atn_header, atnmat);
731 if(ret) {
732 if(status!=NULL) sprintf(status, "cannot write attenuation matrix");
733 if(verbose>1) printf("ret := %d\n", ret);
734 free(tramat); free(normat); free(blkmat);
735 cret=ret;
736 continue;
737 }
738
739
740 /*
741 * Reconstruct and save transmission image, in case user requested it, and
742 * user gave zoom, shifts, rotation, or image dimensions that is different
743 * from the sinogram width (nr or rays or bins).
744 */
745 if(lfpimg!=NULL && (imgDim!=dimx || zoom!=1.0 || shiftX!=0.0 || shiftY!=0.0 || rotation!=0.0)) {
746 if(verbose>1) {printf("transmission matrix reconstruction\n"); fflush(stdout);}
747 imgpxlNr=imgDim*imgDim;
748 float imgmat2[imgpxlNr];
749 ret=trmrp(blkmat, tramat, imgDim, imgmat2, maxIterNr, osSetNr,
750 dimx, dimy, maskDim, zoom, beta,
751 10.0*tra_main_header.axial_fov, 10.0*tra_header.sample_distance,
752 skipPriorNr, 1, shiftX, shiftY, rotation,
753 verbose-7);
754 if(ret) {
755 if(status!=NULL) sprintf(status, "cannot reconstruct transmission image");
756 if(verbose>1) printf("trmrp_return_value := %d\n", ret);
757 } else {
758 f=10.0*tra_header.sample_distance;
759 for(int i=0; i<imgpxlNr; i++) imgmat2[i]*=f;
760 if(verbose>1) printf("writing transmission image matrix\n");
761 matnum=mat_numcod(1, matval.plane, matval.gate, matval.data, matval.bed);
762 ret=ecat63WriteImageMatrix(lfpimg, matnum, &img_header, imgmat2);
763 if(ret) {
764 if(status!=NULL) sprintf(status, "cannot write transmission image matrix");
765 if(verbose>1) printf("ret := %d\n", ret);
766 }
767 }
768 if(ret) {
769 free(tramat); free(normat); free(blkmat);
770 cret=ret;
771 continue;
772 }
773 }
774
775 free(tramat); free(normat); free(blkmat);
776 } /* next matrix */
777 if(verbose==1) {fprintf(stdout, "\n"); fflush(stdout);}
778 if(verbose>0) {fprintf(stdout, "done.\n"); fflush(stdout);}
779
780
781 fclose(fpatn); if(fpimg!=NULL) fclose(fpimg);
782 ecat63EmptyMatlist(&tra_mlist); ecat63EmptyMatlist(&blk_mlist);
783 ecat63EmptyMatlist(&nor_mlist);
784 fclose(fptra); fclose(fpblk); fclose(fpnor);
785 if(cret) {remove(atnfile); if(fpimg!=NULL) remove(imgfile);}
786 return(cret);
787}
788/*****************************************************************************/
790/*****************************************************************************/
794int main(int argc, char **argv)
795{
796 int ai, help=0, version=0, verbose=1;
797 char blkfile[FILENAME_MAX], trafile[FILENAME_MAX], norfile[FILENAME_MAX],
798 atnfile[FILENAME_MAX], imgfile[FILENAME_MAX];
799 int decayCorrection=1;
800 int limit=0; /* 0=no; 1=limits applied */
801 int maxIterNr=45;
802 int skipPriorNr=1;
803 float beta=0.9;
804 float rotate=0.0;
805 float zoom=1.0;
806 float shiftX=0.0, shiftY=0.0;
807 int imgDim=0;
808 int maskDim=3;
809 int osSetNr=1;
810 char *cptr;
811 int ret;
812
813
814
815 /*
816 * Get arguments
817 */
818 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
819 blkfile[0]=trafile[0]=norfile[0]=atnfile[0]=imgfile[0]=(char)0;
820 /* Options */
821 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') { /* options */
822 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
823 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
824 if(strncasecmp(cptr, "DECAY=", 6)==0) {
825 cptr+=6;
826 if(strncasecmp(cptr, "YES", 1)==0 || strcasecmp(cptr, "ON")==0) {
827 decayCorrection=1; continue;
828 }
829 if(strncasecmp(cptr, "NO", 1)==0 || strcasecmp(cptr, "OFF")==0) {
830 decayCorrection=0; continue;
831 }
832 } else if(strncasecmp(cptr, "LIMIT=", 6)==0) {
833 cptr+=6;
834 if(strncasecmp(cptr, "YES", 1)==0 || strcasecmp(cptr, "ON")==0) {
835 limit=1; continue;
836 }
837 if(strncasecmp(cptr, "NO", 1)==0 || strcasecmp(cptr, "OFF")==0) {
838 limit=0; continue;
839 }
840 } else if(strncasecmp(cptr, "ITER=", 5)==0) {
841 maxIterNr=atoi(cptr+5); if(maxIterNr>1) continue;
842 } else if(strncasecmp(cptr, "SKIP=", 5)==0) {
843 skipPriorNr=atoi(cptr+5); if(skipPriorNr>0) continue;
844 } else if(strncasecmp(cptr, "OS=", 3)==0) {
845 osSetNr=atoi(cptr+3);
846 if(osSetNr==0 || osSetNr==1 || osSetNr==2) continue;
847 if(osSetNr==4 || osSetNr==8 || osSetNr==16) continue;
848 if(osSetNr==32 || osSetNr==64 || osSetNr==128) continue;
849 } else if(strncasecmp(cptr, "DIM=", 4)==0) {
850 imgDim=atoi(cptr+4); if(imgDim>1 && imgDim<=2048) continue;
851 } else if(strncasecmp(cptr, "MASK=", 5)==0) {
852 maskDim=atoi(cptr+5); if(maskDim==3 || maskDim==5) continue;
853 } else if(strncasecmp(cptr, "ZOOM=", 5)==0) {
854 zoom=atof_dpi(cptr+5); if(zoom>0.1 && zoom<100.0) continue;
855 } else if(strncasecmp(cptr, "BETA=", 5)==0) {
856 beta=atof_dpi(cptr+5); if(beta>0.05 && beta<100.0) continue;
857 } else if(strncasecmp(cptr, "ROTATION=", 9)==0) {
858 double v;
859 ret=atof_with_check(cptr+9, &v);
860 if(ret==0 && v>=-180.0 && v<=180.0) {rotate=v; continue;}
861 } else if(strncasecmp(cptr, "ROT=", 4)==0) {
862 double v;
863 ret=atof_with_check(cptr+4, &v);
864 if(ret==0 && v>-360.0 && v<360.0) {rotate=v; continue;}
865 } else if(strncasecmp(cptr, "X=", 2)==0) {
866 double v;
867 ret=atof_with_check(cptr+2, &v);
868 if(ret==0 && v>-50.0 && v<50.0) {shiftX=10.0*v; continue;}
869 } else if(strncasecmp(cptr, "Y=", 2)==0) {
870 double v;
871 ret=atof_with_check(cptr+2, &v);
872 if(ret==0 && v>-50.0 && v<50.0) {shiftY=10.0*v; continue;}
873 }
874 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
875 return(1);
876 } else break;
877
878 /* Print help or version? */
879 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
880 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
881 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
882
883 /* Process other arguments, starting from the first non-option */
884 if(ai<argc) {strlcpy(blkfile, argv[ai++], FILENAME_MAX);}
885 if(ai<argc) {strlcpy(trafile, argv[ai++], FILENAME_MAX);}
886 if(ai<argc) {strlcpy(norfile, argv[ai++], FILENAME_MAX);}
887 if(ai<argc) {strlcpy(atnfile, argv[ai++], FILENAME_MAX);}
888 if(ai<argc) {strlcpy(imgfile, argv[ai++], FILENAME_MAX);}
889 if(ai<argc) {
890 fprintf(stderr, "Error: too many arguments.\n");
891 return(1);
892 }
893
894 /* Is something missing? */
895 if(!atnfile[0]) {
896 fprintf(stderr, "Error: missing command-line argument; use --help\n");
897 return(1);
898 }
899
900 /* In verbose mode print arguments and options */
901 if(verbose>1) {
902 printf("blkfile := %s\n", blkfile);
903 printf("trafile := %s\n", trafile);
904 printf("norfile := %s\n", norfile);
905 printf("atnfile := %s\n", atnfile);
906 if(imgfile[0]) printf("imgfile := %s\n", imgfile);
907 printf("decayCorrection := %d\n", decayCorrection);
908 printf("limit := %d\n", limit);
909 printf("beta := %g\n", beta);
910 printf("zoom := %g\n", zoom);
911 if(imgDim>0) printf("dimension := %d\n", imgDim);
912 printf("mask := %d\n", maskDim);
913 printf("rotation := %g\n", rotate);
914 printf("x_shift_mm := %g\n", shiftX);
915 printf("y_shift_mm := %g\n", shiftY);
916 printf("maxIterNr := %d\n", maxIterNr);
917 printf("skipPriorNr := %d\n", skipPriorNr);
918 printf("osSetNr := %d\n", osSetNr);
919 }
920
921 /* Check that input and output files do not have the same filename */
922 if(!strcasecmp(atnfile, blkfile) || !strcasecmp(atnfile, trafile)) {
923 fprintf(stderr, "Error: original data must not be overwritten.\n");
924 return(1);
925 }
926 if(!strcasecmp(imgfile, blkfile) || !strcasecmp(imgfile, trafile)) {
927 fprintf(stderr, "Error: original data must not be overwritten.\n");
928 return(1);
929 }
930 /* Output filenames must have correct extension */
931 if(strcasestr(atnfile, ".atn")==NULL) {
932 fprintf(stderr, "Error: attenuation file must have extension .atn\n");
933 return(1);
934 }
935 if(imgfile[0] && strcasestr(imgfile, ".img")==NULL) {
936 fprintf(stderr, "Error: transmission image must have extension .img\n");
937 return(1);
938 }
939
940
941 /* Call the function that actually does all the work */
942 char buf[128];
943 ret=atnMake(
944 trafile, blkfile, norfile, atnfile, imgfile, decayCorrection, limit, 1,
945 imgDim, zoom, shiftX, shiftY, rotate,
946 maxIterNr, skipPriorNr, beta, maskDim, osSetNr,
947 buf, verbose
948 );
949 if(ret!=0) {
950 fprintf(stderr, "Error: %s.\n", buf);
951 if(verbose>3) printf("ret := %d\n", ret);
952 return(1);
953 }
954
955 return(0);
956}
957/*****************************************************************************/
958
959/*****************************************************************************/
int atnMake(char *trafile, char *blkfile, char *norfile, char *atnfile, char *imgfile, int decay, int limit, int keepNegat, int imgDim, float zoom, float shiftX, float shiftY, float rotation, int maxIterNr, int skipPriorNr, float beta, int maskDim, int osSetNr, char *status, int verbose)
Definition atnmake.c:115
char * ctime_r_int(const time_t *t, char *buf)
Convert calendard time t into a null-terminated string of the form YYYY-MM-DD hh:mm:ss,...
Definition datetime.c:110
int atof_with_check(char *double_as_string, double *result_value)
Definition decpoint.c:107
double atof_dpi(char *str)
Definition decpoint.c:59
int ecat63CopyMainheader(ECAT63_mainheader *h1, ECAT63_mainheader *h2)
Definition ecat63h.c:16
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 mat_numcod(int frame, int plane, int gate, int data, int bed)
Definition ecat63ml.c:242
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
void ecat63PrintScanheader(ECAT63_scanheader *h, FILE *fp)
Definition ecat63p.c:137
int ecat63ReadScanMatrix(FILE *fp, int first_block, int last_block, ECAT63_scanheader *h, float **fdata)
Definition ecat63r.c:731
int ecat63ReadMainheader(FILE *fp, ECAT63_mainheader *h)
Definition ecat63r.c:25
int ecat63WriteAttn(FILE *fp, int matnum, ECAT63_attnheader *h, void *data)
Definition ecat63w.c:563
int ecat63WriteImageMatrix(FILE *fp, int matnum, ECAT63_imageheader *h, float *fdata)
Definition ecat63w.c:697
FILE * ecat63Create(const char *fname, ECAT63_mainheader *h)
Definition ecat63w.c:365
time_t ecat63Scanstarttime(const ECAT63_mainheader *h)
Get calendar time from ECAT 6.3 main header.
Definition ecat63w.c:925
double hl2lambda(double halflife)
Definition halflife.c:84
double hlLambda2factor(double lambda, double frametime, double framedur)
Definition halflife.c:98
Header file for libtpcimgio.
#define ATTN_DATA
#define IEEE_R4
#define RAW_DATA
#define IMAGE_DATA
#define SUN_I2
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
char * strcasestr(const char *haystack, const char *needle)
Definition strext.c:279
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 libtpcrec.
int trmrp(float *bla, float *tra, int dim, float *image, int iter, int sets, int rays, int views, int maskdim, float zoom, float beta, float axial_fov, float sample_distance, int skip_prior, int osl, float shiftX, float shiftY, float rotation, int verbose)
Definition trmrp.c:21
int reprojection(float *image, int dim, int rays, int views, float bpzoom, float *sinogram, int verbose)
short int dimension_1
short int dimension_2
short int attenuation_type
short int num_dimensions
short int calibration_units
char user_process_code[10]
short int dimension_2
short int dimension_1
MatDir * matdir
int endblk
int matnum
int strtblk
int frame
int plane