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

svn_grass at osgeo.org svn_grass at osgeo.org
Tue Jul 3 08:52:42 PDT 2012


Author: momsen
Date: 2012-07-03 08:52:42 -0700 (Tue, 03 Jul 2012)
New Revision: 52290

Modified:
   grass-addons/grass7/imagery/i.segment/create_isegs.c
   grass-addons/grass7/imagery/i.segment/iseg.h
   grass-addons/grass7/imagery/i.segment/parse_args.c
   grass-addons/grass7/imagery/i.segment/times.txt
   grass-addons/grass7/imagery/i.segment/write_output.c
Log:
added final merge step for small segments (ignoring threshold).  Random color table will be assigned to the segment map.

Modified: grass-addons/grass7/imagery/i.segment/create_isegs.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/create_isegs.c	2012-07-03 11:56:31 UTC (rev 52289)
+++ grass-addons/grass7/imagery/i.segment/create_isegs.c	2012-07-03 15:52:42 UTC (rev 52290)
@@ -366,7 +366,7 @@
 		files->candidate_count);
 
 	/*process candidate pixels */
-	G_verbose_message("Row percent complete for pass number %d: ", t);
+	G_verbose_message("\nRow percent complete for pass number %d: ", t);
 	/*check each pixel, start the processing only if it is a candidate pixel */
 	/* for validation, select one of the two... could make this IFDEF or input parameter */
 	/* reverse order 
@@ -407,7 +407,7 @@
 
 		    /* Setting Ri to be not a candidate allows using "itself" when at edge of raster. TODO THIS NEEDS TO BE CHANGED!!!!
 		     * Otherwise need to use a list/count/something to know the number of pixel neighbors */
-		    set_candidate_flag(Ri_head, 0, files);	/* TODO: error trap? */
+		    set_candidate_flag(Ri_head, FALSE, files);	/* TODO: error trap? */
 		    G_debug(4, "line 165, \t\t\t\tcc = %d",
 			    files->candidate_count);
 
@@ -434,7 +434,7 @@
 			G_debug(4, "2a, Segment had no valid neighbors");	/*this could happen if there is a segment surrounded by pixels that have already been processed */
 			pathflag = FALSE;
 			Ri_count = 0;
-			set_candidate_flag(Ri_head, 0, files);	/* TODO: error trap? */
+			set_candidate_flag(Ri_head, FALSE, files);	/* TODO: error trap? */
 			files->candidate_count++;	/* already counted out Ri[0]; */
 			G_debug(4, "line 176, \t\t\t\tcc = %d",
 				files->candidate_count);
@@ -555,7 +555,7 @@
 					Ri_similarity, Rk_similarity);
 
 				/* did this at beginning of path loop */
-				set_candidate_flag(Ri_head, 0, files);	/* remove all Ri members from candidate pixels (set flag) */
+				set_candidate_flag(Ri_head, FALSE, files);	/* remove all Ri members from candidate pixels (set flag) */
 				files->candidate_count++;	/* add one back, we had already set Ri[0] flag at the beginning. */
 				G_debug(4, "line 247, \t\t\t\tcc = %d",
 					files->candidate_count);
@@ -577,7 +577,7 @@
 			     * because we checked already Ri for a mutually best neighbor with all valid candidates
 			     * thus Ri can not be the mutually best neighbor later on during this pass
 			     * unfortunately this does happen sometimes */
-			    set_candidate_flag(Ri_head, 0, files);	/* TODO: error trap? */
+			    set_candidate_flag(Ri_head, FALSE, files);	/* TODO: error trap? */
 			    files->candidate_count++;	/*first pixel was already set */
 			    G_debug(4,
 				    "3b Rk didn't didn't exist, was not valid candidate, or similarity was > threshold");
@@ -589,7 +589,7 @@
 #ifdef REVERSE
 	    G_percent(files->nrows - row, files->nrows, 1);
 #else
-	    G_percent(row, files->nrows, 1);	/* TODO, can a message be included with G_percent? */
+	    G_percent(row, files->nrows-1, 1);	/* TODO, can a message be included with G_percent? */
 #endif
 	    /* TODO, the REVERSE version gets printed on a new line, and isnt' covered.  The else version is. ? */
 	    /* TODO, shows up in CLI, not in GUI */
@@ -601,7 +601,6 @@
 #endif
 
     }				/*next row */
-
     /* finished one pass for processing candidate pixels */
 
     G_debug(4, "Finished one pass, t was = %d", t);
@@ -609,7 +608,200 @@
     }
     while (t <= functions->end_t && endflag == FALSE) ;
     /*end t loop *//*TODO, should there be a max t that it can iterate for?  Include t in G_message? */
+	if(endflag == FALSE) G_warning(_("Merging processes stopped due to reaching max iteration limit, more merges may be possible"));
 
+	/* ****************************************************************************************** */
+	/* final pass, ignore threshold and force a merge for small segments with their best neighbor */
+	
+	if (functions->min_segment_size > 1) {
+		G_verbose_message("Final iteration, force merges for small segments.");
+		
+		/* TODO: It would be possible to use some sort of "forced merge" flag and if statements in the above code.
+ * This might be easier to maintain... but I wasn't sure which would be easier to read
+ * and it would add some extra if statements to each iteration...
+ * 
+ * for the final forced merge, the candidate flag is just to keep track if we have confirmed if:
+ * 		a. the segment size is >= to the minimum allowed size  or
+ * 		b. we have merged it with its best neighbor
+ */
+	/* TODO: repeating this twice, move to helper function? */
+	
+	/* Set candidate flag to true/1 for all pixels TODO: for polygon/vector constraint, need to just set to true for those being processed */
+	if (files->bounds_map == NULL) {	/*normal processing */
+	    for (row = 0; row < files->nrows; row++) {
+		for (col = 0; col < files->ncols; col++) {
+		    /* 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 */
+		    if (!(FLAG_GET(files->null_flag, row, col))) {
+			FLAG_SET(files->candidate_flag, row, col);	/*candidate pixel flag */
+
+			files->candidate_count++;
+		    }		/* Need something to prevent "pathflag" infinite loop */
+		}
+	    }
+	}
+	else {			/* polygon constraints/boundaries were supplied, include that criteria.  TODO: this repeats a lot of code, is there a way to combine this check without having too many extra if/etc statements ??? */
+	    for (row = 0; row < files->nrows; row++) {
+		for (col = 0; col < files->ncols; col++) {
+		    if (!(FLAG_GET(files->null_flag, row, col))) {
+
+			segment_get(&files->bounds_seg, &files->bounds_val,
+				    row, col);
+
+			if (files->bounds_val == files->current_bound) {
+			    /*TODO could move this if statement one line up, and only set "1" flags if we can assume all flags are already zero.  (i.e. only get/put the ones we want to set to 1.) */
+			    FLAG_SET(files->candidate_flag, row, col);	/*candidate pixel flag */
+			    files->candidate_count++;	/*TODO this assumes full grid with no null or mask!! But need something to prevent "pathflag" infinite loop */
+			}
+			//~ else   !!!TODO is it safe to assume that all flag's are zero at this point?
+			//~ FLAG_UNSET(files->candidate_flag, row, col);
+
+		    }
+		}
+	    }
+	}
+
+
+	for (row = 0; row < files->nrows; row++) {
+	for (col = 0; col < files->ncols; col++) {
+
+		if (FLAG_GET(files->candidate_flag, row, col)) {
+		    /*free memory for linked lists */
+		    my_dispose_list(files->token, &Ri_head);
+		    my_dispose_list(files->token, &Rk_head);
+		    my_dispose_list(files->token, &Rin_head);
+		    my_dispose_list(files->token, &Rkn_head);
+		    Rk_count = 0;
+
+		    /* First pixel in Ri is current row/col pixel.  We may add more later if it is part of a segment */
+		    Ri_count = 1;
+		    newpixel = (struct pixels *)link_new(files->token);
+		    newpixel->next = Ri_head;
+		    newpixel->row = row;
+		    newpixel->col = col;
+		    Ri_head = newpixel;
+
+		    G_debug(4, "Next starting pixel: row, %d, col, %d",
+			    Ri_head->row, Ri_head->col);
+
+		    set_candidate_flag(Ri_head, FALSE, files);	/* TODO: error trap? */
+		    G_debug(4, "line 165, \t\t\t\tcc = %d",
+			    files->candidate_count);
+
+		    /* find segment neighbors */
+		    if (find_segment_neighbors
+			(&Ri_head, &Rin_head, &Ri_count, files,
+			 functions) != TRUE) {
+			G_fatal_error("find_segment_neighbors() failed");
+		    }
+
+		    if (Rin_head != NULL)  /*found neighbors */
+		    {
+				if (Ri_count >= functions->min_segment_size) /* don't force a merge */
+					set_candidate_flag(Ri_head, FALSE, files);
+					
+				else /* Merge with most similar neighbor */
+				{
+					G_debug(4, "2b, Found Ri's pixels");
+					
+					/*print out neighbors */
+					for (current = Ri_head; current != NULL;
+						 current = current->next)
+						G_debug(4, "Ri: row: %d, col: %d", current->row,
+							current->col);
+
+					G_debug(4, "2b, Found Ri's neighbors");
+					/*print out neighbors */
+					for (current = Rin_head; current != NULL;
+						 current = current->next)
+						G_debug(4, "Rin: row: %d, col: %d", current->row,
+							current->col);
+
+					/* find Ri's most similar neighbor */
+					Ri_bestn = NULL;
+					Ri_similarity = LDBL_MAX;	/* set current similarity to max value */
+					segment_get(&files->bands_seg, (void *)files->bands_val, Ri_head->row, Ri_head->col);	/* current segment values */
+
+					for (current = Rin_head; current != NULL; current = current->next) {	/* for each of Ri's neighbors */
+						tempsim =
+						(*functions->calculate_similarity) (Ri_head,
+											current,
+											files,
+											functions);
+						G_debug(4,
+							"simularity = %g for neighbor : row: %d, col %d.",
+							tempsim, current->row, current->col);
+
+						if (tempsim < Ri_similarity) {
+						Ri_similarity = tempsim;
+						Ri_bestn = current;	/*TODO want to point to the current pixel...confirm  when current changes need this to stay put! */
+						G_debug(4,
+							"Current lowest Ri_similarity = %g, for neighbor pixel row: %d col: %d",
+							Ri_similarity, Ri_bestn->row,
+							Ri_bestn->col);
+						}
+					}
+
+					if (Ri_bestn != NULL)
+						G_debug(4,
+							"Lowest Ri_similarity = %g, for neighbor pixel row: %d col: %d",
+							Ri_similarity, Ri_bestn->row,
+							Ri_bestn->col);
+				
+					if (Ri_bestn != NULL)
+					{
+						/* we'll have the neighbor pixel to start with. */
+						G_debug(4, "3a: Working with Rk");
+						Rk_count = 1;
+						newpixel =
+						(struct pixels *)link_new(files->token);
+						newpixel->next = NULL;	/* or = Rk_head; *//*TODO, should this be Rk_head or just NULL? amounts to the same thing here? */
+						newpixel->row = Ri_bestn->row;
+						newpixel->col = Ri_bestn->col;
+						Rk_head = newpixel;
+						/* TODO - lists starting, should this be a helper function, did at start of Ri and Rk.  */
+
+						/* using this just to get the full pixel/cell membership list for Rk */
+						find_segment_neighbors(&Rk_head, &Rkn_head, &Rk_count, files, functions);	/* data structure for Rk's neighbors, and pixels in Rk if we don't already have it */
+
+						G_debug(4, "Found Rk's pixels");
+						/*print out neighbors */
+						for (current = Rk_head; current != NULL;
+						 current = current->next)
+						G_debug(4, "Rk: row: %d, col: %d",
+							current->row, current->col);
+
+						G_debug(4, "Found Rk's neighbors");
+						/*print out neighbors */
+						for (current = Rkn_head; current != NULL;
+						 current = current->next)
+						G_debug(4, "Rkn: row: %d, col: %d",
+							current->row, current->col);
+
+						merge_values(Ri_head, Rk_head, Ri_count, Rk_count, files);	/* TODO error trap */
+
+						/* merge_values sets Ri and Rk candidate flag to FALSE.  Put Rk back to TRUE if the size is too small. */
+						if(Ri_count + Rk_count < functions->min_segment_size)
+							set_candidate_flag(Rk_head, TRUE, files);
+					} /* end if best neighbor != null */
+					else
+					G_warning("No best neighbor found in final merge, this shouldn't happen?");
+					
+					
+				}		/* end else - pixel count was below minimum allowed */
+		    } /* end if neighbors found */
+		    else
+				G_warning("no neighbors found, this should NOT happen on merge step.");
+		    
+		}		/*end if pixel is candidate pixel */
+	}			/*next column */
+	G_percent(row, files->nrows, 1);
+    }			/*next row */
+	} /* end if for force merge */
+	else
+		G_verbose_message(_("Minimum pixels for group was set to 1, no final forced merge iteration for small segments."));
+
     /* free memory *//*TODO: anything ? */
 
 

Modified: grass-addons/grass7/imagery/i.segment/iseg.h
===================================================================
--- grass-addons/grass7/imagery/i.segment/iseg.h	2012-07-03 11:56:31 UTC (rev 52289)
+++ grass-addons/grass7/imagery/i.segment/iseg.h	2012-07-03 15:52:42 UTC (rev 52290)
@@ -86,7 +86,8 @@
     int method;			/* Segmentation method */
     int num_pn;			/* number of pixel neighbors  int, 4 or 8. TODO: can remove if pixel neighbors is list instead of array.  But maybe this one is small enough that is faster as array? */
     float threshold;		/* similarity threshold */
-
+	int min_segment_size;		/* smallest number of pixels/cells allowed in a final segment */
+	
     /* Some function pointers to set one time in parse_args() */
     int (*find_pixel_neighbors) (int, int, int[8][2], struct files *);	/*parameters: row, col, pixel_neighbors */
     double (*calculate_similarity) (struct pixels *, struct pixels *, struct files *, struct functions *);	/*parameters: two points (row,col) to compare */

Modified: grass-addons/grass7/imagery/i.segment/parse_args.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/parse_args.c	2012-07-03 11:56:31 UTC (rev 52289)
+++ grass-addons/grass7/imagery/i.segment/parse_args.c	2012-07-03 15:52:42 UTC (rev 52290)
@@ -12,7 +12,7 @@
 {
     /* reference: http://grass.osgeo.org/programming7/gislib.html#Command_Line_Parsing */
 
-    struct Option *group, *seeds, *bounds, *output, *method, *threshold;	/* Establish an Option pointer for each option */
+    struct Option *group, *seeds, *bounds, *output, *method, *threshold, *min_segment_size;	/* Establish an Option pointer for each option */
     struct Flag *diagonal, *weighted;	/* Establish a Flag pointer for each option */
     struct Option *outband, *endt;	/* debugging parameters... TODO: leave in code or remove?  hidden options? */
 
@@ -21,6 +21,7 @@
 
     output = G_define_standard_option(G_OPT_R_OUTPUT);
 
+/*TODO polish: any way to recommend a threshold to the user */
     threshold = G_define_option();
     threshold->key = "threshold";
     threshold->type = TYPE_DOUBLE;
@@ -35,6 +36,14 @@
     method->options = "region_growing, io_debug, ll_test";	/* TODO at end, remove these from list: io_debug just writes row+col to each output pixel, ll_test for testing linked list data structure */
     method->description = _("Segmentation method.");
 
+	min_segment_size = G_define_option();
+	min_segment_size->key = "min"; /*TODO is there a preference for long or short key names? min is pretty generic...but short... */
+	min_segment_size->type = TYPE_INTEGER;
+	min_segment_size->required = YES;
+	min_segment_size->answer = "10"; /* TODO, should a "typical" default be provided? */
+	min_segment_size->options = "1-100000"; /*must be positive number, is >0 allowed in "options" or is 100,000 suitably large? */
+	min_segment_size->description = _("Minimum number of pixels (cells) in a segment.  The final merge step will ignore the threshold for any segments with fewer pixels.");
+    
     /* optional parameters */
 
     diagonal = G_define_flag();
@@ -72,8 +81,9 @@
     endt = G_define_option();
     endt->key = "endt";
     endt->type = TYPE_INTEGER;
-    endt->required = YES;	/* TODO, could put as optional, and if not supplied put in something large. */
-    endt->description = _("Maximum number of time steps to complete.");
+    endt->required = NO;
+    endt->answer = "10000";
+    endt->description = _("Debugging...Maximum number of time steps to complete.");
 
     outband = G_define_standard_option(G_OPT_R_OUTPUT);
     outband->key = "final_mean";
@@ -103,6 +113,7 @@
 	G_fatal_error("Invalid output raster name.");
 
     functions->threshold = atof(threshold->answer);	/* Note: this threshold is scaled after we know more at the beginning of create_isegs() */
+
     if (weighted->answer == FALSE &&
 	(functions->threshold <= 0 || functions->threshold >= 1))
 	G_fatal_error(_("threshold should be >= 0 and <= 1"));	/* TODO OK to have fatal error here, seems this would be an invalid entry. */
@@ -120,6 +131,8 @@
 
     G_debug(1, "segmentation method: %d", functions->method);
 
+	functions->min_segment_size = atoi(min_segment_size->answer);
+
     if (diagonal->answer == FALSE) {
 	functions->find_pixel_neighbors = &find_four_pixel_neighbors;
 	functions->num_pn = 4;
@@ -165,7 +178,11 @@
 	    G_fatal_error("Invalid output raster name for means.");
     }
 
-    functions->end_t = atoi(endt->answer);	/* Note: this threshold is scaled after we know more at the beginning of create_isegs() */
-
+	if (endt->answer != NULL && endt->answer >= 0)
+    functions->end_t = atoi(endt->answer);
+	else {
+		functions->end_t = 10000;
+		G_warning(_("invalid number of iterations, 10000 will be used."));
+	}
     return TRUE;
 }

Modified: grass-addons/grass7/imagery/i.segment/times.txt
===================================================================
--- grass-addons/grass7/imagery/i.segment/times.txt	2012-07-03 11:56:31 UTC (rev 52289)
+++ grass-addons/grass7/imagery/i.segment/times.txt	2012-07-03 15:52:42 UTC (rev 52290)
@@ -5,9 +5,15 @@
 cols:       527
 cells:      250325
 
-still running...
+-w threshold=5
 
+Pass number 1210
 
+real	432m20.014s
+user	429m55.276s
+sys	1m0.640s
+
+
 g.region s=222000 e=637000
 cells:      56334
 < 5 minutes
@@ -19,8 +25,20 @@
 13 min (just one time test)
 
 
+g.region s=223500
+cells: 92752
+Pass number 206:
+real	4m53.324s
 
+g.region s=222000
+cells: 120683
+pass 294
+real	7m22.592s
 
+g.region s=219000
+cells: 176018
+41min
+
 typical i.segment options:
 time i.segment -w --overwrite group=landsat1 at user1 output=val threshold=5 method=region_growing endt=1000 final_mean=val_mean --verbose
 

Modified: grass-addons/grass7/imagery/i.segment/write_output.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/write_output.c	2012-07-03 11:56:31 UTC (rev 52289)
+++ grass-addons/grass7/imagery/i.segment/write_output.c	2012-07-03 15:52:42 UTC (rev 52290)
@@ -16,7 +16,8 @@
     int out_fd, mean_fd, row, col;	/* mean_fd for validiating/debug of means, todo, could add array... */
     CELL *outbuf;
     DCELL *meanbuf;
-
+	struct Colors colors;
+	
     outbuf = Rast_allocate_c_buf();	/*hold one row of data to put into raster */
     meanbuf = Rast_allocate_d_buf();
 
@@ -65,10 +66,16 @@
     if (files->out_band != NULL)
 	Rast_close(mean_fd);
 
+	/* set colors */
+	Rast_init_colors(&colors);
+	Rast_make_random_colors(&colors, 1, files->nrows*files->ncols); /* TODO polish - number segments from 1 - max ? and then can use that max here. */
+	Rast_write_colors(files->out_name, G_mapset(), &colors); /* TODO, OK to just use G_mapset() here, seems I don't use it anywhere else, and that is where the output has to be written.  (Should the library allow changing colors to other mapsets???) */
+	
     /* free memory */
     G_free(outbuf);
     G_free(meanbuf);
-
+	Rast_free_colors(&colors);
+	
     return TRUE;
 }
 



More information about the grass-commit mailing list