TPCCLIB
Loading...
Searching...
No Matches
dcmframe.c
Go to the documentation of this file.
1
8/*****************************************************************************/
9#include "tpcclibConfig.h"
10/*****************************************************************************/
11#include <stdio.h>
12#include <stdlib.h>
13#include <math.h>
14#include <time.h>
15#include <string.h>
16/*****************************************************************************/
17#include "tpcextensions.h"
18#include "tpctac.h"
19#include "tpcdcm.h"
20/*****************************************************************************/
21
22/*****************************************************************************/
23static char *info[] = {
24 "List the time frame information of a PET image in DICOM format.",
25 "The frame start times and lengths can be saved in SIF.",
26 " ",
27 "NOT for production use!",
28 " ",
29 "Usage: @P [-Options] dicomfile [SIF]",
30 " ",
31 "Options:",
32 " -stdoptions", // List standard options like --help, -v, etc
33 " ",
34 "See also: dcmlhdr, dcmmlist, eframe, tacframe",
35 " ",
36 "Keywords: image, DICOM, time, SIF",
37 0};
38/*****************************************************************************/
39
40/*****************************************************************************/
41/* Turn on the globbing of the command line, since it is disabled by default in
42 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
43 In Unix&Linux wildcard command line processing is enabled by default. */
44/*
45#undef _CRT_glob
46#define _CRT_glob -1
47*/
48int _dowildcard = -1;
49/*****************************************************************************/
50
51/*****************************************************************************/
55int main(int argc, char **argv)
56{
57 int ai, help=0, version=0, verbose=1;
58 char dcmfile[FILENAME_MAX], siffile[FILENAME_MAX];
59 int ret;
60
61
62 /*
63 * Get arguments
64 */
65 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
66 dcmfile[0]=siffile[0]=(char)0;
67 /* Options */
68 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
69 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
70 //char *cptr; cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(!*cptr) continue;
71 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
72 return(1);
73 } else break;
74
75 TPCSTATUS status; statusInit(&status);
76 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
77 status.verbose=verbose-3;
78
79 /* Print help or version? */
80 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
81 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
82 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
83
84 /* Process other arguments, starting from the first non-option */
85 if(ai<argc) strlcpy(dcmfile, argv[ai++], FILENAME_MAX);
86 if(ai<argc) strlcpy(siffile, argv[ai++], FILENAME_MAX);
87 if(ai<argc) {
88 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
89 return(1);
90 }
91 /* Did we get all the information that we need? */
92 if(!dcmfile[0]) {
93 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
94 return(1);
95 }
96
97 /* In verbose mode print arguments and options */
98 if(verbose>1) {
99 printf("dcmfile := %s\n", dcmfile);
100 if(siffile[0]) printf("siffile := %s\n", siffile);
101 }
102
103 /*
104 * Get list of DICOM files belonging to the same image
105 */
106 IFT fl; iftInit(&fl);
107 ret=dcmFileList(dcmfile, &fl, &status);
108 if(ret!=TPCERROR_OK) {
109 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
110 return(2);
111 }
112 if(verbose>1) {printf("fileNr := %d\n", fl.keyNr); fflush(stdout);}
113 if(verbose>6) iftWrite(&fl, stdout, NULL);
114
115 /*
116 * Read the first file in list
117 */
118 DCMFILE dcm; dcmfileInit(&dcm);
119 ret=dcmFileRead(fl.item[0].value, &dcm, 1, &status);
120 if(ret!=TPCERROR_OK) {
121 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
122 iftFree(&fl); dcmfileFree(&dcm); return(3);
123 }
124
125 /* Read some information common to the whole image */
126 DCMTAG tag;
127 DCMITEM *iptr;
128
129 /* Get Decay Correction */
130 short unsigned int decayCorrection=0; // 0=NONE, 1=START, 2=ADMIN
131 tag.group=0x0054; tag.element=0x1102;
132 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
133 if(iptr!=NULL) {
134 char *buf=dcmValueString(iptr);
135 if(verbose>2) printf("Decay Correction := %s\n", buf);
136 if(strcasestr(buf, "NONE")!=NULL) decayCorrection=0;
137 else if(strcasestr(buf, "START")!=NULL) decayCorrection=1;
138 else if(strcasestr(buf, "ADMIN")!=NULL) decayCorrection=2;
139 free(buf);
140 } else {
141 tag.group=0x0018; tag.element=0x9758;
142 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
143 if(iptr!=NULL) {
144 char *buf=dcmValueString(iptr);
145 if(verbose>2) printf("Decay Correction := %s\n", buf);
146 if(strcasestr(buf, "YES")!=NULL) decayCorrection=1;
147 free(buf);
148 }
149 }
150 if(verbose>1) printf("decayCorrection := %d\n", decayCorrection);
151
152 /* If decay is corrected to tracer administration time, the we must read it */
153 char injDateTime[32]; injDateTime[0]=(char)0;
154 if(decayCorrection==2) {
155 /* Get the Tracer Start DateTime */
156 tag.group=0x0018; tag.element=0x1078;
157 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
158 if(iptr!=NULL) {
159 if(dcmDT2intl(iptr->rd, injDateTime)==NULL) iptr=NULL;
160 else if(verbose>3) printf("0018,1078 -> %s\n", injDateTime);
161 }
162 /* If not successful, the try other tags */
163 if(iptr==NULL) {
164 char s1[16], s2[16];
165 /* Get the Tracer Start Time */
166 tag.group=0x0018; tag.element=0x1072;
167 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
168 if(iptr!=NULL) {
169 /* Get the Series Date */
170 tag.group=0x0008; tag.element=0x0021;
171 DCMITEM *jptr=dcmFindTag(dcm.item, 0, &tag, 0);
172 if(jptr!=NULL) {
173 if(dcmDA2intl(jptr->rd, s1)!=NULL && dcmTM2intl(iptr->rd, s2)!=NULL)
174 sprintf(injDateTime, "%s %s", s1, s2);
175 }
176 }
177 }
178 /* Check that we got injection date and time, since we will need it */
179 if(!injDateTime[0]) {
180 fprintf(stderr, "Error: missing Tracer Administration Time.\n");
181 iftFree(&fl); dcmfileFree(&dcm);
182 return(4);
183 }
184 if(verbose>1) printf("injection_time := %s\n", injDateTime);
185 }
186
187 /* Get the Series DateTime
188 https://dicom.nema.org/MEDICAL/dicom/current/output/chtml/part03/sect_C.8.9.html#figure_C.8.9.1.1.11-1a
189 */
190 char seriesDateTime[32]; seriesDateTime[0]=(char)0;
191 tag.group=0x0008; tag.element=0x0021;
192 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
193 if(iptr!=NULL) {
194 tag.group=0x0008; tag.element=0x0031;
195 DCMITEM *jptr=dcmFindTag(dcm.item, 0, &tag, 0);
196 if(jptr!=NULL) {
197 char s1[16], s2[16];
198 if(dcmDA2intl(iptr->rd, s1)!=NULL && dcmTM2intl(jptr->rd, s2)!=NULL)
199 sprintf(seriesDateTime, "%s %s", s1, s2);
200 }
201 }
202 if(!seriesDateTime[0]) {
203 fprintf(stderr, "Error: missing Series Date and Time.\n");
204 iftFree(&fl); dcmfileFree(&dcm); return(5);
205 } else {
206 if(verbose>2) printf("seriesDateTime := %s\n", seriesDateTime);
207 }
208
209
210 /* Get and check the series type */
211 tag.group=0x0054; tag.element=0x1000;
212 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
213 if(iptr==NULL) {
214 if(verbose>1) fprintf(stderr, "Warning: cannot find series type.\n");
215 } else {
216 char *buf=dcmValueString(iptr);
217 if(verbose>1) printf("seriesType := %s\n", buf);
218 /* Check that data is either dynamic or static */
219 if(strncasecmp(buf, "DYNAMIC", 7) && strncasecmp(buf, "STATIC", 6)) {
220 fprintf(stderr, "Error: data series type '%s' not supported.\n", buf);
221 free(buf);
222 iftFree(&fl); dcmfileFree(&dcm); return(5);
223 }
224 free(buf);
225 }
226
227
228 /* Get and check the Image Type */
229 int dynamic=0; // 0=no, 1=yes
230 tag.group=0x0008; tag.element=0x0008;
231 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
232 if(iptr==NULL) {
233 fprintf(stderr, "Error: cannot find image type.\n");
234 iftFree(&fl); dcmfileFree(&dcm); return(5);
235 }
236 /* Check if dynamic; see DICOM 3.3 C.7.6.16.2.2.6
237 https://dicom.nema.org/MEDICAL/dicom/current/output/chtml/part03/sect_C.7.6.16.2.html
238 */
239 {
240 char *buf=dcmValueString(iptr);
241 if(verbose>1) printf("imageType := %s\n", buf);
242 if(strcasestr(buf, "DYNAMIC")!=NULL) dynamic=1; else dynamic=0;
243 free(buf);
244 }
245 if(verbose>1) {printf("dynamic := %d\n", dynamic); fflush(stdout);}
246
247
248 /* Check whether Per-frame Functional Groups Sequence is set */
249 int multiframe=0; // 0=no, 1=yes
250 tag.group=0x5200; tag.element=0x9230;
251 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
252 if(iptr!=NULL) {
253 if(verbose>1) fprintf(stderr, "Notice: Per-frame Functional Groups Sequence is available.\n");
254 multiframe=1;
255 }
256
257
258 /* Get the number of frames (time slices) */
259 if(verbose>1) {printf("reading frame number\n"); fflush(stdout);}
260 unsigned short int frameNr=0;
261 tag.group=0x0054; tag.element=0x0101;
262 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
263 if(iptr!=NULL) {
264 frameNr=(unsigned short int)dcmitemGetInt(iptr);
265 } else {
266 /* Get it from 'Temporal position index' */
267 tag.group=0x0020; tag.element=0x9128;
268 int a, b;
269 if(dcmTagIntRange(dcm.item, &tag, &a, &b, verbose-100)==0) frameNr=b;
270 }
271 /* Check that frame number > 0 if dynamic */
272 if(frameNr==0) {
273 if(dynamic!=0) { // error, if dynamic
274 fprintf(stderr, "Error: cannot find frame number.\n");
275 iftFree(&fl); dcmfileFree(&dcm); return(4);
276 } else { // assume 1, if static or whole-body
277 frameNr=1;
278 }
279 }
280 if(verbose>1) printf("frameNr := %u\n", frameNr);
281
282
283
284 /* Get the number of slices (needed to understand image index) */
285 if(verbose>1) {printf("reading plane number\n"); fflush(stdout);}
286 unsigned short int sliceNr=0;
287 tag.group=0x0054; tag.element=0x0081;
288 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
289 if(iptr!=NULL) {
290 sliceNr=(unsigned short int)dcmitemGetInt(iptr);
291 } else {
292 /* Get it from 'In Stack Position Number' */
293 tag.group=0x0020; tag.element=0x9057;
294 int a, b;
295 if(dcmTagIntRange(dcm.item, &tag, &a, &b, verbose-100)==0) sliceNr=b;
296 else {
297 /* Get it from 'Dimension Index Values' */
298 tag.group=0x0020; tag.element=0x9157;
299 if(dcmTagIntRange(dcm.item, &tag, &a, &b, verbose-1)==0) sliceNr=b;
300 }
301 }
302 if(sliceNr<=0) {
303 fprintf(stderr, "Error: cannot find slice number.\n");
304 iftFree(&fl); dcmfileFree(&dcm); return(4);
305 }
306 if(verbose>2) printf("sliceNr := %u\n", sliceNr);
307
308
309 /* Get the isotope */
310 if(verbose>1) {printf("reading isotope\n"); fflush(stdout);}
311 int isotope_code=ISOTOPE_UNKNOWN;
312 tag.group=0x0018; tag.element=0x1075;
313 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
314 if(iptr!=NULL) {
315 double halflife=dcmitemGetReal(iptr);
316 if(verbose>2) printf("isotope_halflife := %g\n", halflife);
317 isotope_code=isotopeIdentifyHalflife(halflife/60.0);
318 }
319 if(isotope_code==ISOTOPE_UNKNOWN && verbose>0) {
320 fprintf(stderr, "Warning: isotope not identified.\n");
321 }
322
323
324 /* Get accession (study) number */
325 if(verbose>1) {printf("reading accession number\n"); fflush(stdout);}
326 char studyNr[17]; studyNr[0]=(char)0;
327 tag.group=0x0008; tag.element=0x0050;
328 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
329 if(iptr!=NULL) {
330 strlcpy(studyNr, iptr->rd, 17);
331 strClean(studyNr);
332 strReplaceChar(studyNr, ' ', '_');
333 }
334 if(strlen(studyNr)==0) {
335 if(verbose>1) {printf("reading series description\n"); fflush(stdout);}
336 tag.group=0x0008; tag.element=0x0103e;
337 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
338 if(iptr!=NULL) {
339 strlcpy(studyNr, iptr->rd, 17);
340 strClean(studyNr);
341 char *cptr=strrchr(studyNr, ' '); if(cptr!=NULL) *cptr=(char)0;
342 cptr=strrchr(studyNr, '/'); if(cptr!=NULL) *cptr=(char)0;
343 }
344 }
345 if(verbose>1) printf("studyNr := %s\n", studyNr);
346
347
348
349 /*
350 * Allocate memory for frame time data
351 */
352 if(verbose>1) {printf("allocating memory for frame times\n"); fflush(stdout);}
353 TAC tac; tacInit(&tac);
354 ret=tacAllocate(&tac, frameNr, 3);
355 if(ret!=TPCERROR_OK) {
356 fprintf(stderr, "Error: %s\n", errorMsg(ret));
357 iftFree(&fl); dcmfileFree(&dcm);
358 return(5);
359 }
361 tac.sampleNr=frameNr;
362 tac.isframe=1;
363 tac.tunit=UNIT_SEC;
364 tac.cunit=UNIT_COUNTS;
365 tac.tacNr=2;
366 /* Set SIF compatible titles into TAC struct */
367 strcpy(tac.c[0].name, "Prompts");
368 strcpy(tac.c[1].name, "Randoms");
369 strcpy(tac.c[2].name, "Trues"); // computed, not saved
370 /* Isotope */
371 tacSetHeaderIsotope(&tac.h, isotopeName(isotope_code));
372 /* Studynumber */
373 if(studyNr[0]) tacSetHeaderStudynr(&tac.h, studyNr);
374 /* Set frame times to NaN */
375 for(int fi=0; fi<frameNr; fi++) tac.x1[fi]=tac.x2[fi]=tac.x[fi]=nan("");
376
377
378 /*
379 * Get frame information from one file at a time
380 */
381 if(verbose>1) {printf("reading frame information\n"); fflush(stdout);}
382 char startDateTime[32]; startDateTime[0]=(char)0;
383 int fi=0, beds=0;
384
385 if(multiframe==0) {
386 /*
387 * Get frame information from one file at a time
388 */
389 if(verbose>1) {printf("frames is separate files\n"); fflush(stdout);}
390 while(1) {
391
392 /* Get the Image Index; DICOM PS3.3 C.8.9.4.1.9 */
393 unsigned short int imageIndex=0;
394 tag.group=0x0054; tag.element=0x1330;
395 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
396 if(iptr==NULL) {
397 fprintf(stderr, "Error: cannot find Image Index.\n");
398 tacFree(&tac); iftFree(&fl); dcmfileFree(&dcm);
399 return(6);
400 }
401 imageIndex=(unsigned short int)dcmitemGetInt(iptr);
402 /* Calculate the current frame and slice (index starting from 0) */
403 unsigned short int frameIndex, sliceIndex;
404 {
405 div_t meh=div(imageIndex-1, sliceNr);
406 frameIndex=meh.quot;
407 sliceIndex=meh.rem;
408 }
409 if(verbose>3) {
410 printf("imageIndex := %u\n", imageIndex);
411 printf("frameIndex := %u\n", frameIndex);
412 printf("sliceIndex := %u\n", sliceIndex);
413 }
414
415 /* If dynamic image, then read certain tags, just for fun */
416 if(dynamic && verbose>5) {
417 tag.group=0x0020; tag.element=0x9128;
418 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
419 if(iptr!=0) {
420 char *buf=dcmValueString(iptr);
421 /*if(verbose>6)*/ printf("Temporal Position Index := %s\n", buf);
422 free(buf);
423 }
424 tag.group=0x0020; tag.element=0x9056;
425 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
426 if(iptr!=0) {
427 char *buf=dcmValueString(iptr);
428 /*if(verbose>6)*/ printf("Stack ID := %s\n", buf);
429 free(buf);
430 }
431 tag.group=0x0020; tag.element=0x9057;
432 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
433 if(iptr!=0) {
434 char *buf=dcmValueString(iptr);
435 /*if(verbose>6)*/ printf("In-Stack Position Number := %s\n", buf);
436 free(buf);
437 }
438 }
439
440 /* Get Frame Reference Time, currently just for fun */
441 tag.group=0x0054; tag.element=0x1300;
442 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
443 if(iptr!=NULL) {
444 char *buf=dcmValueString(iptr);
445 if(verbose>5) printf("Frame Reference Time := %s\n", buf);
446 free(buf);
447 }
448
449
450 /* Get acquisition date and time */
451 char acqDateTime[32]; acqDateTime[0]=(char)0;
452 /* Try first Acquisition Datetime */
453 tag.group=0x0008; tag.element=0x002A;
454 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
455 if(iptr!=NULL) dcmDT2intl(iptr->rd, acqDateTime);
456 /* If that did not work, try Acquisition Date and Time */
457 if(!acqDateTime[0]) {
458 tag.group=0x0008; tag.element=0x0022;
459 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
460 if(iptr!=NULL) {
461 tag.group=0x0008; tag.element=0x0032;
462 DCMITEM *jptr=dcmFindTag(dcm.item, 0, &tag, 0);
463 if(jptr!=NULL) {
464 char s1[16], s2[16];
465 if(dcmDA2intl(iptr->rd, s1)!=NULL && dcmTM2intl(jptr->rd, s2)!=NULL)
466 sprintf(acqDateTime, "%s %s", s1, s2);
467 }
468 }
469 }
470 if(!acqDateTime[0]) {
471 fprintf(stderr, "Error: missing Acquisition Date and Time.\n");
472 tacFree(&tac); iftFree(&fl); dcmfileFree(&dcm);
473 return(6);
474 }
475 if(verbose>4) printf("acqDateTime := %s\n", acqDateTime);
476 /* If this is the first frame, then set the start time of the whole image */
477 if(frameIndex==0) strcpy(startDateTime, acqDateTime);
478
479 /* Calculate the Frame start time */
480 double t1;
481 if(decayCorrection==2) {
482 /* Injection time is the reference time */
483 t1=strDateTimeDifference(acqDateTime, injDateTime);
484 } else {
485 /* Use series time, for now */
486 t1=strDateTimeDifference(acqDateTime, seriesDateTime);
487 }
488 /* Set frame start time */
489 if(frameIndex<frameNr) {
490 if(isnan(tac.x1[frameIndex])) {
491 /* Save frame start time, since there is no previous value for this frame */
492 tac.x1[frameIndex]=t1;
493 if(verbose>3) printf("t1[%d]=%g at sliceIndex := %u\n", frameIndex, t1, sliceIndex);
494 } else {
495 /* Check whether previous frame start time is the same as current */
496 if(!doubleMatch(tac.x1[frameIndex], t1, 0.001)) {
497 beds=1; // not the same, this probably is whole-body study with more than one bed pos
498 if(verbose>3) printf("t1=%g at sliceIndex := %u\n", t1, sliceIndex);
499 }
500 }
501 }
502
503 /* Get Actual Frame Duration */
504 double fdur=0.0;
505 tag.group=0x0018; tag.element=0x1242;
506 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
507 if(iptr!=NULL) fdur=dcmitemGetReal(iptr);
508 if(verbose>3) printf("actualFrameDuration := %g\n", fdur);
509 /* For now, put frame duration in place of frame end time */
510 if(frameIndex<frameNr) tac.x2[frameIndex]=0.001*fdur;
511
512 /* Try to find prompts and Randoms */
513 double prompts=0.0, randoms=0.0;
514 /* total prompts */
515 tag.group=0x0054; tag.element=0x1310;
516 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
517 if(iptr!=NULL) prompts=dcmitemGetReal(iptr);
518 if(prompts<1.0E-03) {
519 tag.group=0x0009; tag.element=0x1071;
520 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
521 if(iptr!=NULL) prompts=dcmitemGetReal(iptr);
522 }
523 if(prompts<1.0E-03) {
524 tag.group=0x0009; tag.element=0x10A7;
525 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
526 if(iptr!=NULL) prompts=dcmitemGetReal(iptr);
527 }
528 tac.c[0].y[frameIndex]=prompts;
529 /* randoms, delayed */
530 tag.group=0x0054; tag.element=0x1311;
531 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
532 if(iptr!=NULL) randoms=dcmitemGetReal(iptr);
533 if(randoms<1.0E-03) {
534 tag.group=0x0009; tag.element=0x1072;
535 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
536 if(iptr!=NULL) randoms=dcmitemGetReal(iptr);
537 }
538 if(randoms<1.0E-03) {
539 tag.group=0x0009; tag.element=0x1022;
540 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
541 if(iptr!=NULL) randoms=dcmitemGetReal(iptr);
542 }
543 tac.c[1].y[frameIndex]=randoms;
544
545 /* Load the next file */
546 dcmfileFree(&dcm);
547 fi++; if(fi==fl.keyNr) break;
548 ret=dcmFileRead(fl.item[fi].value, &dcm, 1, &status);
549 if(ret!=TPCERROR_OK) {
550 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
551 tacFree(&tac); iftFree(&fl); dcmfileFree(&dcm); return(7);
552 }
553
554 } // process next file
555 iftFree(&fl);
556 dcmfileFree(&dcm);
557
558 if(verbose>0 && beds!=0) {
559 fprintf(stdout, "Note: this seems to be whole-body study\n");
560 }
561
562 } else {
563 /*
564 * Get frame information from the single multi-frame file
565 */
566 if(verbose>1) {printf("multi-frame file\n"); fflush(stdout);}
567
568 /* Get Acquisition DateTime */
569 char acqDateTime[32]; acqDateTime[0]=(char)0;
570 tag.group=0x0008; tag.element=0x002A;
571 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
572 if(iptr!=NULL) dcmDT2intl(iptr->rd, acqDateTime);
573 if(!acqDateTime[0]) {
574 fprintf(stderr, "Error: missing Acquisition Date and Time.\n");
575 tacFree(&tac); iftFree(&fl); dcmfileFree(&dcm);
576 return(6);
577 }
578 if(verbose>4) printf("acqDateTime := %s\n", acqDateTime);
579 /* Set the start time of the whole image */
580 strcpy(startDateTime, acqDateTime);
581
582 /* Get Decay Correction DateTime */
583 char dcDateTime[32]; dcDateTime[0]=(char)0;
584 tag.group=0x0018; tag.element=0x9701;
585 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
586 /* Required, if data is decay corrected */
587 if(iptr!=NULL) dcmDT2intl(iptr->rd, dcDateTime);
588 if(decayCorrection>0 && !dcDateTime[0]) {
589 fprintf(stderr, "Error: missing Decay Correction DateTime.\n");
590 tacFree(&tac); iftFree(&fl); dcmfileFree(&dcm);
591 return(6);
592 }
593 if(dcDateTime[0] && verbose>4) printf("dcDateTime := %s\n", dcDateTime);
594
595 /* Find Per Frame Functional Groups Sequence */
596 tag.group=0x5200; tag.element=0x9230;
597 iptr=dcmFindTag(dcm.item, 0, &tag, 0);
598 if(iptr==NULL) {
599 fprintf(stderr, "Error: Per Frame Functional Groups Sequence not found.\n");
600 tacFree(&tac); iftFree(&fl); dcmfileFree(&dcm); return(7);
601 }
602 /* Loop through all Frame Content Sequences under it */
603 tag.group=0x0020; tag.element=0x9111;
604 DCMTAG tagstart; tagstart.group=0x0018; tagstart.element=0x9074;
605 DCMTAG tagdur; tagdur.group=0x0018; tagdur.element=0x9220;
606 DCMTAG tagfr; tagfr.group=0x0020; tagfr.element=0x9128;
607 DCMITEM *fptr=iptr->child_item;
608 while(fptr!=NULL) {
609 /* Find Frame Content Sequence */
610 iptr=dcmFindDownTag(fptr, 0, &tag, 0); if(iptr==NULL) break;
611 if(verbose>10) printf(" found Frame Content Sequence\n");
612 DCMITEM *jptr;
613 /* Find Temporal Position Index */
614 jptr=dcmFindDownTag(iptr->child_item, 0, &tagfr, 0);
615 if(jptr==NULL) {fptr=fptr->next_item; continue;}
616 if(verbose>20) dcmitemPrint(jptr);
617 unsigned short int frameIndex=(unsigned short int)(dcmitemGetInt(jptr)-1);
618 /* Find Frame Acquisition DateTime */
619 jptr=dcmFindDownTag(iptr->child_item, 0, &tagstart, 0);
620 char facqDateTime[32]; facqDateTime[0]=(char)0;
621 if(jptr!=NULL) dcmDT2intl(jptr->rd, facqDateTime);
622 if(!facqDateTime[0]) {
623 fprintf(stderr, "Error: missing Frame Acquisition DateTime.\n");
624 tacFree(&tac); iftFree(&fl); dcmfileFree(&dcm);
625 return(6);
626 }
627 if(verbose>20) printf("facqDateTime := %s\n", facqDateTime);
628 /* Calculate the Frame start time */
629 double t1;
630 if(decayCorrection>0) {
631 /* Decay Correction DatTime is the reference time */
632 t1=strDateTimeDifference(facqDateTime, dcDateTime);
633 } else {
634 /* Use series time, for now */
635 t1=strDateTimeDifference(facqDateTime, acqDateTime);
636 }
637 /* Set frame start time */
638 if(frameIndex<frameNr) {
639 if(isnan(tac.x1[frameIndex])) {
640 /* Save frame start time, since there is no previous value for this frame */
641 tac.x1[frameIndex]=t1;
642 if(verbose>29) printf("t1[%d]=%g\n", frameIndex, t1);
643 } else {
644 /* Check whether previous frame start time is the same as current */
645 if(!doubleMatch(tac.x1[frameIndex], t1, 0.001)) {
646 beds=1; // not the same, this probably is whole-body study with more than one bed pos
647 if(verbose>20) printf("t1=%g\n", t1);
648 }
649 }
650 }
651
652 /* Find Frame Acquisition Duration */
653 jptr=dcmFindDownTag(iptr->child_item, 0, &tagdur, 0);
654 if(jptr==NULL) {fptr=fptr->next_item; continue;}
655 if(verbose>20) dcmitemPrint(jptr);
656 /* For now, put frame duration in place of frame end time */
657 if(frameIndex<frameNr) tac.x2[frameIndex]=0.001*dcmitemGetReal(jptr);
658 /* prepare for the next frame */
659 fptr=iptr->next_item;
660 }
661 }
662
663
664
665 /* Set scan start time */
666 if(decayCorrection==2) {
667 /* Decay is corrected to injection time, therefore put it here too */
668 tacSetHeaderScanstarttime(&tac.h, injDateTime);
669 } else {
670 /* Decay is not corrected, or is corrected to acquisition start time */
671 tacSetHeaderScanstarttime(&tac.h, startDateTime);
672 /* Correct frame start times, too */
673 for(int i=0; i<tac.sampleNr; i++)
674 tac.x1[i]=tac.x1[i]-strDateTimeDifference(startDateTime, seriesDateTime);
675 }
676
677 /* Calculate frame end time */
678 for(int i=0; i<tac.sampleNr; i++) tac.x2[i]+=tac.x1[i];
679
680
681 /*
682 * Write SIF data stored in TAC struct in SIF format
683 */
684 FILE *fp=stdout;
685 if(siffile[0]) {
686 if(verbose>1) printf("writing %s\n", siffile);
687 fp=fopen(siffile, "w");
688 if(fp==NULL) {
689 fprintf(stderr, "Error: cannot open file for writing.\n");
690 tacFree(&tac); return(11);
691 }
692 }
693 ret=tacWriteSIF(&tac, fp, 0, &status);
694 tacFree(&tac); if(siffile[0]) fclose(fp);
695 if(ret!=TPCERROR_OK) {
696 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
697 return(11);
698 }
699
700 return(0);
701}
702/*****************************************************************************/
703
704/*****************************************************************************/
double strDateTimeDifference(const char *dt1, const char *dt0)
Definition datetime.c:519
long int dcmitemGetInt(DCMITEM *d)
Definition dcmdata.c:295
void dcmfileInit(DCMFILE *d)
Definition dcmdata.c:22
DCMITEM * dcmFindTag(DCMITEM *d, const short int omit, DCMTAG *tag, const int verbose)
Definition dcmdata.c:375
DCMITEM * dcmFindDownTag(DCMITEM *d, const short int omit, DCMTAG *tag, const int verbose)
Definition dcmdata.c:428
void dcmfileFree(DCMFILE *d)
Definition dcmdata.c:67
int dcmTagIntRange(DCMITEM *d, DCMTAG *tag, int *mi, int *ma, const int verbose)
Definition dcmdata.c:631
void dcmitemPrint(DCMITEM *d)
Definition dcmdata.c:467
double dcmitemGetReal(DCMITEM *d)
Definition dcmdata.c:331
char * dcmValueString(DCMITEM *d)
Definition dcmdata.c:141
int dcmFileList(const char *filename, IFT *ift, TPCSTATUS *status)
List DICOM files belonging to one image.
Definition dcmfile.c:123
int dcmFileRead(const char *filename, DCMFILE *dcm, const short int headerOnly, TPCSTATUS *status)
Definition dcmio.c:768
char * dcmDT2intl(const char *orig, char *intl)
Definition dcmvr.c:225
char * dcmDA2intl(const char *orig, char *intl)
Definition dcmvr.c:179
char * dcmTM2intl(const char *orig, char *intl)
Definition dcmvr.c:202
int doubleMatch(const double v1, const double v2, const double lim)
Definition doubleutil.c:27
void iftFree(IFT *ift)
Definition ift.c:37
void iftInit(IFT *ift)
Definition ift.c:21
int iftWrite(IFT *ift, FILE *fp, TPCSTATUS *status)
Definition iftio.c:98
char * isotopeName(int isotope_code)
Definition isotope.c:101
int isotopeIdentifyHalflife(double halflife)
Definition isotope.c:121
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:47
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:169
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:339
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:114
int tacWriteSIF(TAC *tac, FILE *fp, int extra, TPCSTATUS *status)
Definition sifio.c:26
void statusInit(TPCSTATUS *s)
Definition statusmsg.c:104
char * errorMsg(tpcerror e)
Definition statusmsg.c:68
void statusSet(TPCSTATUS *s, const char *func, const char *srcfile, int srcline, tpcerror error)
Definition statusmsg.c:142
void strReplaceChar(char *s, char c1, char c2)
Definition stringext.c:134
size_t strlcpy(char *dst, const char *src, size_t dstsize)
Definition stringext.c:632
int strClean(char *s)
Definition stringext.c:389
char * strcasestr(const char *haystack, const char *needle)
Definition stringext.c:155
DCMITEM * item
Definition tpcdcm.h:164
struct DCMITEM * child_item
Definition tpcdcm.h:143
struct DCMITEM * next_item
Definition tpcdcm.h:147
char * rd
Definition tpcdcm.h:152
unsigned short int element
Definition tpcdcm.h:44
unsigned short int group
Definition tpcdcm.h:42
char * value
Definition tpcift.h:37
Definition tpcift.h:43
IFT_ITEM * item
Definition tpcift.h:57
int keyNr
Definition tpcift.h:47
char name[MAX_TACNAME_LEN+1]
Definition tpctac.h:81
double * y
Definition tpctac.h:75
Definition tpctac.h:87
double * x
Definition tpctac.h:97
unit cunit
Definition tpctac.h:105
tacformat format
Definition tpctac.h:93
int sampleNr
Definition tpctac.h:89
IFT h
Optional (but often useful) header information.
Definition tpctac.h:141
int isframe
Definition tpctac.h:95
TACC * c
Definition tpctac.h:117
double * x2
Definition tpctac.h:101
unit tunit
Definition tpctac.h:109
double * x1
Definition tpctac.h:99
int tacNr
Definition tpctac.h:91
int verbose
Verbose level, used by statusPrint() etc.
tpcerror error
Error code.
void tacFree(TAC *tac)
Definition tac.c:106
int tacAllocate(TAC *tac, int sampleNr, int tacNr)
Definition tac.c:130
void tacInit(TAC *tac)
Definition tac.c:24
int tacSetHeaderScanstarttime(IFT *h, const char *s)
Definition tacift.c:427
int tacSetHeaderIsotope(IFT *h, const char *s)
Definition tacift.c:341
int tacSetHeaderStudynr(IFT *h, const char *s)
Definition tacift.c:79
Header file for libtpcdcm.
Header file for library libtpcextensions.
@ UNIT_COUNTS
counts
@ UNIT_SEC
seconds
@ TPCERROR_OK
No error.
@ ISOTOPE_UNKNOWN
Unknown.
Definition tpcisotope.h:51
Header file for library libtpctac.
@ TAC_FORMAT_SIF
Scan information file.
Definition tpctac.h:43