TPCCLIB
Loading...
Searching...
No Matches
taccbv.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 <string.h>
15/*****************************************************************************/
16#include "tpcextensions.h"
17#include "tpcift.h"
18#include "tpctac.h"
19#include "tpcpar.h"
20#include "tpcfileutil.h"
21#include "tpcli.h"
22#include "tpctacmod.h"
23/*****************************************************************************/
24
25/*****************************************************************************/
26static char *info[] = {
27 "Subtracts the contribution of vascular radioactivity from regional",
28 "PET TTACs. Vascular volume fraction Vb can be given as a value that is",
29 "common to all regions, or as regional Vb values in a TAC or PAR file,",
30 "calculated from a [O-15]CO study or estimated by model fitting.",
31 " ",
32 "Usage: @P ttacfile btacfile Vb [outputfile]",
33 " ",
34 "Options:",
35 " -noneg",
36 " Negative TAC values are set to 0.",
37 " -pv | -tv",
38 " Equation Ct=Cpet-Vb*Cb is applied by default or with option -pv;",
39 " with option -tv equation Ct=(Cpet-Vb*Cb)/(1-Vb) is applied.",
40 " -sim",
41 " Simulate the contribution of vascular radioactivity instead of",
42 " correcting for it, calculating Cpet from Ct using equations above.",
43 " --force",
44 " Program does not mind if the time or calibration units",
45 " cannot be converted to match, or if TAC names do not match.",
46 " -stdoptions", // List standard options like --help, -v, etc
47 " ",
48 "Example 1:",
49 " @P uo372.tac uo372ab.bld 0.045 uo372cbv.tac",
50 "Example 2:",
51 " @P uo372.dft uo372ab.kbq uo372vb.dft uo372cbv.dft",
52 " ",
53 "Vb values that are >=1 are assumed to be percentages.",
54 "Blood TAC can be given in a separate BTAC file, or as a region id inside",
55 "TTAC file. Original TTAC file is modified, if output file is not given.",
56 " ",
57 "See also: imgcbv, p2blood, fitvb, tacadd, interpol, taccalc, tacunit",
58 " ",
59 "Keywords: TAC, modelling, vascular fraction, simulation",
60 0};
61/*****************************************************************************/
62
63/*****************************************************************************/
64/* Turn on the globbing of the command line, since it is disabled by default in
65 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
66 In Unix&Linux wildcard command line processing is enabled by default. */
67/*
68#undef _CRT_glob
69#define _CRT_glob -1
70*/
71int _dowildcard = -1;
72/*****************************************************************************/
73
74/*****************************************************************************/
78int main(int argc, char **argv)
79{
80 int ai, help=0, version=0, verbose=1;
81 char ttacfile[FILENAME_MAX], btacfile[FILENAME_MAX], outfile[FILENAME_MAX],
82 parfile[FILENAME_MAX];
83 int leaveNegatives=1;
84 int petVolume=1;
85 int addVb=0;
86 double Vb=nan("");
87 int forceMode=0;
88
89
90 /*
91 * Get arguments
92 */
93 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
94 ttacfile[0]=btacfile[0]=outfile[0]=parfile[0]=(char)0;
95
96 /* Options */
97 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
98 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
99 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(!*cptr) continue;
100 if(strncasecmp(cptr, "NONEGATIVES", 1)==0) {
101 leaveNegatives=0; continue;
102 } else if(strncasecmp(cptr, "PV", 1)==0) {
103 petVolume=1; continue;
104 } else if(strncasecmp(cptr, "TV", 1)==0) {
105 petVolume=0; continue;
106 } else if(strncasecmp(cptr, "SIMULATE", 3)==0) {
107 addVb=1; continue;
108 } else if(strcasecmp(cptr, "F")==0 || strcasecmp(cptr, "FORCE")==0) {
109 forceMode=1; continue;
110 }
111 fprintf(stderr, "Error: invalid option '%s'\n", argv[ai]);
112 return(1);
113 } else break;
114
115 TPCSTATUS status; statusInit(&status);
116 statusSet(&status, __func__, __FILE__, __LINE__, TPCERROR_OK);
117 status.verbose=verbose-3;
118
119 /* Print help or version? */
120 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
121 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
122 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
123
124 /* Process other arguments, starting from the first non-option */
125 if(ai<argc) strlcpy(ttacfile, argv[ai++], FILENAME_MAX);
126 if(ai<argc) strlcpy(btacfile, argv[ai++], FILENAME_MAX);
127 if(ai<argc) {
128 if(fileExist(argv[ai])) strlcpy(parfile, argv[ai++], FILENAME_MAX);
129 else if(!atofCheck(argv[ai], &Vb) && Vb>=0.0) ai++;
130 else {fprintf(stderr, "Error: invalid Vb: '%s'.\n", argv[ai]); return(1);}
131 }
132 if(ai<argc) strlcpy(outfile, argv[ai++], FILENAME_MAX);
133 if(ai<argc) {
134 fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]);
135 return(1);
136 }
137 /* Did we get all the information that we need? */
138 if(isnan(Vb) && !parfile[0]) { // note that output file is optional
139 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
140 return(1);
141 }
142 if(!outfile[0]) strcpy(outfile, ttacfile);
143 if(Vb>=1.0) Vb/=100.;
144
145 /* In verbose mode print arguments and options */
146 if(verbose>1) {
147 printf("ttacfile := %s\n", ttacfile);
148 printf("btacfile := %s\n", btacfile);
149 printf("outfile := %s\n", outfile);
150 if(parfile[0]) printf("parfile := %s\n", parfile);
151 if(!isnan(Vb)) printf("Vb := %g %%\n", 100.0*Vb);
152 printf("petVolume := %d\n", petVolume);
153 printf("leaveNegatives := %d\n", leaveNegatives);
154 printf("addVb := %d\n", addVb);
155 printf("forceMode := %d\n", forceMode);
156 fflush(stdout);
157 }
158 if(verbose>1) {
159 printf("\nApplying formula:\n");
160 if(addVb==0) {
161 if(petVolume==0) printf("Ct=(Cpet-Vb*Cb)/(1-Vb)\n");
162 else printf("Ct=Cpet-Vb*Cb\n");
163 } else {
164 if(petVolume==0) printf("Cpet=(1-Vb)*Ct+Vb*Cb\n");
165 else printf("Cpet=Ct+Vb*Cb\n");
166 }
167 printf("\n"); fflush(stdout);
168 }
169
170
171 /*
172 * Read the TTAC file
173 */
174 if(verbose>1) printf("reading %s\n", ttacfile);
175 TAC ttac; tacInit(&ttac);
176 if(tacRead(&ttac, ttacfile, &status) || tacSortByTime(&ttac, &status)) {
177 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
178 tacFree(&ttac); return(2);
179 }
180 if(verbose>2) {
181 printf("ttac.fileformat := %s\n", tacFormattxt(ttac.format));
182 printf("ttac.tacNr := %d\n", ttac.tacNr);
183 printf("ttac.sampleNr := %d\n", ttac.sampleNr);
184 printf("ttac.xunit := %s\n", unitName(ttac.tunit));
185 printf("ttac.yunit := %s\n", unitName(ttac.cunit));
186 }
187
188
189 /*
190 * Read the BTAC file
191 */
192 if(verbose>1) printf("reading %s\n", btacfile);
193 TAC btac; tacInit(&btac);
194 if(tacRead(&btac, btacfile, &status) || tacSortByTime(&btac, &status)) {
195 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
196 tacFree(&ttac); tacFree(&btac); return(3);
197 }
198 if(verbose>2) {
199 printf("btac.fileformat := %s\n", tacFormattxt(btac.format));
200 printf("btac.tacNr := %d\n", btac.tacNr);
201 printf("btac.sampleNr := %d\n", btac.sampleNr);
202 printf("btac.xunit := %s\n", unitName(btac.tunit));
203 printf("btac.yunit := %s\n", unitName(btac.cunit));
204 }
205 /* Check the number of TACs */
206 if(btac.tacNr>1) {
207 fprintf(stderr, "Warning: BTAC file contains %d TACs.\n", btac.tacNr);
208 fprintf(stdout, "Note: using the first TAC in BTAC file.\n");
209 fflush(stderr); fflush(stdout);
210 btac.tacNr=1;
211 }
212 /* Try to convert time units */
213 {
214 int ret=tacXUnitConvert(&btac, ttac.tunit, &status);
215 if(ret!=TPCERROR_OK && verbose>1) {
216 if(verbose>0) fprintf(stdout, "Note: TAC files have different or unknown time units.\n");
217 if(verbose>2) fprintf(stderr, "Status: %s\n", errorMsg(status.error));
218 }
219 /* Error does not matter, if user told so */
220 if(forceMode) ret=TPCERROR_OK;
221 if(ret!=TPCERROR_OK) {
222 fprintf(stderr, "Error: %s.\n", errorMsg(status.error));
223 tacFree(&ttac); tacFree(&btac); return(3);
224 }
225 }
226 /* Try to convert concentration units */
227 {
228 int ret=tacYUnitConvert(&btac, ttac.cunit, &status);
229 if(ret!=TPCERROR_OK && verbose>1) {
230 if(verbose>0) fprintf(stdout, "Note: TAC files have different or unknown concentration units.\n");
231 if(verbose>2) fprintf(stderr, "Status: %s\n", errorMsg(status.error));
232 }
233 /* Error does not matter, if user told so */
234 if(forceMode) ret=TPCERROR_OK;
235 if(ret!=TPCERROR_OK) {
236 fprintf(stderr, "Error: %s.\n", errorMsg(status.error));
237 tacFree(&ttac); tacFree(&btac); return(3);
238 }
239 }
240 /* Interpolate BTAC to TTAC sample times */
241 TAC bitac; tacInit(&bitac);
242 if(tacInterpolate(&btac, &ttac, &bitac, NULL, NULL, &status) != TPCERROR_OK) {
243 fprintf(stderr, "Error: %s.\n", errorMsg(status.error));
244 tacFree(&ttac); tacFree(&btac); tacFree(&bitac); return(3);
245 }
246 tacFree(&btac);
247
248
249 /*
250 * If Vb values are given in a file, then read that
251 */
252 double VbArray[ttac.tacNr];
253 if(parfile[0]) {
254 if(verbose>1) printf("reading %s\n", parfile);
255 PAR par; parInit(&par);
256 int ret=parRead(&par, parfile, &status);
257 if(ret!=TPCERROR_OK) {
258 TAC tmp; tacInit(&tmp);
259 ret=tacRead(&tmp, parfile, &status);
260 if(ret==TPCERROR_OK) ret=tacToPAR(&tmp, &par, &status);
261 tacFree(&tmp);
262 }
263 if(ret!=TPCERROR_OK) {
264 fprintf(stderr, "Error: %s.\n", errorMsg(status.error));
265 fprintf(stderr, "Error: cannot read Vb values from %s\n", parfile);
266 tacFree(&ttac); tacFree(&bitac); parFree(&par);
267 return(4);
268 }
269 if(verbose>2) {
270 printf("\n Vb file contents:\n");
271 parWrite(&par, stdout, PAR_FORMAT_CSV_UK, 0, NULL); fflush(stdout);
272 }
273 if(par.tacNr!=ttac.tacNr) {
274 fprintf(stderr, "Error: different number of TACs in Vb file.\n");
275 tacFree(&ttac); tacFree(&bitac); parFree(&par);
276 return(4);
277 }
278 int VbIndex; if(par.parNr>1) VbIndex=parFindParameter(&par, "Vb"); else VbIndex=0;
279 if(VbIndex<0) {
280 fprintf(stderr, "Error: parameter Vb not identified in %s\n", parfile);
281 tacFree(&ttac); tacFree(&bitac); parFree(&par);
282 return(4);
283 }
284 /* check TAC names */
285 ret=0;
286 for(int ri=0; ri<par.tacNr; ri++) {
287 if(strcasecmp(par.r[ri].name, ttac.c[ri].name)!=0) {
288 if(verbose>1) {
289 printf(" not matching: '%s' vs '%s'\n", par.r[ri].name, ttac.c[ri].name);
290 fflush(stdout);
291 }
292 ret++;
293 }
294 }
295 if(ret>0) {
296 if(forceMode) fprintf(stderr, "Warning: Vb file does not contain the same TACs.\n");
297 else {
298 fprintf(stderr, "Error: Vb file does not contain the same TACs.\n");
299 tacFree(&ttac); tacFree(&bitac); parFree(&par);
300 return(4);
301 }
302 }
303 /* Copy Vb values */
304 for(int ri=0; ri<par.tacNr; ri++) VbArray[ri]=par.r[ri].p[VbIndex];
305 parFree(&par);
306 /* Check that Vb is fraction */
307 double VbMax, VbMin; doubleRange(VbArray, ttac.tacNr, &VbMin, &VbMax);
308 if(verbose>1) {printf("Vb range: %g - %g\n", VbMin, VbMax); fflush(stdout);}
309 if(VbMin<0.0 || VbMax>100.0) {
310 fprintf(stderr, "Error: invalid range of Vb values.\n");
311 tacFree(&ttac); tacFree(&bitac);
312 return(4);
313 }
314 if(VbMax>1.0) {
315 for(int ri=0; ri<ttac.tacNr; ri++) VbArray[ri]*=0.01;
316 fprintf(stderr, "Warning: Vb values were converted to fractions.\n"); fflush(stderr);
317 }
318 }
319
320
321 /*
322 * Make subtraction, or add Vb contribution
323 */
324 {
325 int ret=0;
326 if(!isnan(Vb)) {
327 ret=tacVb(&ttac, -1, &bitac, Vb, addVb, petVolume, &status);
328 } else {
329 for(int ri=0; ri<ttac.tacNr; ri++)
330 if((ret=tacVb(&ttac, ri, &bitac, VbArray[ri], addVb, petVolume, &status))) break;
331 }
332 if(ret!=TPCERROR_OK) {
333 fprintf(stderr, "Error: %s.\n", errorMsg(status.error));
334 tacFree(&ttac); tacFree(&bitac);
335 return(8);
336 }
337 if(!leaveNegatives) {
338 for(int ri=0; ri<ttac.tacNr; ri++) for(int fi=0; fi<ttac.sampleNr; fi++)
339 if(ttac.c[ri].y[fi]<0.0) ttac.c[ri].y[fi]=0.0;
340 }
341 }
342 tacFree(&bitac);
343
344
345 /*
346 * Save data
347 */
348 {
349 if(verbose>1) printf("writing %s\n", outfile);
350 FILE *fp; fp=fopen(outfile, "w");
351 if(fp==NULL) {
352 fprintf(stderr, "Error: cannot open file for writing (%s)\n", outfile);
353 tacFree(&ttac); return(11);
354 }
355 int ret=tacWrite(&ttac, fp, TAC_FORMAT_UNKNOWN, 1, &status);
356 fclose(fp); tacFree(&ttac);
357 if(ret!=TPCERROR_OK) {
358 fprintf(stderr, "Error: %s\n", errorMsg(status.error));
359 return(12);
360 }
361 }
362
363 if(verbose>=0) printf("%s saved.\n", outfile);
364 return(0);
365}
366/*****************************************************************************/
367
368/*****************************************************************************/
int atofCheck(const char *s, double *v)
Definition decpoint.c:94
unsigned int doubleRange(double *a, const unsigned int n, double *amin, double *amax)
Definition doubleutil.c:174
int fileExist(const char *filename)
Definition filexist.c:17
int tacVb(TAC *ttac, const int i, TAC *btac, double Vb, const int simVb, const int petVolume, TPCSTATUS *status)
Correct TTACs for vascular blood, or simulate its effect.
Definition lisim.c:138
int tacInterpolate(TAC *inp, TAC *xinp, TAC *tac, TAC *itac, TAC *iitac, TPCSTATUS *status)
Interpolate and/or integrate TACs from one TAC structure into a new TAC structure,...
Definition litac.c:141
void parFree(PAR *par)
Definition par.c:75
void parInit(PAR *par)
Definition par.c:25
int parWrite(PAR *par, FILE *fp, parformat format, int extra, TPCSTATUS *status)
Definition pario.c:148
int parRead(PAR *par, const char *fname, TPCSTATUS *status)
Definition pario.c:232
int parFindParameter(PAR *d, const char *par_name)
Definition parselect.c:219
int tacToPAR(TAC *tac, PAR *par, TPCSTATUS *status)
Copy the contents of TAC struct into PAR struct.
Definition partac.c:169
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
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 tpcpar.h:100
int parNr
Definition tpcpar.h:108
int tacNr
Definition tpcpar.h:104
PARR * r
Definition tpcpar.h:114
char name[MAX_TACNAME_LEN+1]
Definition tpcpar.h:50
double * p
Definition tpcpar.h:64
char name[MAX_TACNAME_LEN+1]
Definition tpctac.h:81
double * y
Definition tpctac.h:75
Definition tpctac.h:87
unit cunit
Definition tpctac.h:105
tacformat format
Definition tpctac.h:93
int sampleNr
Definition tpctac.h:89
TACC * c
Definition tpctac.h:117
unit tunit
Definition tpctac.h:109
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
void tacInit(TAC *tac)
Definition tac.c:24
int tacRead(TAC *d, const char *fname, TPCSTATUS *status)
Definition tacio.c:413
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 tacSortByTime(TAC *d, TPCSTATUS *status)
Definition tacorder.c:74
int tacYUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:72
int tacXUnitConvert(TAC *tac, const int u, TPCSTATUS *status)
Definition tacunits.c:23
Header file for library libtpcextensions.
@ TPCERROR_OK
No error.
char * unitName(int unit_code)
Definition units.c:143
Header file for libtpcfileutil.
Header file for library libtpcift.
Header file for libtpcli.
Header file for libtpcpar.
@ PAR_FORMAT_CSV_UK
UK CSV.
Definition tpcpar.h:33
Header file for library libtpctac.
@ TAC_FORMAT_UNKNOWN
Unknown format.
Definition tpctac.h:28
Header file for libtpctacmod.