[GRASS-SVN] r52006 - grass-addons/grass7/imagery/i.segment

svn_grass at osgeo.org svn_grass at osgeo.org
Thu Jun 7 21:14:10 PDT 2012


Author: momsen
Date: 2012-06-07 21:14:09 -0700 (Thu, 07 Jun 2012)
New Revision: 52006

Modified:
   grass-addons/grass7/imagery/i.segment/create_isegs.c
   grass-addons/grass7/imagery/i.segment/iseg.h
   grass-addons/grass7/imagery/i.segment/main.c
   grass-addons/grass7/imagery/i.segment/parse_args.c
   grass-addons/grass7/imagery/i.segment/write_output.c
Log:
create_isegs.c is just a draft, much to work out still.  Other files with changes to support data structures and parameters.

Modified: grass-addons/grass7/imagery/i.segment/create_isegs.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/create_isegs.c	2012-06-07 23:03:39 UTC (rev 52005)
+++ grass-addons/grass7/imagery/i.segment/create_isegs.c	2012-06-08 04:14:09 UTC (rev 52006)
@@ -3,6 +3,7 @@
 /* Currently only region growing is implemented */
 
 #include <stdlib.h>
+#include <float.h>		/* to get value of LDBL_MAX -> change this if there is a more usual grass way */
 #include <grass/gis.h>
 #include <grass/raster.h>
 #include <grass/segment.h>	/* segmentation library */
@@ -10,23 +11,52 @@
 
 int create_isegs(struct files *files, struct functions *functions)
 {
-    int row, col, nrows, ncols;
 
-    /* **************write fake data to test I/O portion of module */
+    int successflag = 1;
 
-    /*picked this up in the file reading as well, just do it again? */
-    nrows = Rast_window_rows();
-    ncols = Rast_window_cols();
+    /* method specific parameter set up and memory allocation */
 
-    /* initialize data structure */
-    files->out_val = (int *)G_malloc(2 * sizeof(int));
+    if (functions->method == 1) {	/*region growing */
 
+	/* nothing yet */
+    }
+
+    /*TODO: implement outer loop to process polygon interior, then all remaining pixels */
+    /* This loop could go in here, or outside in main (only make segmentation file for what is currently being processed.) */
+
+    G_debug(1, "Threshold: %g", functions->threshold);
+    G_debug(1, "segmentation method: %d", functions->method);
+
+
+    if (functions->method == 0)
+	successflag = io_debug(files, functions);
+    else if (functions->method == 1)
+	successflag = region_growing(files, functions);
+
+    if (successflag != 0)
+	G_fatal_error("Error creating segments");
+
+    /* end outer loop for processing polygons */
+
+    /* clean up */
+
+    /* should there be a free() for every malloc?  Or only the large ones? */
+
+    return 0;
+}
+
+int io_debug(struct files *files, struct functions *functions)
+{
+    int row, col;
+
+    /* **************write fake data to test I/O portion of module */
+
     G_verbose_message("writing fake data to segmentation file");
-    for (row = 0; row < nrows; row++) {
-	G_percent(row, nrows, 1);	/*this didn't get displayed in the output??? Does it get erased when done? */
-	for (col = 0; col < ncols; col++) {
-	    //files->out_val[0] = files->out_val[0]; /*segment number */  /* just copying the map for testing. */
-	    files->out_val[0] = col+2*row;
+    for (row = 0; row < files->nrows; row++) {
+	G_percent(row, files->nrows, 1);	/*this didn't get displayed in the output??? Does it get erased when done? */
+	for (col = 0; col < files->ncols; col++) {
+	    /*files->out_val[0] = files->out_val[0]; *//*segment number *//* just copying the map for testing. */
+	    files->out_val[0] = col + row;
 	    files->out_val[1] = 1;	/*processing flag */
 	    segment_put(&files->out_seg, (void *)files->out_val, row, col);
 	}
@@ -36,3 +66,212 @@
 
     return 0;
 }
+
+
+int region_growing(struct files *files, struct functions *functions)
+{
+    int row, col, n, t, pixel_neighbors[8][2];	/* could dynamically declare to be only 4 or 8 elements, but this is shorter for now */
+    double threshold, Ri_simularity, Rk_simularity, tempsim;
+    int endflag;		/* =1 if there were no merges on that processing iteration */
+    int pathflag;		/* =1 if we didn't find mutual neighbors, and should continue with Rk */
+    int mergeflag;		/* =1 if we have mutually agreeing best neighbors */
+
+    /* Ri = current focus segment
+     * Rk = Ri's most similar neighbor
+     * Rkn = Rk's neighbors
+     * Rin = Ri's neigbors (as pixels or segments ?!?
+     */
+
+    G_verbose_message("Running region growing algorithm");
+
+    t = 0;
+
+    do {
+	/* for loop on t to slowly lower threshold. also check that endflag=0 */
+
+	threshold = functions->threshold;	/* when implement t loop, this will be a function of t. */
+
+	endflag = 1;
+
+
+	/* Set candidate flag to true/1 for all pixels TODO: for polygon group, need to just set to true for those being processed */
+
+	for (row = 0; row < files->nrows; row++) {
+	    for (col = 0; col < files->ncols; col++) {
+		segment_get(&files->out_seg, (void *)files->out_val, row, col);	/*need to get, since we only want to change the flag, and not overwrite the segment value. */
+		/* TODO: if we are starting from seeds...and only allow merges between unassigned pixels
+		 *  and seeds/existing segments, then this needs an if (and will be very inefficient)
+		 * maybe consider the sorted array, btree, map... but the number of seeds could still be high for a large map */
+		files->out_val[1] = 1;	/*candidate pixel flag */
+		segment_put(&files->out_seg, (void *)files->out_val, row,
+			    col);
+	    }
+	}
+
+	/*process candidate pixels */
+
+	/*check each pixel, start the processing only if it is a candidate pixel */
+	for (row = 0; row < files->nrows; row++) {
+	    for (col = 0; col < files->ncols; col++) {
+		segment_get(&files->out_seg, (void *)files->out_val, row,
+			    col);
+		if (files->out_val[1] == 1) {	/* out_val[1] is the candidate pixel flag */
+
+		    /*TODO: need to empty/reset Ri, Rn, and Rk */
+		    Ri = Rin = Rk = Rkn = NULL;
+
+		    pathflag = 1;
+
+		    while (pathflag == 1) {	/*if don't find mutual neighbors on first try, will use Rk as next Ri. */
+
+			if (find_segment_neighbors(Ri, Rin) != 0) {
+			    G_debug(1, "Couldn't find neighbors");	/*this could happen if there is a pixel surrounded by pixels that have already been processed */
+			    pathflag = 0;
+			    set candidate flag to false for this pixel;
+			}
+			else {	/*found neighbors, go ahead until find mutually agreeing neighbors */
+
+			    /* find Ri's most similar neighbor */
+			    Ri_similarity
+				= LDBL_MAX;
+
+			    for (each Rin) {
+				tempsim = calculate_simularity(Ri, one neighbor);	/*set up as function pointer... */
+				if tempsim
+				    <Ri_similarity {
+				    Ri_similarity = tempsim;
+				    Rk = current neighbor;
+				    }
+			    }
+
+			    if (Rk != null(need correct null finding !) AND Ri_similarity < threshold) {	/* small TODO: should this be < or <=? */
+
+				Rkn = Ri;	/* we know Ri should be a neighbor of Rk *//*Todo: is there a way to skip similarity calculations on these?  keep a count, and pop them before doing the similarity check? */
+				find_segment_neighbors(Rk, Rkn);	/* data structure for Rk's neighbors, and pixels in Rk if we don't already have it */
+
+				/*find Rk's most similar neighbor */
+				Rk_similarity = Ri_simularity;	/*Ri gets first priority - ties won't change anything, so we'll accept Ri and Rk as mutually best neighbors */
+
+				for (each Rkn) {
+				    tempsim =
+					calculate_similarity(Rk,
+							     one neighbor);
+				    if (tempsim < Rk_similarity) {
+					Rk_similarity = tempsim;
+					break;	/* exit loop here, we know that Ri and Rk aren't mutually best neighbors */
+				    }
+				}
+
+				if (Rk_similarity == Ri_simularity) {	/* so they agree, both are mutually most similar neighbors */
+				    /* put these steps in merge_segments(Ri, Rk) function?  */
+				    update segment values for all pixels in Ri + Rk(mean)	/* I assume this is a weighted mean? */
+					set candidate flag to false for all pixels in Ri + Rk	/* do this at the same time, so there is only one segment_put statement */
+					  endflag = 0;	/* we've made at least one merge, so need another iteration */
+
+				    pathflag = 0;	/* go to next row,column pixel - end of Rk -> Ri chain since we found neighbors */
+				}
+				else {	/* they weren't mutually best neighbors */
+				    set candidate flag to false for all pixels in Ri;	/* TODO !!!!!!!!!!!! hmm, maybe this raster should be its own data structure */
+
+				    Rk
+					= Ri;	/* note, this is the eCognition technique.  Seems this is a bit faster, we already have segment membership pixels */
+				}
+			    }	/*end if Rk exists and < threshold */
+			}	/* end else - Ri did have neighbors */
+		    }		/*end pathflag do loop */
+		}		/*end if pixel is candidate pixel */
+	    }			/*next column */
+	}			/*next row */
+
+	/* finished one pass for processing candidate pixels */
+
+	t++;
+    } while (endflag == 0);
+    /*end t loop */
+
+    /* TODO: free memory */
+
+    return 0;
+}
+
+int find_segment_neighbors(Ri, Rin)
+{
+
+    /* neighbor list will be a listing of pixels that are neighbors?  Include segment numbers?  Only include unique segments?
+     * Maybe the most complete return would be a structure array, structure to include the segment ID and a list of points in it?  
+     * But the list of points would NOT be inclusive - just the points bordering the current segment...
+     */
+
+
+    /* Ri could be single pixel or list of pixels. */
+    /* Rin could have a list already, or could be empty ?  Or just the head?  */
+
+    /*local data structures... but maybe they should be allocated out in the main function, is it really slow to create/free on each pass? */
+
+    /* Ri : input parameter, list of pixels in the current segment */
+    to_check;			/* queue or stack - need to check the neighbors of these pixels */
+    no_check;			/* sorted array or btree: list of pixels (by row / column ?) that have been put into the to_check queue, been processed, or are not candidate pixels */
+
+    current_pixel;		/*what data type?  This will be the popped pixel in each loop. */
+    /* functions->pixel_neighbors  ...array, 4 or 8 long.  (Can be 4 or 8 neighbors to each pixel)
+     * functions->num_pn  int, 4 or 8.
+
+     to_check = Ri; /*need to copy data, not just pointer... */
+    /* Put input in "current segment" list  NOTE: in pseudo code, but think we should just pass Ri and use it directly */
+    no_check = Ri;		/*need to copy data, not just pointer... */
+    /* empty "neighbor" list  Note: in pseudo code, but think we just pass in Rin - it was already initialized, and later could have Ri data available to start from */
+
+    While(!empty(to_check)) {
+	current_pixel = pop next to_check element;
+	functions->(*find_pixel_neighbors) (current_pixel, pixel_neighbors);
+
+	for (n = 0; n < functions->num_pn; n++) {
+	    if (!pixel_neighbor[n] in "don't check" list) {
+		put pixel_neighbor[n] in "don't check" list segment_get(&files->out_seg, (void *)files->out_val, pixel_neighbor[n][0], pixel_neighbor[n][1]);	/*TODO : do I need a second "out_val" data structure? */
+
+		if (files->out_val[1] == 1) {	/* valid candidate pixel */
+		    put pixel_neighbor[n] in to_check;	/*want to check this pixels neighbors */
+
+		    if (files->out_val[0] = current segment ID)
+			put pixel_neighbor[n] in Ri
+			else
+		  put pixel_neighbor[n] in Rin}
+	    }			/*end if for pixel_neighbor was in "don't check" list */
+	}			/* end for loop - next neighbor */
+    }				/* while to_check has more elements */
+
+    return 0;
+}
+
+find_four_pixel_neighbors(pixel, pixel_neighbors)
+{
+    /* north */
+    pixel_neighbors[0][1] = pixel column;
+    if (pixel row > 0)
+	pixel_neighbors[0][0] = pixel row + 1;
+    else
+	pixel_neighbors[0][0] = pixel row;	/*This is itself, which will be in "already checked" list.  TODO: use null or -1 as flag to skip?  What is fastest to process? */
+
+    /* east */
+    pixel_neighbors[1][0] = pixel row;
+    if (pixel column < files->ncols)
+	pixel_neighbors[1][1] = pixel column + 1;
+    else
+	pixel_neighbors[1][1] = pixel column;	/* ditto... */
+
+    /*TODO: continue for south and north */
+
+    /*TODO: seems there should be a more elegent way to do this... */
+    return 0;
+}
+
+find_eight_pixel_neighbors(pixel, neighbors)
+{
+    /* get the 4 orthogonal neighbors */
+    find_four_pixel_neighbors(pixel, neighbors);
+
+    /* get the 4 diagonal neighbors */
+
+    /*TODO... continue as above */
+    return 0;
+}

Modified: grass-addons/grass7/imagery/i.segment/iseg.h
===================================================================
--- grass-addons/grass7/imagery/i.segment/iseg.h	2012-06-07 23:03:39 UTC (rev 52005)
+++ grass-addons/grass7/imagery/i.segment/iseg.h	2012-06-08 04:14:09 UTC (rev 52006)
@@ -18,18 +18,25 @@
 {
     /* user parameters */
     char *image_group;
-    
+
+    /* region info */
+    int nrows, ncols;
+
+    /* files */
     int nbands;
-    SEGMENT bands_seg, out_seg; /* bands is for input, normal application is landsat bands, but other input can be included in the group. */
+    SEGMENT bands_seg, out_seg;	/* bands is for input, normal application is landsat bands, but other input can be included in the group. */
     double *bands_val;		/* array, to hold all input values at one pixel */
-	int *out_val;			/* array, to hold the segment ID and processing flag(s) */
-    
+    int *out_val;		/* array, to hold the segment ID and processing flag(s) */
+    char *out_name;		/* name of output raster map */
+
+    /* RASTER_MAP_TYPE data_type;       Removed: input is always DCELL, output is CELL. 
+     *  TODO: if input might be smaller then DCELL, we could detect size and allocate accordingly. */
     /*
-    * Todo: Memory managment:
-    * If we want to implement the option to load directly to memory
-    * instead of declaring "SEGMENT" could we declare as void
-    * then assign either a SEGMENT or a matrix during the get_input procedure?
-    */
+     * Todo: Memory managment:
+     * If we want to implement the option to load directly to memory
+     * instead of declaring "SEGMENT" could we declare as void
+     * then assign either a SEGMENT or a matrix during the get_input procedure?
+     */
 
     /* TODO: some parts of the data structure from i.smap
      * confirm if there is any reason to be using them here
@@ -43,28 +50,27 @@
      * Need to consider if they should be put in RAM or SEGMENT?
      */
 
-    char *out_name;		/* name of output raster map */
-    /* RASTER_MAP_TYPE data_type;	Removed: input is always DCELL, output is CELL. 
-     *  TODO: if input might be smaller then DCELL, we could detect size and allocate accordingly. */
 };
 
 
 /* I think if I use function pointers, I can set up one time in the input
-* what similarity function, etc, will be used later in the processing
-* and make it easier to add additional variations later.
-*/
+ * what similarity function, etc, will be used later in the processing
+ * and make it easier to add additional variations later.
+ */
 
 struct functions
 {
+    int method;			/* Segmentation method */
+    int (*find_pixel_neighbors) (int *[], int *[][]);	/*pixel, pixel_neighbors *//*TODO need to define data types for these! */
 
     /*based on options, such as diagonal neighbors, etc, some function pointers:
-    *      find_neighbor  - point to euclidean or manhattan or ... neighbor function
-    *      calc_simularity
-*/
+     *      calc_simularity
+     */
 
-    
-    float threshold; /* similarity threshold */
+    int num_pn;			/* number of pixel neighbors  int, 4 or 8. */
 
+    float threshold;		/* similarity threshold */
+
 };
 
 /* parse_args.c */
@@ -74,9 +80,12 @@
 /* open_files.c */
 int open_files(struct files *);
 
+/* create_isegs.c */
+int create_isegs(struct files *, struct functions *);
+int io_debug(struct files *, struct functions *);
+int region_growing(struct files *, struct functions *);
+int find_segment_neighbors(Ri, Rin);	/* TODO: need data structure for Ri, Rin */
+
 /* write_output.c */
 /* also currently closes files */
 int write_output(struct files *);
-
-/* create_isegs.c */
-int create_isegs(struct files *, struct functions *);

Modified: grass-addons/grass7/imagery/i.segment/main.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/main.c	2012-06-07 23:03:39 UTC (rev 52005)
+++ grass-addons/grass7/imagery/i.segment/main.c	2012-06-08 04:14:09 UTC (rev 52006)
@@ -34,17 +34,23 @@
     module = G_define_module();
     G_add_keyword(_("imagery"));
     G_add_keyword(_("segmentation"));
-    module->description = _("Outputs a single segmention map (raster) based on input values in an image group.");
+    module->description =
+	_("Outputs a single segmention map (raster) based on input values in an image group.");
 
-    parse_args(argc, argv, &files, &functions);
-	G_debug(1, "Main: starting open_files()");
-	open_files(&files);
-	
+    if (parse_args(argc, argv, &files, &functions) != 0)
+	G_fatal_error("Error in parse_args()");
+
+    G_debug(1, "Main: starting open_files()");
+    if (open_files(&files) != 0)
+	G_fatal_error("Error in open_files()");
+
     G_debug(1, "Main: starting create_isegs()");
-    create_isegs(&files, &functions);
+    if (create_isegs(&files, &functions) != 0)
+	G_fatal_error("Error in create_isegs()");
 
-    G_debug(1, "starting write_output...");
-    write_output(&files);
+    G_debug(1, "Main: starting write_output()");
+    if (write_output(&files) != 0)
+	G_fatal_error("Error in write_output()");
 
     G_done_msg("Number of segments created: ");
 

Modified: grass-addons/grass7/imagery/i.segment/parse_args.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/parse_args.c	2012-06-07 23:03:39 UTC (rev 52005)
+++ grass-addons/grass7/imagery/i.segment/parse_args.c	2012-06-08 04:14:09 UTC (rev 52006)
@@ -1,8 +1,10 @@
 /* PURPOSE:      Parse and validate the input */
 
 #include <stdlib.h>
+#include <string.h>
 #include <grass/gis.h>
 #include <grass/glocale.h>
+#include <grass/raster.h>
 #include "iseg.h"
 
 int parse_args(int argc, char *argv[], struct files *files,
@@ -31,7 +33,7 @@
     method->type = TYPE_STRING;
     method->required = NO;
     method->answer = "region_growing";
-    method->options = "region_growing, io_debug";
+    method->options = "region_growing, io_debug";	/*io_debug just writes row+col to each output pixel */
     method->description = _("Segmentation method.");
 
     threshold = G_define_option();
@@ -77,21 +79,50 @@
 
 
     /* Check and save parameters */
-	
-	files->image_group = group->answer;
-	
-    sscanf(threshold->answer, "%f", &functions->threshold);
 
+    files->image_group = group->answer;
+
     /* TODO: I'm assuming it is already validated as a number.  Is this OK, or is there a better way to cast the input? */
     /* reference r.cost line 313 
        if (sscanf(opt5->answer, "%d", &maxcost) != 1 || maxcost < 0)
        G_fatal_error(_("Inappropriate maximum cost: %d"), maxcost); */
 
-	if (G_legal_filename(output->answer)==1)
-		files->out_name = output->answer;	/* name of output raster map */
-	else
-		G_fatal_error("Invalid output raster name.");
+    if (G_legal_filename(output->answer) == 1)
+	files->out_name = output->answer;	/* name of output raster map */
+    else
+	G_fatal_error("Invalid output raster name.");
 
+    /* segmentation methods:  0 = debug, 1 = region growing */
 
+    if (strncmp(method->answer, "io_debug", 5) == 0)
+	functions->method = 0;
+    else if (strncmp(method->answer, "region_growing", 10) == 0)
+	functions->method = 1;
+    else
+	G_fatal_error("Couldn't assign segmentation method.");	/*shouldn't be able to get here */
+
+    G_debug(1, "segmentation method: %d", functions->method);
+
+
+    sscanf(threshold->answer, "%f", &functions->threshold);
+
+    if (diagonal->answer == 0) {
+	functions->find_pixel_neighbors = &find_four_pixel_neighbors;
+	functions->num_pn = 4;
+	G_debug(1, "four pixel neighborhood");
+    }
+    else if (diagonal->answer == 1) {
+	functions->find_pixel_neighbors = &find_eight_pixel_neighbors;
+	functions->num_pn = 8;
+	G_debug(1, "eight (3x3) pixel neighborhood");
+    }
+
+    /* note from tutorial: You may have got to use the complete name of the member function 
+     * including class-name and scope-operator (::).) */
+
+    /* other data */
+    files->nrows = Rast_window_rows();
+    files->ncols = Rast_window_cols();
+
     return 0;
 }

Modified: grass-addons/grass7/imagery/i.segment/write_output.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/write_output.c	2012-06-07 23:03:39 UTC (rev 52005)
+++ grass-addons/grass7/imagery/i.segment/write_output.c	2012-06-08 04:14:09 UTC (rev 52006)
@@ -9,15 +9,11 @@
 
 int write_output(struct files *files)
 {
-    int out_fd, row, col, nrows, ncols;
+    int out_fd, row, col;
     CELL *outbuf;
 
     outbuf = Rast_allocate_c_buf();	/*hold one row of data to put into raster */
 
-    /*picked this up in the file reading as well, just do it again? */
-    nrows = Rast_window_rows();
-    ncols = Rast_window_cols();
-
     /* Todo: return codes are 1 for these, need to check and react to errors? programmer's manual didn't include it... */
 
     segment_flush(&files->out_seg);	/* force all data to disk */
@@ -30,9 +26,9 @@
     /* transfer data from segmentation file to raster */
     /* output segmentation file: each element includes the segment ID then processing flag(s).  So just need the first part of it. */
 
-    for (row = 0; row < nrows; row++) {
-	G_percent(row, nrows, 1); /*TODO: Why isn't this getting printed? */
-	for (col = 0; col < ncols; col++) {
+    for (row = 0; row < files->nrows; row++) {
+	G_percent(row, files->nrows, 1);	/*TODO: Why isn't this getting printed? */
+	for (col = 0; col < files->ncols; col++) {
 	    segment_get(&files->out_seg, (void *)files->out_val, row, col);
 	    G_debug(5, "outval[0] = %i", files->out_val[0]);
 	    outbuf[col] = files->out_val[0];	/*just want segment assignment, not the other processing flag(s) */



More information about the grass-commit mailing list