TPCCLIB
Loading...
Searching...
No Matches
imgbkgrm.c
Go to the documentation of this file.
1
9/*****************************************************************************/
10#include "tpcclibConfig.h"
11/*****************************************************************************/
12#include <stdio.h>
13#include <stdlib.h>
14#include <math.h>
15#include <string.h>
16#include <unistd.h>
17#include <time.h>
18/*****************************************************************************/
19#include "libtpcmisc.h"
20#include "libtpcimgio.h"
21#include "libtpcimgp.h"
22#include "libtpccurveio.h"
23#include "libtpcmodel.h"
24#include "libtpcmodext.h"
25/*****************************************************************************/
26
27/*****************************************************************************/
28static char *info[] = {
29 "Threshold dynamic PET image file in ECAT, NIfTI, or Analyze format.",
30 "AIC is used to determine whether the TAC of a single pixel consists of",
31 "noise only, or if it resembles the scaled mean of all image pixels.",
32 "If no significant signal is found, that pixel is set to zero.",
33 "After this operation, PET data can usually be compressed to about",
34 "one-third of the original file size.",
35 " ",
36 "If file name for mask file is given, then original image file is not",
37 "modified but a mask file is written, with 0 marking the noise pixels and",
38 "1 marking the pixels with signal. Otherwise the original image file is",
39 "modified.",
40 " ",
41 "Usage: @P [Options] imgfile [maskfile]",
42 " ",
43 "Options:",
44 " -keepnegat",
45 " By default, all pixels with AUC<0 are set to zero; with this option",
46 " those are tested as usual.",
47 " -bkg=<zero|border>",
48 " Use zero values as background (zero, default), or mean of pixels at",
49 " image x,y-border (border).",
50 " -bordertac=<filename>",
51 " Save TAC of average pixel values at image x,y-borders.",
52 " -stdoptions", // List standard options like --help, -v, etc
53 " ",
54 "Example:",
55 " @P b123dy1.v",
56 " ",
57 "See also: imgmask, imgthrs, imginteg, imgposv, imgslim",
58 " ",
59 "Keywords: image, threshold, mask, noise",
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/*****************************************************************************/
76
87 IMG *img,
90 float *tac,
93 float *sd,
96 float *med
97) {
98 if(img==NULL || img->status<IMG_STATUS_OCCUPIED) return(1);
99 if(tac==NULL && sd==NULL && med==NULL) return(0);
100
101 /* compute the TAC */
102 int pxlNr=img->dimz*(2*img->dimx+2*(img->dimy-2));
103 float a[pxlNr], fm, fs;
104 for(int fi=0; fi<img->dimt; fi++) {
105 int i=0;
106 for(int zi=0; zi<img->dimz; zi++) {
107 int yi, xi;
108 yi=0;
109 for(xi=0; xi<img->dimx; xi++) a[i++]=img->m[zi][yi][xi][fi];
110 yi=img->dimy-1;
111 for(xi=0; xi<img->dimx; xi++) a[i++]=img->m[zi][yi][xi][fi];
112 xi=0;
113 for(yi=1; yi<img->dimy-1; yi++) a[i++]=img->m[zi][yi][xi][fi];
114 xi=img->dimx-1;
115 for(yi=1; yi<img->dimy-1; yi++) a[i++]=img->m[zi][yi][xi][fi];
116 }
117 if(tac!=NULL || sd!=NULL) {
118 fm=fmean(a, pxlNr, &fs);
119 if(tac!=NULL) tac[fi]=fm;
120 if(sd!=NULL) sd[fi]=fs;
121 }
122 if(med!=NULL) med[fi]=fmedian(a, pxlNr);
123 }
124 return(0);
125}
126/*****************************************************************************/
127
128/*****************************************************************************/
140 IMG *img,
142 int keepNegat,
145 int border,
148 IMG *timg,
150 int verbose,
153 char *status
154) {
155 if(verbose>0) printf("imgNoiseTemplate(*img, %d, %d, *timg, %d, status)\n",
156 keepNegat, border, verbose);
157 if(status!=NULL) strcpy(status, "fault in calling routine");
158 if(img==NULL || img->status!=IMG_STATUS_OCCUPIED) return(1);
159 if(timg==NULL) return(1);
160 if(timg->status==IMG_STATUS_OCCUPIED) {
161 if(img->dimx!=timg->dimx) return(2);
162 if(img->dimy!=timg->dimy) return(2);
163 if(img->dimz!=timg->dimz) return(2);
164 }
165 if(img->dimt<2) {
166 if(status!=NULL) strcpy(status, "method works only for dynamic data");
167 return(3);
168 }
169
170 int ret, n;
171
172 /*
173 * Compute the mean TAC of all pixels
174 */
175 n=img->dimt;
176 float fdur[n], meany[n], bkgtac[n];
177
178 if(verbose>1) fprintf(stdout, "computing mean of pixels\n");
179 if(imgExistentTimes(img)) {
180 for(int i=0; i<n; i++) fdur[i]=img->end[i]-img->start[i];
181 } else {
182 for(int i=0; i<n; i++) fdur[i]=1.0;
183 }
184 ret=imgAverageTAC(img, meany);
185 if(ret) {
186 if(status!=NULL) strcpy(status, "cannot calculate average TAC");
187 if(verbose>1) printf("ret := %d\n", ret);
188 return(4);
189 }
190
191 if(verbose>1) fprintf(stdout, "computing AUC of the mean TAC\n");
192 float mean_tac_auc=0.0;
193 for(int i=0; i<n; i++) mean_tac_auc+=fdur[i]*meany[i];
194 if(verbose>2) printf("mean_tac_auc := %g\n", mean_tac_auc);
195 if(fabs(mean_tac_auc)<1.0E-10) {
196 if(status!=NULL) strcpy(status, "very small TAC average");
197 return(4);
198 }
199
200 /* If requested, compute the background TAC as the mean of x,y-border pixels */
201 double bkg_tac_auc=0.0;
202 if(border==0) {
203 /* or just zeroes */
204 for(int i=0; i<n; i++) bkgtac[i]=0.0;
205 } else {
206 if(verbose>1) fprintf(stdout, "computing background from border pixels\n");
207 if(imgBorderAverageTAC(img, NULL, NULL, bkgtac)!=0) {
208 if(status!=NULL) strcpy(status, "cannot calculate background TAC");
209 return(5);
210 }
211 /* Compute AUC for background TAC */
212 for(int i=0; i<n; i++) bkg_tac_auc+=fdur[i]*bkgtac[i];
213 }
214 if(verbose>2) printf("bkg_tac_auc := %g\n", bkg_tac_auc);
215
216 /*
217 * Allocate the mask image, if not allocated already
218 */
219 int maskExisted=0;
220 if(timg->status==IMG_STATUS_OCCUPIED) {
221 /* Pre-existing mask contents are used later */
222 maskExisted=1;
223 } else {
224 ret=imgAllocateWithHeader(timg, img->dimz, img->dimy, img->dimx, 1, img);
225 if(ret) {
226 if(status!=NULL) strcpy(status, "cannot allocate memory for mask data");
227 return(6);
228 }
229 }
230
231 /*
232 * Go through all the pixels
233 */
234 if(verbose>1) fprintf(stdout, "processing pixels...\n");
235 float wsum=0.0; for(int i=0; i<n; i++) wsum+=fdur[i]; if(verbose>4) printf("wsum=%g\n", wsum);
236 int zi, yi, xi;
237 for(zi=0; zi<img->dimz; zi++) {
238 for(yi=0; yi<img->dimy; yi++) {
239 for(xi=0; xi<img->dimx; xi++) {
240 /* If pixel was already masked out, then keep it that way */
241 if(maskExisted && timg->m[zi][yi][xi][0]<0.1) {
242 timg->m[zi][yi][xi][0]=0.0; continue;
243 }
244 /* Compute AUC for this pixel */
245 double pxl_tac_auc=0.0;
246 for(int i=0; i<n; i++) pxl_tac_auc+=fdur[i]*img->m[zi][yi][xi][i];
247 /* If tacAUC<AUCbkg and user did not want to keep negatives, then remove it */
248 if(keepNegat==0 && pxl_tac_auc<=bkg_tac_auc) {
249 timg->m[zi][yi][xi][0]=0.0; continue;
250 }
251
252 /* Calculate WSS between this TAC and zero */
253 double wss0=0.0;
254 for(int i=0; i<n; i++) {
255 double g=img->m[zi][yi][xi][i];
256 wss0+=fdur[i]*g*g;
257 }
258
259 /* Calculate WSS between this TAC and background TAC */
260 double wss1=0.0;
261 for(int i=0; i<n; i++) {
262 double g=img->m[zi][yi][xi][i]-bkgtac[i];
263 wss1+=fdur[i]*g*g;
264 }
265
266 /* Calculate WSS between this TAC and scaled background TAC */
267 double wss2=0.0; double sf2=1.0;
268 if(bkg_tac_auc>1.0E-10) sf2=pxl_tac_auc/bkg_tac_auc;
269 if(sf2>2.0) sf2=2.0; // Limit for the scale
270 for(int i=0; i<n; i++) {
271 double g=img->m[zi][yi][xi][i]-sf2*bkgtac[i];
272 wss2+=fdur[i]*g*g;
273 }
274
275 /* Calculate WSS between this TAC and scaled mean TAC */
276 double wss3=0.0; double sf3=pxl_tac_auc/mean_tac_auc;
277 if(sf3<0.5) sf3=0.5; // Limit for the scale
278 for(int i=0; i<n; i++) {
279 double g=img->m[zi][yi][xi][i]-sf3*meany[i];
280 wss3+=fdur[i]*g*g;
281 }
282
283 /* Compute AICs */
284 double aic0=aicSS(wss0, n, 0);
285 double aic1=aicSS(wss1, n, 0);
286 double aic2=aicSS(wss2, n, 1);
287 double aic3=aicSS(wss3, n, 1);
288 /* Conserve pixel value if aic3 is better than others */
289 if(aic3<aic0 && aic3<aic1 && aic3<aic2) {
290 timg->m[zi][yi][xi][0]=1.0;
291 } else {
292 timg->m[zi][yi][xi][0]=0.0;
293 }
294 if(verbose>10) {
295 printf(" p[%d][%d][%d]: wss0=%g wss1=%g wss2=%g wss3=%g; aic0=%g aic1=%g aic2=%g aic3=%g\n",
296 zi, yi, xi, wss0, wss1, wss2, wss3, aic0, aic1, aic2, aic3);
297 printf(" -> %g\n", timg->m[zi][yi][xi][0]);
298 } else if(verbose>1 && yi==img->dimy-1 && xi==img->dimx-1 && timg->m[zi][yi][xi][0]==1.0) {
299 printf(" p[%d][%d][%d]: wss0=%g wss1=%g wss2=%g wss3=%g; aic0=%g aic1=%g aic2=%g aic3=%g\n",
300 zi, yi, xi, wss0, wss1, wss2, wss3, aic0, aic1, aic2, aic3);
301 printf(" sf2=%g sf3=%g\n", sf2, sf3);
302 printf(" -> %g\n", timg->m[zi][yi][xi][0]);
303 }
304 }
305 }
306 } // next zi
307 if(verbose>1) fprintf(stdout, "... done.\n");
308
309
310 if(status!=NULL) strcpy(status, "ok");
311 return(0);
312}
314/*****************************************************************************/
315
316/*****************************************************************************/
320int main(int argc, char **argv)
321{
322 int ai, help=0, version=0, verbose=1;
323 char petfile[FILENAME_MAX], maskfile[FILENAME_MAX], borderfile[FILENAME_MAX];
324 int keepNegat=0; // 0=off; 1=on
325 int bkgMethod=0; // 0=zeroes; 1=x,y-border
326 int ret=0;
327
328
329 /*
330 * Get arguments
331 */
332 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
333 petfile[0]=maskfile[0]=borderfile[0]=(char)0;
334 /* Get options */
335 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
336 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
337 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
338 if(strncasecmp(cptr, "KEEPNEGAT", 5)==0) {
339 keepNegat=1; continue;
340 } else if(strncasecmp(cptr, "BORDERTAC=", 10)==0) {
341 strlcpy(borderfile, cptr+10, FILENAME_MAX); if(borderfile[0]) continue;
342 } else if(strncasecmp(cptr, "BKG=", 4)==0) {
343 cptr+=4; if(strncasecmp(cptr, "ZERO", 1)==0) {bkgMethod=0; continue;}
344 if(strncasecmp(cptr, "BORDER", 1)==0) {bkgMethod=1; continue;}
345 }
346 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
347 return(1);
348 } else break;
349
350 /* Print help or version? */
351 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
352 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
353 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
354
355 /* Process other arguments, starting from the first non-option */
356 if(ai<argc) {strlcpy(petfile, argv[ai], FILENAME_MAX); ai++;}
357 if(ai<argc) {strlcpy(maskfile, argv[ai], FILENAME_MAX); ai++;}
358 if(ai<argc) {fprintf(stderr, "Error: too many arguments.\n"); return(1);}
359
360
361 /* Did we get all the information that we need? */
362 if(!petfile[0]) {
363 fprintf(stderr, "Error: missing command-line argument; use option --help\n");
364 return(1);
365 }
366
367
368 /* In verbose mode print arguments and options */
369 if(verbose>1) {
370 printf("petfile := %s\n", petfile);
371 if(maskfile[0]) printf("maskfile := %s\n", maskfile);
372 if(borderfile[0]) printf("borderfile := %s\n", borderfile);
373 printf("keepNegat := %d\n", keepNegat);
374 }
375
376
377 /*
378 * Read the contents of the PET file to img data structure
379 */
380 if(verbose>0) {printf("reading %s\n", petfile); fflush(stdout);}
381 IMG img; imgInit(&img);
382 ret=imgRead(petfile, &img);
383 if(ret) {
384 fprintf(stderr, "Error: %s\n", img.statmsg);
385 if(verbose>1) printf("ret=%d\n", ret);
386 imgEmpty(&img); return(2);
387 }
388 if(verbose>1) {
389 printf("dimt := %d\n", img.dimt);
390 printf("dimx := %d\n", img.dimx);
391 printf("dimy := %d\n", img.dimy);
392 printf("dimz := %d\n", img.dimz);
393 printf("type := %d\n", img.type);
394 }
395 /* Check that we have dynamic data */
396 if(img.dimt<2) {
397 fprintf(stderr, "Error: method works only for dynamic data.\n");
398 imgEmpty(&img); return(2);
399 }
400 if(imgNaNs(&img, 1)>0)
401 if(verbose>0) fprintf(stderr, "Warning: missing pixel values.\n");
402 /* Check that frame times are available */
403 if(!imgExistentTimes(&img)) {
404 if(img.dimt>1) fprintf(stderr, "Warning: unknown frame times.\n");
405 }
406
407 /* For raw data, divide counts by frame duration */
408 ret=imgRawCountsPerTime(&img, 1);
409 if(ret) {
410 fprintf(stderr, "Error: cannot correct sinogram counts.\n");
411 if(verbose>1) printf("ret=%d\n", ret);
412 imgEmpty(&img); return(2);
413 }
414
415
416 /*
417 * Calculate border TAC, if requested
418 */
419 if(borderfile[0]) {
420 if(verbose>0) fprintf(stdout, "calculating the TAC of border pixels\n");
421 float bv[img.dimt], bs[img.dimt], bm[img.dimt];
422 if(imgBorderAverageTAC(&img, bv, bs, bm)!=0) {
423 fprintf(stderr, "Error: cannot calculate TAC of border pixels.\n");
424 imgEmpty(&img); return(3);
425 }
426 DFT dft; dftInit(&dft);
427 if(dftAllocateWithIMG(&dft, 3, &img)!=0) {
428 fprintf(stderr, "Error: cannot make TAC of border pixels.\n");
429 imgEmpty(&img); return(3);
430 }
431 for(int i=0; i<dft.frameNr; i++) dft.voi[0].y[i]=bv[i];
432 for(int i=0; i<dft.frameNr; i++) dft.voi[1].y[i]=bs[i];
433 for(int i=0; i<dft.frameNr; i++) dft.voi[2].y[i]=bm[i];
434 strcpy(dft.voi[0].voiname, "Mean"); strcpy(dft.voi[0].name, dft.voi[0].voiname);
435 strcpy(dft.voi[1].voiname, "SD"); strcpy(dft.voi[1].name, dft.voi[1].voiname);
436 strcpy(dft.voi[2].voiname, "Median"); strcpy(dft.voi[2].name, dft.voi[2].voiname);
437 ret=dftWrite(&dft, borderfile);
438 if(ret) {
439 fprintf(stderr, "Error in writing '%s': %s\n", borderfile, dfterrmsg);
440 imgEmpty(&img); dftEmpty(&dft); return(11);
441 }
442 if(verbose>0) printf("TAC of border pixels written in %s\n", borderfile);
443 dftEmpty(&dft);
444 }
445
446
447 /*
448 * Make the mask
449 */
450 if(verbose>0) fprintf(stdout, "calculating the mask\n");
451 IMG mask; imgInit(&mask);
452 char buf[128];
453 ret=imgNoiseTemplate(&img, keepNegat, bkgMethod, &mask, verbose-1, buf);
454 if(ret) {
455 fprintf(stderr, "Error: %s.\n", buf);
456 if(verbose>1) printf("ret=%d\n", ret);
457 imgEmpty(&img); imgEmpty(&mask); return(4);
458 }
459 if(imgMaskCount(&mask)<1) {
460 fprintf(stderr, "Error: whole image would be considered as noise.\n");
461 imgEmpty(&img); imgEmpty(&mask); return(4);
462 }
463
464
465 /*
466 * If user gave file name for mask, then save it, and quit
467 */
468 if(maskfile[0]) {
469 imgEmpty(&img);
470 if(verbose>1) fprintf(stdout, "writing mask image...\n");
471 ret=imgWrite(maskfile, &mask);
472 if(ret) {
473 fprintf(stderr, "Error: %s\n", mask.statmsg);
474 imgEmpty(&mask); return(11);
475 }
476 imgEmpty(&mask);
477 if(verbose>0) printf("Written %s\n", maskfile);
478 return(0);
479 }
480
481
482 /*
483 * Otherwise apply the mask to the original PET data and save it.
484 */
485 if(verbose>1) fprintf(stdout, "applying the mask\n");
486 ret=imgThresholdByMask(&img, &mask, 0.0);
487 imgEmpty(&mask);
488 if(ret) {
489 fprintf(stderr, "Error: cannot apply mask to PET data.\n");
490 imgEmpty(&img); return(9);
491 }
492 if(verbose>1) fprintf(stdout, "writing masked image...\n");
493 ret=imgWrite(petfile, &img);
494 if(ret) {
495 fprintf(stderr, "Error: %s\n", img.statmsg);
496 imgEmpty(&img); return(12);
497 }
498 imgEmpty(&img);
499 if(verbose>0) printf("Written modified %s\n", petfile);
500
501 return(0);
502}
503/*****************************************************************************/
504
505/*****************************************************************************/
double aicSS(double ss, const int n, const int k)
Definition aic.c:20
void dftInit(DFT *data)
Definition dft.c:38
char dfterrmsg[64]
Definition dft.c:6
void dftEmpty(DFT *data)
Definition dft.c:20
int dftWrite(DFT *data, char *filename)
Definition dftio.c:594
int imgExistentTimes(IMG *img)
Definition img.c:613
unsigned long long imgNaNs(IMG *img, int fix)
Definition img.c:658
int imgAllocateWithHeader(IMG *image, int planes, int rows, int columns, int frames, IMG *image_from)
Definition img.c:279
void imgEmpty(IMG *image)
Definition img.c:121
void imgInit(IMG *image)
Definition img.c:60
int imgRawCountsPerTime(IMG *img, int operation)
Definition imgarithm.c:442
int imgBorderAverageTAC(IMG *img, float *tac, float *sd, float *med)
Definition imgbkgrm.c:85
int imgNoiseTemplate(IMG *img, int keepNegat, int border, IMG *timg, int verbose, char *status)
Definition imgbkgrm.c:137
int imgAverageTAC(IMG *img, float *tac)
Definition imgeval.c:15
int imgRead(const char *fname, IMG *img)
Definition imgfile.c:26
int imgWrite(const char *fname, IMG *img)
Definition imgfile.c:136
float fmean(float *data, long long int n, float *sd)
Definition imgminmax.c:618
float fmedian(float *data, long long int n)
Definition imgminmax.c:593
int imgThresholdByMask(IMG *img, IMG *templt, float thrValue)
Header file for libtpccurveio.
Header file for libtpcimgio.
#define IMG_STATUS_OCCUPIED
Header file for libtpcimgp.
long long imgMaskCount(IMG *img)
Definition mask.c:15
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 libtpcmodel.
Header file for libtpcmodext.
int dftAllocateWithIMG(DFT *dft, int tacNr, IMG *img)
Definition misc_model.c:296
Voi * voi
int frameNr
unsigned short int dimx
char type
float **** m
char status
unsigned short int dimt
float * start
unsigned short int dimz
unsigned short int dimy
float * end
const char * statmsg
char voiname[MAX_REGIONSUBNAME_LEN+1]
double * y
char name[MAX_REGIONNAME_LEN+1]