[GRASS-SVN] r52742 - grass-addons/grass7/imagery/i.segment
svn_grass at osgeo.org
svn_grass at osgeo.org
Sun Aug 19 23:01:19 PDT 2012
Author: momsen
Date: 2012-08-19 23:01:18 -0700 (Sun, 19 Aug 2012)
New Revision: 52742
Removed:
grass-addons/grass7/imagery/i.segment/testing.c
grass-addons/grass7/imagery/i.segment/times.txt
Modified:
grass-addons/grass7/imagery/i.segment/create_isegs.c
grass-addons/grass7/imagery/i.segment/i.segment.html
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:
updated manual, removed development/debug statements, pathflag is no longer an option (always will use Rk if not mutually most similar).
Modified: grass-addons/grass7/imagery/i.segment/create_isegs.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/create_isegs.c 2012-08-19 23:07:43 UTC (rev 52741)
+++ grass-addons/grass7/imagery/i.segment/create_isegs.c 2012-08-20 06:01:18 UTC (rev 52742)
@@ -13,18 +13,14 @@
#include <grass/rbtree.h> /* Red Black Tree library functions */
#include "iseg.h"
-#include <time.h> //todo polish...remove at end
-
-#ifdef DEBUG
-#include <limits.h>
+#ifdef PROFILE
+#include <time.h>
#endif
-#define LINKM
-
/* This will do a typical rowmajor processing of the image(s).
* Commenting it out will switch to z-order processing.
* Z-order does NOT support the bounds option, and assumes a somewhat square rectangle (otherwise the power of 2 square could overrun the long long maximum). */
-#define ROWMAJOR /* note: gives nesting error with INDENT program since this changes the for loops. comment out when running INDENT. */
+#define ROWMAJOR /* note: gives nesting error with INDENT program since this changes the for loops. comment out when running INDENT. */
int create_isegs(struct files *files, struct functions *functions)
{
@@ -124,24 +120,12 @@
* so need to increment by one */
files->maxrow++;
files->maxcol++;
- G_debug(1,
- "minrow: %d, maxrow: %d, nrows: %d, mincol: %d, maxcol: %d, ncols: %d",
- files->minrow, files->maxrow, files->nrows, files->mincol,
- files->maxcol, files->ncols);
/* run the segmentation algorithm */
if (functions->method == 1) {
successflag = region_growing(files, functions);
}
-#ifdef DEBUG
- else if (functions->method == 0)
- successflag = io_debug(files, functions);
- else if (functions->method == 2)
- successflag = ll_test(files, functions);
- else if (functions->method == 3)
- successflag = seg_speed_test(files, functions);
-#endif
/* check if something went wrong */
if (successflag == FALSE)
@@ -162,7 +146,7 @@
}
- return successflag; /* todo, successflag was assuming one pass... don't have anything to pick up a failure if there is a bounds constraint */
+ return successflag;
}
int region_growing(struct files *files, struct functions *functions)
@@ -171,12 +155,20 @@
double threshold, Ri_similarity, Rk_similarity, tempsim;
int endflag; /* =TRUE if there were no merges on that processing iteration */
int pathflag; /* =TRUE if we didn't find mutual neighbors, and should continue with Rk */
- struct pixels *Ri_head, *Rk_head, *Rin_head, *Rkn_head, //, *Rclose_head, *Rc_head, *Rc_tail, *Rcn_head,
- *current, *newpixel, *Ri_bestn;
- int Ri_count, Rk_count; //, Rc_count; /* number of pixels/cells in Ri and Rk */
+ struct pixels *Ri_head, *Rk_head, *Rin_head, *Rkn_head,
+ *current, *newpixel, *Ri_bestn;
+ int Ri_count, Rk_count; /* number of pixels/cells in Ri and Rk */
+
+#ifndef ROWMAJOR
unsigned long int z, end_z; /* only used for z-order */
int j;
+#endif
+#ifdef VCLOSE
+ struct pixels *Rclose_head, *Rc_head, *Rc_tail, *Rcn_head;
+ int Rc_count;
+#endif
+
#ifdef PROFILE
clock_t start, end;
clock_t merge_start, merge_end;
@@ -208,9 +200,11 @@
Rin_head = NULL;
Rkn_head = NULL;
Ri_bestn = NULL;
- //~ Rclose_head = NULL;
- //~ Rc_head = NULL;
- //~ Rcn_head = NULL;
+#ifdef VCLOSE
+ Rclose_head = NULL;
+ Rc_head = NULL;
+ Rcn_head = NULL;
+#endif
/* One paper mentioned gradually lowering the threshold at each iteration.
* if this is implemented, move this assignment inside the do loop and make it a function of t. */
@@ -221,7 +215,9 @@
#ifdef PROFILE
pass_start = clock();
#endif
- /* fprintf(stdout, "pass %d\n", t); */
+#ifdef SIGNPOST
+ fprintf(stdout, "pass %d\n", t);
+#endif
G_debug(3, "####### Starting outer do loop! t = %d #######", t);
if (files->bounds_map == NULL)
G_percent(t, functions->end_t, 1);
@@ -279,7 +275,6 @@
continue; /* slight speed enhancement, if z-order is helpful, figure out how to skip to the next z that is in the processing area. */
}
#endif
- G_debug(4, "Starting pixel from next row/col, not from Rk");
if (FLAG_GET(files->candidate_flag, row, col) &&
FLAG_GET(files->seeds_flag, row, col)) {
@@ -289,7 +284,9 @@
my_dispose_list(files->token, &Rk_head);
my_dispose_list(files->token, &Rin_head);
my_dispose_list(files->token, &Rkn_head);
- //~ my_dispose_list(files->token, &Rclose_head);
+#ifdef VCLOSE
+ my_dispose_list(files->token, &Rclose_head);
+#endif
Rk_count = 0;
/* First pixel in Ri is current row/col pixel. We may add more later if it is part of a segment */
@@ -328,21 +325,6 @@
if (Rin_head != NULL) { /*found neighbors, find best neighbor then see if is mutually best neighbor */
-#ifdef DEBUG
- /*print out segment membership */
- G_debug(4, "2b, Found Ri's pixels");
- for (current = Ri_head; current != NULL;
- current = current->next)
- G_debug(4, "Ri: row: %d, col: %d",
- current->row, current->col);
- /*print out neighbors */
- G_debug(4, "2b, Found Ri's neighbors");
- for (current = Rin_head; current != NULL;
- current = current->next)
- G_debug(4, "Rin: row: %d, col: %d",
- current->row, current->col);
-#endif
-
/* ******** find Ri's most similar neighbor ******** */
Ri_bestn = NULL;
Ri_similarity = threshold + 1; /* set current similarity to max value */
@@ -353,72 +335,64 @@
current = current->next) {
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);
+#ifdef VCLOSE
/* if very close, will merge, but continue checking other neighbors */
- //~ if (tempsim < functions->very_close * threshold){
- //~ /* add to Rclose list */
- //~ newpixel = (struct pixels *)link_new(files->token);
- //~ newpixel->next = Rclose_head;
- //~ newpixel->row = current->row;
- //~ newpixel->col = current->col;
- //~ Rclose_head = newpixel;
- //~ }
+ if (tempsim < functions->very_close * threshold) {
+ /* add to Rclose list */
+ newpixel =
+ (struct pixels *)link_new(files->token);
+ newpixel->next = Rclose_head;
+ newpixel->row = current->row;
+ newpixel->col = current->col;
+ Rclose_head = newpixel;
+ }
/* If "sort of" close, merge only if it is the mutually most similar */
- //~ else
+ else
+#endif
if (tempsim < Ri_similarity) {
Ri_similarity = tempsim;
Ri_bestn = current;
- G_debug(4,
- "Current lowest Ri_similarity = %g, for neighbor pixel row: %d col: %d",
- Ri_similarity, Ri_bestn->row,
- Ri_bestn->col);
}
} /* finished similiarity check for all neighbors */
/* *** merge all the "very close" pixels/segments *** */
/* doing this after checking all Rin, so we don't change the bands_val between similarity comparisons
- * TODO... but that leaves the possibility that we have the wrong best Neighbor after doing these merges...
+ * ... but that leaves the possibility that we have the wrong best Neighbor after doing these merges...
* but it seems we can't put this merge after the Rk/Rkn portion of the loop, because we are changing the available neighbors
* ...maybe this extra "very close" idea has to be done completely differently or dropped??? */
- //~ for (current = Rclose_head; current != NULL;
- //~ current = current->next) {
- //~ my_dispose_list(files->token, &Rc_head);
- //~ my_dispose_list(files->token, &Rcn_head);
- //~
- //~ /* get membership of neighbor segment */
- //~ Rc_count = 1;
- //~ newpixel =
- //~ (struct pixels *)link_new(files->token);
- //~ newpixel->next = NULL;
- //~ newpixel->row = current->row;
- //~ newpixel->col = current->col;
- //~ Rc_head = Rc_tail = newpixel;
- //~ find_segment_neighbors(&Rc_head, &Rcn_head, &Rc_count, files, functions); /* just to get members, not looking at neighbors now */
- //~ merge_values(Ri_head, Rc_head, Ri_count,
- //~ Rc_count, files);
- //~
- //~ /* Add Rc pixels to Ri */
- //~ Rc_tail->next = Ri_head;
- //~ Ri_head = Rc_head;
- //~
- //~ //todo, recurse? Check all Rcn neighbors if they are very close?
- //~ // not needed if the combining works... my_dispose_list(files->token, &Rc_head);
- //~ Rc_head = NULL;
- //~ my_dispose_list(files->token, &Rcn_head);
- //~ }
- //~ my_dispose_list(files->token, &Rclose_head);
+#ifdef VCLOSE
+ for (current = Rclose_head; current != NULL;
+ current = current->next) {
+ my_dispose_list(files->token, &Rc_head);
+ my_dispose_list(files->token, &Rcn_head);
+ /* get membership of neighbor segment */
+ Rc_count = 1;
+ newpixel =
+ (struct pixels *)link_new(files->token);
+ newpixel->next = NULL;
+ newpixel->row = current->row;
+ newpixel->col = current->col;
+ Rc_head = Rc_tail = newpixel;
+ find_segment_neighbors(&Rc_head, &Rcn_head, &Rc_count, files, functions); /* just to get members, not looking at neighbors now */
+ merge_values(Ri_head, Rc_head, Ri_count,
+ Rc_count, files);
+
+ /* Add Rc pixels to Ri */
+ Rc_tail->next = Ri_head;
+ Ri_head = Rc_head;
+
+ /*to consider, recurse? Check all Rcn neighbors if they are very close? */
+
+ Rc_head = NULL;
+ my_dispose_list(files->token, &Rcn_head);
+ }
+ my_dispose_list(files->token, &Rclose_head);
+#endif
+
/* check if we have a bestn that is valid to use to look at Rk */
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);
-
- //todo this "limited" flag will probably be removed? Then this entire if section could be removed if we always allow multiple merges per pass?
if ((functions->limited == TRUE) && !
(FLAG_GET
(files->candidate_flag,
@@ -431,11 +405,11 @@
if (Ri_bestn != NULL && Ri_similarity < threshold) { /* small TODO: should this be < or <= for threshold? */
/* Rk starts from Ri's best neighbor */
if (Rk_head) {
- G_warning("Rk_head is not NULL!");
+ G_warning(_("Rk_head is not NULL!"));
my_dispose_list(files->token, &Rk_head);
}
if (Rkn_head) {
- G_warning("Rkn_head is not NULL!");
+ G_warning(_("Rkn_head is not NULL!"));
my_dispose_list(files->token, &Rkn_head);
}
Rk_count = 1;
@@ -450,21 +424,6 @@
&Rk_count, files,
functions);
-#ifdef DEBUG
- /*print out neighbors */
- G_debug(4, "Found Rk's pixels");
- for (current = Rk_head; current != NULL;
- current = current->next)
- G_debug(4, "Rk: row: %d, col: %d",
- current->row, current->col);
- /*print out neighbors */
- G_debug(4, "Found Rk's neighbors");
- for (current = Rkn_head; current != NULL;
- current = current->next)
- G_debug(4, "Rkn: row: %d, col: %d",
- current->row, current->col);
-#endif
-
/* ******** find Rk's most similar neighbor ******** */
Rk_similarity = Ri_similarity; /*Ri gets first priority - ties won't change anything, so we'll accept Ri and Rk as mutually best neighbors */
segment_get(&files->bands_seg, (void *)files->bands_val, Rk_head->row, Rk_head->col); /* current segment values */
@@ -501,9 +460,6 @@
endflag = FALSE; /* we've made at least one merge, so want another t iteration */
}
else { /* they weren't mutually best neighbors */
- G_debug(4,
- "Ri was not Rk's best neighbor, Ri_sim: %g, Rk_sim, %g",
- Ri_similarity, Rk_similarity);
/* checked Ri once, didn't find a mutually best neighbor, so remove all members of Ri from candidate pixels for this iteration */
set_candidate_flag(Ri_head, FALSE, files);
@@ -523,8 +479,6 @@
* thus Ri can not be the mutually best neighbor later on during this pass
* unfortunately this does happen sometimes */
set_candidate_flag(Ri_head, FALSE, files);
- G_debug(4,
- "3b Ri's best neighbor was not valid candidate, or their similarity was > threshold");
}
} /* end if(Rin_head != NULL) */
@@ -534,9 +488,7 @@
}
if (pathflag) { /*initialize Ri, Rin, Rk, Rin using Rk as Ri. */
- /* So for the next iteration, lets start with Rk as the focus segment */
- /* Seems this should be a bit faster, since we already have segment membership pixels */
- /* TODO: this shortened each iteration time by about 10% but increased the number of iterations by 20% ?!?!?!? */
+ /* For the next iteration, lets start with Rk as the focus segment */
if (functions->path == TRUE) {
Ri_count = Rk_count;
Rk_count = 0;
@@ -557,7 +509,7 @@
}
} /*end pathflag do loop */
- } /*end if pixel is candidate and seed pixel */
+ } /*end if pixel is candidate and seed pixel */
} /*next column (or next z) */
#ifdef ROWMAJOR
} /*next row */
@@ -570,7 +522,6 @@
/* finished one iteration over entire raster */
- G_debug(4, "Finished one pass, t was = %d", t);
t++;
}
while (t <= functions->end_t && endflag == FALSE) ; /*end t loop, either reached max iterations or didn't merge any segments */
@@ -583,7 +534,20 @@
G_message(_("Merging processes stopped due to reaching max iteration limit, more merges may be possible"));
+
/* ****************************************************************************************** */
+ /* speed enhancement: after < 3 (?) merges are made in one pass, switch processing modes.
+ * It seems a significant portion of the time is spent merging small pixels into the largest
+ * segments. So consider allowing multiple merges after finding one large Ri.
+ *
+ * Maybe related to this, and/or integrated into the first loops: If a segment has only one neighbor
+ * go ahead and merge it if similarity is < threshold.
+ *
+ * ****************************************************************************************** */
+
+
+
+ /* ****************************************************************************************** */
/* final pass, ignore threshold and force a merge for small segments with their best neighbor */
/* ****************************************************************************************** */
@@ -645,24 +609,13 @@
current = current->next) {
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;
- 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);
/* we'll have the neighbor pixel to start with. */
Rk_count = 1;
@@ -688,14 +641,14 @@
} /* end if best neighbor != null */
else
G_warning
- ("No best neighbor found in final merge for small segment, this shouldn't happen!");
+ (_("No best neighbor found in final merge for small segment, this shouldn't happen!"));
} /* end else - pixel count was below minimum allowed */
} /* end if neighbors found */
else { /* no neighbors were found */
G_warning
- ("no neighbors found, this means only one segment was created.");
+ (_("no neighbors found, this means only one segment was created."));
set_candidate_flag(Ri_head, FALSE, files);
}
} /* end if pixel is candidate pixel */
@@ -704,7 +657,7 @@
t++; /* to count one more "iteration" */
} /* end if for force merge */
else if (t > 2 && files->bounds_map == NULL)
- G_verbose_message("Number of passes completed: %d", t - 1);
+ G_verbose_message(_("Number of passes completed: %d"), t - 1);
#ifdef PROFILE
end = clock();
fprintf(stdout, "time spent merging: %g\n", merge_accum);
@@ -715,8 +668,8 @@
return TRUE;
}
- /* TODO, for now will return borderPixels instead of passing a pointer, I saw mentioned that each parameter slows down the function call? */
- /* TODO, My first impression is that the borderPixels count is ONLY needed for the case of initial seeds, and not used later on. Another reason to split the function... */
+ /* perimeter todo, for now will return borderPixels instead of passing a pointer, I saw mentioned that each parameter slows down the function call? */
+ /* perimeter todo, My first impression is that the borderPixels count is ONLY needed for the case of initial seeds, and not used later on. Another reason to split the function... */
int find_segment_neighbors(struct pixels **R_head,
struct pixels **neighbors_head, int *seg_count,
struct files *files,
@@ -732,7 +685,7 @@
struct RB_TRAV trav;
#endif
- /* TODO, any time savings to move any variables to files (mem allocation in open_files) */
+ /* speed enhancement, any time savings to move any variables to files (mem allocation once in open_files) */
/* neighbor list will be a listing of pixels that are neighbors, but will be limited to just one pixel from each neighboring segment.
* */
@@ -774,9 +727,8 @@
newpixel->row = pixel_neighbors[n][0];
newpixel->col = pixel_neighbors[n][1];
*neighbors_head = newpixel; /*change the first pixel to be the new pixel. */
- /* todo polish... could use a tree and only return pixels from unique segments. */
}
- borderPixels++; /* increment for all non null pixels TODO perimeter: OK to ignore these cells? */
+ borderPixels++; /* increment for all non null pixels *//* TODO perimeter: OK to ignore NULL cells? */
}
}
@@ -800,13 +752,10 @@
tree_pix.row = current->row;
tree_pix.col = current->col;
if (rbtree_insert(no_check_tree, &tree_pix) == 0) /* don't check it again */
- G_warning("could not insert data!?");
+ G_warning(_("could not insert data into tree, out of memory?"));
}
while (to_check != NULL) { /* removing from to_check list as we go, NOT iterating over the list. */
- G_debug(5,
- "\tfind_pixel_neighbors for row: %d , col %d",
- to_check->row, to_check->col);
functions->find_pixel_neighbors(to_check->row,
to_check->col,
@@ -817,26 +766,6 @@
to_check = to_check->next; /*head now points to the next element in the list */
link_dispose(files->token, (VOID_T *) current);
- /*print out to_check */
-#ifdef DEBUG
- G_debug(5, "remaining pixel's in to_check, after popping:");
- for (current = to_check; current != NULL;
- current = current->next)
- G_debug(5, "to_check... row: %d, col: %d", current->row,
- current->col);
- for (current = *neighbors_head; current != NULL;
- current = current->next)
- G_debug(5, "Rn... row: %d, col: %d", current->row,
- current->col);
-#endif
-
- /*now check the pixel neighbors and add to the lists */
-
- /*print what pixel neighbors were found: */
- /* for (n = 0; n < functions->num_pn; n++){
- G_debug(5, "\tpixel_neighbors[n][0]: %d, pixel_neighbors[n][1]: %d", pixel_neighbors[n][0], pixel_neighbors[n][1]);
- } */
-
/* for each pixel neighbors, check if they should be processed, check segment ID, and add to appropriate lists */
for (n = 0; n < functions->num_pn; n++) {
@@ -852,9 +781,6 @@
tree_pix.row = pixel_neighbors[n][0];
tree_pix.col = pixel_neighbors[n][1];
- G_debug(5,
- "********* rbtree_find(no_check_tree, &tree_pix) = %p",
- rbtree_find(no_check_tree, &tree_pix));
if (rbtree_find(no_check_tree, &tree_pix) == FALSE) { /* want to check this neighbor */
segment_get(&files->iseg_seg, ¤t_seg_ID,
@@ -863,13 +789,7 @@
rbtree_insert(no_check_tree, &tree_pix); /* don't check it again */
- //~ G_debug(5, "\tfiles->iseg[][] = %d Ri_seg_ID = %d",
- //~ files->iseg[pixel_neighbors[n][0]]
- //~ [pixel_neighbors[n]
- //~ [1]], Ri_seg_ID);
-
if (current_seg_ID == R_iseg) { /* pixel is member of current segment, add to R */
- G_debug(5, "\tputing pixel_neighbor in Ri");
/* put pixel_neighbor[n] in Ri */
newpixel =
(struct pixels *)link_new(files->token);
@@ -878,7 +798,6 @@
newpixel->col = pixel_neighbors[n][1];
*R_head = newpixel; /*change the first pixel to be the new pixel. */
*seg_count = *seg_count + 1; /* zero index... Ri[0] had first pixel and set count =1. increment after save data. */
- G_debug(5, "\t*seg_count now = %d", *seg_count);
/* put pixel_neighbor[n] in to_check -- want to check this pixels neighbors */
newpixel =
@@ -896,12 +815,10 @@
if (current_seg_ID != 0)
/* with seeds, non seed pixels are defaulted to zero. Should we use null instead?? then could skip this check? Or we couldn't insert it??? */
/* add to known neighbors list */
- rbtree_insert(known_iseg, ¤t_seg_ID); /* todo: could I just try to insert it, if it fails I know it is already there?
- * I guess it would depend on how much faster the find() is, and what fraction of the
- * neighbors are in a duplicate segment... */
+ rbtree_insert(known_iseg,
+ ¤t_seg_ID);
/* put pixel_neighbor[n] in Rin */
- G_debug(5, "Put in neighbors_head");
newpixel =
(struct pixels *)link_new(files->token);
newpixel->next = *neighbors_head; /*point the new pixel to the current first pixel */
@@ -909,27 +826,16 @@
newpixel->col = pixel_neighbors[n][1];
*neighbors_head = newpixel; /*change the first pixel to be the new pixel. */
}
- else { /* TODO we need to keep track of (and return!) a total count of neighbors pixels for each neighbor segment, to update the perimeter value in the similarity calculation. */
+ else { /* todo perimeter we need to keep track of (and return!) a total count of neighbors pixels for each neighbor segment, to update the perimeter value in the similarity calculation. */
/* todo perimeter: need to initalize this somewhere!!! */
/* todo perimeter... need to find pixel with same segment ID.... countShared++;
- * Oh! Should we change the tree to sort on segment ID...need to think of fast way to return this count? with pixel? or with something else? */
+ * hmmm, Should we change the known_iseg tree to sort on segment ID...need to think of fast way to return this count? with pixel? or with something else? */
}
}
} /*end if for pixel_neighbor was in "don't check" list */
} /* end for loop - next pixel neighbor */
-
-#ifdef DEBUG
- G_debug(5,
- "remaining pixel's in to_check, after processing the last pixel's neighbors:");
- for (current = to_check; current != NULL;
- current = current->next)
- G_debug(5, "to_check... row: %d, col: %d", current->row,
- current->col);
-
- G_debug(5, "\t### end of pixel neighors");
-#endif
} /* end while to_check has more elements */
/* clean up */
@@ -994,9 +900,10 @@
return TRUE;
}
- /* similarity / distance between two points based on their input raster values */
- /* assumes first point values already saved in files->bands_seg - only run segment_get once for that value... */
- /* TODO: segment_get already happened for a[] values in the main function. Could remove a[] from these parameters */
+ /* similarity / distance functions between two points based on their input raster values */
+ /* assumes first point values already saved in files->bands_seg */
+ /* speed enhancement: segment_get was already done for a[] values in the main function. Could remove a[] from these parameters, reducing number of parameters in function call could provide a speed improvement. */
+
double calculate_euclidean_similarity(struct pixels *a, struct pixels *b,
struct files *files,
struct functions *functions)
@@ -1023,9 +930,6 @@
}
- /* similarity / distance between two points based on their input raster values */
- /* assumes first point values already saved in files->bands_seg - only run segment_get once for that value... */
- /* TODO: segment_get already happened for a[] values in the main function. Could remove a[] from these parameters */
double calculate_manhattan_similarity(struct pixels *a, struct pixels *b,
struct files *files,
struct functions *functions)
@@ -1039,7 +943,7 @@
/* Manhattan distance, sum the absolute difference between values for each dimension */
for (n = 0; n < files->nbands; n++) {
- val += fabs(files->bands_val[n] - files->second_val[n]); /* todo check if fabs is the "fast" way for absolute value */
+ val += fabs(files->bands_val[n] - files->second_val[n]); /* speed enhancement: is fabs() is the "fast" way for absolute value calculations? */
}
return val;
@@ -1072,7 +976,7 @@
int n, Ri_iseg, Rk_iseg;
struct pixels *current;
- /*get input values *//*TODO polish, confirm if we can assume we already have bands_val for Ri, so don't need to segment_get() again? note...current very_close implementation requires getting this value again... */
+ /*get input values *//*speed enhancement: Confirm if we can assume we already have bands_val for Ri, so don't need to segment_get() again? note...current very_close implementation requires getting this value again... */
segment_get(&files->bands_seg, (void *)files->bands_val, Ri_head->row,
Ri_head->col);
segment_get(&files->bands_seg, (void *)files->second_val,
@@ -1080,7 +984,6 @@
segment_get(&files->iseg_seg, &Rk_iseg, Rk_head->row, Rk_head->col);
segment_get(&files->iseg_seg, &Ri_iseg, Ri_head->row, Ri_head->col);
- /* todo polish, maybe we have some of these values already? Or they should be stored in files structure? */
for (n = 0; n < files->nbands; n++) {
files->bands_val[n] =
@@ -1089,20 +992,17 @@
}
/* update segment number and candidate flag ==0 */
-
- //~ G_debug(4, "\t\tMerging, segment number: %d, including pixels:",
- //~ files->iseg[Ri_head->row][Ri_head->col]);
-
+#ifdef SIGNPOST
fprintf(stdout,
"merging Ri (pixel count): %d (%d) with Rk (count): %d (%d).\n",
Ri_iseg, Ri_count, Rk_iseg, Rk_count);
+#endif
/* for each member of Ri and Rk, write new average bands values and segment values */
for (current = Ri_head; current != NULL; current = current->next) {
segment_put(&files->bands_seg, (void *)files->bands_val,
current->row, current->col);
FLAG_UNSET(files->candidate_flag, current->row, current->col); /*candidate pixel flag, only one merge allowed per t iteration */
- G_debug(4, "\t\tRi row: %d, col: %d", current->row, current->col);
}
for (current = Rk_head; current != NULL; current = current->next) {
segment_put(&files->bands_seg, (void *)files->bands_val,
@@ -1110,8 +1010,6 @@
segment_put(&files->iseg_seg, &Ri_iseg, current->row,
current->col);
FLAG_UNSET(files->candidate_flag, current->row, current->col);
- G_debug(4, "\t\tRk row: %d, col: %d", current->row, current->col);
-
}
/* merged two segments, decrement count if Rk was an actual segment (not a non-seed pixel) */
@@ -1153,7 +1051,7 @@
/* add in the shape values */
files->bands_val[files->nbands] = count; /* area (Num Pixels) */
- files->bands_val[files->nbands + 1] = borderPixels; /* Perimeter Length *//* todo polish, not exact for edges...close enough for now? */
+ files->bands_val[files->nbands + 1] = borderPixels; /* Perimeter Length *//* todo perimeter, not exact for edges...close enough for now? */
/* save the results */
for (current = R_head; current != NULL; current = current->next) {
@@ -1184,7 +1082,7 @@
}
else
G_fatal_error
- ("programming bug, helper function called with invalid argument");
+ (_("programming bug, helper function called with invalid argument"));
}
return TRUE;
}
@@ -1205,7 +1103,7 @@
/* functions used by binary tree to compare items */
- /* TODO "static" was used in break_polygons.c extern was suggested in docs. */
+ /* speed enhancement: Maybe changing this would be an improvement? "static" was used in break_polygons.c extern was suggested in docs. */
int compare_ids(const void *first, const void *second)
{
@@ -1261,29 +1159,3 @@
}
return TRUE;
}
-
-
- /* TODO polish: helper functions:
- *
- * starting a list
- *
- * */
-
-#ifdef NODEF
- G_message("2b, Found Ri's pixels");
- /*print out neighbors */
- for (current = Ri_head; current != NULL; current = current->next)
- G_message("Ri: row: %d, col: %d", current->row, current->col);
- G_message("2b, Found Ri's neighbors");
- /*print out neighbors */
- for (current = Rin_head; current != NULL; current = current->next)
- G_message("Rin: row: %d, col: %d", current->row, current->col);
- G_message("Found Rk's pixels");
- /*print out neighbors */
- for (current = Rk_head; current != NULL; current = current->next)
- G_message("Rk: row: %d, col: %d", current->row, current->col);
- G_message("Found Rk's neighbors");
- /*print out neighbors */
- for (current = Rkn_head; current != NULL; current = current->next)
- G_message("Rkn: row: %d, col: %d", current->row, current->col);
-#endif
Modified: grass-addons/grass7/imagery/i.segment/i.segment.html
===================================================================
--- grass-addons/grass7/imagery/i.segment/i.segment.html 2012-08-19 23:07:43 UTC (rev 52741)
+++ grass-addons/grass7/imagery/i.segment/i.segment.html 2012-08-20 06:01:18 UTC (rev 52742)
@@ -1,10 +1,10 @@
<h2>DESCRIPTION</h2>
-<em>i.segment</em> generates a segmented raster based on the input imagery group. A region growing and merging algorithm is currently implemented.
+Image segmentation is the process of grouping similar pixels into unique segments. Boundary and region based algorithms are described in the literature, currently a region growing and merging algorithm is implemented. Each object found during the segmentation process is given a unique ID and is a collection of contiguous pixels meeting some criteria. (Note the contrast with image classification, where continuity and spatial characteristics are not important, but rather only the spectral simularity.) The results can be useful on their own, or used as a preprocessing step for image classification. The segmentation preprocessing step can reduce noise and speed up the classification.
<H2>NOTES</h2>
<h3>Region Growing and Merging</h3>
-This segmentation algorithm sequentially examines all current segments in the map. The similarity between the current segment and each of its neighbors is calculated according to the given distance formula. Segments will be merged if they meet a number of criteria, including: 1. The pair mutually most similar to each other (the similarity distance will be smaller then all other neighbors), 2. The similarity must be lower then the input threshold, and 3. a segment can only be merged once per pass. All segments are checked once per pass. The process is repeated until no merges are made during a complete pass.
+This segmentation algorithm sequentially examines all current segments in the map. The similarity between the current segment and each of its neighbors is calculated according to the given distance formula. Segments will be merged if they meet a number of criteria, including: 1. The pair is mutually most similar to each other (the similarity distance will be smaller then all other neighbors), and 2. The similarity must be lower then the input threshold. All segments are checked once per pass. The process is repeated until no merges are made during a complete pass.
<h3>Threshold</h3>
During normal processing, merges are only allowed when the similarity between two segments is lower then the calculated threshold value. During the final pass, however, if a minimum segment size of 2 or larger is given with the <em>minsize</em> parameter, segments with a smaller pixel count will be merged with their most similar neighbor even if the similarity is greater then the threshold.
@@ -15,13 +15,13 @@
</p>
<h3>Seeds</h3>
The seeds map can be used to provide either seed pixels (random or selected points from which to start the segmentation process) or seed segments (results of previous segmentations or classifications). The different approaches are automatically detected by the program: any pixels that have identical seed values and are contiguous will be lumped into a single segment ID.
-
+<p>
It is expected that the <em>minsize</em> will be set to 1 if a seed map is used, but the program will allow other values to be used. If both options are used, the final iteration that ignores the threshold also will ignore the seed map and force merges for all pixels (not just segments that have grown/merged from the seeds).
-
+</p>
<h3>Maximum number of starting segments</h3>
For the region growing algorithm without starting seeds, each pixel is sequentially numbered. The current limit with CELL storage is 2 billion starting segment ID's. If the initial map has a larger number of non-null pixels, there are two workarounds:
<p>
-1. Use starting seed pixels. (Maximum 2 billion pixels can be labeled with positive integers, either as a grid or random selection.)
+1. Use starting seed pixels. (Maximum 2 billion pixels can be labeled with positive integers.)
</p><p>
2. Use starting seed segments. (By initial classification or other methods.)
</p>
@@ -32,24 +32,56 @@
To reduce the salt and pepper affect, a <em>minsize</em> greater than 1 will add one additional pass to the processing. During the final pass, the threshold is ignored for any segments smaller then the set size, thus forcing very small segments to merge with their most similar neighbor.
<H2>EXAMPLES</h2>
-Need to give an example using NC data set.
-<p>
-i.group
-</p><p>
-i.segment (lower threshold)
-</p><p>
-i.segment (higher threshold, using previous output as boundaries)
-</p>
+This example uses the ortho photograph included in the NC Sample Dataset. Set up an imagery group:<br>
+<blockquote>i.group group=ortho_group input=ortho_2001_t792_1m at PERMANENT</blockquote>
+
+<p>Because the segmentation process is computationally expensive, start with a small processing area to confirm if the segmentation results meet your requirements. Some manual adjustment of the threshold may be required. <br>
+
+<blockquote>g.region rast=ortho_2001_t792_1m at PERMANENT n=220400 s=220200 e=639000 w=638800</blockquote>
+<p></p>
+Try out a first threshold and check the results.<br>
+<blockquote>i.segment -w -l group=ortho_group output=ortho_segs threshold=4 method=region_growing</blockquote>
+<p></p>
+From a visual inspection, it seems this results in oversegmentation. Increasing the threshold: <br>
+<blockquote>i.segment -w -l --overwrite group=ortho_group output=ortho_segs threshold=10 method=region_growing</blockquote>
+<p></p>
+This looks better. There is some noise in the image, lets next force all segments smaller then 5 pixels to be merged into their most similar neighbor (even if they are less similar then required by our threshold):<br>
+<blockquote>i.segment -w -l --overwrite group=ortho_group output=ortho_segs threshold=10 method=region_growing minsize=5</blockquote>
+<p></p>
+Each of these segmentation steps took less then 1 minute on a decent machine. Now that we are satisfied with the settings, we'll process the entire image:
+<blockquote>g.region rast=ortho_2001_t792_1m at PERMANENT<br>
+i.segment -w -l --overwrite group=ortho_group output=ortho_segs threshold=10 method=region_growing minsize=5 endt=5000</blockquote>
+<p></p>
+Processing the entire ortho image (over 9 million cells) took about !currently running, will insert time in the morning! .
+
<h2>TODO</h2>
-Copy the remaining tasks from:
-http://grass.osgeo.org/wiki/GRASS_GSoC_2012_Image_Segmentation#ToDo_List
+<h3>Functionality</h3>
+<ul>
+<li>Add shape characteristics (smoothness, compactness) to the similarity measurement. This will add two input parameters (weight of radiometric to shape, and weight of compactness to smoothness.)</li>
+<li>Malahanobis distance for the similarity calculation.</li>
+<li>Estimating the threshold value. (1 to 5% of the max difference gave me subjectively good results.)</li>
+</ul>
+<h3>Use of Segmentation Results</h3>
+<ul>
+<li>Improve the optional output from this module, or better yet, add a module for i.segment.metrics.</li>
+<li>Providing updates to i.maxlik to ensure the segmentation output can be used as input for the existing classification functionality.</li>
+<li>Integration/workflow for r.fuzzy.</li>
+</ul>
+<h3>Speed</h3>
+<ul>
+<li>See create_isegs.c</li>
+</ul>
+<h3>Memory</h3>
+<ul>
+<li>User input for how much RAM can be used.</li>
+<li>Check input map type(s), currently storing in DCELL sized SEG file, could reduce this dynamically depending on input map time. (Could only reduce to FCELL, since will be storing mean we can't use CELL. Might not be worth the added code complexity.)</li>
+</ul>
<h2>BUGS</h2>
-If the seeds map is used to give starting seed segments, the segments are renumbered starting from 1. There is a chance a segment could be renumbered to a seed value that has not yet been processed. If they happen to be adjacent, they would be merged. (Possible fixes: use negative segment ID's as a placeholder and negate all values after the seed map has been processed. Use an additional data structure, though this costs more RAM.)
+If the seeds map is used to give starting seed segments, the segments are renumbered starting from 1. There is a chance a segment could be renumbered to a seed value that has not yet been processed. If they happen to be adjacent, they would be merged. (Possible fixes: a. use a processing flag to make sure the pixels hasn't been previously used, or b. use negative segment ID's as a placeholder and negate all values after the seed map has been processed.)
<H2>REFERENCES</h2>
This project was first developed during GSoC 2012. Project documentation, Image Segmentation references, and other information is at the <a href="http://grass.osgeo.org/wiki/GRASS_GSoC_2012_Image_Segmentation">project wiki</a>.
<p>
Information about <a href="http://grass.osgeo.org/wiki/Image_classification">classification in GRASS</a> is at available on the wiki.
-<!-- TODO Markus, should the Image_classification page be updated to include i.segment in the areas where i.seg belong? -->
</p>
<h2>SEE ALSO</h2>
<em>
Modified: grass-addons/grass7/imagery/i.segment/iseg.h
===================================================================
--- grass-addons/grass7/imagery/i.segment/iseg.h 2012-08-19 23:07:43 UTC (rev 52741)
+++ grass-addons/grass7/imagery/i.segment/iseg.h 2012-08-20 06:01:18 UTC (rev 52742)
@@ -16,13 +16,14 @@
#include <grass/linkm.h>
#include "flag.h"
-/* DEBUG will add some additional testing options to the segmentation method drop down.
- * it also add some while loops that just have G_debug statements in them. */
-/* #define DEBUG */
-
/* PROFILE will add some rough time checks for finding neighbors, merging, and pass times. */
/* #define PROFILE */
+/* SIGNPOST will add some fprintf statments to indicate what segments are being merged.
+ * other diagnostics could be included here for during further development and speed improvements.
+ */
+/* #define SIGNPOST */
+
/* pixel stack */
struct pixels
{
@@ -45,7 +46,7 @@
/* files */
char *out_name; /* name of output raster map */
const char *seeds_map, *seeds_mapset, *bounds_map, *bounds_mapset; /* optional segment seeds and polygon constraints/boundaries */
- char *out_band; /* for debug */
+ char *out_band; /* for segment average values */
/* file processing */
/* bands_seg is initialized with the input raster valuess, then is updated with current mean values for the segment. */
@@ -84,17 +85,18 @@
/* max number of iterations/passes */
int end_t;
- /* todo remove when decide on pathflag */
- int path;
+ int path; /* flag if we are using Rk as next Ri for non-mutually best neighbor. */
+ int limited; /* flag if we are limiting merges to one per pass */
- /* todo remove when decide on allowing multiple merges per pass */
- int limited;
-
- /* todo: should this be an option, set at a specific value, or left out. */
- // double very_close; /* segments with very_close similarity will be merged without changing or checking the candidate flag. The algorithm will continue looking for the "most similar" neighbor that isn't "very close". */
- // todo markus... I tried this out briefly, but realized that we need to find the segment membership (the find neighbors function only returns single pixels) , might be some faster ways to do this, but my first tries actually slowed down the processing.
- // should I leave in the commented code for "very_close", or remove it entirely?
-
+ /* todo: is there a fast way (and valid from an algorithm standpoint) to merge all neighbors that are within some small % of the treshold?
+ * There is some code using "very_close" that is excluded with IFDEF
+ * The goal is to speed processing, since in the end many of these very similar neighbors will be merged.
+ * But the problem is that the find_segment_neighbors() function only returns a single pixel, not the entire segment membership.
+ * The commented out code actually slowed down processing times in the first tries. */
+#ifdef VCLOSE
+ double very_close; /* segments with very_close similarity will be merged without changing or checking the candidate flag.
+ * The algorithm will continue looking for the "most similar" neighbor that isn't "very close". */
+#endif
};
@@ -127,11 +129,3 @@
/* write_output.c */
int write_output(struct files *);
int close_files(struct files *);
-
-/* testing.c */
-int io_debug(struct files *, struct functions *);
-int ll_test(struct files *, struct functions *);
-int test_pass_token(struct pixels **, struct files *);
-int seg_speed_test(struct files *, struct functions *);
-int get_segID_SEG(struct files *, int, int);
-int get_segID_RAM(struct files *, int, int);
Modified: grass-addons/grass7/imagery/i.segment/open_files.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/open_files.c 2012-08-19 23:07:43 UTC (rev 52741)
+++ grass-addons/grass7/imagery/i.segment/open_files.c 2012-08-20 06:01:18 UTC (rev 52742)
@@ -1,6 +1,6 @@
/* PURPOSE: opening input rasters and creating segmentation files */
-#include <limits.h> /* for INT_MAX, todo remove if there is a GRASS max CELL */
+#include <limits.h> /* for INT_MAX */
#include <stdlib.h>
#include <grass/gis.h>
#include <grass/glocale.h>
@@ -27,7 +27,7 @@
struct pixels *R_head, *Rn_head, *newpixel, *current;
int R_count;
- G_verbose_message("Opening files and initializing");
+ G_verbose_message(_("Opening files and initializing"));
/* confirm output maps can be opened (don't want to do all this work for nothing!) */
out_fd = Rast_open_new(files->out_name, CELL_TYPE);
@@ -46,18 +46,20 @@
/*allocate memory for flags */
files->null_flag = flag_create(files->nrows, files->ncols);
+ flag_clear_all(files->null_flag);
files->candidate_flag = flag_create(files->nrows, files->ncols);
+
if (files->bounds_map != NULL)
files->orig_null_flag = flag_create(files->nrows, files->ncols);
- //if (files->seeds_map != NULL)
+
files->seeds_flag = flag_create(files->nrows, files->ncols);
+ flag_clear_all(files->seeds_flag);
/* references for segmentation library: i.cost r.watershed/seg and http://grass.osgeo.org/programming7/segmentlib.html */
/* ****** open the input rasters ******* */
/* Note: I confirmed, the API does not check this: */
- G_debug(1, "Checking image group...");
if (!I_get_group_ref(files->image_group, &Ref))
G_fatal_error(_("Unable to read REF file for group <%s>"),
files->image_group);
@@ -74,12 +76,11 @@
min = G_malloc(Ref.nfiles * sizeof(DCELL));
max = G_malloc(Ref.nfiles * sizeof(DCELL));
- G_debug(1, "Opening input rasters...");
for (n = 0; n < Ref.nfiles; n++) {
inbuf[n] = Rast_allocate_d_buf();
in_fd[n] = Rast_open_old(Ref.file[n].name, Ref.file[n].mapset);
if (in_fd[n] < 0)
- G_fatal_error("Error opening %s@%s", Ref.file[n].name,
+ G_fatal_error(_("Error opening %s@%s"), Ref.file[n].name,
Ref.file[n].mapset);
}
@@ -107,12 +108,9 @@
Ref.file[n].name);
Rast_get_fp_range_min_max(&(fp_range[n]), &min[n], &max[n]);
}
- G_debug(1, "scaling, for first layer, min: %f, max: %f",
- min[0], max[0]);
}
/* ********** find out file segmentation size ************ */
- G_debug(1, "Calculate temp file sizes...");
files->nbands = Ref.nfiles;
@@ -124,12 +122,18 @@
srows = 64;
scols = 64;
- /* TODO: make calculations for this, check i.cost and i.watershed */
- nseg = 16; // 1000; //16;
+ /* RAM enhancement: have user input limit and make calculations for this, reference i.cost and i.watershed
+ * One segment tile is tile_mb = (nbands * sizeof(double) + sizeof(CELL) * srows * scols / (1024*1024)
+ * (check if sizeof(CELL) was from when iseg would be included, or the extra overhead?)
+ * If user inputs total RAM available, need to subtract the size of the flags, and the size of the linked lists.
+ * I'm not sure how to estimate the size of the linked lists, it is allowed to grow when needed. Assume one segment
+ * could be 50% of the map? Or more? So ll_mb = sizeof(pixels) * nrows * ncols * 0.5 / (1024*1024)
+ * then split the remaining RAM between bands_seg and iseg_seg. */
+ nseg = 16;
+
/* ******* create temporary segmentation files ********* */
- G_debug(1, "Getting temporary file names...");
/* Initalize access to database and create temporary files */
G_debug(1, "Image size: %d rows, %d cols", files->nrows, files->ncols);
@@ -146,7 +150,7 @@
/* ******* remaining memory allocation ********* */
/* save the area and perimeter as well */
- /* TODO: currently saving this with the input DCELL values. Better to have a second segment structure to save as integers ??? */
+ /* perimeter todo: currently saving this with the input DCELL values. Better to have a second segment structure to save as integers ??? */
inlen = inlen + sizeof(double) * 2;
files->bands_val = (double *)G_malloc(inlen);
@@ -156,14 +160,13 @@
(&files->iseg_seg, G_tempfile(), files->nrows, files->ncols, srows,
scols, sizeof(int), nseg) != TRUE)
G_fatal_error(_("Unable to allocate memory for initial segment ID's"));
- /* NOTE: SEGMENT file should be initialized to zeros for all data. TODO double check this. */
/* bounds/constraints (start with processing constraints to get any possible NULL values) */
if (files->bounds_map != NULL) {
if (segment_open
(&files->bounds_seg, G_tempfile(), files->nrows, files->ncols,
srows, scols, sizeof(int), nseg) != TRUE)
- G_fatal_error("Unable to create bounds temporary files");
+ 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);
@@ -184,7 +187,6 @@
/* ******** load input bands to segment structure and fill initial seg ID's ******** */
- G_debug(1, "Reading input rasters into segmentation data files...");
s = 0; /* initial segment ID will be 1 */
for (row = 0; row < files->nrows; row++) {
@@ -211,18 +213,18 @@
files->bands_val[n] = (inbuf[n][col] - min[n]) / (max[n] - min[n]); /* scaled */
}
files->bands_val[Ref.nfiles] = 1; /* area (just using the number of pixels) */
- files->bands_val[Ref.nfiles + 1] = 4; /* Perimeter Length *//* todo polish, not exact for edges...close enough for now? */
+ files->bands_val[Ref.nfiles + 1] = 4; /* Perimeter Length *//* todo perimeter, not exact for edges...close enough for now? */
segment_put(&files->bands_seg, (void *)files->bands_val, row, col); /* store input bands */
if (null_check != -1) { /*good pixel */
FLAG_UNSET(files->null_flag, row, col); /*flag */
if (files->seeds_map != NULL) {
if (Rast_is_c_null_value(ptr) == TRUE) {
- // todo... old data structure, switch to SEG if need to initalize to zero: files->iseg[row][col] = 0;
- FLAG_UNSET(files->seeds_flag, row, col); //todo markus, I expect iseg and seeds flag are initialized to zero, so shouldn't need to set either flag. Leave these two lines of code out?
+ /* when using iseg_seg the segmentation file is already initialized to zero. Just initialize seeds_flag: */
+ FLAG_UNSET(files->seeds_flag, row, col);
}
else {
- FLAG_SET(files->seeds_flag, row, col); //todo might not need this... just look for seg ID > 0 ?
+ FLAG_SET(files->seeds_flag, row, col); /* RAM enhancement, but it might cost speed. Could look for seg ID > 0 instead of using seed_flag. */
/* seed value is starting segment ID. */
segment_put(&files->iseg_seg, ptr, row, col);
}
@@ -230,7 +232,7 @@
}
else { /* no seeds provided */
s++; /* sequentially number all pixels with their own segment ID */
- if (s < INT_MAX) { /* Check that the starting seeds aren't too large. (checking for < instead of <= since TODO Markus, Is there a GRASS constant for the maximum size of CELL? */
+ if (s < INT_MAX) { /* Check that the starting seeds aren't too large. */
segment_put(&files->iseg_seg, &s, row, col); /*starting segment number */
FLAG_SET(files->seeds_flag, row, col); /*all pixels are seeds */
}
@@ -250,17 +252,14 @@
for (col = 0; col < files->ncols; col++) {
if (FLAG_GET(files->null_flag, row, col))
FLAG_SET(files->orig_null_flag, row, col);
- else /* todo polish, flags are initialized to zero... could just skip this else? */
+ else
FLAG_UNSET(files->orig_null_flag, row, col);
}
}
} /* end: if (files->bounds_map != NULL) */
- else {
- G_debug(1, "no boundary constraint supplied.");
- }
/* linked list memory management linkm */
- link_set_chunk_size(1000); /* TODO polish: fine tune this number */
+ link_set_chunk_size(1000); /* TODO RAM: fine tune this number */
files->token = link_init(sizeof(struct pixels));
@@ -291,14 +290,16 @@
newpixel->col = col;
R_head = newpixel;
- /*get pixel list, todo polish, could use custom (shorter) function, not using all of what fsn() does... hmm, after adding perimeter, we do use most of it... */
- borderPixels = find_segment_neighbors(&R_head, &Rn_head, &R_count, files, functions); /* todo, I suppose there is a small chance that a renumbered segment matches and borders an original segment. This would be a good reason to write a custom fnp() function to chop out the neighbors and also check the candidate flag. */
+ /* get pixel list, possible initialization speed enhancement: could use a custom (shorter) function, some results from find_segment_neighbors are not used here */
+ /* bug todo: There is a small chance that a renumbered segment matches and borders an original segment. This would be a good reason to write a custom function - use the candidate flag to see if the pixel was already processed. */
+ borderPixels =
+ find_segment_neighbors(&R_head, &Rn_head, &R_count, files,
+ functions);
- /* update the segment ID *//* TODO, Markus, this could also be done in merge_pixels to avoid iterating this list twice.
- * for now I've put it here, to make merge_pixels() more general. Unless you think initialization speed is more important then future flexibility? */
+ /* update the segment ID */
s++;
- if (s == INT_MAX) /* Check that the starting seeds aren't too large. TODO Markus, Is there a GRASS constant for the maximum size of CELL? */
+ if (s == INT_MAX) /* Check that the starting seeds aren't too large. */
G_fatal_error(_("Exceeded integer storage limit, too many initial pixels."));
for (current = R_head; current != NULL;
Modified: grass-addons/grass7/imagery/i.segment/parse_args.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/parse_args.c 2012-08-19 23:07:43 UTC (rev 52741)
+++ grass-addons/grass7/imagery/i.segment/parse_args.c 2012-08-20 06:01:18 UTC (rev 52742)
@@ -9,37 +9,32 @@
int parse_args(int argc, char *argv[], struct files *files,
struct functions *functions)
-{ /* todo Markus, is it helpful to leave tips like this that are helpful to someone new to GRASS? Or is it just clutter now, and should go to a reference sheet? */
- /* reference: http://grass.osgeo.org/programming7/gislib.html#Command_Line_Parsing */
-
+{
struct Option *group, *seeds, *bounds, *output, *method, *similarity, *threshold, *min_segment_size, *endt; /* Establish an Option pointer for each option */
- struct Flag *diagonal, *weighted, *path, *limited; /* Establish a Flag pointer for each option */
- struct Option *outband; /* TODO scrub: put all outband code inside of #ifdef DEBUG */
+ struct Flag *diagonal, *weighted, *limited; /* Establish a Flag pointer for each option */
+ struct Option *outband; /* optional saving of segment data, until a seperate module is written */
- //~ struct Option *very_close;
+#ifdef VCLOSE
+ struct Option *very_close;
+#endif
/* required parameters */
- group = G_define_standard_option(G_OPT_I_GROUP); /* TODO? Polish, consider giving the option to process just one raster directly, without creating an image group. */
+ group = G_define_standard_option(G_OPT_I_GROUP); /* enhancement: consider giving the option to process just one raster directly, without creating an image group. */
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;
threshold->required = YES;
- threshold->description = _("Similarity threshold."); /* TODO? Polish, should all descriptions get the _() locale macro? */
+ threshold->description = _("Similarity threshold.");
method = G_define_option();
method->key = "method";
method->type = TYPE_STRING;
method->required = YES;
method->answer = "region_growing";
-#ifdef DEBUG
- method->options = "region_growing, io_debug, ll_test, seg_time";
-#else
method->options = "region_growing";
-#endif
method->description = _("Segmentation method.");
similarity = G_define_option();
@@ -60,13 +55,17 @@
min_segment_size->description =
_("The final iteration will ignore the threshold for any segments with fewer pixels.");
- //~ very_close = G_define_option();
- //~ very_close->key = "very_close";
- //~ very_close->type = TYPE_DOUBLE;
- //~ very_close->required = YES;
- //~ very_close->answer = "0";
- //~ very_close->options = "0-1";
- //~ very_close->description = _("For testing: very close segments will be merged, the focus segment will not be changed.");
+#ifdef VCLOSE
+ very_close = G_define_option();
+ very_close->key = "very_close";
+ very_close->type = TYPE_DOUBLE;
+ very_close->required = YES;
+ very_close->answer = "0";
+ very_close->options = "0-1";
+ very_close->label = _("Fraction of the threshold.");
+ very_close->description =
+ _("Neighbors similarity lower then this fraction of the threshold will be merged without regard to any other processing rules.");
+#endif
/* optional parameters */
@@ -92,36 +91,41 @@
bounds = G_define_standard_option(G_OPT_R_INPUT);
bounds->key = "bounds";
bounds->required = NO;
+ bounds->label = _("Optional bounding/constraining raster map");
bounds->description =
- _("Optional bounding/constraining raster map, must be integer values, each area will be segmented independent of the others.");
+ _("Pixels with the same integer value will be segmented independent of the others.");
- /* debug parameters */
+ /* other parameters */
endt = G_define_option();
endt->key = "endt";
endt->type = TYPE_INTEGER;
endt->required = NO;
endt->answer = "1000";
endt->description =
- _("Debugging...Maximum number of time steps to complete.");
+ _("Maximum number of passes (time steps) to complete.");
- path = G_define_flag();
- path->key = 'p';
- path->description =
- _("temporary option, pathflag, select to use Rk as next Ri if not mutually best neighbors.");
-
+ /* Leaving path flag out of user options, will hardcode TRUE for this option. This does change the resulting segments, but I don't see any
+ * reason why one version is more valid. It reduced the processing time by 50% when segmenting the ortho image.
+ * Code in the segmenation algorithm remains, in case more validation of this option should be done. */
+ /*
+ path = G_define_flag();
+ path->key = 'p';
+ path->description =
+ _("temporary option, pathflag, select to use Rk as next Ri if not mutually best neighbors.");
+ */
limited = G_define_flag();
limited->key = 'l';
limited->description =
- _("temporary option, limit to one merge to pass, if *not* selected a segment can be included in multiple merges in one pass.");
+ _("segments are limited to be included in only one merge per pass");
outband = G_define_standard_option(G_OPT_R_OUTPUT);
outband->key = "final_mean";
outband->required = NO;
outband->description =
- _("debug - save band mean, currently implemented for only 1 band.");
+ _("Save the final mean values for the first band in the imagery group.");
+ /* enhancement: save mean values for all bands. Multiple rasters or switch to polygons? */
-
if (G_parser(argc, argv))
exit(EXIT_FAILURE);
@@ -131,13 +135,10 @@
files->image_group = group->answer;
- /* TODO Markus, I saw in the GRASS Programmer's manual that I could use a checker function for the data validation steps.
- * But when I did a find|grep for "checker" it didn't show up. Is there any reason to use it? Or just leave the validation like I have it now? */
-
if (G_legal_filename(output->answer) == TRUE)
files->out_name = output->answer; /* name of output (segment ID) raster map */
else
- G_fatal_error("Invalid output raster name.");
+ 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() */
@@ -148,42 +149,32 @@
/* segmentation methods: 1 = region growing */
if (strncmp(method->answer, "region_growing", 10) == 0)
functions->method = 1;
-#ifdef DEBUG
- else if (strncmp(method->answer, "io_debug", 5) == 0)
- functions->method = 0;
- else if (strncmp(method->answer, "ll_test", 5) == 0)
- functions->method = 2;
- else if (strncmp(method->answer, "seg_time", 5) == 0)
- functions->method = 3;
-#endif
else
- G_fatal_error("Couldn't assign segmentation method."); /*shouldn't be able to get here */
+ G_fatal_error(_("Couldn't assign segmentation method.")); /*shouldn't be able to get here */
- G_debug(1, "segmentation method: %d", functions->method);
-
/* distance methods for similarity measurement */
if (strncmp(similarity->answer, "euclidean", 5) == 0)
functions->calculate_similarity = &calculate_euclidean_similarity;
else if (strncmp(similarity->answer, "manhattan", 5) == 0)
functions->calculate_similarity = &calculate_manhattan_similarity;
else
- G_fatal_error("Couldn't assign similarity method."); /*shouldn't be able to get here */
+ G_fatal_error(_("Couldn't assign similarity method.")); /*shouldn't be able to get here */
- //~ functions->very_close = atof(very_close->answer);
+#ifdef VCLOSE
+ functions->very_close = atof(very_close->answer);
+#endif
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;
- G_debug(1, "four pixel neighborhood");
}
else if (diagonal->answer == TRUE) {
functions->find_pixel_neighbors = &find_eight_pixel_neighbors;
functions->num_pn = 8;
- G_debug(1, "eight (3x3) pixel neighborhood");
}
- /* TODO polish, check if function pointer or IF statement is faster */
+ /* speed enhancement: Check if function pointer or IF statement is faster */
/* default/0 for performing the scaling, but selected/1 if user has weighted values so scaling should be skipped. */
files->weighted = weighted->answer;
@@ -230,10 +221,12 @@
G_warning(_("invalid number of iterations, 1000 will be used."));
}
- /* debug help */
+ /* other parameters */
/* default/0 for no pathflag, but selected/1 to use Rk as next Ri if not mutually best neighbors. */
- functions->path = path->answer;
+ /* functions->path = path->answer; */
+ functions->path = TRUE;
+ /* see notes above about pathflag. */
functions->limited = limited->answer;
@@ -243,7 +236,7 @@
if (G_legal_filename(outband->answer) == TRUE)
files->out_band = outband->answer; /* name of current means */
else
- G_fatal_error("Invalid output raster name for means.");
+ G_fatal_error(_("Invalid output raster name for means."));
}
return TRUE;
Deleted: grass-addons/grass7/imagery/i.segment/testing.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/testing.c 2012-08-19 23:07:43 UTC (rev 52741)
+++ grass-addons/grass7/imagery/i.segment/testing.c 2012-08-20 06:01:18 UTC (rev 52742)
@@ -1,627 +0,0 @@
-/* This file includes some functions that are called as "segmentation methods" when DEBUG is defined. They were used to test the I/O without doing any segmentation,
- * but then later were modified to test the use and speed of various data structures. */
-
-#ifdef DEBUG
-
-/* writes row+col to the output raster. Also using for the linkm speed test. */
-int io_debug(struct files *files, struct functions *functions)
-{
- int row, col;
-
- /* from speed.c to test speed of malloc vs. memory manager */
- unsigned long int z, end_z; // depending on shape of the rectangle...will need a larger max i. long long is avail in C99 spec...
- register int i, j;
- int s;
- struct link_head *head;
- struct pixels *p;
-
- /* **************write fake data to test I/O portion of module */
-
- /* G_verbose_message("writing fake data to segmentation file"); */
- G_verbose_message("writing scaled input (layer 1) to output file");
- G_verbose_message("weighted flag = %d", files->weighted);
- //~ for (row = 0; row < files->nrows; row++) {
- //~ 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; */
- //~ segment_get(&files->bands_seg, (void *)files->bands_val, row,
- //~ col);
- //~ files->iseg[row][col] = files->bands_val[0] * 100; /*pushing DCELL into CELL */
- //~ }
- //~ G_percent(row, files->nrows, 1);
- //~ }
-
- /* Trying out peano ordering */
- /* idea is for large maps, if the width of SEG tiles in RAM is less than the width of the map, this should avoid a lot of I/O */
- /* this is probably closer to a z-order curve then peano order. */
-
- s = -1;
- /*blank slate */
- for (row = 0; row < files->nrows; row++) {
- for (col = 0; col < files->ncols; col++) {
- //files->iseg[row][col] = -1;
- segment_put(&files->iseg_seg, &s, row, col);
- }
- }
- s = 0;
-
- //~ for(i=1; i<9; i++)
- //~ {
- //~ G_message("i: %d", i);
- //~ for(j=4; j>=0; j--){
- //~ G_message("\tj=%d, 1 & (i >> j) = %d", j, 1 & (i >> j));
- //~ }
- //~ }
-
- /* need to get a "square" power of 2 around our processing area */
-
- /*largest dimension: */
- if (files->nrows > files->ncols)
- end_z = files->nrows;
- else
- end_z = files->ncols;
-
- /* largest power of 2: */
- end_z--; /* in case we are already a power of two. */
- end_z = (end_z >> 1) | end_z;
- end_z = (end_z >> 2) | end_z;
- end_z = (end_z >> 4) | end_z;
- end_z = (end_z >> 8) | end_z;
- end_z = (end_z >> 16) | end_z;
- end_z = (end_z >> 32) | end_z; /* only for 64-bit architecture TODO, would this mess things up on 32? */
- /*todo does this need to repeat more since it is long unsigned??? */
- end_z++;
-
- /*squared: */
- end_z *= end_z;
-
- for (z = 0; z < end_z; z++) { // if square and power of 2: files->nrows*files->ncols
- row = col = 0;
- /*bit wise construct row and col from i */
- for (j = 8 * sizeof(long int) - 1; j > 1; j--) {
- row = row | (1 & (z >> j));
- row = row << 1;
- j--;
- col = col | (1 & (z >> j));
- col = col << 1;
- }
- row = row | (1 & (z >> j));
- j--;
- col = col | (1 & (z >> j));
- G_message("Done: z: %li, row: %d, col: %d", z, row, col);
- if (row >= files->nrows || col >= files->ncols)
- continue;
-
- segment_put(&files->iseg_seg, &s, row, col);
- s++;
- }
-
-
- //~ for(i=0; i<8; i++){ //files->nrows*files->ncols
- // G_message("i: %d", i);
- //~ row=col=0;
- //~
- //~ /*bit wise construct row and col from i*/
- //~ for(j=4; j>0; j=j-2){ //8*sizeof(int)
- // G_message("j: %d", j);
- //~ row = row | ( i & (1 << (2 * j))); /* row | a[rmax-r]; */
- //~ row = row << 1;
- //~ col = col | ( i & (1 << (2 * j + 1)));
- //~ col = col << 1;
- //~ G_message("j: %d, row: %d, col: %d", j, row, col);
- //~ }
- //~ row = row | ( i & (1 << (2 * j))); /* row | a[rmax-r]; */
- //~ col = col | ( i & (1 << ((2 * j) + 1)));
- //~ G_message("(1 << ((2 * j) + 1)) = %d", (1 << ((2 * j) + 1)));
- //~ G_message("Done: i: %d, row: %d, col: %d", i, row, col);
- //~ files->iseg[row][col] = s;
- //~ s++;
- //~ }
-
-
-
- /*speed test... showed a difference of 1min 9s for G_malloc and 34s for linkm. (with i < 2000000000 */
-
-#ifdef LINKM
- head = (struct link_head *)link_init(sizeof(struct pixels));
-#endif
-
- for (i = 0; i < 10; i++) {
-#ifdef LINKM
- p = (struct pixels *)link_new(head);
- link_dispose((struct link_head *)head, (VOID_T *) p);
-#else
- p = (struct pixels *)G_malloc(sizeof(struct pixels));
- G_free(p);
-#endif
- }
-
-#ifdef LINKM
- link_cleanup(head);
- G_message("used linkm");
-#else
- G_message("used G_malloc");
-#endif
-
- G_message("end speed test");
- return TRUE;
-}
-
-int ll_test(struct files *files, struct functions *functions)
-{
- int row, col, n, count;
- struct pixels *Rkn, *current, *newpixel, *Rin_head, *Rin_second; /*current will be used to iterate over any of the linked lists. */
- struct link_head *Rkn_token;
-
- G_message("testing linked lists");
-
- Rin_head = NULL;
- Rin_second = NULL;
- Rkn = NULL;
- Rkn_token = link_init(sizeof(struct pixels));
- if (Rkn_token == NULL)
- G_message("Rkn_token is null");
-
- /* make a neighbor list */
- for (n = 0; n < 5; n++) {
- newpixel = (struct pixels *)link_new(files->token);
- newpixel->next = Rin_head; /*point the new pixel to the current first pixel */
- newpixel->row = n;
- newpixel->col = n + 2;
- Rin_head = newpixel; /*change the first pixel to be the new pixel. */
-
- G_message("Added: Rin %d: row: %d, col: %d", n,
- Rin_head->row, Rin_head->col);
- }
- /* make a second neighbor list, using same token */
- for (n = 0; n < 4; n++) {
- newpixel = (struct pixels *)link_new(files->token);
- newpixel->next = Rin_second; /*point the new pixel to the current first pixel */
- newpixel->row = n * 100;
- newpixel->col = n * 100;
- Rin_second = newpixel; /*change the first pixel to be the new pixel. */
-
- G_message("Added: Rin (second list) %d: row: %d, col: %d", n,
- Rin_second->row, Rin_second->col);
- }
-
- /* make a third neighbor list, using local token */
- for (n = 0; n < 5; n++) {
- newpixel = (struct pixels *)link_new(Rkn_token);
- newpixel->next = Rkn; /*point the new pixel to the current first pixel */
- newpixel->row = 5 * n;
- newpixel->col = n;
- Rkn = newpixel; /*change the first pixel to be the new pixel. */
-
- G_message("Added: Rkn %d: row: %d, col: %d", n, Rkn->row, Rkn->col);
- }
-
- G_message(" Test pass token result: %d",
- test_pass_token(&Rin_head, files));
-
- G_message("Printing out:");
- /*print out neighbors */
- for (current = Rin_head; current != NULL; current = current->next)
- G_message("Rin: row: %d, col: %d", current->row, current->col);
-
- for (current = Rin_second; current != NULL; current = current->next)
- G_message("Rin (second): row: %d, col: %d", current->row,
- current->col);
-
- for (current = Rkn; current != NULL; current = current->next)
- G_message("Rkn: row: %d, col: %d", current->row, current->col);
-
- /* remove all from Rkn list, 5 from Rin list */
- G_message("removing 5 from Rin...");
- for (n = 0; n < 5; n++) {
- current = Rin_head; /* get first in list */
- Rin_head = Rin_head->next; /* point head to the next one *//*pop */
- link_dispose(files->token, (VOID_T *) current);
- }
-
- G_message("Printing out, after 5 removed from Rin:");
- for (current = Rin_head; current != NULL; current = current->next)
- G_message("Rin: row: %d, col: %d", current->row, current->col);
-
- for (current = Rin_second; current != NULL; current = current->next)
- G_message("Rin (second): row: %d, col: %d", current->row,
- current->col);
-
- for (current = Rkn; current != NULL; current = current->next)
- G_message("Rkn: row: %d, col: %d", current->row, current->col);
-
-
- G_message("removing all from Rkn...");
- /* for (current = Rkn; current != NULL; current = current->next) { ||||this shortcut won't work, current is gone! */
- while (Rkn != NULL) {
- G_message("In Rkn remove loop");
- current = Rkn; /* rememer "old" head */
- Rkn = Rkn->next; /* move head to next pixel */
- link_dispose(Rkn_token, (VOID_T *) current); /* remove "old" head */
- }
- G_message("removed Rkn...");
- if (Rkn == NULL)
- G_message("which set Rkn to null");
- else
- G_message("hmm, still need to set Rkn to null?!");
-
-
- G_message("Printing out, after removed Rin and Rkn:");
- for (current = Rin_head; current != NULL; current = current->next)
- G_message("Rin: row: %d, col: %d", current->row, current->col);
-
- for (current = Rin_second; current != NULL; current = current->next)
- G_message("Rin (second): row: %d, col: %d", current->row,
- current->col);
-
- for (current = Rkn; current != NULL; current = current->next)
- G_message("Rkn: row: %d, col: %d", current->row, current->col);
-
-
- /* **************write fake data to test I/O portion of module */
-
- G_message("writing fake data to segmentation file");
- for (row = 0; row < files->nrows; row++) {
- for (col = 0; col < files->ncols; col++) {
- /*files->out_val[0] = files->out_val[0]; *//*segment number *//* just copying the map for testing. */
- //~ files->iseg[row][col] = col + row;
- //todo need variable if this speed test will be used again..
- //segment_put(&files->iseg_seg, (void *)s, row, col);
- }
- G_percent(row, files->nrows, 1);
- }
-
- /*test how many pixels can be made and disposed of */
-
- for (n = 0; n < functions->threshold; n++) {
- /*make tokens */
- test_pass_token(&Rin_head, files);
- count += 5;
- G_message("estimate of tokens created %d", count + 15);
- /*dispose tokens */
- while (Rin_head != NULL) {
- current = Rin_head; /* remember "old" head */
- Rin_head = Rin_head->next; /* move head to next pixel */
- link_dispose(files->token, (VOID_T *) current); /* remove "old" head */
- }
-
- G_message("are they gone?");
- for (current = Rin_head; current != NULL; current = current->next)
- G_message("Rin: row: %d, col: %d", current->row, current->col);
-
- }
-
- link_cleanup(Rkn_token);
- G_message("after link_cleanup(Rkn_token)");
-
- G_message("end linked list test");
- return TRUE;
-}
-
-int test_pass_token(struct pixels **head, struct files *files)
-{
- int n;
- struct pixels *newpixel;
-
- for (n = 10; n < 15; n++) {
- newpixel = (struct pixels *)link_new(files->token);
- newpixel->next = *head; /*point the new pixel to the current first pixel */
- newpixel->row = n;
- newpixel->col = n * 2;
- *head = newpixel; /*change the first pixel to be the new pixel. */
-
- G_message("Added: Rin %d: row: %d, col: %d", n,
- newpixel->row, newpixel->col);
- }
- return TRUE;
-
-}
-
-int seg_speed_test(struct files *files, struct functions *functions)
-{
- int i, j, k, n, max;
- clock_t start, end;
- double temp, cpu_time_used;
- int (*get) (struct files *, int, int); /* files, row, col */
- struct RB_TREE *no_check_tree, *known_iseg_tree;
- struct RB_TRAV trav;
- struct pixels *to_check, *newpixel, *current, *tree_pix;
- FLAG *check_flag;
-
- G_message("checking speed of RAM vs SEG vs get function performance");
-
- G_message("Access in the same region, so shouldn't have any disk I/O");
-
- max = 100000000;
- G_message("repeating everything %d times.", max);
-
- { /* Array vs. SEG ... when working in local area */
- start = clock();
- for (i = 0; i < max; i++) {
- segment_get(&files->bands_seg, (void *)files->bands_val, 12, 12);
- temp = files->bands_val[0];
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message("Using SEG: %g", cpu_time_used);
-
- start = clock();
- for (i = 0; i < max; i++) {
- //~ temp = files->iseg[12][12];
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message("Using array in RAM: %g", cpu_time_used);
-
- get = &get_segID_SEG;
-
- start = clock();
- for (i = 0; i < max; i++) {
- temp = get(files, 12, 12);
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message("Using SEG w/ get(): %g", cpu_time_used);
-
- get = &get_segID_RAM;
-
- start = clock();
- for (i = 0; i < max; i++) {
- temp = get(files, 12, 12);
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message("Using RAM w/ get(): %g", cpu_time_used);
- }
-
- G_message("to check storage requirements... system dependent... :");
- G_message("unsigned char: %lu", sizeof(unsigned char));
- G_message("unsigned char pointer: %lu", sizeof(unsigned char *));
- G_message("int: %lu", sizeof(int));
- G_message("unsigned int: %lu", sizeof(unsigned int));
- G_message("double: %lu", sizeof(double));
-
-
- max = 100000;
- G_message("repeating everything %d times.", max);
- { /* compare rbtree with linked list and array */
-
- n = 100;
- start = clock();
- for (i = 0; i < max; i++) {
- no_check_tree = rbtree_create(compare_ids, sizeof(struct pixels));
-
- /*build */
- for (j = 0; j < n; j++) {
- tree_pix = (struct pixels *)link_new(files->token);
- tree_pix->row = tree_pix->col = j;
- rbtree_insert(no_check_tree, &tree_pix);
- }
- /*access */
- for (j = 0; j < n; j++) {
- if (rbtree_find(no_check_tree, &tree_pix))
- continue; /* always looking for the same pixel...is this an easy or hard one to find? */
- }
- /*free memory */
- rbtree_destroy(no_check_tree);
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message
- ("Using rbtree of pixels ( build/find/destroy), %d elements, time: %g",
- n, cpu_time_used);
-
- start = clock();
- for (i = 0; i < max; i++) {
- known_iseg_tree = rbtree_create(compare_ids, sizeof(int));
-
- /*build */
- for (j = 0; j < n; j++) {
- rbtree_insert(known_iseg_tree, &j);
- }
-
- /*access */
- for (j = 0; j < n; j++) {
- if (rbtree_find(known_iseg_tree, &j))
- continue;
- }
-
- /*free memory */
- rbtree_destroy(known_iseg_tree);
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message
- ("Using rbtree ints ( build/find/destroy), %d elements, time: %g",
- n, cpu_time_used);
-
-
- to_check = NULL;
-
- start = clock();
- for (i = 0; i < max; i++) {
- /*build */
- for (j = 0; j < n; j++) {
- newpixel = (struct pixels *)link_new(files->token);
- newpixel->next = to_check; /*point the new pixel to the current first pixel */
- newpixel->row = j;
- newpixel->col = i;
- to_check = newpixel; /*change the first pixel to be the new pixel. */
- }
- /*access */
- for (current = to_check; current != NULL; current = current->next) { /* for each of Ri's neighbors */
- temp = current->row;
- }
- /*free memory */
- my_dispose_list(files->token, &to_check);
-
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message
- ("Using linked list and linkm (build/access/free), %d elements, time: %g",
- n, cpu_time_used);
-
-
- n = 1000;
- /* repeat for both with larger membership */
-
- start = clock();
- for (i = 0; i < max; i++) {
- known_iseg_tree = rbtree_create(compare_ids, sizeof(int));
-
- /*build */
- for (j = 0; j < n; j++) {
- rbtree_insert(known_iseg_tree, &j);
- }
-
- /*access */
- for (j = 0; j < n; j++) {
- if (rbtree_find(known_iseg_tree, &j))
- continue;
- }
-
- /*free memory */
- rbtree_destroy(known_iseg_tree);
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message("Using rbtree ints, %d elements, time: %g", n,
- cpu_time_used);
-
- to_check = NULL;
-
- start = clock();
- for (i = 0; i < max; i++) {
- /*build */
- for (j = 0; j < n; j++) {
- newpixel = (struct pixels *)link_new(files->token);
- newpixel->next = to_check; /*point the new pixel to the current first pixel */
- newpixel->row = j;
- newpixel->col = i;
- to_check = newpixel; /*change the first pixel to be the new pixel. */
- }
- /*access */
- for (current = to_check; current != NULL; current = current->next) { /* for each of Ri's neighbors */
- temp = current->row;
- }
- /*free memory */
- my_dispose_list(files->token, &to_check);
-
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message("Using linked list and linkm, %d elements, time: %g", n,
- cpu_time_used);
-
- k = 100;
- n = 50;
- check_flag = flag_create(k, k);
- start = clock();
- for (i = 0; i < max; i++) {
- /*set to zero */
- flag_clear_all(check_flag);
- /*write and access */
- for (j = 0; j < n; j++) {
- FLAG_SET(check_flag, j, j);
- temp = FLAG_GET(check_flag, j, j);
- }
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message
- ("Using %d pixel flag array (all cleared), %d elements set and get, time: %g",
- k * k, n, cpu_time_used);
-
-
- k = 10000;
- check_flag = flag_create(k, k);
- start = clock();
- for (i = 0; i < max; i++) {
- /*set to zero */
- flag_clear_all(check_flag);
- /*write and access */
- for (j = 0; j < n; j++) {
- FLAG_SET(check_flag, j, j);
- temp = FLAG_GET(check_flag, j, j);
- }
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message
- ("Using %d pixel flag array (all cleared), %d elements set and get, time: %g",
- k * k, n, cpu_time_used);
-
- }
-
- /* iff bounding constraints have been given, need to confirm neighbors are in the same area.
- * Faster to check if the bounds map is null, or if no bounds just set all the bounds flags to 1 and directly check the bounds flag? */
- {
- max = INT_MAX;
- j = 0;
- G_message("compare if statement to FLAG_GET, repeating %d times",
- max);
- G_message("Flag = %d", FLAG_GET(files->candidate_flag, 1, 1));
-
- start = clock();
- for (i = 0; i < max; i++) {
- if ((FLAG_GET(files->candidate_flag, 1, 1)) != 0) {
- j++;
- }
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message("FLAG_GET: %g, temp: %d", cpu_time_used, j);
- j = 0;
- start = clock();
- for (i = 0; i < max; i++) {
- if (files->bounds_map != NULL) {
- j++;
- }
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message("check for NULL: %g, temp: %d", cpu_time_used, j); /* was faster by about 20% */
- }
-
- /* is accessing a variable different then a value? */
- max = INT_MAX;
- j = k = 0;
- G_message("compare variable to number, repeating %d times", max);
-
- start = clock();
- for (i = 0; i < max; i++) {
- if (i > 0) {
- j++;
- }
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message("compare to zero: %g, temp: %d", cpu_time_used, j);
- j = 0;
- start = clock();
- for (i = 0; i < max; i++) {
- if (i > k) {
- j++;
- }
- }
- end = clock();
- cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
- G_message("compare to k: %g, temp: %d", cpu_time_used, j); /* was faster by about 20% */
-
-
- return TRUE;
-}
-
-int get_segID_SEG(struct files *files, int row, int col)
-{
- segment_get(&files->bands_seg, (void *)files->bands_val, row, col);
- return files->bands_val[0]; /* for accurate comparison, is converting double to int a time penalty? */
-}
-
-int get_segID_RAM(struct files *files, int row, int col)
-{
- return 0; //files->iseg[row][col];
-}
-
-#endif
Deleted: grass-addons/grass7/imagery/i.segment/times.txt
===================================================================
--- grass-addons/grass7/imagery/i.segment/times.txt 2012-08-19 23:07:43 UTC (rev 52741)
+++ grass-addons/grass7/imagery/i.segment/times.txt 2012-08-20 06:01:18 UTC (rev 52742)
@@ -1,192 +0,0 @@
-Machine specifications:
-
-Virtual Machine, 12 cores. (Only 1 is ever used at a time for i.segment)
-4 Ghz per core
-32 GB RAM
-
-
-Segment ID storage - RAM vs SEG and speed access.
-
-i.segment -w --overwrite --verbose group=landsat_1band at samples output=temp threshold=5 method=seg_time min=1
-
-checking speed of RAM vs SEG vs get function performance
-Access in the same region, so shouldn't have any disk I/O
-repeating everything 100000000 times.
-Using SEG: 2.63
-Using array in RAM: 0.25
-Using SEG w/ get(): 3.27
-Using RAM w/ get(): 0.68
-to check storage requirements... system dependent... :
-unsigned char: 1
-unsigned char pointer: 8
-_Bool: 1
-int: 4
-unsigned int: 4
-double: 8
-repeating everything 100000 times.
-Using rbtree of pixels (just build/destroy), 100 elements, time: 0.25
-Using rbtree ints (just build/destroy), 100 elements, time: 3.1
-Using linked list and linkm (build/access/free), 100 elements, time: 0.27
-Using rbtree ints, 1000 elements, time: 49.37
-Using linked list and linkm, 1000 elements, time: 2.64
-Using 10000 pixel flag array (all cleared), 50 elements set and get, time: 0.32
-Using 100000000 pixel flag array (all cleared), 50 elements set and get, time: 2560.09
-
-
-Storage requirement:
-
-100,000,000 pixels
-
-
-#######################################################################################
-NC sample landsat images, lsat7_2000_10
-
-g.region rast=lsat7_2000_10 at landsat
-rows: 475
-cols: 527
-cells: 250325
-
--w threshold=5
-
-Pass number 1210
-
-real 432m20.014s
-user 429m55.276s
-sys 1m0.640s
-
-changing to tree: real 777m50.251s
-But there was one very large segment.
-
-
--w threshold=3:
-real 140m42.401s
-The SW corner is still mostly one segment.
-
-So the processing time is sensitive to the size of the segments.
-
-
-Turning "debug" mode off (gets rid of some loops with G_debug statements)
-
-time i.segment --overwrite --verbose group=landsat_1band at samples output=val threshold=.02 method=region_growing min=1 final_mean=val_mean
-Pass 878:
-real 13m15.149s
-
-
-
-
-
-g.region s=222000 e=637000
-cells: 56334
-< 5 minutes
-
-
-g.region s=220000 e=639000
-cells: 94783
-
-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
-
-changing flag to tree:
-real 8m26.741s
-
-
-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
-
-threshold of 3 ran faster, but visually seemed oversegmented.
-
-
-
-#########older data below
-
-Using version from around June 20, SVN 52181, it does not remember fragment membership.
-
-After some updates...June 26, SVN 52232. (I don't recall changing anything to specifically improve things by this much ?!?)
-
-50 x 50 = 2500 pixels
-
-real 0m5.767s
-user 0m5.704s
-sys 0m0.028s
-
- 0.00228 s/pixel
-
-
-100 x 100 = 10,000 pixels
-
-real 4m8.335s
-user 4m7.363s
-sys 0m0.048s
-
-0.0248 s/pixel
-
-
-140 x 140 = 19,600 pixels
-
-52181:
-real 150m7.509s
-user 149m27.656s
-sys 0m9.025s
-
-0.45 s/pixel
-
-52232:
-
-real 0m55.961s
-user 0m52.655s
-sys 0m3.040s
-
-
-
-10k pixels, but [Raster MASK present]
-52232:
-real 1m43.085s
-user 1m42.338s
-sys 0m0.152s
-10k pixels, but [Raster MASK present]
-
-basic flag implementation:
-real 0m47.079s
-user 0m46.651s
-sys 0m0.092s
-[Raster MASK present]
-
-
-19600 pixels
-52232:
-real 0m55.961s
-user 0m52.655s
-sys 0m3.040s
-
-basic implementation:
-real 1m4.718s
-user 1m0.016s
-sys 0m4.340s
-
-
-Rough target:
-
-10,000 x 10,000 = 100,000,000 pixels
-
-10 hours?
-
-
-removing squareroot
-17s --> 4s at 50x50 resolution
-23 --> 17s at 70x70
-
-
Modified: grass-addons/grass7/imagery/i.segment/write_output.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/write_output.c 2012-08-19 23:07:43 UTC (rev 52741)
+++ grass-addons/grass7/imagery/i.segment/write_output.c 2012-08-20 06:01:18 UTC (rev 52742)
@@ -7,11 +7,6 @@
#include <grass/segment.h> /* segmentation library */
#include "iseg.h"
-/* TODO polish: some time delay here with meanbuf, etc being processed. I only put if statements on the actual files
- * to try and keep the code more clear. Need to see if this raster makes stats processing easier? Or IFDEF it out?
- * If it is left in, need to refine it to save a raster for each band in the input group.
- */
-
int write_output(struct files *files)
{
int out_fd, mean_fd, row, col; /* mean_fd for validiating/debug of means */
@@ -29,7 +24,6 @@
segment_flush(&files->iseg_seg);
/* open output raster map */
- G_debug(1, "preparing output raster");
out_fd = Rast_open_new(files->out_name, CELL_TYPE);
if (files->out_band != NULL)
mean_fd = Rast_open_new(files->out_band, DCELL_TYPE);
@@ -64,7 +58,6 @@
Rast_write_colors(files->out_name, G_mapset(), &colors);
/* add command line to history */
- /* todo polish, any other information that would be interesting? Number of passes? Number of segments made? */
/* see i.pca as an example of setting custom info */
Rast_short_history(files->out_name, "raster", &history);
Rast_command_history(&history);
@@ -82,27 +75,20 @@
{
/* close segmentation files and output raster */
- G_debug(1, "closing bands_seg...");
segment_close(&files->bands_seg);
- G_debug(1, "closing bounds_seg...");
if (files->bounds_map != NULL)
segment_close(&files->bounds_seg);
- G_debug(1, "freeing _val");
G_free(files->bands_val);
G_free(files->second_val);
- G_debug(1, "freeing iseg");
segment_close(&files->iseg_seg);
- G_debug(1, "destroying flags");
flag_destroy(files->null_flag);
flag_destroy(files->candidate_flag);
flag_destroy(files->seeds_flag);
- G_debug(1, "close_files() before link_cleanup()");
link_cleanup(files->token);
- G_debug(1, "close_files() after link_cleanup()");
return TRUE;
}
More information about the grass-commit
mailing list