TPCCLIB
Loading...
Searching...
No Matches
simframe.c
Go to the documentation of this file.
1
8/*****************************************************************************/
9#include "tpcclibConfig.h"
10/*****************************************************************************/
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <math.h>
15/*****************************************************************************/
16#include "tpcextensions.h"
17#include "tpcift.h"
18#include "tpccsv.h"
19#include "tpctac.h"
20#include "tpcisotope.h"
21#include "tpcli.h"
22/*****************************************************************************/
23
24/*****************************************************************************/
25static char *info[] = {
26 "PET TACs are simulated with short sample time intervals.",
27 "This program sums up those points within specified time frames to simulate",
28 "a measured PET tissue uptake curve.",
29 " ",
30 "Usage: @P [options] tacfile framefile newfile [isotope]",
31 " ",
32 "Options:",
33 " -sec or -min",
34 " Sample times are known to be in seconds or minutes, but is not",
35 " specified or is wrong in TAC file.",
36 " -mid",
37 " Frame mid time is written in output file instead of start and end times.",
38 " -i Calculates integrals at frame mid times, instead of frame averages;",
39 " not available with [isotope].",
40 " -ii Calculates 2nd integrals at frame mid times, instead of frame averages;",
41 " not available with [isotope].",
42 " --force",
43 " Program allows extensive extrapolation.",
44 " -stdoptions", // List standard options like --help, -v, etc
45 " ",
46 "TAC file can contain more than one TAC.",
47 "Time frames data can be in SIF or TAC format. Alternatively, file can consist",
48 "of one or two columns of data, containing either 1) frame durations, or",
49 "2) frame start times and frame durations; frame time units must be same",
50 "as in the datafile. Frames are allowed to overlap.",
51 " ",
52 "If the isotope is specified, the correction for physical decay is at first",
53 "removed, then PET framing is simulated, and after that the framed data",
54 "is decay corrected again, based on the frame start time and length as is",
55 "the normal procedure when collecting PET image data.",
56 "Verify that time units are correct when using this possibility.",
57 " ",
58 "See also: interpol, tacframe, tac4frpl, tactime, fit2dat, sim_3tcm",
59 " ",
60 "Keywords: TAC, SIF, simulation, interpolation, time frame",
61 0};
62/*****************************************************************************/
63
64/*****************************************************************************/
65/* Turn on the globbing of the command line, since it is disabled by default in
66 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
67 In Unix&Linux wildcard command line processing is enabled by default. */
68/*
69#undef _CRT_glob
70#define _CRT_glob -1
71*/
72int _dowildcard = -1;
73/*****************************************************************************/
74
75/*****************************************************************************/
79int main(int argc, char **argv)
80{
81 int ai, help=0, version=0, verbose=1;
82 unit knownTimeunit=UNIT_UNKNOWN;
84 int mode=0; // 0=mean, 1=integral, 2=2nd integral
85 int forceMode=0; // 0=off, 1=on
86 int midFrame=0; // save with mid frame times (1) or not (0)
87 char datfile[FILENAME_MAX], frafile[FILENAME_MAX], outfile[FILENAME_MAX];
88
89
90
91
92 /*
93 * Get arguments
94 */
95 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
96 datfile[0]=frafile[0]=outfile[0]=(char)0;
97 /* Options */
98 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
99 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
100 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(!*cptr) continue;
101 if(strcasecmp(cptr, "C")==0 || strcasecmp(cptr, "Y")==0) {
102 mode=0; continue;
103 } else if(strcasecmp(cptr, "I")==0) {
104 mode=1; continue;
105 } else if(strcasecmp(cptr, "II")==0) {
106 mode=2; continue;
107 } else if(strncasecmp(cptr, "MINUTES", 3)==0) {
108 knownTimeunit=UNIT_MIN; continue;
109 } else if(strncasecmp(cptr, "SECONDS", 3)==0) {
110 knownTimeunit=UNIT_SEC; continue;
111 } else if(strcasecmp(cptr, "MID")==0) {
112 midFrame=1; continue;
113 } else if(strcasecmp(cptr, "F")==0 || strcasecmp(cptr, "FORCE")==0) {
114 forceMode=1; continue;
115 }
116 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
117 return(1);
118 } else break; // tac name argument may start with '-'
119
120 TPCSTATUS status; statusInit(&status);
121 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
122 status.verbose=verbose-1;
123
124 /* Print help or version? */
125 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
126 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
127 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
128
129 /* Process other arguments, starting from the first non-option */
130 if(ai<argc) strlcpy(datfile, argv[ai++], FILENAME_MAX);
131 if(ai<argc) strlcpy(frafile, argv[ai++], FILENAME_MAX);
132 if(ai<argc) strlcpy(outfile, argv[ai++], FILENAME_MAX);
133 if(ai<argc) {
134 if((isot=isotopeIdentify(argv[ai]))==ISOTOPE_UNKNOWN) {
135 fprintf(stderr, "Error: invalid isotope argument '%s'.\n", argv[ai]);
136 return(1);
137 }
138 if(mode>0) {
139 fprintf(stderr, "Error: decay option cannot be used with integrals.\n");
140 return(1);
141 }
142 ai++;
143 }
144 if(ai<argc) {
145 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
146 return(1);
147 }
148
149 /* Is something missing? */
150 if(!outfile[0]) {
151 fprintf(stderr, "Error: missing file name; use option --help\n");
152 return(1);
153 }
154
155 /* In verbose mode print arguments and options */
156 if(verbose>1) {
157 printf("datfile := %s\n", datfile);
158 printf("frafile := %s\n", frafile);
159 printf("outfile := %s\n", outfile);
160 printf("mode := %d\n", mode);
161 printf("forceMode := %d\n", forceMode);
162 if(knownTimeunit!=UNIT_UNKNOWN) printf("knownTimeunit := %s\n", unitName(knownTimeunit));
163 printf("midFrame := %d\n", midFrame);
164 if(isot!=ISOTOPE_UNKNOWN) printf("isotope := %s\n", isotopeName(isot));
165 fflush(stdout);
166 }
167
168
169 /*
170 * Read TAC data
171 */
172 if(verbose>1) {fprintf(stdout, "reading %s\n", datfile); fflush(stdout);}
173 TAC tac; tacInit(&tac);
174 if(tacRead(&tac, datfile, &status)!=TPCERROR_OK) {
175 fprintf(stderr, "Error: %s (%s)\n", errorMsg(status.error), datfile);
176 tacFree(&tac); return(2);
177 }
178 if(verbose>2) {
179 printf("fileformat := %s\n", tacFormattxt(tac.format));
180 printf("tacNr := %d\n", tac.tacNr);
181 printf("sampleNr := %d\n", tac.sampleNr);
182 if(tac.isframe) printf("frames := yes\n"); else printf("frames := no\n");
183 fflush(stdout);
184 }
186 if(tacSortByTime(&tac, &status)!=TPCERROR_OK) {
187 fprintf(stderr, "Error: invalid sample times.\n");
188 tacFree(&tac); return(2);
189 }
190 /* Correct the time unit, if given by user */
191 if(knownTimeunit!=UNIT_UNKNOWN) {
192 tac.tunit=knownTimeunit;
193 } else if(!unitIsTime(tac.tunit)) {
194 double xmin, xmax;
195 if(tacXRange(&tac, &xmin, &xmax)) {
196 fprintf(stderr, "Error: invalid sample times.\n");
197 tacFree(&tac); return(2);
198 }
199 if(xmax>30.0 && lambdaFromIsotope(isot)>0.3) {
200 tac.tunit=UNIT_SEC;
201 fprintf(stderr, "Warning: assuming that sample times are in seconds.\n");
202 } else {
203 tac.tunit=UNIT_MIN;
204 fprintf(stderr, "Warning: assuming that sample times are in minutes.\n");
205 }
206 }
207 if(verbose>2) {
208 printf("xunit := %s\n", unitName(tac.tunit));
209 printf("yunit := %s\n", unitName(tac.cunit));
210 fflush(stdout);
211 }
212
213
214 /* If isotope was given, remove decay correction */
215 if(isot!=ISOTOPE_UNKNOWN) {
216 if(verbose>1) printf("removing decay correction.\n");
217 if(tacDecayCorrection(&tac, isot, 0, &status)!=TPCERROR_OK) {
218 fprintf(stderr, "Error: %s (%s)\n", errorMsg(status.error), datfile);
219 tacFree(&tac); return(2);
220 }
221 }
222
223
224 /*
225 * Read frame data
226 */
227 if(verbose>1) printf("reading frame data in %s\n", frafile);
228 TAC sif; tacInit(&sif);
229 {
230 /* Open the file */
231 FILE *fp;
232 fp=fopen(frafile, "r");
233 if(fp==NULL) {
234 fprintf(stderr, "Error: cannot open %s\n", frafile);
235 tacFree(&tac); return(3);
236 }
237 /* Read contents into CSV structure */
238 CSV csv; csvInit(&csv);
239 if(csvRead(&csv, fp, &status)!=TPCERROR_OK) {
240 fprintf(stderr, "Error: %s (%s)\n", errorMsg(status.error), frafile);
241 tacFree(&tac); csvFree(&csv); fclose(fp); return(3);
242 }
243 fclose(fp);
244 /* Try to read as SIF */
245 if(tacReadSIF(&sif, &csv, NULL, &status)==TPCERROR_OK) {
246 if(verbose>2) printf("frame times from SIF\n");
247 csvFree(&csv);
248 } else {
249 if(verbose>2) printf("not SIF, trying to read as TAC file.\n");
250 csvFree(&csv);
251 if(tacRead(&sif, frafile, &status)!=TPCERROR_OK) {
252 fprintf(stderr, "Error: %s (%s)\n", errorMsg(status.error), frafile);
253 tacFree(&tac); return(3);
254 }
255 if(verbose>2) {
256 printf("fileformat := %s\n", tacFormattxt(sif.format));
257 printf("tacNr := %d\n", sif.tacNr);
258 printf("sampleNr := %d\n", sif.sampleNr);
259 printf("xunit := %s\n", unitName(sif.tunit));
260 printf("isframe := %d\n", sif.isframe);
261 if(verbose>3) tacWrite(&sif, stdout, TAC_FORMAT_TSV_UK, 1, NULL);
262 fflush(stdout);
263 }
264
265 if(sif.tacNr>0 && sif.isframe) {
266 if(verbose>2) printf("seems to be valid TAC file\n");
267 } else if(sif.isframe==0 && (sif.format==TAC_FORMAT_PMOD || sif.format==TAC_FORMAT_DFT)) {
268 fprintf(stderr, "Error: no frame start and end times in %s\n", frafile);
269 tacFree(&tac); tacFree(&sif); return(3);
270 } else if(sif.tacNr==1 && sif.isframe==0 && tacYNaNs(&sif, 0)==sif.sampleNr) {
271 if(verbose>2) printf("just one column containing frame lengths\n");
272 sif.x1[0]=0.0; sif.x2[0]=sif.x[0]; sif.x[0]=0.5*(sif.x1[0]+sif.x2[0]);
273 for(int i=1; i<sif.sampleNr; i++) {
274 sif.x1[i]=sif.x2[i-1]; sif.x2[i]=sif.x1[i]+sif.x[i]; sif.x[i]=0.5*(sif.x1[i]+sif.x2[i]);
275 }
276 sif.isframe=1;
277 } else if(sif.tacNr==1 && sif.isframe==0) {
278 if(verbose>2) printf("two columns, probably containing frame start times and lengths\n");
279 for(int i=0; i<sif.sampleNr; i++) {
280 sif.x1[i]=sif.x[i]; sif.x2[i]=sif.x1[i]+sif.c[0].y[i]; sif.x[i]=0.5*(sif.x1[i]+sif.x2[i]);
281 }
282 sif.isframe=1;
283 } else {
284 fprintf(stderr, "Error: invalid format in %s\n", frafile);
285 tacFree(&tac); tacFree(&sif); return(3);
286 }
287 if(verbose>3) tacWrite(&sif, stdout, TAC_FORMAT_TSV_UK, 1, NULL);
288 }
289 /* If time unit is known, convert to same units as in the data to be edited */
290 if(sif.tunit!=UNIT_UNKNOWN) {
291 if(tacXUnitConvert(&sif, tac.tunit, &status)!=TPCERROR_OK)
292 fprintf(stderr, "Warning: %s.\n", errorMsg(status.error));
293 }
294 }
295 /* Warning, if it looks like time units are different in frame frame */
296 /* Check for the need to extrapolate */
297 {
298 double xmin1, xmax1, xmin2, xmax2;
299 if(tacXRange(&tac, &xmin1, &xmax1)) {
300 fprintf(stderr, "Error: invalid sample times in %s.\n", datfile);
301 tacFree(&tac); tacFree(&sif); return(2);
302 }
303 if(tacXRange(&sif, &xmin2, &xmax2)) {
304 fprintf(stderr, "Error: invalid frame times in %s.\n", frafile);
305 tacFree(&tac); tacFree(&sif); return(3);
306 }
307 if(xmax2<0.1*xmax1 || xmax2>10.*xmax1) {
308 fprintf(stderr, "Warning: PET frame times may not be in same unit as TAC data.\n");
309 fflush(stderr);
310 }
311 if(verbose>0 && xmax1<xmax2) {
312 printf("Note: extrapolation needed from %g to %g\n", xmax1, xmax2); fflush(stdout);}
313 if(xmax1<0.95*xmax2 || (isot!=ISOTOPE_UNKNOWN && xmax1<0.99*xmax2)) {
314 if(forceMode==0) {
315 fprintf(stderr, "Error: required extrapolation is too risky.\n");
316 tacFree(&tac); tacFree(&sif); return(3);
317 }
318 }
319 if(forceMode==0 && xmin2<0.95*xmin1) {
320 printf("Note: extrapolation needed from %g to %g\n", xmin1, xmin2); fflush(stdout);
321 /* If mean value of first sample is <=0 then there's no problem */
322 double a=0.0;
323 for(int i=0; i<tac.tacNr; i++) a+=tac.c[i].y[0];
324 a/=(double)tac.tacNr;
325 if(a>1.0E-12) {
326 fprintf(stderr, "Error: required extrapolation is too risky.\n");
327 tacFree(&tac); tacFree(&sif); return(3);
328 }
329 }
330 }
331
332
333 /*
334 * Allocate memory for output data
335 */
336 TAC tac2; tacInit(&tac2);
337 if(tacDuplicate(&tac, &tac2)!=TPCERROR_OK) {
338 fprintf(stderr, "Error: cannot setup new TAC data.\n");
339 tacFree(&tac); tacFree(&sif); return(4);
340 }
341 if(sif.sampleNr>tac.sampleNr && tacAllocateMoreSamples(&tac2, sif.sampleNr-tac.sampleNr)) {
342 fprintf(stderr, "Error: cannot setup new TAC data.\n");
343 tacFree(&tac); tacFree(&sif); return(4);
344 }
345 tac2.sampleNr=sif.sampleNr;
346 /* Set output frame times */
347 tacXCopy(&sif, &tac2, 0, tac2.sampleNr-1);
348 if(midFrame) tac2.isframe=0; else tac2.isframe=1;
349 /* Simple file format cannot store frame start and end times; change that, when necessary */
350 if(tac2.isframe && tac2.format==TAC_FORMAT_SIMPLE) {
351 char *ex=filenameGetExtension(outfile); if(ex!=NULL) ex++;
352 tac2.format=tacFormatIdentify(ex);
354 }
355 /* Make sure that file format writing is supported */
358 }
359
360
361 /*
362 * Interpolate
363 */
364 if(verbose>1) {printf("interpolating\n"); fflush(stdout);}
365 {
366 int ret=0;
367 for(int i=0; i<tac2.tacNr; i++) {
368 double *y=NULL, *yi=NULL, *yii=NULL;
369 if(mode==0) y=tac2.c[i].y; else if(mode==1) yi=tac2.c[i].y; else yii=tac2.c[i].y;
370 ret=liInterpolateForPET(tac.x, tac.c[i].y, tac.sampleNr,
371 tac2.x1, tac2.x2, y, yi, yii, tac2.sampleNr, 3, 1, verbose-10);
372 if(ret) break;
373 }
374 if(ret) {
375 fprintf(stderr, "Error: cannot interpolate the data.\n");
376 if(verbose>1) printf(" ret := %d\n", ret);
377 tacFree(&sif); tacFree(&tac); tacFree(&tac2); return(5);
378 }
379 }
380 tacFree(&tac);
381
382 /* If necessary, correct again for radioactive decay */
383 if(isot!=ISOTOPE_UNKNOWN) {
384 //tacWrite(&tac2, stdout, TAC_FORMAT_TSV_UK, 1, NULL);
385 if(verbose>1) printf("correcting for decay.\n");
386 if(tacDecayCorrection(&tac2, isot, 1, &status)!=TPCERROR_OK) {
387 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
388 tacFree(&sif); tacFree(&tac2); return(6);
389 }
390 }
391
392
393
394
395 /*
396 * Save TACs
397 */
398 /* Isotope from SIF is not used to do the decay off/on procedure, but it will be written into
399 TAC file if it can be found */
400 if(isot==ISOTOPE_UNKNOWN) isot=tacGetIsotope(&sif);
401 if(isot!=ISOTOPE_UNKNOWN) tacSetIsotope(&tac2, isot);
402 if(verbose>1) printf("saving %s\n", outfile);
403 {
404 FILE *fp; fp=fopen(outfile, "w");
405 if(fp==NULL) {
406 fprintf(stderr, "Error: cannot open file for writing\n");
407 tacFree(&sif); tacFree(&tac2); return(11);
408 }
409 int ret=tacWrite(&tac2, fp, TAC_FORMAT_UNKNOWN, 1, &status);
410 fclose(fp);
411 if(ret!=TPCERROR_OK) {
412 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
413 tacFree(&sif); tacFree(&tac2); return(12);
414 }
415
416 /* Tell user what we did */
417 if(verbose>=0) {
418 if(mode==0) printf(" %d frames saved.\n", tac2.sampleNr);
419 else if(mode==1) printf(" %d integral frames saved.\n", tac2.sampleNr);
420 else if(mode==2) printf(" %d 2nd integral frames saved.\n", tac2.sampleNr);
421 }
422 }
423 tacFree(&tac2);
424 tacFree(&sif);
425
426 return(0);
427}
428/*****************************************************************************/
429
430/*****************************************************************************/
void csvInit(CSV *csv)
Definition csv.c:22
void csvFree(CSV *csv)
Definition csv.c:38
int csvRead(CSV *csv, FILE *fp, TPCSTATUS *status)
Definition csvio.c:124
double lambdaFromIsotope(int isotope)
Definition decay.c:63
char * filenameGetExtension(const char *s)
Get the last extension of a file name.
Definition filename.c:178
int liInterpolateForPET(double *x, double *y, const int nr, double *newx1, double *newx2, double *newy, double *newyi, double *newyii, const int newnr, const int se, const int ee, const int verbose)
Linear TAC interpolation and/or integration to PET frames.
char * isotopeName(int isotope_code)
Definition isotope.c:101
int isotopeIdentify(const char *isotope)
Definition isotope.c:145
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:47
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:169
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:339
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:114
int tacReadSIF(TAC *tac, CSV *csv, IFT *hdr, TPCSTATUS *status)
Definition sifio.c:129
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
size_t strlcpy(char *dst, const char *src, size_t dstsize)
Definition stringext.c:632
Definition tpccsv.h:36
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
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 tacDuplicate(TAC *tac1, TAC *tac2)
Make a duplicate of TAC structure.
Definition tac.c:356
void tacInit(TAC *tac)
Definition tac.c:24
int tacAllocateMoreSamples(TAC *tac, int addNr)
Allocate memory for more samples in TAC data.
Definition tac.c:435
int tacGetIsotope(TAC *tac)
Definition tacdc.c:25
int tacDecayCorrection(TAC *tac, int isotope, int mode, TPCSTATUS *status)
Definition tacdc.c:59
void tacSetIsotope(TAC *tac, int isotope)
Definition tacdc.c:41
int tacFormatWriteSupported(tacformat format)
Definition tacio.c:291
int tacRead(TAC *d, const char *fname, TPCSTATUS *status)
Definition tacio.c:413
int tacFormatIdentify(const char *s)
Definition tacio.c:113
char * tacFormattxt(tacformat c)
Definition tacio.c:98
int tacWrite(TAC *tac, FILE *fp, tacformat format, int extra, TPCSTATUS *status)
Definition tacio.c:332
int tacYNaNs(TAC *tac, const int i)
Definition tacnan.c:47
int tacSortByTime(TAC *d, TPCSTATUS *status)
Definition tacorder.c:74
int tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:23
int tacDeleteMissingSamples(TAC *d)
Delete those samples (time frames) from TAC structure, which contain only missing y values,...
Definition tacx.c:450
int tacXCopy(TAC *tac1, TAC *tac2, int i1, int i2)
Definition tacx.c:24
int tacXRange(TAC *d, double *xmin, double *xmax)
Get the range of x values (times) in TAC structure.
Definition tacx.c:124
Header file for library libtpccsv.
Header file for library libtpcextensions.
unit
@ UNIT_MIN
minutes
@ UNIT_UNKNOWN
Unknown unit.
@ UNIT_SEC
seconds
@ TPCERROR_OK
No error.
char * unitName(int unit_code)
Definition units.c:143
int unitIsTime(int u)
Definition units.c:359
Header file for library libtpcift.
Header file for library libtpcisotope.
isotope
Definition tpcisotope.h:50
@ ISOTOPE_UNKNOWN
Unknown.
Definition tpcisotope.h:51
Header file for libtpcli.
Header file for library libtpctac.
@ TAC_FORMAT_UNKNOWN
Unknown format.
Definition tpctac.h:28
@ TAC_FORMAT_TSV_UK
UK TSV (point as decimal separator).
Definition tpctac.h:37
@ TAC_FORMAT_PMOD
PMOD TAC format.
Definition tpctac.h:33
@ TAC_FORMAT_SIMPLE
x and y's with space delimiters
Definition tpctac.h:29
@ TAC_FORMAT_DFT
Data format of Turku PET Centre.
Definition tpctac.h:30