[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