// Todo // ==== // // Versions // ======== // 9. A serious bug in previous versions has been removed, leading to much // better segmentations. The probabilities were being computed in such // a way that they could be noticably greater than 1.0, so // the Metropolis update step was producing incorrect results. // Interestingly, this bug was originally hidden by a bit of defensive // programming. // Pixels at the boundaries are now assumed to be in the background. // 8. Random scans can now be enabled with the -r option. // 7. RGB probabilities are now computed with the Laplace Rule of Succession // by default. This is producing good results so far, but there is // an option to disable it (-R) just in case. // 6. A bug in Metropolis-Hastings was removed. The final result is saved // as a png file: ppm-seg-result.png // 5. Metropolis-Hastings updating has been added. This helps to avoid // islands of spurious background within the brain. // 4. Gibbs sampling from an MRF has been added. // 3. 3D Gaussians have been replaced by RGB histograms. // 2. -t threshold option has been added. // 1. Basic functionality // #define HAVE_GDK #ifdef HAVE_GDK #include #include #endif #include #include #include #include #include #include #include #include #ifndef min #define min(a,b) ((a)<(b)?(a):(b)) #endif #ifndef max #define max(a,b) ((a)>(b)?(a):(b)) #endif typedef struct { int r,g,b; } Color; typedef struct { double mu,sigma; } Gaussian1D; double p_fg=0.5; double p_bg=0.5; double p_rgb_given_fg[256][256][256]; double p_rgb_given_bg[256][256][256]; double p_neis_given_fg[16]; // probs for sixteen possible neighbor configurations double p_neis_given_bg[16]; double p_neis[16]; char *p_filename="p.pgm"; char *rgb_filename="foreground.ppm"; char *color_truth_filename="ground-truth.ppm"; char *class_truth_filename="ground-truth.pbm"; // options char *filename=NULL; /* of image to segment */ double threshold=1e-2; int num_gibbs_samples=3; int passes_per_gibbs_sample=4; int using_metropolis_hastings=1; int using_ros=1; /* ros means the Laplace Rule of Succession */ int using_random_scans=0; static void print_usage(void); static void do_gibbs(void); static void init_distros_from_ground_truth_images(void); static void output_simple_seg_and_p_fg_given_colors(void); int main(int argc, char *argv[]) { ppm_init(&argc, argv); #ifdef HAVE_GDK gdk_init(&argc, &argv); #endif /* Read commands from standard input, to load example images and set up the probabilities. */ { int ch; while ((ch=getopt(argc,argv,"hMp:rs:t:")) != -1) { switch(ch) { case 'h': print_usage(); exit(0); break; case 'M': using_metropolis_hastings=0; fprintf(stderr,"Metropolis-Hastings mode disabled.\n"); break; case 'p': passes_per_gibbs_sample=atoi(optarg); fprintf(stderr,"passes_per_gibbs_sample: %i\n", passes_per_gibbs_sample); if(passes_per_gibbs_sample<=0||passes_per_gibbs_sample>50) { fprintf(stderr, "Unreasonable value given for -p option: %s\n",optarg); print_usage(); exit(10); } break; case 'R': using_ros=0; fprintf(stderr,"Disabling Rule of Succession for RGBs.\n"); break; case 'r': using_random_scans=1; fprintf(stderr,"Enabling random scans for Gibbs.\n"); break; case 's': num_gibbs_samples=atoi(optarg); fprintf(stderr,"num_gibbs_samples: %i\n",num_gibbs_samples); if(num_gibbs_samples<0||num_gibbs_samples>50) { fprintf(stderr, "Unreasonable value given for -s option: %s\n",optarg); print_usage(); exit(11); } break; case 't': threshold=atof(optarg); fprintf(stderr,"threshold: %f\n",threshold); if(threshold<=0.0||threshold>=1.0) { fprintf(stderr,"Bad threshold value: %s\n",optarg); print_usage(); exit(8); } break; } } argc -= optind; argv += optind; filename=argv[0]; fprintf(stderr,"filename: %s\n",filename); } if(argc==0) { print_usage(); exit(9); } filename=argv[0]; argv++; argc--; init_distros_from_ground_truth_images(); output_simple_seg_and_p_fg_given_colors(); if(num_gibbs_samples>0) { do_gibbs(); } return 0; } static void print_usage(void) { fprintf(stderr, "\n" "Welcome to ppm-blockface-seg\n" "written in 2005 by Issac Trotts, with support from the NIH Human Brain Project.\n" "\n" "This program segments a color blockface image in PPM format.\n" "The general form for how to invoke this program is\n" "\n" " ppm-seg [options] image-to-segment.ppm\n" "\n" "For example:" "\n" " ppm-seg image-to-segment.ppm\n" "\n" "Options:\n" "=======\n" " -M : Disable Metropolis-Hastings update for Gibbs sampling.\n" " -p # : Set # passes per Gibbs sample. 1 to 50. Default is 2.\n" " -R : Disable the Laplace rule of succession for RGB probabilities.\n" " -r : Enable random scans for Gibbs sampling.\n" " -s # : Set # Gibbs samples. 1 to 50. Default is 0.\n" " -t # : Set threshold. 0 to 1, exclusive. Default is 1e-2." "\n"); } /* Load the image to segment, one line at a time. Each time a line is loaded, classify each pixel in it and write the results out. This way, we can process huge images. */ static void output_simple_seg_and_p_fg_given_colors(void) { int xi,yi,nx,ny,format; pixval maxval; pixel* pixelrow; FILE* file; double dr,dg,db; dr=1./255.; dg=1./255.; db=1./255.; file=fopen(filename,"rb"); if(!file) { fprintf(stderr,"Could not open %s for reading.\n",filename); exit(1); } ppm_readppminit(file, &nx, &ny, &maxval, &format); pixelrow=ppm_allocrow(nx); FILE *p_out,*rgb_out; p_out=fopen(p_filename,"wb"); rgb_out=fopen(rgb_filename,"wb"); if(!p_out) { fprintf(stderr,"Could not open %s for writing.\n",p_filename); exit(6); } if(!rgb_out) { fprintf(stderr,"Could not open %s for writing.\n",rgb_filename); exit(7); } fprintf(p_out,"P5\n%i %i\n255\n",nx,ny); fprintf(rgb_out,"P6\n%i %i\n255\n",nx,ny); fprintf(stderr,"Generating %s and %s\n",p_filename,rgb_filename); for(yi=0;yi=threshold) { fputc(pxl.r, rgb_out); fputc(pxl.g, rgb_out); fputc(pxl.b, rgb_out); } else { fputc(0, rgb_out); fputc(0, rgb_out); fputc(0, rgb_out); } } } fprintf(stderr," \r"); fclose(p_out); fclose(rgb_out); fclose(file); ppm_freerow(pixelrow); } static void do_gibbs(void) { int s,pass,xi,yi,nx,ny; pixval maxval; bit** bitmap; pixel** pixels; gray** opacities; /* Load the file to be segmented. */ { FILE* file=fopen(filename,"rb"); if(!file) { fprintf(stderr,"Could not open %s for reading.\n",filename); exit(12); } pixels=ppm_readppm(file,&nx,&ny,&maxval); fclose(file); } /* Allocate the bitmap and initialize it to noise. */ { bitmap=pbm_allocarray(nx,ny); fprintf(stderr,"Initializing segmentation bitmap to noise\n"); for(yi=0;yi0.5?1:0); } } fprintf(stderr," \r"); } /* Allocate the opacity map and initialize it to zeros. */ { opacities=pgm_allocarray(nx,ny); for(yi=0;yi=0.0); assert(p_fg_given_rgb_neis<=1.0); if(using_metropolis_hastings) { double p; p=p_fg_given_rgb_neis; p=bitmap[yi2][xi2]?p:1.0-p; if(drand48()<(p==0.0?1.0:min(1.0,(1.0-p)/p))) { /* flip */ bitmap[yi2][xi2]=bitmap[yi2][xi2]?0:1; } } else { bitmap[yi2][xi2]=(drand48()message); exit(15); } fprintf(stderr,"Wrote %s\n",filename); #else char filename[]="ppm-seg-result.pam"; FILE* file=fopen(filename,"wb"); if(!file) { fprintf(stderr,"Could not open %s for writing.\n",filename); exit(14); } fprintf(stderr,"Writing %s\n",filename); fprintf(file,"P7\nWIDTH %i\nHEIGHT %i\nDEPTH 4\nMAXVAL 255\n",nx,ny); fprintf(file,"TUPLTYPE RGB_ALPHA\nENDHDR\n"); for(yi=0;yi>3)&0x0001, (neis>>2)&0x0001, (neis>>1)&0x0001, (neis>>0)&0x0001, p_neis_given_fg[neis]); fprintf(stderr,"p(neis:%i%i%i%i|bg) = %1.8f\n", (neis>>3)&0x0001, (neis>>2)&0x0001, (neis>>1)&0x0001, (neis>>0)&0x0001, p_neis_given_bg[neis]); } } }