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