[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