TPCCLIB
Loading...
Searching...
No Matches
imgthrs.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#include <unistd.h>
16#include <time.h>
17/*****************************************************************************/
18#include "libtpcmisc.h"
19#include "libtpcimgio.h"
20#include "libtpcimgp.h"
21#include "libtpcmodext.h"
22/*****************************************************************************/
23
24/*****************************************************************************/
25static char *info[] = {
26 "Threshold PET image file in ECAT, NIfTI, or Analyze format.",
27 "Program calculates an integral (AUC) over time frames of a dynamic image,",
28 "finds the maximal integral value of all planes, and cuts off (sets to zero)",
29 "the pixels in dynamic image which have lower or higher integral than",
30 "the specified threshold-%.",
31 "Original image file is not modified.",
32 " ",
33 "Usage: @P [Options] imgfile lowerthreshold upperthreshold [outputfile]",
34 " ",
35 "Options:",
36 " -mask=<filename>",
37 " Save cutoff mask file containing pixels values 1 (between threshold",
38 " limits) and 0 (under lower or over upper threshold).",
39 " -abs",
40 " Thresholds are given as absolute values instead of percentages",
41 " of maximum in the static image; not applicable to dynamic images.",
42 " -start=<time (min)>",
43 " AUC calculation starts from given time. By default from scan start.",
44 " -end=<time (min)>",
45 " AUC calculation ends to given time. By default until scan end.",
46 " --force",
47 " Program does not mind if none of pixels are thresholded;",
48 " otherwise that is considered as an error.",
49 " -stdoptions", // List standard options like --help, -v, etc
50 " ",
51 "Lower and upper limit can be given directly as values on the command line,",
52 "or as names of files containing only the value, or value with key 'lower' or",
53 "'upper', respectively.",
54 " ",
55 "Example: threshold the background and cerebrospinal fluid:",
56 " @P b123dy1.v 30 100 b123thres.v",
57 " ",
58 "See also: img2dft, imgmask, imgqntls, imginteg, imgposv, imgslim, imgcutof",
59 " ",
60 "Keywords: image, threshold, mask",
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 char petfile[FILENAME_MAX], maskfile[FILENAME_MAX], outfile[FILENAME_MAX];
83 float lowerThreshold=-1.0, upperThreshold=-1.0;
84 int absThreshold=0; // 0=percentages; 1=values
85 int forceMode=0; // 0=off; 1=on
86 char *cptr;
87 int ret=0;
88 IMG img, sum, mask;
89 float tstart, tstop;
90
91
92 /*
93 * Get arguments
94 */
95 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
96 petfile[0]=outfile[0]=maskfile[0]=(char)0;
97 tstart=tstop=-1.0;
98 /* Get options */
99 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
100 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
101 cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
102 if(strncasecmp(cptr, "MASK=", 5)==0) {
103 strlcpy(maskfile, cptr+5, FILENAME_MAX); continue;
104 } else if(strncasecmp(cptr, "ABSOLUTE", 3)==0) {
105 absThreshold=1; continue;
106 } else if(strncasecmp(cptr, "START=", 6)==0) {
107 double v;
108 if(atof_with_check(cptr+6, &v)==0) {
109 if(v>0.0) tstart=60.0*v; else tstart=0.0;
110 continue;
111 }
112 } else if(strncasecmp(cptr, "END=", 4)==0) {
113 double v;
114 if(atof_with_check(cptr+4, &v)==0) {
115 if(v>0.0) {tstop=60.0*v; continue;}
116 }
117 } else if(strncasecmp(cptr, "STOP=", 5)==0) {
118 double v;
119 if(atof_with_check(cptr+5, &v)==0) {
120 if(v>0.0) {tstop=60.0*v; continue;}
121 }
122 } else if(strcasecmp(cptr, "F")==0 || strcasecmp(cptr, "FORCE")==0) {
123 forceMode=1; continue;
124 }
125 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
126 return(1);
127 } else break;
128
129 /* Print help or version? */
130 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
131 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
132 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
133
134 /* Process other arguments, starting from the first non-option */
135 if(ai<argc) {strlcpy(petfile, argv[ai], FILENAME_MAX); ai++;}
136 if(ai<argc) {
137 double v;
138 char *s=iftReadValue(argv[ai], "lower", 0);
139 if(s!=NULL) {ret=atof_with_check(s, &v); free(s);}
140 else ret=atof_with_check(argv[ai], &v);
141 if(ret!=0) {
142 fprintf(stderr, "Error: invalid lower limit '%s'.\n", argv[ai]);
143 return(1);
144 }
145 lowerThreshold=v;
146 if(absThreshold==0) lowerThreshold*=0.01;
147 ai++;
148 } else {
149 fprintf(stderr, "Error: missing command-line argument; try %s --help\n", argv[0]);
150 return(1);
151 }
152 if(ai<argc) {
153 double v;
154 char *s=iftReadValue(argv[ai], "upper", 0);
155 if(s!=NULL) {ret=atof_with_check(s, &v); free(s);}
156 else ret=atof_with_check(argv[ai], &v);
157 if(ret!=0) {
158 fprintf(stderr, "Error: invalid lower limit '%s'.\n", argv[ai]);
159 return(1);
160 }
161 upperThreshold=v;
162 if(absThreshold==0) {
163 if(upperThreshold<=0.0) {
164 fprintf(stderr, "Error: invalid upper threshold '%s'.\n", argv[ai]);
165 return(1);
166 }
167 upperThreshold*=0.01;
168 }
169 ai++;
170 } else {
171 fprintf(stderr, "Error: missing command-line argument; try %s --help\n",
172 argv[0]);
173 return(1);
174 }
175 if(ai<argc) {strlcpy(outfile, argv[ai], FILENAME_MAX); ai++;}
176 if(ai<argc) {fprintf(stderr, "Error: too many arguments.\n"); return(1);}
177
178
179 /* Did we get all the information that we need? */
180 if(!petfile[0]) {
181 fprintf(stderr, "Error: missing command-line argument; try %s --help\n", argv[0]);
182 return(1);
183 }
184 if(lowerThreshold>upperThreshold) {
185 fprintf(stderr, "Error: invalid threshold range.\n");
186 if(verbose>0) {
187 if(absThreshold==0)
188 printf("Threshold range: %g - %g\n", 100.*lowerThreshold, 100.*upperThreshold);
189 else
190 printf("Threshold range: %g - %g\n", lowerThreshold, upperThreshold);
191 }
192 return(1);
193 }
194 if(lowerThreshold==upperThreshold) {
195 /* Image data is stored with limited precision, therefore limits should not be equal */
196 float f;
197 if(absThreshold==0) {
198 f=0.001*lowerThreshold; lowerThreshold-=f; upperThreshold+=f;
199 if(verbose>1) printf("Threshold range: %g - %g\n", 100.*lowerThreshold, 100.*upperThreshold);
200 } else {
201 f=0.001*lowerThreshold; if(f>0.1) f=0.1;
202 lowerThreshold-=f; upperThreshold+=f;
203 if(verbose>1) printf("Threshold range: %g - %g\n", lowerThreshold, upperThreshold);
204 if(verbose>10) printf("Threshold range: %.2f - %.2f\n", lowerThreshold, upperThreshold);
205 }
206 }
207
208
209
210 /* In verbose mode print arguments and options */
211 if(verbose>1) {
212 printf("petfile := %s\n", petfile);
213 if(outfile[0]) printf("outfile := %s\n", outfile);
214 if(maskfile[0]) printf("maskfile := %s\n", maskfile);
215 if(absThreshold==0) {
216 printf("upper_threshold_percentage := %g\n", 100.*upperThreshold);
217 printf("lower_threshold_percentage := %g\n", 100.*lowerThreshold);
218 } else {
219 printf("upper_threshold := %g\n", upperThreshold);
220 printf("lower_threshold := %g\n", lowerThreshold);
221 }
222 if(tstart>=0) printf("start_time := %g\n", tstart/60.0);
223 if(tstop>=0) printf("end_time := %g\n", tstop/60.0);
224 }
225
226
227 /*
228 * Read the contents of the PET file to img data structure
229 */
230 imgInit(&img);
231 if(verbose>0) printf("reading %s\n", petfile);
232 ret=imgRead(petfile, &img);
233 if(ret) {
234 fprintf(stderr, "Error: %s\n", img.statmsg);
235 if(verbose>1) printf("ret=%d\n", ret);
236 imgEmpty(&img); return(2);
237 }
238 if(imgNaNs(&img, 1)>0)
239 if(verbose>0) fprintf(stderr, "Warning: missing pixel values.\n");
240 /* Absolute thresholds can be used for static image only */
241 if(absThreshold!=0 && img.dimt>1) {
242 fprintf(stderr, "Error: do not use absolute thresholds for dynamic image.\n");
243 imgEmpty(&img); return(2);
244 }
245 /* Check that frame times are available */
246 if(!imgExistentTimes(&img)) {
247 if(tstart>0.0 || tstop>0.0) {
248 fprintf(stderr, "Error: image does not contain frame times.\n");
249 imgEmpty(&img); return(2);
250 }
251 if(img.dimt>1)
252 fprintf(stderr, "Warning: image does not contain frame times.\n");
253 }
254 /* For raw data, divide counts by frame duration */
255 ret=imgRawCountsPerTime(&img, 1);
256 if(ret) {
257 fprintf(stderr, "Error: cannot correct sinogram counts.\n");
258 if(verbose>1) printf("ret=%d\n", ret);
259 imgEmpty(&img); return(2);
260 }
261
262
263 /*
264 * Calculate the integral image from dynamic image
265 */
266 if(img.dimt>1) {
267 if(verbose>0) fprintf(stdout, "calculating integral image\n");
268 imgInit(&sum);
269 if(tstart<=0.0 && tstop<=0.0) {
270 /* Time range not specified, thus just sum over all frames */
271 ret=imgFrameIntegral(&img, 0, img.dimt-1, &sum, verbose-4);
272 } else {
273 /* Check that reasonable time range was given by user */
274 if(tstart<img.start[0]) tstart=img.start[0];
275 if(tstop<0.01 || tstop>img.end[img.dimt-1]) tstop=img.end[img.dimt-1];
276 if(verbose>1) {
277 printf("tstart := %g\n", tstart);
278 printf("tstop := %g\n", tstop);
279 }
280 ret=imgTimeIntegral(&img, tstart, tstop, &sum, 0, NULL, verbose-4);
281 }
282 if(ret) {
283 fprintf(stderr, "Error: cannot integrate.\n");
284 if(verbose>1) printf("ret=%d\n", ret);
285 imgEmpty(&img); imgEmpty(&sum); return(4);
286 }
287 } else {
288 /* If image is static, then just copy it. Do _not_ multiply with
289 frame duration, because it would hamper thresholding with absolute
290 values, would not work if frame times are zero, and
291 would not help in thresholding with percentages of max. */
292 if(verbose>2) fprintf(stdout, "static image\n");
293 imgInit(&sum);
294 if(imgDup(&img, &sum)!=0) {
295 fprintf(stderr, "Error: cannot duplicate static image.\n");
296 imgEmpty(&img); imgEmpty(&sum); return(4);
297 }
298 }
299
300 /*
301 * Thresholding
302 */
303 if(verbose>0) fprintf(stdout, "thresholding\n");
304
305 if(absThreshold==0) {
306 /* Search for maximum pixel value */
307 if(verbose>1) printf("searching maximum pixel value...\n");
308 float max;
309 ret=imgMax(&sum, &max);
310 if(ret) {
311 fprintf(stderr, "Error: cannot find maximum.\n");
312 if(verbose>1) printf("ret=%d\n", ret);
313 imgEmpty(&img); imgEmpty(&sum);
314 return(5);
315 }
316 if(verbose>1) printf("max := %g\n", max);
317 lowerThreshold*=max; upperThreshold*=max;
318 if(verbose>1)
319 printf("lowerThresholdValue := %g\nupperThresholdValue:=%g\n",
320 lowerThreshold, upperThreshold);
321 }
322
323 /* Make a mask image */
324 imgInit(&mask);
325 ret=imgThresholdMask(&sum, lowerThreshold, upperThreshold, &mask);
326 if(ret) {
327 fprintf(stderr, "Error: cannot threshold.\n");
328 if(verbose>1) printf("ret=%d\n", ret);
329 imgEmpty(&img); imgEmpty(&sum); imgEmpty(&mask);
330 return(6);
331 }
332 /* Sum image is not needed any more */
333 imgEmpty(&sum);
334
335 /* Report the number of retained pixels, in verbose mode in each plane */
336 {
337 int zi, xi, yi;
338 long long nr, total_nr=0, thrs_nr;
339 if(verbose>1 && mask.dimz>1) printf("Nr of retained pixels:\n");
340 for(zi=0; zi<mask.dimz; zi++) {
341 nr=0;
342 for(yi=0; yi<mask.dimy; yi++)
343 for(xi=0; xi<mask.dimx; xi++)
344 if(fabs(mask.m[zi][yi][xi][0])>0.01) nr++;
345 if(verbose>1 && mask.dimz>1) printf(" plane %d: %lld\n", zi+1, nr);
346 total_nr+=nr;
347 }
348 if(verbose>0 || !outfile[0]) printf("retained_pixels := %lld\n", total_nr);
349 thrs_nr=mask.dimz*mask.dimy*mask.dimx-total_nr;
350 if(verbose>0 || !outfile[0]) printf("thresholded_pixels := %lld\n", thrs_nr);
351 /* If no pixels were thresholded, and not in force mode, then do no more */
352 if(thrs_nr==0) {
353 if(forceMode) {
354 fprintf(stderr, "Warning: no pixels thresholded.\n");
355 } else {
356 fprintf(stderr, "Error: no pixels thresholded.\n");
357 imgEmpty(&img); imgEmpty(&mask);
358 return(7);
359 }
360 }
361 }
362
363 /* Write the mask image, if requested */
364 if(maskfile[0]) {
365 if(verbose>1) fprintf(stdout, "writing %s\n", maskfile);
366 ret=imgWrite(maskfile, &mask);
367 if(ret) {
368 fprintf(stderr, "Error: %s\n", img.statmsg);
369 if(verbose>1) printf("ret=%d\n", ret);
370 imgEmpty(&img); imgEmpty(&mask);
371 return(11);
372 }
373 if(verbose>0) printf("Written %s\n", maskfile);
374 }
375
376 /* If thresholded image was not requested, then quit */
377 if(!outfile[0]) {
378 if(verbose>0) printf("done.\n");
379 imgEmpty(&img); imgEmpty(&mask);
380 return(0);
381 }
382
383 /* According to the mask, threshold the original image */
384 ret=imgThresholdByMask(&img, &mask, 0.0);
385 if(ret) {
386 fprintf(stderr, "Error: cannot threshold by the mask.\n");
387 if(verbose>1) printf("ret=%d\n", ret);
388 imgEmpty(&img); imgEmpty(&mask);
389 return(8);
390 }
391 imgEmpty(&mask);
392
393
394 /*
395 * Write the thresholded image
396 */
397 /* For raw data, multiply counts by frame duration */
398 ret=imgRawCountsPerTime(&img, 0);
399 if(ret) {
400 fprintf(stderr, "Error: cannot correct sinogram counts.\n");
401 imgEmpty(&img); return(9);
402 }
403 if(verbose>1) fprintf(stdout, "writing %s\n", outfile);
404 if(imgWrite(outfile, &img)) {
405 fprintf(stderr, "Error: %s\n", img.statmsg);
406 if(verbose>1) printf("ret=%d\n", ret);
407 imgEmpty(&img);
408 return(13);
409 }
410 if(verbose>0) printf("Written %s\n", outfile);
411 imgEmpty(&img);
412
413 return(0);
414}
415/*****************************************************************************/
416
417/*****************************************************************************/
int atof_with_check(char *double_as_string, double *result_value)
Definition decpoint.c:107
char * iftReadValue(char *filename, char *keystr, int verbose)
Definition iftfile.c:180
int imgExistentTimes(IMG *img)
Definition img.c:613
unsigned long long imgNaNs(IMG *img, int fix)
Definition img.c:658
int imgDup(IMG *img1, IMG *img2)
Definition img.c:304
void imgEmpty(IMG *image)
Definition img.c:121
void imgInit(IMG *image)
Definition img.c:60
int imgFrameIntegral(IMG *img, int first, int last, IMG *iimg, int verbose)
Definition imgarithm.c:346
int imgRawCountsPerTime(IMG *img, int operation)
Definition imgarithm.c:442
int imgRead(const char *fname, IMG *img)
Definition imgfile.c:26
int imgWrite(const char *fname, IMG *img)
Definition imgfile.c:136
int imgMax(IMG *img, float *maxvalue)
Definition imgminmax.c:15
int imgThresholdMask(IMG *img, float minValue, float maxValue, IMG *timg)
int imgThresholdByMask(IMG *img, IMG *templt, float thrValue)
Header file for libtpcimgio.
Header file for libtpcimgp.
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:40
size_t strlcpy(char *dst, const char *src, size_t dstsize)
Definition strext.c:245
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 libtpcmodext.
int imgTimeIntegral(IMG *img, float t1, float t2, IMG *iimg, int calc_mode, char *status, int verbose)
Definition misc_model.c:147
unsigned short int dimx
float **** m
unsigned short int dimt
float * start
unsigned short int dimz
unsigned short int dimy
float * end
const char * statmsg