[GRASS-SVN] r52182 - grass-addons/grass7/imagery/i.segment
svn_grass at osgeo.org
svn_grass at osgeo.org
Thu Jun 21 13:29:59 PDT 2012
Author: momsen
Date: 2012-06-21 13:29:58 -0700 (Thu, 21 Jun 2012)
New Revision: 52182
Modified:
grass-addons/grass7/imagery/i.segment/create_isegs.c
grass-addons/grass7/imagery/i.segment/iseg.h
grass-addons/grass7/imagery/i.segment/open_files.c
grass-addons/grass7/imagery/i.segment/parse_args.c
grass-addons/grass7/imagery/i.segment/write_output.c
Log:
implemented use of polygons (raster based) as constraints. Cleanup and reordering of iseg.h and parse_args.c
Modified: grass-addons/grass7/imagery/i.segment/create_isegs.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/create_isegs.c 2012-06-21 13:47:30 UTC (rev 52181)
+++ grass-addons/grass7/imagery/i.segment/create_isegs.c 2012-06-21 20:29:58 UTC (rev 52182)
@@ -4,8 +4,9 @@
#include <stdlib.h>
#include <float.h> /* to get value of LDBL_MAX -> change this if there is a more usual grass way */
- /* #include <math.h> *//* for sqrt() and pow() */
+ /* #include <math.h> *//* for sqrt() and pow() */
#include <grass/gis.h>
+#include <grass/glocale.h>
#include <grass/raster.h>
#include <grass/segment.h> /* segmentation library */
#include <grass/linkm.h> /* memory manager for linked lists */
@@ -15,30 +16,43 @@
int create_isegs(struct files *files, struct functions *functions)
{
-
+ int lower_bound, upper_bound;
int successflag = 1;
+ struct Range range;
/* TODO consider if there are _method specific_ parameter set up and memory allocation, should that happen here? */
- /*TODO: implement outer loop to process polygon interior, then all remaining pixels */
- /* This loop could go in here, or in main to contain open/create/write to reduced memory reqs. But how merge the writes? */
-
G_debug(1, "Threshold: %g", functions->threshold);
G_debug(1, "segmentation method: %d", functions->method);
functions->threshold = functions->threshold * functions->threshold * files->nbands; /* use modified threshold to account for scaled input and to avoid square root in similarity comparison. *//* Todo, small, could put this in main outside of polygon loop */
- if (functions->method == 0)
- successflag = io_debug(files, functions); /* TODO: why does it want `&files` in main, but `files` here ??? */
- else if (functions->method == 1) {
- G_debug(1, "starting region_growing()");
- successflag = region_growing(files, functions);
+ /* set parameters for outer processing loop for polygon constraints */
+ if (files->bounds_map == NULL) { /*normal processing */
+ lower_bound = upper_bound = 0;
+ } /* just one time through loop */
+ else {
+ if (Rast_read_range(files->bounds_map, files->bounds_mapset, &range) <
+ 0) {
+ G_fatal_error(_("Unable to read fp range for raster map <%s>"),
+ files->bounds_map);
+ } /* TODO, still should close files? */
+ Rast_get_range_min_max(&range, &lower_bound, &upper_bound); /* todo, faster way to do this? maybe do it manually when open and write to the segment file? But it is just once.... */
}
- else if (functions->method == 2)
- successflag = ll_test(files, functions);
- /* end outer loop for processing polygons */
+ for (files->current_bound = lower_bound; files->current_bound <= upper_bound; files->current_bound++) { /* outer processing loop for polygon constraints */
+ G_debug(1, "current_bound = %d", files->current_bound);
+ if (functions->method == 0)
+ successflag = io_debug(files, functions); /* TODO: why does it want `&files` in main, but `files` here ??? */
+ else if (functions->method == 1) {
+ G_debug(1, "starting region_growing()");
+ successflag = region_growing(files, functions);
+ }
+ else if (functions->method == 2)
+ successflag = ll_test(files, functions);
+ } /* end outer loop for processing polygons */
+
/* clean up? */
@@ -304,6 +318,8 @@
Rkn_head = NULL;
Ri_bestn = NULL;
+ /* TODO, want to get a min/max row/col to narrow the processing window ??? */
+
do {
/* do while loop on t to slowly lower threshold. also check that endflag==0 (no merges were made) */
@@ -314,33 +330,53 @@
endflag = 1;
/* 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++) {
+ 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: consider splitting this, put flag in one segmentmentation file (or RAM), and segment assignment in another. */
+ /* 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);
- 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: consider splitting this, put flag in one segmentmentation file (or RAM), and segment assignment in another. */
- /* 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);
+ files->candidate_count++; /*TODO this assumes full grid with no null or mask!! But 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++) {
+ segment_get(&files->bounds_seg, &files->bounds_val, row,
+ col);
+ segment_get(&files->out_seg, (void *)files->out_val, row,
+ col);
- files->candidate_count++; /*TODO this assumes full grid with no null or mask!! But need something to prevent "pathflag" infinite loop */
+ 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.) */
+ files->out_val[1] = 1; /*candidate pixel flag */
+ else
+ files->out_val[1] = 0;
+ segment_put(&files->out_seg, (void *)files->out_val, row,
+ col);
+
+ files->candidate_count++; /*TODO this assumes full grid with no null or mask!! But need something to prevent "pathflag" infinite loop */
+ }
}
}
+
G_debug(4, "Starting to process %d candidate pixels",
files->candidate_count);
/*process candidate pixels */
-
+ G_verbose_message("Row percent complete for pass number %d: ", t);
/*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++) {
- /* G_verbose_message("Completion for pass number %d: ", t); */
- G_percent(row, files->nrows, 1); /*this didn't get displayed in the output??? Does it get erased when done? */
+ G_percent(row, files->nrows, 1); /* TODO, can a message be included with G_percent? */
G_debug(4,
"Next starting pixel from next row/col, not from Rk");
Modified: grass-addons/grass7/imagery/i.segment/iseg.h
===================================================================
--- grass-addons/grass7/imagery/i.segment/iseg.h 2012-06-21 13:47:30 UTC (rev 52181)
+++ grass-addons/grass7/imagery/i.segment/iseg.h 2012-06-21 20:29:58 UTC (rev 52182)
@@ -23,6 +23,7 @@
int col;
};
+/* input and output files, as well as some other processing info */
struct files
{
/* user parameters */
@@ -32,19 +33,25 @@
/* region info */
int nrows, ncols;
- /* files */
- int nbands;
- int candidate_count; /*how many candidate pixels remain */
- SEGMENT bands_seg, out_seg; /* bands is for input, normal application is landsat bands, but other input can be included in the group. */
+ /* files *//* TODO, for all map names, is this better for any reason, saw it in manual example: char name[GNAME_MAX]; */
+ char *out_name; /* name of output raster map */
+ char *seeds, *bounds_map, *bounds_mapset; /* optional segment seeds and polygon constraints/boundaries */
+
+ /* file processing */
+ int nbands; /* number of rasters in the image group */
+ SEGMENT bands_seg, out_seg, bounds_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 */
double *second_val; /* to hold values at second point for similarity comparison */
- int *out_val; /* array, to hold the segment ID and processing flag(s) */
- char *out_name; /* name of output raster map */
+ int *out_val, bounds_val, current_bound; /* out_val is array, to hold the segment ID and processing flag(s) */
SEGMENT no_check; /* pixels that have already been checked during this neighbor finding routine */
+ /* memory management, linked lists */
struct link_head *token; /* for linkm linked list memory management. */
+ /* other info */
+ int candidate_count; /*how many candidate pixels remain */
+
/* 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. */
/*
@@ -71,15 +78,13 @@
struct functions
{
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 */
/* 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 */
-
- 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 */
-
};
Modified: grass-addons/grass7/imagery/i.segment/open_files.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/open_files.c 2012-06-21 13:47:30 UTC (rev 52181)
+++ grass-addons/grass7/imagery/i.segment/open_files.c 2012-06-21 20:29:58 UTC (rev 52182)
@@ -10,9 +10,10 @@
int open_files(struct files *files)
{
struct Ref Ref; /* group reference list */
- int *in_fd;
+ int *in_fd, bounds_fd;
int n, row, col, srows, scols, inlen, outlen, nseg;
DCELL **inbuf; /* buffer array, to store lines from each of the imagery group rasters */
+ CELL *boundsbuf;
struct FPRange *fp_range; /* for getting min/max values on each input raster */
DCELL *min, *max;
@@ -94,7 +95,7 @@
scols, inlen, nseg) != 1)
G_fatal_error("Unable to create input temporary files");
- G_debug(1, "finished segment_open(...bands_seg...)");
+ /* G_debug(1, "finished segment_open(...bands_seg...)"); */
/* TODO: signed integer gives a 2 billion segment limit, depending on how the initialization is done, this means 2 billion max input pixels. */
if (segment_open
@@ -144,9 +145,33 @@
}
}
+ /* bounds/constraints */
+ if (files->bounds_map != NULL) {
+ if (segment_open
+ (&files->bounds_seg, G_tempfile(), files->nrows, files->ncols,
+ srows, scols, sizeof(int), nseg) != 1)
+ G_fatal_error("Unable to create bounds temporary files");
+
+ boundsbuf = Rast_allocate_c_buf();
+ bounds_fd = Rast_open_old(files->bounds_map, files->bounds_mapset); /*OK to use directly, or need to convert to name and mapset? */
+
+ for (row = 0; row < files->nrows; row++) {
+ Rast_get_c_row(bounds_fd, boundsbuf, row);
+ for (col = 0; col < files->ncols; col++) {
+ files->bounds_val = boundsbuf[col];
+ segment_put(&files->bounds_seg, &files->bounds_val, row, col);
+ }
+ }
+ Rast_close(bounds_fd);
+ G_free(boundsbuf);
+ }
+ else {
+ G_debug(1, "no boundary constraint supplied.");
+ }
+
+ /* other info */
files->candidate_count = 0; /* counter for remaining candidate pixels */
-
/* linked list memory management linkm */
link_set_chunk_size(20); /* TODO: fine tune this number */
@@ -155,6 +180,7 @@
/* Free memory */
for (n = 0; n < Ref.nfiles; n++) {
+ /* G_free(inbuf[n]); TODO - didn't have this line to start with, but should it be added? */
Rast_close(in_fd[n]);
}
@@ -163,7 +189,6 @@
G_free(fp_range);
G_free(min);
G_free(max);
-
/* Need to clean up anything else? */
return 0;
Modified: grass-addons/grass7/imagery/i.segment/parse_args.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/parse_args.c 2012-06-21 13:47:30 UTC (rev 52181)
+++ grass-addons/grass7/imagery/i.segment/parse_args.c 2012-06-21 20:29:58 UTC (rev 52182)
@@ -12,35 +12,29 @@
{
/* reference: http://grass.osgeo.org/programming7/gislib.html#Command_Line_Parsing */
- struct Option *group, *seeds, *output, *method, *threshold; /* Establish an Option pointer for each option */
+ struct Option *group, *seeds, *bounds, *output, *method, *threshold; /* Establish an Option pointer for each option */
struct Flag *diagonal, *weighted; /* Establish a Flag pointer for each option */
- group = G_define_standard_option(G_OPT_I_GROUP);
+ /* required parameters */
+ group = G_define_standard_option(G_OPT_I_GROUP); /* TODO: OK to require the user to create a group? Otherwise later add an either/or option to give just a single raster map... */
- /* TODO: OK to require the user to create a group? Otherwise later add an either/or option to give just a single raster map... */
+ output = G_define_standard_option(G_OPT_R_OUTPUT);
- /* Using raster for seeds, Low priority TODO: allow vector points/centroids seed input. */
- seeds = G_define_standard_option(G_OPT_R_INPUT);
- seeds->key = "seeds";
- seeds->type = TYPE_STRING;
- seeds->required = NO;
- seeds->description = _("Optional raster map with starting seeds.");
+ threshold = G_define_option();
+ threshold->key = "threshold";
+ threshold->type = TYPE_DOUBLE;
+ threshold->required = YES;
+ threshold->description = _("Similarity threshold.");
- output = G_define_standard_option(G_OPT_R_OUTPUT);
-
method = G_define_option();
method->key = "method";
method->type = TYPE_STRING;
- method->required = NO;
+ method->required = YES;
method->answer = "region_growing";
method->options = "region_growing, io_debug, ll_test"; /*io_debug just writes row+col to each output pixel, ll_test for testing linked list data structure */
method->description = _("Segmentation method.");
- threshold = G_define_option();
- threshold->key = "threshold";
- threshold->type = TYPE_DOUBLE;
- threshold->required = YES;
- threshold->description = _("Similarity threshold.");
+ /* optional parameters */
diagonal = G_define_flag();
diagonal->key = 'd';
@@ -52,36 +46,57 @@
weighted->description =
_("Weighted input, don't perform the default scaling of input maps.");
+ /* Using raster for seeds, Low priority TODO: allow vector points/centroids seed input. */
+ seeds = G_define_standard_option(G_OPT_R_INPUT);
+ seeds->key = "seeds";
+ seeds->type = TYPE_STRING;
+ seeds->required = NO;
+ seeds->description = _("Optional raster map with starting seeds.");
+
+ /* Polygon constraints. */
+ bounds = G_define_standard_option(G_OPT_R_INPUT);
+ bounds->key = "bounds";
+ bounds->type = TYPE_STRING;
+ bounds->required = NO;
+ bounds->description =
+ _("Optional bounding/constraining raster map, must be integer values, each area will be segmented independent of the others.");
+ /* bounds->description = _("Optional vector map with polygons to bound (constrain) segmentation."); */
+ /* TODO: if performing second segmentation, will already have raster map from this module
+ * If have preexisting boundaries (landuse, etc) will have vector map
+ * Seems we need to have it in raster format for processing, is it OK to have the user run v.to.rast before doing the segmentation?
+ * Or for hierarchical segmentation, will it be easier to have the polygons?
+ * ....will start with rasters, quickest to implement.... */
+
/* TODO input for distance function */
if (G_parser(argc, argv))
exit(EXIT_FAILURE);
- G_debug(1, "For the option <%s> you chose: <%s>",
- group->description, group->answer);
+ /* G_debug(1, "For the option <%s> you chose: <%s>",
+ group->description, group->answer);
- G_debug(1, "For the option <%s> you chose: <%s>",
- seeds->description, seeds->answer);
+ G_debug(1, "For the option <%s> you chose: <%s>",
+ seeds->description, seeds->answer);
- G_debug(1, "For the option <%s> you chose: <%s>",
- output->description, output->answer);
+ G_debug(1, "For the option <%s> you chose: <%s>",
+ output->description, output->answer);
- G_debug(1, "For the option <%s> you chose: <%s>",
- method->description, method->answer);
+ G_debug(1, "For the option <%s> you chose: <%s>",
+ method->description, method->answer);
- G_debug(1, "For the option <%s> you chose: <%s>",
- threshold->description, threshold->answer);
+ G_debug(1, "For the option <%s> you chose: <%s>",
+ threshold->description, threshold->answer);
- G_debug(1, "The value of the diagonal flag is: %d", diagonal->answer);
+ G_debug(1, "The value of the diagonal flag is: %d", diagonal->answer); */
/* Validation */
- /* use checker for any of the data validation steps!? */
+ /* TODO: use checker for any of the data validation steps!? */
/* ToDo The most important things to check are if the
input and output raster maps can be opened (non-negative file
- descriptor). */
+ descriptor). Do this here or in open_files? */
/* Check and save parameters */
@@ -93,6 +108,12 @@
else
G_fatal_error("Invalid output raster name.");
+ /* TODO: I'm assuming threshold 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); */
+ sscanf(threshold->answer, "%f", &functions->threshold); /* Note: this gets changed after we know more at beginning of create_isegs() */
+
/* segmentation methods: 0 = debug, 1 = region growing */
/* TODO, instead of string compare, does the Option structure have these already numbered? */
@@ -107,12 +128,6 @@
G_debug(1, "segmentation method: %d", functions->method);
- /* TODO: I'm assuming threshold 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); */
- sscanf(threshold->answer, "%f", &functions->threshold); /* Note: this gets changed after we know more at beginning of create_isegs() */
-
if (diagonal->answer == 0) {
functions->find_pixel_neighbors = &find_four_pixel_neighbors;
functions->num_pn = 4;
@@ -126,9 +141,24 @@
files->weighted = weighted->answer; /* default/0 for performing the scaling, but selected/1 if user has weighted values so scaling should be skipped. */
- /* TODO add user input for this */
- functions->calculate_similarity = &calculate_euclidean_similarity;
+ functions->calculate_similarity = &calculate_euclidean_similarity; /* TODO add user input for this */
+ files->seeds = seeds->answer;
+
+ /*todo, check error trapping here, what if nothing is entered? what if nothing is found? what if in same mapset */
+ if (bounds->answer == NULL) { /*no polygon constraints */
+ files->bounds_map = NULL;
+ }
+ else { /* polygon constraints given */
+
+ files->bounds_map = bounds->answer; /*todo, this needs to set files->bounds = NULL if no answer was given to the parameter */
+ if ((files->bounds_mapset = G_find_raster(files->bounds_map, "")) == NULL) { /* TODO, warning here: parse_args.c:149:27: warning: assignment discards ‘const’ qualifier from pointer target type [enabled by default] */
+ G_fatal_error(_("Segmentation constraint/boundary map not found."));
+ }
+ }
+ /* todo, check raster type, needs to be CELL (integer) */
+ /*todo print out what these were before and after */
+
/* other data */
files->nrows = Rast_window_rows();
files->ncols = Rast_window_cols();
Modified: grass-addons/grass7/imagery/i.segment/write_output.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/write_output.c 2012-06-21 13:47:30 UTC (rev 52181)
+++ grass-addons/grass7/imagery/i.segment/write_output.c 2012-06-21 20:29:58 UTC (rev 52182)
@@ -49,9 +49,8 @@
segment_close(&files->bands_seg);
segment_close(&files->out_seg);
segment_close(&files->no_check);
+ segment_close(&files->bounds_seg);
- /* close segmentation files and output raster */
-
G_free(files->bands_val);
G_free(files->second_val);
G_free(files->out_val);
More information about the grass-commit
mailing list