[GRASS-SVN] r52419 - grass-addons/grass7/imagery/i.segment
svn_grass at osgeo.org
svn_grass at osgeo.org
Thu Jul 19 22:36:54 PDT 2012
Author: momsen
Date: 2012-07-19 22:36:54 -0700 (Thu, 19 Jul 2012)
New Revision: 52419
Added:
grass-addons/grass7/imagery/i.segment/flag.c
Removed:
grass-addons/grass7/imagery/i.segment/flag_clr_all.c
grass-addons/grass7/imagery/i.segment/flag_create.c
grass-addons/grass7/imagery/i.segment/flag_destroy.c
grass-addons/grass7/imagery/i.segment/flag_get.c
grass-addons/grass7/imagery/i.segment/flag_set.c
grass-addons/grass7/imagery/i.segment/flag_unset.c
Modified:
grass-addons/grass7/imagery/i.segment/create_isegs.c
grass-addons/grass7/imagery/i.segment/flag.h
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:
added 8 neighbor option. Lots of small todo's resolved, general code clean up. Added pathflag implementation, decreases time/iteration but increased number of iterations???
Modified: grass-addons/grass7/imagery/i.segment/create_isegs.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/create_isegs.c 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/create_isegs.c 2012-07-20 05:36:54 UTC (rev 52419)
@@ -3,6 +3,7 @@
/* Currently only region growing is implemented */
#include <stdlib.h>
+#include <float.h> /* for DBL_MAX */
#include <grass/gis.h>
#include <grass/glocale.h>
#include <grass/raster.h>
@@ -12,57 +13,133 @@
#include "iseg.h"
#ifdef DEBUG
- #include <time.h>
+#include <time.h>
+#include <limits.h>
#endif
#define LINKM
-/* #define REVERSE */
int create_isegs(struct files *files, struct functions *functions)
{
- int lower_bound, upper_bound;
+ int lower_bound, upper_bound, row, col;
int successflag = 1;
struct Range range;
- /* TODO consider if there are _method specific_ parameter set up and memory allocation, should that happen here? */
+ functions->threshold = functions->threshold * functions->threshold * files->nbands; /* use modified threshold to account for scaled input and to avoid square root in similarity comparison. */
- 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 */
-
/* 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 */
+ lower_bound = upper_bound = 0; /* so run the segmentation algorithm just one time */
+ }
else {
if (Rast_read_range(files->bounds_map, files->bounds_mapset, &range) != 1) { /* returns -1 on error, 2 on empty range, quiting either way. */
G_fatal_error(_("No min/max found in raster map <%s>"),
files->bounds_map);
}
- 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.... */
+ Rast_get_range_min_max(&range, &lower_bound, &upper_bound);
+ /* TODO polish, should we instead/also get unique values?
+ * As is, we will iterate at least one time over the entire raster for each integer between the upper and lower bound. */
}
- 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);
+ /* processing loop for polygon/boundary constraints */
+ for (files->current_bound = lower_bound;
+ files->current_bound <= upper_bound; files->current_bound++) {
+
+ /* check the processing window */
+ if (files->bounds_map == NULL) {
+ files->minrow = files->mincol = 0;
+ files->maxrow = files->nrows;
+ files->maxcol = files->ncols;
+ /* todo polish: could actually check the NULL flag to see where the first/last row/col of real data are, and reduce the processing window.
+ * This could help (a little?) if a MASK is used that removes a large part of the map. */
+ }
+ else {
+ /* todo, Markus, should there be a faster way to find this? Something already in GRASS that I can call? */
+ /* find first row that matches the current bound value */
+ for (row = 0; row < files->nrows; row++) {
+ for (col = 0; col < files->ncols; col++) {
+ segment_get(&files->bounds_seg, &files->bounds_val, row,
+ col);
+ if (files->bounds_val == files->current_bound) {
+ files->minrow = row;
+ /* break a nested loop... hmm, could change this to a function call, use a flag, or use peano iteration. */
+ goto done1; /* or is this one of the few places where a goto loop is OK? */
+ }
+ }
+ }
+ done1:
+
+ for (col = 0; col < files->ncols; col++) {
+ for (row = 0; row < files->nrows; row++) {
+ segment_get(&files->bounds_seg, &files->bounds_val, row,
+ col);
+ if (files->bounds_val == files->current_bound) {
+ files->mincol = col;
+ goto done2;
+ }
+ }
+ }
+ done2:
+
+ for (row = files->nrows - 1; row >= 0; row--) {
+ for (col = files->ncols - 1; col >= 0; col--) {
+ segment_get(&files->bounds_seg, &files->bounds_val, row,
+ col);
+ if (files->bounds_val == files->current_bound) {
+ files->maxrow = row;
+ goto done3;
+ }
+ }
+ }
+ done3:
+
+ for (col = files->ncols - 1; col >= 0; col--) {
+ for (row = files->nrows - 1; row >= 0; row--) {
+ segment_get(&files->bounds_seg, &files->bounds_val, row,
+ col);
+ if (files->bounds_val == files->current_bound) {
+ files->maxcol = col;
+ goto done4;
+ }
+ }
+ }
+ done4:
+ G_message("minrow: %d, maxrow: %d, mincol: %d, maxcol: %d", files->minrow, files->maxrow, files->mincol, files->maxcol); //TODO change to debug.
+
+ /* set the in_bound_flag */
+ for (row = 0; row < files->nrows; row++) {
+ for (col = 0; col < files->ncols; col++) {
+ if (!(FLAG_GET(files->null_flag, row, col))) {
+ segment_get(&files->bounds_seg, &files->bounds_val,
+ row, col);
+ if (files->bounds_val == files->current_bound) {
+ FLAG_SET(files->in_bounds_flag, row, col);
+ }
+ else
+ FLAG_UNSET(files->in_bounds_flag, row, col);
+ }
+ }
+ }
+ /* clear candidate flag, so only need to reset the processing area on each iteration. */
+ flag_clear_all(files->candidate_flag);
+
+ } /* end of else, set up for bounded segmentation */
+
+ /* run the segmentation algorithm */
+
if (functions->method == 1) {
- G_debug(1, "starting region_growing()");
successflag = region_growing(files, functions);
}
- #ifdef DEBUG
+#ifdef DEBUG
else if (functions->method == 0)
- successflag = io_debug(files, functions); /* TODO: why does it want `&files` in main, but `files` here ??? */
+ 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
+#endif
} /* end outer loop for processing polygons */
- /* clean up? */
-
-
return successflag;
}
@@ -218,7 +295,6 @@
else
G_message("hmm, still need to set Rkn to null?!");
- /* Rkn = NULL; not needed *//* TODO: if emptying whole list - can just empty and then set head to null ? */
G_message("Printing out, after removed Rin and Rkn:");
for (current = Rin_head; current != NULL; current = current->next)
@@ -261,7 +337,6 @@
for (current = Rin_head; current != NULL; current = current->next)
G_message("Rin: row: %d, col: %d", current->row, current->col);
- /* TODO: anyway to confirm if linkm memory manager knows they are gone???" */
}
link_cleanup(Rkn_token);
@@ -292,237 +367,279 @@
int seg_speed_test(struct files *files, struct functions *functions)
{
- int i, j, k, n, max;
- clock_t start, end;
+ 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 */
+ 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];
+ 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;
+ 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];
+ for (i = 0; i < max; i++) {
+ temp = files->iseg[12][12];
}
end = clock();
- cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
+ 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);
+ for (i = 0; i < max; i++) {
+ temp = get(files, 12, 12);
}
end = clock();
- cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
+ 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);
+ for (i = 0; i < max; i++) {
+ temp = get(files, 12, 12);
}
end = clock();
- cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
+ 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 */
+ 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->row = tree_pix->col = j;
- rbtree_insert(no_check_tree, &tree_pix);
- }
- //~ /*access*/
- rbtree_init_trav(&trav, no_check_tree);
- //~ while ((data = rbtree_traverse(&trav)) != NULL) {
- //~ if (my_compare_fn(data, threshold_data) == 0) break;
- //~ G_message("%d", data);
- //~ }
- /*free memory*/
- rbtree_destroy(no_check_tree);
+ 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->row = tree_pix->col = j;
+ rbtree_insert(no_check_tree, &tree_pix);
+ }
+ /*access */
+ rbtree_init_trav(&trav, no_check_tree);
+ /*not sure how to do this...
+ while ((data = rbtree_traverse(&trav)) != NULL) {
+ if (my_compare_fn(data, threshold_data) == 0) break;
+ G_message("%d", data);
+ }
+ */
+ /*free memory */
+ rbtree_destroy(no_check_tree);
}
end = clock();
- cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
- G_message("Using rbtree of pixels (just build/destroy), %d elements, time: %g", n, cpu_time_used);
+ cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
+ G_message
+ ("Using rbtree of pixels (just build/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*/
- rbtree_init_trav(&trav, known_iseg_tree);
- //~ while ((data = rbtree_traverse(&trav)) != NULL) {
- //~ if (my_compare_fn(data, threshold_data) == 0) break;
- //~ G_message("%d", data);
- //~ }
- /*free memory*/
- rbtree_destroy(known_iseg_tree);
+ 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 */
+ rbtree_init_trav(&trav, known_iseg_tree);
+ /*
+ while ((data = rbtree_traverse(&trav)) != NULL) {
+ if (my_compare_fn(data, threshold_data) == 0) break;
+ G_message("%d", data);
+ } */
+ /*free memory */
+ rbtree_destroy(known_iseg_tree);
}
end = clock();
- cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
- G_message("Using rbtree ints (just build/destroy), %d elements, time: %g", n, cpu_time_used);
+ cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
+ G_message
+ ("Using rbtree ints (just build/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);
+ 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);
+ 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));
- rbtree_init_trav(&trav, known_iseg_tree);
- /*build*/
- for(j=0; j<n; j++){
- rbtree_insert(known_iseg_tree, &j);
- }
- //~ /*access*/
- //~ while ((data = rbtree_traverse(&trav)) != NULL) {
- //~ if (my_compare_fn(data, threshold_data) == 0) break;
- //~ G_message("%d", data);
- //~ }
- /*free memory*/
- rbtree_destroy(known_iseg_tree);
+ 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));
+ rbtree_init_trav(&trav, known_iseg_tree);
+
+ /*build */
+ for (j = 0; j < n; j++) {
+ rbtree_insert(known_iseg_tree, &j);
+ }
+ /*access */
+ /* while ((data = rbtree_traverse(&trav)) != NULL) {
+ if (my_compare_fn(data, threshold_data) == 0) break;
+ G_message("%d", data);
+ } */
+ /*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);
+ 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);
+ 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);
+ 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);
- }
+ 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);
-
+ 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);
+
+ 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);
- }
+ 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);
+ 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% */
+
+ return TRUE;
}
-
- 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]; /*todo for accurate comparison, is converting double to int a time penalty? */
+ 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 files->iseg[row][col];
+ return files->iseg[row][col];
}
#endif
@@ -530,25 +647,23 @@
{
int row, col, t;
double threshold, Ri_similarity, Rk_similarity, tempsim;
- int endflag; /* =1 if there were no merges on that processing iteration */
- int pathflag; /* =1 if we didn't find mutual neighbors, and should continue with Rk */
+ 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, *current,
+ *newpixel, *Ri_bestn;
+ int Ri_count, Rk_count; /* number of pixels/cells in Ri and Rk */
- /*int mergeflag; just did it based on if statment... *//* =1 if we have mutually agreeing best neighbors */
-
- /* Ri = current focus segment
+ /* files->token has the "link_head" for linkm: linked list memory allocation.
+ *
+ * 4 linked lists of pixels:
+ * Ri = current focus segment
* Rk = Ri's most similar neighbor
* Rkn = Rk's neighbors
- * Rin = Ri's neigbors currently as pixels, so repeat calculations are made when multiple neighbors in same segment
- * Todo: should Rin be checked for unique segments? Or is checking for unique longer then just calculating similarity a few extra times?
- * files->token has the "link_head" for linkm: linked list memory allocation.
- * just moved iseg to RAM, bands is in SEG, so probably faster to check for unique. */
+ * Rin = Ri's neigbors
+ * */
- struct pixels *Ri_head, *Rk_head, *Rin_head, *Rkn_head, *current, *newpixel; /*current will be used to iterate over any of the linked lists. */
- int Ri_count, Rk_count; /*TODO when to calculate these, currently accumulating during find_neighbor() */
- struct pixels *Ri_bestn; /* best neighbor pixel for Ri, not used as a linked list, just one pixel... */
+ G_message("Running region growing algorithm, the percent completed is based on %d max iterations, but the process will end earlier if no further merges can be made.", functions->end_t); /*TODO polish: can _() be combined with %d ? */
- G_verbose_message("Running region growing algorithm");
-
t = 1;
files->candidate_count = 0;
@@ -559,81 +674,39 @@
Rkn_head = NULL;
Ri_bestn = NULL;
- /* TODO, want to get a min/max row/col to narrow the processing window ??? */
+ /* TODO, want to get a min/max row/col to narrow the processing window ??? certainly if polygon constraints. */
+ /* do while loop until no merges are made, or until t reaches maximum number of iterations */
do {
- /* do while loop on t to slowly lower threshold. also check that endflag==0 (no merges were made) */
G_debug(3, "####### Starting outer do loop! t = %d #######", t);
+ G_verbose_message("Pass %d: ", t);
+ G_percent(t, functions->end_t, 1);
threshold = functions->threshold; /* TODO, consider making this a function of t. */
endflag = TRUE;
- /* Set candidate flag to true/1 for all pixels TODO: for polygon/vector constraint, need to just set to true for those being processed */
- if (files->bounds_map == NULL) { /*normal processing */
- for (row = 0; row < files->nrows; row++) {
- for (col = 0; col < files->ncols; col++) {
- /* TODO: if we are starting from seeds...and only allow merges between unassigned pixels
- * and seeds/existing segments, then this needs an if (and will be very inefficient)
- * maybe consider the sorted array, btree, map... but the number of seeds could still be high for a large map */
- if (!(FLAG_GET(files->null_flag, row, col))) {
- FLAG_SET(files->candidate_flag, row, col); /*candidate pixel flag */
+ set_all_candidate_flags(files);
+ /* Set candidate flag to true/1 for all pixels */
- files->candidate_count++;
- } /* Need something to prevent "pathflag" infinite loop */
- }
- }
- }
- else { /* polygon constraints/boundaries were supplied, include that criteria. TODO: this repeats a lot of code, is there a way to combine this check without having too many extra if/etc statements ??? */
- for (row = 0; row < files->nrows; row++) {
- for (col = 0; col < files->ncols; col++) {
- if (!(FLAG_GET(files->null_flag, row, col))) {
-
- segment_get(&files->bounds_seg, &files->bounds_val,
- row, col);
-
- if (files->bounds_val == files->current_bound) {
- /*TODO could move this if statement one line up, and only set "1" flags if we can assume all flags are already zero. (i.e. only get/put the ones we want to set to 1.) */
- FLAG_SET(files->candidate_flag, row, col); /*candidate pixel flag */
- files->candidate_count++; /*TODO this assumes full grid with no null or mask!! But need something to prevent "pathflag" infinite loop */
- }
- //~ else !!!TODO is it safe to assume that all flag's are zero at this point?
- //~ FLAG_UNSET(files->candidate_flag, row, col);
-
- }
- }
- }
- }
-
G_debug(4, "Starting to process %d candidate pixels",
files->candidate_count);
- /*process candidate pixels */
- G_message("Pass %d: ", t);
+ /*process candidate pixels for this iteration */
+
/*check each pixel, start the processing only if it is a candidate pixel */
- /* for validation, select one of the two... could make this IFDEF or input parameter */
- /* reverse order
- */
-#ifdef REVERSE
- for (row = files->nrows - 1; row >= 0; row--) {
- G_percent(files->nrows - row, files->nrows, 5);
- for (col = files->ncols - 1; col >= 0; col--) {
-#else
for (row = 0; row < files->nrows; row++) {
- G_percent(row, files->nrows, 5); /* TODO, can a message be included with G_percent? */
for (col = 0; col < files->ncols; col++) {
-#endif
- G_debug(4,
- "Next starting pixel from next row/col, not from Rk");
+ G_debug(4, "Starting pixel from next row/col, not from Rk");
if (FLAG_GET(files->candidate_flag, row, col)) {
/*free memory for linked lists */
my_dispose_list(files->token, &Ri_head);
my_dispose_list(files->token, &Rk_head);
my_dispose_list(files->token, &Rin_head);
- my_dispose_list(files->token, &Rkn_head); /* TODO, better style for repeating this for all structures? */
+ my_dispose_list(files->token, &Rkn_head);
Rk_count = 0;
/* First pixel in Ri is current row/col pixel. We may add more later if it is part of a segment */
@@ -646,280 +719,224 @@
pathflag = TRUE;
- // while (pathflag == TRUE && files->candidate_count > 0) { /*if don't find mutual neighbors on first try, will use Rk as next Ri. */
+ while (pathflag == TRUE && files->candidate_count > 0) { /*if don't find mutual neighbors on first try, will use Rk as next Ri. */
- G_debug(4, "Next starting pixel: row, %d, col, %d",
- Ri_head->row, Ri_head->col);
+ G_debug(4, "Next starting pixel: row, %d, col, %d",
+ Ri_head->row, Ri_head->col);
- /* Setting Ri to be not a candidate allows using "itself" when at edge of raster. TODO THIS NEEDS TO BE CHANGED!!!!
- * Otherwise need to use a list/count/something to know the number of pixel neighbors */
- set_candidate_flag(Ri_head, FALSE, files); /* TODO: error trap? */
- G_debug(4, "line 165, \t\t\t\tcc = %d",
- files->candidate_count);
+ /* find segment neighbors, if we don't already have them */
+ if (Rin_head == NULL) {
+ if (find_segment_neighbors
+ (&Ri_head, &Rin_head, &Ri_count, files,
+ functions) != TRUE) {
+ G_fatal_error
+ ("find_segment_neighbors() failed");
+ }
+ }
- /* what is passed to find segment neighors: */
- /* G_debug(4, "calling find_segment_neigors() with:");
- for (current = Ri_head; current != NULL;
- current = current->next)
- G_debug(4, "Ri: row: %d, col: %d", current->row,
- current->col);
- for (current = Rin_head; current != NULL;
- current = current->next)
- G_debug(4, "Rin: row: %d, col: %d", current->row,
- current->col);
- G_debug(4, "also passing Ri_count: %d", Ri_count);
- */
-
- /* find segment neighbors */
- if (find_segment_neighbors
- (&Ri_head, &Rin_head, &Ri_count, files,
- functions) != TRUE) {
- G_fatal_error("find_segment_neighbors() failed");
- }
+ if (Rin_head != NULL) { /*found neighbors, find best neighbor then see if is mutually best neighbor */
- if (Rin_head == NULL) {
- G_debug(4, "2a, Segment had no valid neighbors"); /*this could happen if there is a segment surrounded by pixels that have already been processed */
- pathflag = FALSE;
- Ri_count = 0;
- set_candidate_flag(Ri_head, FALSE, files); /* TODO: error trap? */
- files->candidate_count++; /* already counted out Ri[0]; */
- G_debug(4, "line 176, \t\t\t\tcc = %d",
- files->candidate_count);
- }
- else { /*found neighbors, go ahead until find mutually agreeing neighbors */
+#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
- G_debug(4, "2b, Found Ri's pixels");
- #ifdef DEBUG
- /*print out neighbors */
- for (current = Ri_head; current != NULL;
- current = current->next)
- G_debug(4, "Ri: row: %d, col: %d", current->row,
- current->col);
- #endif
- G_debug(4, "2b, Found Ri's neighbors");
- #ifdef DEBUG
- /*print out 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 */
+ segment_get(&files->bands_seg, (void *)files->bands_val, Ri_head->row, Ri_head->col); /* current segment values */
- /* find Ri's most similar neighbor */
- Ri_bestn = NULL;
- Ri_similarity = threshold + 1; /* set current similarity to max value */
- segment_get(&files->bands_seg, (void *)files->bands_val, Ri_head->row, Ri_head->col); /* current segment values */
+ /* for each of Ri's neighbors */
+ for (current = Rin_head; current != NULL;
+ 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);
- for (current = Rin_head; current != NULL; current = current->next) { /* for each of Ri's neighbors */
- tempsim =
- (*functions->calculate_similarity) (Ri_head,
- current,
- files,
- functions);
- G_debug(4,
- "simularity = %g for neighbor : row: %d, col %d.",
- tempsim, current->row, current->col);
+ if (tempsim < Ri_similarity) {
+ Ri_similarity = tempsim;
+ Ri_bestn = current;
+ 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 */
- if (tempsim < Ri_similarity) {
- Ri_similarity = tempsim;
- Ri_bestn = current; /*TODO want to point to the current pixel...confirm when current changes need this to stay put! */
+ if (Ri_bestn != NULL) {
G_debug(4,
- "Current lowest Ri_similarity = %g, for neighbor pixel row: %d col: %d",
+ "Lowest Ri_similarity = %g, for neighbor pixel row: %d col: %d",
Ri_similarity, Ri_bestn->row,
Ri_bestn->col);
+ if (!
+ (FLAG_GET
+ (files->candidate_flag, Ri_bestn->row,
+ Ri_bestn->col))) {
+ /* this check is important:
+ * best neighbor is not a valid candidate, was already merged earlier in this time step */
+ Ri_bestn = NULL;
+ pathflag = FALSE;
+ }
}
- }
- if (Ri_bestn != NULL) {
- G_debug(4,
- "Lowest Ri_similarity = %g, for neighbor pixel row: %d col: %d",
- Ri_similarity, Ri_bestn->row,
- Ri_bestn->col);
+ if (Ri_bestn != NULL && Ri_similarity < threshold) { /* small TODO: should this be < or <= for threshold? */
- //~ segment_get(&files->out_seg,
- //~ (void *)files->out_val, Ri_bestn->row,
- //~ Ri_bestn->col);
- if (!
- (FLAG_GET
- (files->candidate_flag, Ri_bestn->row,
- Ri_bestn->col)))
- /* this check is important:
- * best neighbor is not a valid candidate, was already merged earlier in this time step */
- Ri_bestn = NULL;
- }
+ /* Rk starts from Ri's best neighbor */
+ Rk_count = 1;
+ newpixel =
+ (struct pixels *)link_new(files->token);
+ newpixel->next = NULL;
+ newpixel->row = Ri_bestn->row;
+ newpixel->col = Ri_bestn->col;
+ Rk_head = newpixel;
- if (Ri_bestn != NULL && Ri_similarity < threshold) { /* small TODO: should this be < or <= for threshold? */
- /* we'll have the neighbor pixel to start with. */
- G_debug(4, "3a: Working with Rk");
- Rk_count = 1;
- newpixel =
- (struct pixels *)link_new(files->token);
- newpixel->next = NULL; /* or = Rk_head; *//*TODO, should this be Rk_head or just NULL? amounts to the same thing here? */
- newpixel->row = Ri_bestn->row;
- newpixel->col = Ri_bestn->col;
- Rk_head = newpixel;
- /* TODO - lists starting, should this be a helper function, did at start of Ri and Rk. */
+ find_segment_neighbors(&Rk_head, &Rkn_head,
+ &Rk_count, files,
+ functions);
- /*Todo: Do we want to put Ri into Rkn ? Hmm, we don't want to actually check similarity on them when they are in Rkn, since we'll
- * start with Ri as the initial similarity. Maybe add a 3rd input list to find_segment_neighbors(), and put them directily into
- * the no_check list so we avoid a small amount of processing? But that might take longer then just checking their similarity in the first place! */
+#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_segment_neighbors(&Rk_head, &Rkn_head, &Rk_count, files, functions); /* data structure for Rk's neighbors, and pixels in Rk if we don't already have it */
+ /* ******** 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 */
- G_debug(4, "Found Rk's pixels");
- #ifdef DEBUG
- /*print out neighbors */
- for (current = Rk_head; current != NULL;
- current = current->next)
- G_debug(4, "Rk: row: %d, col: %d",
- current->row, current->col);
- #endif
- G_debug(4, "Found Rk's neighbors");
- #ifdef DEBUG
- /*print out 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 */
+ /* check similarity for each of Rk's neighbors */
+ for (current = Rkn_head; current != NULL;
+ current = current->next) {
+ tempsim =
+ functions->calculate_similarity
+ (Rk_head, current, files, functions);
- for (current = Rkn_head; current != NULL; current = current->next) { /* for each of Rk's neighbors */
- tempsim = functions->calculate_similarity(Rk_head, current, files, functions); /*TODO: need an error trap here, if something goes wrong with calculating similarity? */
+ if (tempsim < Rk_similarity) {
+ Rk_similarity = tempsim;
+ break; /* exit for Rk's neighbors loop here, we know that Ri and Rk aren't mutually best neighbors */
+ }
+ } /* have checked all of Rk's neighbors */
- if (tempsim < Rk_similarity) {
- Rk_similarity = tempsim;
- break; /* exit for Rk's neighbors loop here, we know that Ri and Rk aren't mutually best neighbors */
+ if (Rk_similarity == Ri_similarity) { /* mutually most similar neighbors */
+ merge_values(Ri_head, Rk_head, Ri_count,
+ Rk_count, files);
+ endflag = FALSE; /* we've made at least one merge, so want another t iteration */
+ pathflag = FALSE; /* go to next row,column pixel - end of Rk -> Ri chain since we found mutual best neighbors */
}
+ 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);
+ G_debug(4, "line 247, \t\t\t\tcc = %d",
+ files->candidate_count);
+ }
+ } /* end if (Ri_bestn != NULL && Ri_similarity < threshold) */
+ else {
+ /* no valid best neighbor for this Ri
+ * exclude this Ri from further comparisons
+ * because we checked already Ri for a mutually best neighbor with all valid candidates
+ * thus Ri can not be the mutually best neighbor later on during this pass
+ * unfortunately this does happen sometimes */
+ set_candidate_flag(Ri_head, FALSE, files);
+ G_debug(4,
+ "3b Ri's best neighbor was not valid candidate, or their similarity was > threshold");
+ pathflag = FALSE;
}
+ } /* end if(Rin_head != NULL) */
+ else { /* Ri didn't have a neighbor */
+ G_debug(4, "Segment had no neighbors");
+ pathflag = FALSE;
+ set_candidate_flag(Ri_head, FALSE, files);
+ G_debug(4, "line 176, \t\t\t\tcc = %d",
+ files->candidate_count);
+ }
- if (Rk_similarity == Ri_similarity) { /* so they agree, both are mutually most similar neighbors, none of Rk's other neighbors were more similar */
- merge_values(Ri_head, Rk_head, Ri_count, Rk_count, files); /* TODO error trap */
- endflag = FALSE; /* we've made at least one merge, so need another t iteration */
- pathflag = FALSE; /* go to next row,column pixel - end of Rk -> Ri chain since we found mutual best neighbors */
+ 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% ?!?!?!? */
+ if(functions->path == TRUE){
+ Ri_count = Rk_count;
+ Rk_count = 0;
+ my_dispose_list(files->token, &Ri_head);
+ Ri_head = Rk_head;
+ Rk_head = NULL;
+ if (Rkn_head != NULL) {
+ my_dispose_list(files->token, &Rin_head);
+ Rin_head = Rkn_head;
+ Rkn_head = NULL;
}
- 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);
+ else
+ my_dispose_list(files->token, &Rin_head);
+ }
+ else
+ pathflag = FALSE;
+
+ }
- /* did this at beginning of path loop */
- set_candidate_flag(Ri_head, FALSE, files); /* remove all Ri members from candidate pixels (set flag) */
- files->candidate_count++; /* add one back, we had already set Ri[0] flag at the beginning. */
- G_debug(4, "line 247, \t\t\t\tcc = %d",
- files->candidate_count);
- //~ /* Use Rk as next Ri: this is the eCognition technique. Seems this is a bit faster, we already have segment membership pixels */
- //~ Ri_count = Rk_count;
- //~ /* Ri = &Rk; *//* TODO fast/correct way to use arrays and pointers? Ri now will just point to Rk? */
- //~ /* at beginning, when initialize Rk ` Rk[n][m] = 0 ` ?? with that just remove the Rk pointer, and leave Ri pointing to the original Rk data? */
- //~ for (n = 0; n < 100; n++) { /*TODO shortcut code...get rid of this... */
- //~ for (m = 0; m < 2; m++) {
- //~ Ri[n][m] = Rk[n][m];
- //~ }
- //~ }
-
- }
- } /*end else (from if mutually best neighbors) */
- else {
- /* no valid best neighbor for this Ri
- * exclude this Ri from further comparisons
- * because we checked already Ri for a mutually best neighbor with all valid candidates
- * thus Ri can not be the mutually best neighbor later on during this pass
- * unfortunately this does happen sometimes */
- set_candidate_flag(Ri_head, FALSE, files); /* TODO: error trap? */
- files->candidate_count++; /*first pixel was already set */
- G_debug(4,
- "3b Rk didn't didn't exist, was not valid candidate, or similarity was > threshold");
- } /*end else - Ri's best neighbor was not a candidate */
- } /* end else - Ri did have neighbors */
- // } /*end pathflag do loop */
+ } /*end pathflag do loop */
} /*end if pixel is candidate pixel */
} /*next column */
-#ifdef REVERSE
- G_percent(files->nrows - row, files->nrows, 5);
-#else
- G_percent(row, files->nrows-1, 5); /* TODO, can a message be included with G_percent? */
-#endif
- /* TODO, the REVERSE version gets printed on a new line, and isnt' covered. The else version is. ? */
- /* TODO, shows up in CLI, not in GUI */
+ } /*next row */
-#ifdef NODEF
- }
+ /* finished one iteration over entire raster */
+ G_debug(4, "Finished one pass, t was = %d", t);
+ t++;
}
- /* to balance brackets in first ifdef statement */
-#endif
+ while (t <= functions->end_t && endflag == FALSE); /*end t loop, either reached max iterations or didn't merge any segments */
- } /*next row */
- /* finished one pass for processing candidate pixels */
+ if (t == 2 && files->bounds_map == NULL)
+ G_warning(_("No segments were created. Verify threshold and region settings."));
+ /* included the bound_map check, since we check all values between min/max, intermediate values might not be present. TODO polish, add back in if we check for unique bounds values. */
- G_debug(4, "Finished one pass, t was = %d", t);
- t++;
- } while (t <= functions->end_t && endflag == FALSE);
- /*end t loop *//*TODO, should there be a max t that it can iterate for? Include t in G_message? */
-
- if(endflag == FALSE) G_message(_("Merging processes stopped due to reaching max iteration limit, more merges may be possible"));
+ if (endflag == FALSE)
+ G_message(_("Merging processes stopped due to reaching max iteration limit, more merges may be possible"));
- /* ****************************************************************************************** */
- /* final pass, ignore threshold and force a merge for small segments with their best neighbor */
- /* ****************************************************************************************** */
-
-
- if (functions->min_segment_size > 1) {
- G_verbose_message("Final iteration, force merges for small segments.");
-
- /* TODO: It would be possible to use some sort of "forced merge" flag and if statements in the above code.
- * This might be easier to maintain... but I wasn't sure which would be easier to read
- * and it would add some extra if statements to each iteration...
- *
- * for the final forced merge, the candidate flag is just to keep track if we have confirmed if:
- * a. the segment size is >= to the minimum allowed size or
- * b. we have merged it with its best neighbor
- */
- /* TODO: repeating this twice, move to helper function? */
-
- /* Set candidate flag to true/1 for all pixels TODO: for polygon/vector constraint, need to just set to true for those being processed */
- if (files->bounds_map == NULL) { /*normal processing */
- for (row = 0; row < files->nrows; row++) {
- for (col = 0; col < files->ncols; col++) {
- /* TODO: if we are starting from seeds...and only allow merges between unassigned pixels
- * and seeds/existing segments, then this needs an if (and will be very inefficient)
- * maybe consider the sorted array, btree, map... but the number of seeds could still be high for a large map */
- if (!(FLAG_GET(files->null_flag, row, col))) {
- FLAG_SET(files->candidate_flag, row, col); /*candidate pixel flag */
+ /* ****************************************************************************************** */
+ /* final pass, ignore threshold and force a merge for small segments with their best neighbor */
+ /* ****************************************************************************************** */
- files->candidate_count++;
- } /* Need something to prevent "pathflag" infinite loop */
- }
- }
- }
- else { /* polygon constraints/boundaries were supplied, include that criteria. TODO: this repeats a lot of code, is there a way to combine this check without having too many extra if/etc statements ??? */
- for (row = 0; row < files->nrows; row++) {
- for (col = 0; col < files->ncols; col++) {
- if (!(FLAG_GET(files->null_flag, row, col))) {
- segment_get(&files->bounds_seg, &files->bounds_val,
- row, col);
+ if (functions->min_segment_size > 1) {
+ G_verbose_message
+ (_("Final iteration, forcing merges for small segments, percent complete based on rows."));
- if (files->bounds_val == files->current_bound) {
- /*TODO could move this if statement one line up, and only set "1" flags if we can assume all flags are already zero. (i.e. only get/put the ones we want to set to 1.) */
- FLAG_SET(files->candidate_flag, row, col); /*candidate pixel flag */
- files->candidate_count++; /*TODO this assumes full grid with no null or mask!! But need something to prevent "pathflag" infinite loop */
- }
- //~ else !!!TODO is it safe to assume that all flag's are zero at this point?
- //~ FLAG_UNSET(files->candidate_flag, row, col);
+ /* for the final forced merge, the candidate flag is just to keep track if we have confirmed if:
+ * a. the segment size is >= to the minimum allowed size or
+ * b. we have merged it with its best neighbor
+ */
- }
- }
- }
- }
+ set_all_candidate_flags(files);
-
for (row = 0; row < files->nrows; row++) {
- for (col = 0; col < files->ncols; col++) {
+ G_percent(row, files->nrows - 1, 5);
+ for (col = 0; col < files->ncols; col++) {
if (FLAG_GET(files->candidate_flag, row, col)) {
/*free memory for linked lists */
@@ -940,10 +957,6 @@
G_debug(4, "Next starting pixel: row, %d, col, %d",
Ri_head->row, Ri_head->col);
- set_candidate_flag(Ri_head, FALSE, files); /* TODO: error trap? */
- G_debug(4, "line 165, \t\t\t\tcc = %d",
- files->candidate_count);
-
/* find segment neighbors */
if (find_segment_neighbors
(&Ri_head, &Rin_head, &Ri_count, files,
@@ -951,516 +964,534 @@
G_fatal_error("find_segment_neighbors() failed");
}
- if (Rin_head != NULL) /*found neighbors */
- {
- if (Ri_count >= functions->min_segment_size) /* don't force a merge */
- set_candidate_flag(Ri_head, FALSE, files);
-
- else /* Merge with most similar neighbor */
- {
- //~ TODO DELETE?
- //~ G_debug(4, "2b, Found Ri's pixels");
- //~
- //~ /*print out neighbors */
- //~ for (current = Ri_head; current != NULL;
- //~ current = current->next)
- //~ G_debug(4, "Ri: row: %d, col: %d", current->row,
- //~ current->col);
-//~
- //~ G_debug(4, "2b, Found Ri's neighbors");
- //~ /*print out neighbors */
- //~ for (current = Rin_head; current != NULL;
- //~ current = current->next)
- //~ G_debug(4, "Rin: row: %d, col: %d", current->row,
- //~ current->col);
+ if (Rin_head != NULL) { /*found neighbors */
+ if (Ri_count >= functions->min_segment_size) /* don't force a merge */
+ set_candidate_flag(Ri_head, FALSE, files);
- /* find Ri's most similar neighbor */
- Ri_bestn = NULL;
- Ri_similarity = threshold + 1; /* set current similarity to max value */
- segment_get(&files->bands_seg, (void *)files->bands_val, Ri_head->row, Ri_head->col); /* current segment values */
+ else { /* Merge with most similar neighbor */
- for (current = Rin_head; current != NULL; current = current->next) { /* for each of Ri's neighbors */
- tempsim =
- (*functions->calculate_similarity) (Ri_head,
- current,
- files,
- functions);
- G_debug(4,
- "simularity = %g for neighbor : row: %d, col %d.",
- tempsim, current->row, current->col);
+ /* find Ri's most similar neighbor */
+ Ri_bestn = NULL;
+ Ri_similarity = DBL_MAX; /* set current similarity to max value */
+ segment_get(&files->bands_seg, (void *)files->bands_val, Ri_head->row, Ri_head->col); /* current segment values */
- if (tempsim < Ri_similarity) {
- Ri_similarity = tempsim;
- Ri_bestn = current; /*TODO want to point to the current pixel...confirm when current changes need this to stay put! */
- G_debug(4,
- "Current lowest Ri_similarity = %g, for neighbor pixel row: %d col: %d",
- Ri_similarity, Ri_bestn->row,
- Ri_bestn->col);
- }
- }
+ /* for each of Ri's neighbors */
+ for (current = Rin_head; current != NULL;
+ 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 (Ri_bestn != NULL)
- G_debug(4,
- "Lowest Ri_similarity = %g, for neighbor pixel row: %d col: %d",
- Ri_similarity, Ri_bestn->row,
- Ri_bestn->col);
-
- if (Ri_bestn != NULL)
- {
- /* we'll have the neighbor pixel to start with. */
- G_debug(4, "3a: Working with Rk");
- Rk_count = 1;
- newpixel =
- (struct pixels *)link_new(files->token);
- newpixel->next = NULL; /* or = Rk_head; *//*TODO, should this be Rk_head or just NULL? amounts to the same thing here? */
- newpixel->row = Ri_bestn->row;
- newpixel->col = Ri_bestn->col;
- Rk_head = newpixel;
- /* TODO - lists starting, should this be a helper function, did at start of Ri and Rk. */
+ 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);
- /* using this just to get the full pixel/cell membership list for Rk */
- find_segment_neighbors(&Rk_head, &Rkn_head, &Rk_count, files, functions); /* data structure for Rk's neighbors, and pixels in Rk if we don't already have it */
+ /* we'll have the neighbor pixel to start with. */
+ Rk_count = 1;
+ newpixel =
+ (struct pixels *)link_new(files->token);
+ newpixel->next = NULL;
+ newpixel->row = Ri_bestn->row;
+ newpixel->col = Ri_bestn->col;
+ Rk_head = newpixel;
-//~ TODO DELETE?
- //~ G_debug(4, "Found Rk's pixels");
- //~ /*print out neighbors */
- //~ for (current = Rk_head; current != NULL;
- //~ current = current->next)
- //~ G_debug(4, "Rk: row: %d, col: %d",
- //~ current->row, current->col);
-//~
- //~ G_debug(4, "Found Rk's neighbors");
- //~ /*print out neighbors */
- //~ for (current = Rkn_head; current != NULL;
- //~ current = current->next)
- //~ G_debug(4, "Rkn: row: %d, col: %d",
- //~ current->row, current->col);
+ /* get the full pixel/cell membership list for Rk *//* todo polish: worth having a seperate function for this, since we don't need the neighbors? */
+ find_segment_neighbors(&Rk_head, &Rkn_head,
+ &Rk_count, files,
+ functions);
- merge_values(Ri_head, Rk_head, Ri_count, Rk_count, files); /* TODO error trap */
+ merge_values(Ri_head, Rk_head, Ri_count,
+ Rk_count, files);
- /* merge_values sets Ri and Rk candidate flag to FALSE. Put Rk back to TRUE if the size is too small. */
- if(Ri_count + Rk_count < functions->min_segment_size)
- set_candidate_flag(Rk_head, TRUE, files);
- } /* end if best neighbor != null */
- else
- G_warning("No best neighbor found in final merge, this shouldn't happen?");
-
-
- } /* end else - pixel count was below minimum allowed */
- } /* end if neighbors found */
- else{ /* no neighbors were found */
- G_warning("no neighbors found, this means only one segment was created.");
- set_candidate_flag(Ri_head, FALSE, files);
+ /* merge_values sets Ri and Rk candidate flag to FALSE. Put Rk back to TRUE if the size is too small. */
+ if (Ri_count + Rk_count <
+ functions->min_segment_size)
+ set_candidate_flag(Rk_head, TRUE, files);
+ } /* end if best neighbor != null */
+ else
+ G_warning
+ ("No best neighbor found in final merge 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.");
+ set_candidate_flag(Ri_head, FALSE, files);
}
- } /*end if pixel is candidate pixel */
- } /*next column */
- G_percent(row, files->nrows-1, 5);
- } /*next row */
- } /* end if for force merge */
- else
- G_message(_("Input for minimum pixels in a segment was 1, skipping final iteration for joining small segments."));
+ } /* end if pixel is candidate pixel */
+ } /* next column */
+ } /* next row */
+ } /* end if for force merge */
+ else
+ G_verbose_message(_("Input for minimum pixels in a segment was 1, will not force a merge for small segments."));
- /* free memory *//*TODO: anything ? */
+ G_message("temporary(?) message, number of passes: %d", t - 1);
+ return TRUE;
+}
- return TRUE;
+int find_segment_neighbors(struct pixels **R_head,
+ struct pixels **neighbors_head, int *seg_count,
+ struct files *files, struct functions *functions)
+{
+ int n, current_seg_ID, Ri_seg_ID = -1;
+ struct pixels *newpixel, *current, *to_check, tree_pix; /* need to check the pixel neighbors of to_check */
+ int pixel_neighbors[8][2];
+ struct RB_TREE *no_check_tree; /* pixels that should no longer be checked on this current find_neighbors() run */
+ struct RB_TREE *known_iseg;
+
+#ifdef DEBUG
+ struct RB_TRAV trav;
+#endif
+
+ /* TODO, any time savings to move any variables to files (mem allocation in open_files) */
+
+ /* neighbor list will be a listing of pixels that are neighbors? Include segment numbers? Only include unique segments?
+ * MM: counting unique neighbor segments could have the advantage of dealing with special
+ * segments that have only one neighbor, i.e. segments within segments
+ *
+
+ * Maybe the most complete return would be a structure array, structure to include the segment ID and a list of points in it?
+ * But the list of points would NOT be inclusive - just the points bordering the current segment...
+ */
+
+ /* parameter: R, current segment membership, could be single pixel (incomplete list) or list of pixels.
+ * parameter: neighbors/Rin/Rik, neighbor pixels, could have a list already, or could be empty ?
+ * functions->num_pn int, 4 or 8, for number of pixel neighbors
+ * */
+
+
+ /* *** initialize data *** */
+
+ Ri_seg_ID = files->iseg[(*R_head)->row][(*R_head)->col];
+ no_check_tree = rbtree_create(compare_pixels, sizeof(struct pixels));
+ known_iseg = rbtree_create(compare_ids, sizeof(int));
+ to_check = NULL;
+
+ /* Copy R in to_check and no_check data structures (don't expand them if we find them again) */
+
+ for (current = *R_head; current != NULL; current = current->next) {
+ /* put in to_check linked list */
+ newpixel = (struct pixels *)link_new(files->token);
+ newpixel->next = to_check; /*point the new pixel to the current first pixel */
+ newpixel->row = current->row;
+ newpixel->col = current->col;
+ to_check = newpixel; /*change the first pixel to be the new pixel. */
+
+ /* put in no_check tree */
+ 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!?");
}
- int find_segment_neighbors(struct pixels **R_head,
- struct pixels **neighbors_head, int *seg_count,
- struct files *files,
- struct functions *functions)
- {
- int n, current_seg_ID, Ri_seg_ID = -1;
- struct pixels *newpixel, *current, *to_check, tree_pix; /* need to check the pixel neighbors of to_check */
- int pixel_neighbors[8][2]; /* TODO: data type? use list instead? put in files to allocate memory once? */
-
-//TODO remove... /* files->no_check is a FLAG structure, only used here but allocating memory in open_files */
- struct RB_TREE *no_check_tree; /* pixels that should no longer be checked on this current find_neighbors() run */
- struct RB_TREE *known_iseg;
+ 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,
+ pixel_neighbors, files);
+
+ /* Done using this to_check pixels coords, remove from list */
+ current = to_check; /* temporary store the old head */
+ 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
- struct RB_TRAV trav;
+ 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
-
- /* TODO, any time savings to move any variables to files (mem allocation in open_files) */
- /* neighbor list will be a listing of pixels that are neighbors? Include segment numbers? Only include unique segments?
- * Maybe the most complete return would be a structure array, structure to include the segment ID and a list of points in it?
- * But the list of points would NOT be inclusive - just the points bordering the current segment...
- */
+ /*now check the pixel neighbors and add to the lists */
- /* parameter: R, current segment membership, could be single pixel or list of pixels.
- * parameter: neighbors/Rin/Rik, neighbor pixels, could have a list already, or could be empty ?
- * functions->num_pn int, 4 or 8, for number of pixel neighbors
- * */
+ /*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]);
+ } */
- /* show what was sent to function *//*
- G_debug(5, "in find_segment_neigors() with:");
- for (current = *R_head; current != NULL; current = current->next)
- G_debug(5, "R: row: %d, col: %d", current->row, current->col);
- for (current = *neighbors_head; current != NULL; current = current->next)
- G_debug(5, "neig: row: %d, col: %d", current->row, current->col);
- G_debug(5, "also passing Ri_count: %d", *seg_count); */
+ /* 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++) {
- /* *** initialize data *** */
-
- /* get Ri's segment ID */
- Ri_seg_ID = files->iseg[(*R_head)->row][(*R_head)->col];
-
-// flag_clear_all(files->no_check);
- no_check_tree = rbtree_create(compare_pixels, sizeof(struct pixels));
- known_iseg = rbtree_create(compare_ids, sizeof(int));
- to_check = NULL;
+ /* skip pixel if out of computational area or null */
+ if (pixel_neighbors[n][0] < 0 ||
+ pixel_neighbors[n][0] >= files->nrows ||
+ pixel_neighbors[n][1] < 0 ||
+ pixel_neighbors[n][1] >= files->ncols ||
+ FLAG_GET(files->null_flag, pixel_neighbors[n][0],
+ pixel_neighbors[n][1])
+ )
+ continue;
- /* Copy R in to_check and no_check data structures (don't expand them if we find them again) */
+ /* skip pixel if not in same boundary area */
+ //~ if(files->bounds_map != NULL){
+ //~ segment_get(&files->bounds_seg, &files->bounds_val, pixel_neighbors[n][0], pixel_neighbors[n][1]);
+ //~ if (files->bounds_val != files->current_bound) {
+ //~ continue;
+ //~ }
+ //~ }
- for (current = *R_head; current != NULL; current = current->next) {
- /* put in to_check linked list */
- newpixel = (struct pixels *)link_new(files->token);
- newpixel->next = to_check; /*point the new pixel to the current first pixel */
- newpixel->row = current->row;
- newpixel->col = current->col;
- to_check = newpixel; /*change the first pixel to be the new pixel. */
-
- /* put in no_check tree */
- 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!?");
-
- //todo delete flag_set(files->no_check, current->row, current->col);
-
- }
-
- while (to_check != NULL) { /* removing from to_check list as we go, NOT iterating over the list. */
+ tree_pix.row = pixel_neighbors[n][0];
+ tree_pix.col = pixel_neighbors[n][1];
G_debug(5,
- "\tfind_pixel_neighbors for row: %d , col %d",
- to_check->row, to_check->col);
+ "********* rbtree_find(no_check_tree, &tree_pix) = %p",
+ rbtree_find(no_check_tree, &tree_pix));
- functions->find_pixel_neighbors(to_check->row,
- to_check->col,
- pixel_neighbors, files);
+ if (rbtree_find(no_check_tree, &tree_pix) == FALSE) { /* want to check this neighbor */
+ current_seg_ID =
+ files->iseg[pixel_neighbors[n][0]][pixel_neighbors[n][1]];
- /* Done using this to_check pixels coords, remove from list */
- current = to_check; /* temporary store the old head */
- to_check = to_check->next; /*head now points to the next element in the list */
- link_dispose(files->token, (VOID_T *) current);
+ rbtree_insert(no_check_tree, &tree_pix); /* don't check it again */
- /*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 */
+ G_debug(5, "\tfiles->iseg[][] = %d Ri_seg_ID = %d",
+ files->iseg[pixel_neighbors[n][0]]
+ [pixel_neighbors[n]
+ [1]], Ri_seg_ID);
- /*debug 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]);
- } */
+ if (current_seg_ID == Ri_seg_ID) { /* 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);
+ newpixel->next = *R_head; /*point the new pixel to the current first pixel */
+ newpixel->row = pixel_neighbors[n][0];
+ 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);
- for (n = 0; n < functions->num_pn; n++) { /* with pixel neighbors */
+ /* put pixel_neighbor[n] in to_check -- want to check this pixels neighbors */
+ newpixel = (struct pixels *)link_new(files->token);
+ newpixel->next = to_check; /*point the new pixel to the current first pixel */
+ newpixel->row = pixel_neighbors[n][0];
+ newpixel->col = pixel_neighbors[n][1];
+ to_check = newpixel; /*change the first pixel to be the new pixel. */
- // TODO delete if (flag_get(files->no_check, pixel_neighbors[n][0], pixel_neighbors[n][1]) == FALSE)
- tree_pix.row = pixel_neighbors[n][0];
- tree_pix.col = pixel_neighbors[n][1];
- G_debug(5, "\tcurrent_seg_ID = %d", current_seg_ID);
- G_debug(5, "********* rbtree_find(no_check_tree, &tree_pix) = %p", rbtree_find(no_check_tree, &tree_pix));
- G_debug(5, "if evaluation: %d", rbtree_find(no_check_tree, &tree_pix) == FALSE);
+ }
+ else { /* segment id's were different */
+ //TODO: if current ID not found in known neighbors list
+ //add to known neighbors list
+ /* 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 */
+ newpixel->row = pixel_neighbors[n][0];
+ newpixel->col = pixel_neighbors[n][1];
+ *neighbors_head = newpixel; /*change the first pixel to be the new pixel. */
+ //}
- if(rbtree_find(no_check_tree, &tree_pix) == FALSE) { /* want to check this neighbor */
- current_seg_ID = files->iseg[pixel_neighbors[n][0]][pixel_neighbors[n][1]];
+ }
- rbtree_insert(no_check_tree, &tree_pix); /* don't check it again */
- if (!(FLAG_GET(files->null_flag, pixel_neighbors[n][0], pixel_neighbors[n][1]))) { /* all pixels, not just valid pixels */
+ } /*end if for pixel_neighbor was in "don't check" list */
+ } /* end for loop - next pixel neighbor */
- 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 == Ri_seg_ID) { /* 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);
- newpixel->next = *R_head; /*point the new pixel to the current first pixel */
- newpixel->row = pixel_neighbors[n][0];
- 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);
+#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);
- /* put pixel_neighbor[n] in to_check -- want to check this pixels neighbors */
- newpixel =
- (struct pixels *)link_new(files->token);
- newpixel->next = to_check; /*point the new pixel to the current first pixel */
- newpixel->row = pixel_neighbors[n][0];
- newpixel->col = pixel_neighbors[n][1];
- to_check = newpixel; /*change the first pixel to be the new pixel. */
+ G_debug(5, "\t### end of pixel neighors");
+#endif
+ } /* end while to_check has more elements */
- }
- else { /* segment id's were different */
- //if current ID not found in known neighbors list
- //add to known neighbors list
- /* put pixel_neighbor[n] in Rin */
- G_debug(5, "Put in neighbors_head");
- /* TODO - helper function for adding pixel to a list */
- newpixel =
- (struct pixels *)link_new(files->token);
- newpixel->next = *neighbors_head; /*point the new pixel to the current first pixel */
- newpixel->row = pixel_neighbors[n][0];
- newpixel->col = pixel_neighbors[n][1];
- *neighbors_head = newpixel; /*change the first pixel to be the new pixel. */
- //}
+ /* clean up */
+ rbtree_destroy(no_check_tree);
+ rbtree_destroy(known_iseg);
- }
- } /*end if not a null pixel */
- else
- G_debug(5,
- "pixel row: %d col: %d was a null pixel",
- pixel_neighbors[n][0], pixel_neighbors[n][1]);
+ return TRUE;
+}
- } /*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
- } /* while to_check has more elements */
+int find_four_pixel_neighbors(int p_row, int p_col,
+ int pixel_neighbors[8][2], struct files *files)
+{
+ /* Note: this will return neighbors outside of the raster boundary.
+ * Check in the calling routine if the pixel should be processed.
+ */
- /* TODO - anything to free??? */
- /* clean up */
- rbtree_destroy(no_check_tree);
- rbtree_destroy(known_iseg);
+ /* north */
+ pixel_neighbors[0][1] = p_col;
+ pixel_neighbors[0][0] = p_row - 1;
- return TRUE;
- }
+ /* east */
+ pixel_neighbors[1][0] = p_row;
+ pixel_neighbors[1][1] = p_col + 1;
- int find_four_pixel_neighbors(int p_row, int p_col,
- int pixel_neighbors[8][2],
- struct files *files)
- {
- /*
- G_debug(5,"\t\tin find 4 pixel neighbors () ");
- G_debug(5,"\t\tpixel row: %d pixel col: %d", p_row, p_col);
- G_debug(5, "\t\tTotal rows: %d, total cols: %d", files->nrows, files->ncols); *//*check that we have files... */
+ /* south */
+ pixel_neighbors[2][1] = p_col;
+ pixel_neighbors[2][0] = p_row + 1;
- /* north */
- pixel_neighbors[0][1] = p_col;
- if (p_row > 0)
- pixel_neighbors[0][0] = p_row - 1;
- else
- pixel_neighbors[0][0] = p_row; /*This is itself, which will be in "already checked" list. TODO: use null or -1 as flag to skip? What is fastest to process? */
+ /* west */
+ pixel_neighbors[3][0] = p_row;
+ pixel_neighbors[3][1] = p_col - 1;
- /* east */
- pixel_neighbors[1][0] = p_row;
- if (p_col < files->ncols - 1)
- pixel_neighbors[1][1] = p_col + 1;
- else
- pixel_neighbors[1][1] = p_col;
+ return TRUE;
+}
- /* south */
- pixel_neighbors[2][1] = p_col;
- if (p_row < files->nrows - 1)
- pixel_neighbors[2][0] = p_row + 1;
- else
- pixel_neighbors[2][0] = p_row;
+int find_eight_pixel_neighbors(int p_row, int p_col,
+ int pixel_neighbors[8][2], struct files *files)
+{
+ /* get the 4 orthogonal neighbors: */
+ find_four_pixel_neighbors(p_row, p_col, pixel_neighbors, files);
- /* west */
- pixel_neighbors[3][0] = p_row;
- if (p_col > 0)
- pixel_neighbors[3][1] = p_col - 1;
- else
- pixel_neighbors[3][1] = p_col;
+ /* and then the diagonals: */
- /*TODO: seems there should be a more elegent way to do this... */
- return TRUE;
- }
+ /* north west */
+ pixel_neighbors[4][0] = p_row - 1;
+ pixel_neighbors[4][1] = p_col - 1;
- int find_eight_pixel_neighbors(int p_row, int p_col,
- int pixel_neighbors[8][2],
- struct files *files)
- {
- /* get the 4 orthogonal neighbors */
- find_four_pixel_neighbors(p_row, p_col, pixel_neighbors, files);
+ /* north east */
+ pixel_neighbors[5][0] = p_row - 1;
+ pixel_neighbors[5][1] = p_col + 1;
- /* get the 4 diagonal neighbors */
- G_warning("Diagonal neighbors Not Implemented");
- /*TODO... continue as above? or "nicer" way to do it? */
- return TRUE;
- }
+ /* south east */
+ pixel_neighbors[6][0] = p_row + 1;
+ pixel_neighbors[6][1] = p_col + 1;
+ /* south west */
+ pixel_neighbors[7][0] = p_row + 1;
+ pixel_neighbors[7][1] = p_col - 1;
+
+ 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 */
- double calculate_euclidean_similarity(struct pixels *a, struct pixels *b,
- struct files *files,
- struct functions *functions)
- {
- double val = 0;
- int n;
+double calculate_euclidean_similarity(struct pixels *a, struct pixels *b,
+ struct files *files,
+ struct functions *functions)
+{
+ double val = 0;
+ int n;
- /* get values for pixel b */
- segment_get(&files->bands_seg, (void *)files->second_val, b->row,
- b->col);
+ /* get values for pixel b */
+ segment_get(&files->bands_seg, (void *)files->second_val, b->row, b->col);
- /* euclidean distance, sum the square differences for each dimension */
- for (n = 0; n < files->nbands; n++) {
- val =
- val + (files->bands_val[n] -
- files->second_val[n]) * (files->bands_val[n] -
- files->second_val[n]);
- }
+ /* euclidean distance, sum the square differences for each dimension */
+ for (n = 0; n < files->nbands; n++) {
+ val =
+ val + (files->bands_val[n] -
+ files->second_val[n]) * (files->bands_val[n] -
+ files->second_val[n]);
+ }
- /* val = sqrt(val); *//* use squared distance, save the calculation time */
+ /* val = sqrt(val); *//* use squared distance, save the calculation time. */
- return val;
+ return val;
- }
+}
- int merge_values(struct pixels *Ri_head, struct pixels *Rk_head,
- int Ri_count, int Rk_count, struct files *files)
- { /* TODO: correct assumption that this should be a weighted mean? */
- int n;
- struct pixels *current;
+int merge_values(struct pixels *Ri_head, struct pixels *Rk_head,
+ int Ri_count, int Rk_count, struct files *files)
+{
+ int n;
+ struct pixels *current;
- /*get input values, maybe if handle earlier gets correctly this can be avoided. */
- segment_get(&files->bands_seg, (void *)files->bands_val, Ri_head->row,
- Ri_head->col);
- segment_get(&files->bands_seg, (void *)files->second_val,
- Rk_head->row, Rk_head->col);
+ /*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? */
+ segment_get(&files->bands_seg, (void *)files->bands_val, Ri_head->row,
+ Ri_head->col);
+ segment_get(&files->bands_seg, (void *)files->second_val,
+ Rk_head->row, Rk_head->col);
- for (n = 0; n < files->nbands; n++) {
- files->bands_val[n] =
- (files->bands_val[n] * Ri_count +
- files->second_val[n] * Rk_count) / (Ri_count + Rk_count);
- }
+ for (n = 0; n < files->nbands; n++) {
+ files->bands_val[n] =
+ (files->bands_val[n] * Ri_count +
+ files->second_val[n] * Rk_count) / (Ri_count + Rk_count);
+ }
- /* update segment number and candidate flag ==0 */
+ /* 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]);
+ G_debug(4, "\t\tMerging, segment number: %d, including pixels:",
+ files->iseg[Ri_head->row][Ri_head->col]);
- /* 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 */
- files->candidate_count--;
- G_debug(4, "line 508, \t\t\t\tcc = %d", files->candidate_count);
- 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,
- current->row, current->col);
- files->iseg[current->row][current->col] =
- files->iseg[Ri_head->row][Ri_head->col];
- FLAG_UNSET(files->candidate_flag, current->row, current->col);
- files->candidate_count--;
- G_debug(4, "line 516, \t\t\t\tcc = %d", files->candidate_count);
- G_debug(4, "\t\tRk row: %d, col: %d", current->row, current->col);
+ /* 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 */
+ files->candidate_count--;
+ G_debug(4, "line 508, \t\t\t\tcc = %d", files->candidate_count);
+ 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,
+ current->row, current->col);
+ files->iseg[current->row][current->col] =
+ files->iseg[Ri_head->row][Ri_head->col];
+ FLAG_UNSET(files->candidate_flag, current->row, current->col);
+ files->candidate_count--;
+ G_debug(4, "line 516, \t\t\t\tcc = %d", files->candidate_count);
+ G_debug(4, "\t\tRk row: %d, col: %d", current->row, current->col);
- }
-
- files->candidate_count++; /* had already counted down the starting pixel Ri[0] at the beginning... */
- G_debug(4, "line 522, \t\t\t\tcc = %d", files->candidate_count);
- return TRUE;
}
- /* TODO.. helper function, maybe make more general? */
- /* todo, not using this in all cases, plus this used to be more complicated but now is two lines. maybe get rid of this function. */
+ return TRUE;
+}
+
/* besides setting flag, also increments how many pixels remain to be processed */
- int set_candidate_flag(struct pixels *head, int value,
- struct files *files)
- {
- /* head is linked list of pixels, value is new value of flag */
- struct pixels *current;
+int set_candidate_flag(struct pixels *head, int value, struct files *files)
+{
+ /* head is linked list of pixels, value is new value of flag */
+ struct pixels *current;
- for (current = head; current != NULL; current = current->next) {
+ for (current = head; current != NULL; current = current->next) {
- if (value == FALSE) {
- FLAG_UNSET(files->candidate_flag, current->row, current->col);
- files->candidate_count--;
- }
- else if (value == TRUE) {
- FLAG_SET(files->candidate_flag, current->row, current->col);
- files->candidate_count++;
- }
- else
- G_fatal_error
- ("programming bug, helper function called with invalid argument");
+ if (value == FALSE) {
+ FLAG_UNSET(files->candidate_flag, current->row, current->col);
+ files->candidate_count--;
+ }
+ else if (value == TRUE) {
+ FLAG_SET(files->candidate_flag, current->row, current->col);
+ files->candidate_count++;
+ }
+ else
+ G_fatal_error
+ ("programming bug, helper function called with invalid argument");
- G_debug(4, "line 544, \t\t\t\tcc = %d", files->candidate_count);
- }
- return TRUE;
+ G_debug(4, "line 1253, \t\t\t\tcc = %d", files->candidate_count);
}
+ return TRUE;
+}
/* let memory manager know space is available again and reset head to NULL */
- int my_dispose_list(struct link_head *token, struct pixels **head)
- {
- struct pixels *current;
+int my_dispose_list(struct link_head *token, struct pixels **head)
+{
+ struct pixels *current;
- while ((*head) != NULL) {
- current = *head; /* remember "old" head */
- *head = (*head)->next; /* move head to next pixel */
- link_dispose(token, (VOID_T *) current); /* remove "old" head */
- }
-
- return TRUE;
+ while ((*head) != NULL) {
+ current = *head; /* remember "old" head */
+ *head = (*head)->next; /* move head to next pixel */
+ link_dispose(token, (VOID_T *) current); /* remove "old" head */
}
-/* function used by binary tree to compare items */
+ return TRUE;
+}
-/* TODO
- * "static" was used in break_polygons.c extern was suggested in docs. */
+ /* functions used by binary tree to compare items */
+ /* TODO "static" was used in break_polygons.c extern was suggested in docs. */
+
int compare_ids(const void *first, const void *second)
{
- int *a = (int *)first, *b = (int *)second;
+ int *a = (int *)first, *b = (int *)second;
- if (*a < *b)
- return -1;
- else if (*a > *b)
- return 1;
- else if (*a == *b)
- return 0;
-
-
- G_warning(_("find neighbors: Bug in binary tree!"));
+ if (*a < *b)
+ return -1;
+ else if (*a > *b)
return 1;
-
+ else if (*a == *b)
+ return 0;
+
+
+ G_warning(_("find neighbors: Bug in binary tree!"));
+ return 1;
+
}
int compare_pixels(const void *first, const void *second)
{
- struct pixels *a = (struct pixels *)first, *b = (struct pixels *)second;
+ struct pixels *a = (struct pixels *)first, *b = (struct pixels *)second;
- if (a->row < b->row)
- return -1;
- else if (a->row > b->row)
- return 1;
- //else if (*a->row == *b->row) todo - a little faster to use else. But what if (can) a null pixel be passed?
- else {
- /* same row */
- if (a->col < b->col)
- return -1;
- else if (a->col > b->col)
- return 1;
- }
- /* same row and col todo, same as above, need an == check to be sure?*/
- return 0;
+ if (a->row < b->row)
+ return -1;
+ else if (a->row > b->row)
+ return 1;
+ else {
+ /* same row */
+ if (a->col < b->col)
+ return -1;
+ else if (a->col > b->col)
+ return 1;
+ }
+ /* same row and col */
+ return 0;
}
+
+/* Set candidate flag to true/1 or false/0 for all pixels in current processing area
+ * checks for NULL flag and if it is in current "polygon" if a bounds map is given */
+int set_all_candidate_flags(struct files *files)
+{
+ int row, col;
+
+ if (files->bounds_map == NULL) { /* process entire raster */
+ for (row = files->minrow; row < files->maxrow; row++) {
+ for (col = files->mincol; col < files->maxcol; col++) {
+ /* TODO: if we are starting from seeds...and only allow merges between unassigned pixels
+ * and seeds/existing segments, then this needs an if (and will be very inefficient)
+ * maybe consider the sorted array, btree, map... but the number of seeds could still be high for a large map */
+ /* MM: could be solved/not necessary if all pixels of an existing segment have the same ID */
+ if (!(FLAG_GET(files->null_flag, row, col))) {
+ FLAG_SET(files->candidate_flag, row, col);
+ files->candidate_count++;
+ }
+ else
+ FLAG_UNSET(files->candidate_flag, row, col);
+ }
+ }
+ }
+ else { /* process part of the raster, polygon constraints/boundaries */
+ for (row = files->minrow; row < files->maxrow; row++) {
+ for (col = files->mincol; col < files->maxcol; col++) {
+ if (!(FLAG_GET(files->in_bounds_flag, row, col))) {
+ FLAG_SET(files->candidate_flag, row, col);
+ files->candidate_count++;
+ }
+ else
+ FLAG_UNSET(files->candidate_flag, row, col);
+ }
+ }
+ }
+
+ 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
Added: grass-addons/grass7/imagery/i.segment/flag.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/flag.c (rev 0)
+++ grass-addons/grass7/imagery/i.segment/flag.c 2012-07-20 05:36:54 UTC (rev 52419)
@@ -0,0 +1,49 @@
+#include <grass/gis.h>
+#include "flag.h"
+
+int flag_clear_all(FLAG * flags)
+{
+ register int r, c;
+
+ for (r = 0; r < flags->nrows; r++) {
+ for (c = 0; c < flags->leng; c++) {
+ flags->array[r][c] = 0;
+ }
+ }
+
+ return 0;
+}
+
+FLAG *flag_create(int nrows, int ncols)
+{
+ unsigned char *temp;
+ FLAG *new_flag;
+ register int i;
+
+ new_flag = (FLAG *) G_malloc(sizeof(FLAG));
+ new_flag->nrows = nrows;
+ new_flag->ncols = ncols;
+ new_flag->leng = (ncols + 7) / 8;
+ new_flag->array =
+ (unsigned char **)G_malloc(nrows * sizeof(unsigned char *));
+
+ temp =
+ (unsigned char *)G_malloc(nrows * new_flag->leng *
+ sizeof(unsigned char));
+
+ for (i = 0; i < nrows; i++) {
+ new_flag->array[i] = temp;
+ temp += new_flag->leng;
+ }
+
+ return (new_flag);
+}
+
+int flag_destroy(FLAG * flags)
+{
+ G_free(flags->array[0]);
+ G_free(flags->array);
+ G_free(flags);
+
+ return 0;
+}
Modified: grass-addons/grass7/imagery/i.segment/flag.h
===================================================================
--- grass-addons/grass7/imagery/i.segment/flag.h 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/flag.h 2012-07-20 05:36:54 UTC (rev 52419)
@@ -20,6 +20,8 @@
** FLAG *flags;
** sets all values in flags to zero.
**
+ * following 3 were changed to macros, same usage
+ *
** flag_unset(flags, row, col)
** FLAG *flags;
** int row, col;
@@ -55,23 +57,9 @@
#define FLAG_GET(flags,row,col) \
(flags)->array[(row)][(col)>>3] & (1<<((col) & 7))
-/* flag_clr_all.c */
+/* flag.c */
int flag_clear_all(FLAG *);
-
-/* flag_create.c */
FLAG *flag_create(int, int);
-
-/* flag_destroy.c */
int flag_destroy(FLAG *);
-/* flag_get.c */
-int flag_get(FLAG *, int, int);
-
-/* flag_set.c */
-int flag_set(FLAG *, int, int);
-
-/* flag_unset.c */
-int flag_unset(FLAG *, int, int);
-
-
#endif /* __FLAG_H__ */
Deleted: grass-addons/grass7/imagery/i.segment/flag_clr_all.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/flag_clr_all.c 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/flag_clr_all.c 2012-07-20 05:36:54 UTC (rev 52419)
@@ -1,14 +0,0 @@
-#include "flag.h"
-
-int flag_clear_all(FLAG * flags)
-{
- register int r, c;
-
- for (r = 0; r < flags->nrows; r++) {
- for (c = 0; c < flags->leng; c++) {
- flags->array[r][c] = 0;
- }
- }
-
- return 0;
-}
Deleted: grass-addons/grass7/imagery/i.segment/flag_create.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/flag_create.c 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/flag_create.c 2012-07-20 05:36:54 UTC (rev 52419)
@@ -1,27 +0,0 @@
-#include <grass/gis.h>
-#include "flag.h"
-
-FLAG *flag_create(int nrows, int ncols)
-{
- unsigned char *temp;
- FLAG *new_flag;
- register int i;
-
- new_flag = (FLAG *) G_malloc(sizeof(FLAG));
- new_flag->nrows = nrows;
- new_flag->ncols = ncols;
- new_flag->leng = (ncols + 7) / 8;
- new_flag->array =
- (unsigned char **)G_malloc(nrows * sizeof(unsigned char *));
-
- temp =
- (unsigned char *)G_malloc(nrows * new_flag->leng *
- sizeof(unsigned char));
-
- for (i = 0; i < nrows; i++) {
- new_flag->array[i] = temp;
- temp += new_flag->leng;
- }
-
- return (new_flag);
-}
Deleted: grass-addons/grass7/imagery/i.segment/flag_destroy.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/flag_destroy.c 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/flag_destroy.c 2012-07-20 05:36:54 UTC (rev 52419)
@@ -1,11 +0,0 @@
-#include <grass/gis.h>
-#include "flag.h"
-
-int flag_destroy(FLAG * flags)
-{
- G_free(flags->array[0]);
- G_free(flags->array);
- G_free(flags);
-
- return 0;
-}
Deleted: grass-addons/grass7/imagery/i.segment/flag_get.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/flag_get.c 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/flag_get.c 2012-07-20 05:36:54 UTC (rev 52419)
@@ -1,6 +0,0 @@
-#include "flag.h"
-
-int flag_get(FLAG * flags, int row, int col)
-{
- return (flags->array[row][col >> 3] & (1 << (col & 7)));
-}
Deleted: grass-addons/grass7/imagery/i.segment/flag_set.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/flag_set.c 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/flag_set.c 2012-07-20 05:36:54 UTC (rev 52419)
@@ -1,8 +0,0 @@
-#include "flag.h"
-
-int flag_set(FLAG * flags, int row, int col)
-{
- flags->array[row][col >> 3] |= (1 << (col & 7));
-
- return 0;
-}
Deleted: grass-addons/grass7/imagery/i.segment/flag_unset.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/flag_unset.c 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/flag_unset.c 2012-07-20 05:36:54 UTC (rev 52419)
@@ -1,8 +0,0 @@
-#include "flag.h"
-
-int flag_unset(FLAG * flags, int row, int col)
-{
- flags->array[row][col >> 3] &= ~(1 << (col & 7));
-
- return 0;
-}
Modified: grass-addons/grass7/imagery/i.segment/i.segment.html
===================================================================
--- grass-addons/grass7/imagery/i.segment/i.segment.html 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/i.segment.html 2012-07-20 05:36:54 UTC (rev 52419)
@@ -2,7 +2,9 @@
<H2>NOTES</h2>
<H2>EXAMPLES</h2>
<h2>TODO</h2>
+For Region Growing, the current limit with CELL storage is 2 billion starting segment ID's. Workaround: If the original image has a larger number of pixels, please use the starting seeds option to limit the number of starting segments.
<h2>BUGS</h2>
<H2>REFERENCES</h2>
<h2>SEE ALSO</h2>
<h2>AUTHORS</h2>
+Eric Momsen
Modified: grass-addons/grass7/imagery/i.segment/iseg.h
===================================================================
--- grass-addons/grass7/imagery/i.segment/iseg.h 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/iseg.h 2012-07-20 05:36:54 UTC (rev 52419)
@@ -16,6 +16,8 @@
#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 */
/* pixel stack */
@@ -36,66 +38,51 @@
/* region info */
int nrows, ncols;
- /* files *//* TODO, for all map names, is this better for any reason, saw it in manual example: char name[GNAME_MAX]; */
+ /* files */
char *out_name; /* name of output raster map */
- char *seeds, *bounds_map, *bounds_mapset; /* optional segment seeds and polygon constraints/boundaries */
+ const char *seeds, *bounds_map;
+ const char *bounds_mapset; /* optional segment seeds and polygon constraints/boundaries */
char *out_band; /* for debug */
- /* file processing *//* TODO decide if bounds should be RAM or SEG */
- /* input values initially, then bands_seg is updated with current mean values for the segment. */
+ /* file processing */
+ /* bands_seg is initialized with the input raster valuess, then is updated with current mean values for the segment. */
int nbands; /* number of rasters in the image group */
SEGMENT bands_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 bounds_val, current_bound;
+ int minrow, maxrow, mincol, maxcol;
/* results */
int **iseg; /*segment ID assignment. */
/* processing flags */
- FLAG *candidate_flag, *null_flag; /*TODO, need some way to remember MASK/NULL values. Was using -1, 0, 1 in int array. Better to use 2 FLAG structures, better readibility? */
-// FLAG *no_check; /* pixels that have already been checked during this neighbor finding routine */
+ FLAG *candidate_flag, *null_flag, *in_bounds_flag;
/* memory management, linked lists */
- struct link_head *token; /* for linkm linked list memory management. */
+ struct link_head *token; /* for linkm.h linked list memory management. */
/* other info */
- int candidate_count; /*how many candidate pixels remain */
+ int candidate_count; /*Number of remaining candidate pixels */
- /* 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. */
- /*
- * Todo: Memory managment:
- * If we want to implement the option to load directly to memory
- * instead of declaring "SEGMENT" could we declare as void
- * then assign either a SEGMENT or a matrix during the get_input procedure?
- */
-
- /* TODO: some parts of the data structure from i.smap
- * confirm if there is any reason to be using them here
- * struct Categories output_labels;
- * char *isdata;
- */
-
- /* Todo: Additional data to be stored in this structure:
- * seeds (Put directly into output map?)
- */
-
};
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? */
+ int num_pn; /* number of pixel neighbors int, 4 or 8. */
float threshold; /* similarity threshold */
- int min_segment_size; /* smallest number of pixels/cells allowed in a final segment */
-
+ int min_segment_size; /* smallest number of pixels/cells allowed in a final segment */
+
/* Some function pointers to set one time in parse_args() */
int (*find_pixel_neighbors) (int, int, int[8][2], struct files *); /*parameters: row, col, pixel_neighbors */
double (*calculate_similarity) (struct pixels *, struct pixels *, struct files *, struct functions *); /*parameters: two points (row,col) to compare */
- /* for debug */
+ /* max number of iterations/passes */
int end_t;
+
+ /* todo remove when decide on pathflag*/
+ int path;
};
@@ -126,6 +113,7 @@
int my_dispose_list(struct link_head *, struct pixels **);
int compare_ids(const void *, const void *);
int compare_pixels(const void *, const void *);
+int set_all_candidate_flags(struct files *);
/* write_output.c */
int write_output(struct files *);
Modified: grass-addons/grass7/imagery/i.segment/open_files.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/open_files.c 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/open_files.c 2012-07-20 05:36:54 UTC (rev 52419)
@@ -10,24 +10,40 @@
int open_files(struct files *files)
{
struct Ref Ref; /* group reference list */
- int *in_fd, bounds_fd, null_check;
- int i, n, s, row, col, srows, scols, inlen, outlen, nseg;
+ int *in_fd, bounds_fd, null_check, out_fd, mean_fd;
+ int i, n, s, row, col, srows, scols, inlen, 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;
+ /* 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);
+ if (out_fd < 0)
+ G_fatal_error(_("Could not open output raster for writing segment ID's"));
+ else
+ Rast_unopen(out_fd);
+
+ if (files->out_band != NULL) {
+ mean_fd = Rast_open_new(files->out_band, DCELL_TYPE);
+ if (mean_fd < 0)
+ G_fatal_error(_("Could not open output raster for writing mean segment values"));
+ else
+ Rast_unopen(mean_fd);
+ }
+
/*allocate memory for flags */
files->null_flag = flag_create(files->nrows, files->ncols);
files->candidate_flag = flag_create(files->nrows, files->ncols);
-// files->no_check = flag_create(files->nrows, files->ncols);
+ if (files->bounds_map != NULL)
+ files->in_bounds_flag = flag_create(files->nrows, files->ncols);
- G_debug(1, "Checking image group...");
- /* references: i.cost r.watershed/seg and http://grass.osgeo.org/programming7/segmentlib.html */
+ /* references for segmentation library: i.cost r.watershed/seg and http://grass.osgeo.org/programming7/segmentlib.html */
/* ****** open the input rasters ******* */
- /* TODO: I confirmed, the API does not check this. Should this be checked in parse_args / validation ? Or best to do here where I want to use the REF data? */
+ /* TODO: I confirmed, the API does not check this. Markus, should this be checked in parse_args / validation ? Or best to do here where I want to use the REF data? */
+ 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);
@@ -40,7 +56,7 @@
in_fd = G_malloc(Ref.nfiles * sizeof(int));
inbuf = (DCELL **) G_malloc(Ref.nfiles * sizeof(DCELL *));
- fp_range = G_malloc(Ref.nfiles * sizeof(struct FPRange)); /* TODO, is this correct memory allocation for these three? */
+ fp_range = G_malloc(Ref.nfiles * sizeof(struct FPRange));
min = G_malloc(Ref.nfiles * sizeof(DCELL));
max = G_malloc(Ref.nfiles * sizeof(DCELL));
@@ -48,6 +64,9 @@
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,
+ Ref.file[n].mapset);
}
/* Get min/max values of each input raster for scaling */
@@ -71,14 +90,12 @@
/* size of each element to be stored */
inlen = sizeof(double) * Ref.nfiles;
- outlen = sizeof(int) * 2; /* change in write_output.c if this value changes TODO: better to save this in the files data structure? */
- /*TODO: i.cost and i.watershed take different approaches...hardcode for now */
/* when fine tuning, should be a power of 2 and not larger than 256 for speed reasons */
srows = 64;
scols = 64;
- /* TODO: make calculations for this */
+ /* TODO: make calculations for this, check i.cost and i.watershed */
nseg = 10000;
@@ -89,28 +106,31 @@
G_debug(1, "Image size: %d rows, %d cols", files->nrows, files->ncols);
G_debug(1, "Segmented to tiles with size: %d rows, %d cols", srows,
scols);
- G_debug(1, "Data element size, in: %d , out: %d ", inlen, outlen);
+ G_debug(1, "Data element size, in: %d", inlen);
G_debug(1, "number of segments to have in memory: %d", nseg);
- /* size: reading all input bands as DCELL TODO: could consider checking input to see if it is all FCELL or CELL, could reduce memory requirements. */
-
if (segment_open
(&files->bands_seg, G_tempfile(), files->nrows, files->ncols, srows,
scols, inlen, nseg) != TRUE)
G_fatal_error("Unable to create input temporary files");
- /* TODO: signed integer gives a 2 billion segment limit, depending on how the initialization is done, this means 2 billion max input pixels. */
+ /* ******* remaining memory allocation ********* */
+
+ files->bands_val = (double *)G_malloc(inlen);
+ files->second_val = (double *)G_malloc(inlen);
+
files->iseg = G_malloc(files->nrows * sizeof(int *));
for (i = 0; i < files->nrows; i++)
files->iseg[i] = G_malloc(files->ncols * sizeof(int));
- /* TODO, need error check for running out of memory, or does G_malloc included a G_fatal_error call? */
+ /*check the last one to make sure there was enough memory */
+ if (files->iseg[i - 1] != NULL) { /* everything is OK, and assume all previous memory allocations are OK too. */
+ }
+ else
+ G_fatal_error(_("Unable to allocate memory for initial segment ID's"));
- /* load input bands to segment structure and fill iseg array */
+ /* ******** load input bands to segment structure and fill iseg array ******** */
G_debug(1, "Reading input rasters into segmentation data files...");
-
- files->bands_val = (double *)G_malloc(inlen);
- files->second_val = (double *)G_malloc(inlen);
s = 1; /* initial segment ID */
for (row = 0; row < files->nrows; row++) {
@@ -152,7 +172,7 @@
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? */
+ bounds_fd = Rast_open_old(files->bounds_map, files->bounds_mapset);
for (row = 0; row < files->nrows; row++) {
Rast_get_c_row(bounds_fd, boundsbuf, row);
@@ -172,7 +192,7 @@
files->candidate_count = 0; /* counter for remaining candidate pixels */
/* linked list memory management linkm */
- link_set_chunk_size(20); /* TODO: fine tune this number */
+ link_set_chunk_size(100); /* TODO polish: fine tune this number */
files->token = link_init(sizeof(struct pixels));
@@ -188,7 +208,6 @@
G_free(fp_range);
G_free(min);
G_free(max);
- /* Need to clean up anything else? */
return TRUE;
}
Modified: grass-addons/grass7/imagery/i.segment/parse_args.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/parse_args.c 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/parse_args.c 2012-07-20 05:36:54 UTC (rev 52419)
@@ -12,42 +12,43 @@
{
/* reference: http://grass.osgeo.org/programming7/gislib.html#Command_Line_Parsing */
- struct Option *group, *seeds, *bounds, *output, *method, *threshold, *min_segment_size; /* Establish an Option pointer for each option */
- struct Flag *diagonal, *weighted; /* Establish a Flag pointer for each option */
- struct Option *outband, *endt; /* debugging parameters... TODO: leave in code or remove? hidden options? */
+ struct Option *group, *seeds, *bounds, *output, *method, *threshold, *min_segment_size, *endt; /* Establish an Option pointer for each option */
+ struct Flag *diagonal, *weighted, *path; /* Establish a Flag pointer for each option */
+ struct Option *outband; /* TODO scrub: put all outband code inside of #ifdef DEBUG */
/* 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... */
+ 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. */
output = G_define_standard_option(G_OPT_R_OUTPUT);
-/*TODO polish: any way to recommend a threshold to the user */
+ /*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.");
+ threshold->description = _("Similarity threshold."); /* TODO? Polish, should all descriptions get the _() locale macro? */
method = G_define_option();
method->key = "method";
method->type = TYPE_STRING;
method->required = YES;
method->answer = "region_growing";
- #ifdef DEBUG
+#ifdef DEBUG
method->options = "region_growing, io_debug, ll_test, seg_time";
- #else
+#else
method->options = "region_growing";
- #endif
+#endif
method->description = _("Segmentation method.");
- min_segment_size = G_define_option();
- min_segment_size->key = "min"; /*TODO is there a preference for long or short key names? min is pretty generic...but short... */
- min_segment_size->type = TYPE_INTEGER;
- min_segment_size->required = YES;
- min_segment_size->answer = "1"; /* default: no merges */
- min_segment_size->options = "1-100000";
- min_segment_size->description = _("Minimum number of pixels (cells) in a segment. The final merge step will ignore the threshold for any segments with fewer pixels.");
-
+ min_segment_size = G_define_option();
+ min_segment_size->key = "min"; /*TODO Markus, is there a preference for long or short key names? min is pretty generic...but short... */
+ min_segment_size->type = TYPE_INTEGER;
+ min_segment_size->required = YES;
+ min_segment_size->answer = "1"; /* default: no merges, a minimum of 1 pixel is allowed in a segment. */
+ min_segment_size->options = "1-100000";
+ min_segment_size->description =
+ _("Minimum number of pixels (cells) in a segment. The final merge step will ignore the threshold for any segments with fewer pixels.");
+
/* optional parameters */
diagonal = G_define_flag();
@@ -60,7 +61,7 @@
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. */
+ /* Raster for initial segment seeds *//* TODO polish: allow vector points/centroids for seed input. */
seeds = G_define_standard_option(G_OPT_R_INPUT);
seeds->key = "seeds";
seeds->required = NO;
@@ -80,9 +81,15 @@
endt->key = "endt";
endt->type = TYPE_INTEGER;
endt->required = NO;
- endt->answer = "10000";
- endt->description = _("Debugging...Maximum number of time steps to complete.");
+ endt->answer = "1000";
+ endt->description =
+ _("Debugging...Maximum number of 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.");
+
outband = G_define_standard_option(G_OPT_R_OUTPUT);
outband->key = "final_mean";
outband->required = NO;
@@ -94,19 +101,14 @@
/* Validation */
- /* TODO: 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). ??? Do this here or in open_files? */
-
-
/* Check and save parameters */
files->image_group = group->answer;
if (G_legal_filename(output->answer) == TRUE)
- files->out_name = output->answer; /* name of output raster map */
+ files->out_name = output->answer; /* name of output (segment ID) raster map */
else
G_fatal_error("Invalid output raster name.");
@@ -116,22 +118,23 @@
(functions->threshold <= 0 || functions->threshold >= 1))
G_fatal_error(_("threshold should be >= 0 and <= 1"));
- /* segmentation methods: 0 = debug, 1 = region growing */
- /* TODO, instead of string compare, does the Option structure have these already numbered? */
- if (strncmp(method->answer, "io_debug", 5) == 0)
+ /* 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, "region_growing", 10) == 0)
- functions->method = 1;
else if (strncmp(method->answer, "ll_test", 5) == 0)
functions->method = 2;
- else if (strncmp(method->answer, "seg_time", 5) == 0)
+ 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_debug(1, "segmentation method: %d", functions->method);
- functions->min_segment_size = atoi(min_segment_size->answer);
+ functions->min_segment_size = atoi(min_segment_size->answer);
if (diagonal->answer == FALSE) {
functions->find_pixel_neighbors = &find_four_pixel_neighbors;
@@ -143,6 +146,7 @@
functions->num_pn = 8;
G_debug(1, "eight (3x3) pixel neighborhood");
}
+ /* TODO polish, check if function pointer or IF statement is faster */
files->weighted = weighted->answer; /* default/0 for performing the scaling, but selected/1 if user has weighted values so scaling should be skipped. */
@@ -155,7 +159,8 @@
}
else { /* polygon constraints given */
files->bounds_map = bounds->answer;
- if ((files->bounds_mapset = G_find_raster(files->bounds_map, "")) == NULL) { /* TODO, compiler warning here: parse_args.c:149:27: warning: assignment discards ‘const’ qualifier from pointer target type [enabled by default] */
+ if ((files->bounds_mapset =
+ G_find_raster2(files->bounds_map, "")) == NULL) {
G_fatal_error(_("Segmentation constraint/boundary map not found."));
}
if (Rast_map_type(files->bounds_map, files->bounds_mapset) !=
@@ -168,7 +173,17 @@
files->nrows = Rast_window_rows();
files->ncols = Rast_window_cols();
+ if (endt->answer != NULL && atoi(endt->answer) >= 0)
+ functions->end_t = atoi(endt->answer);
+ else {
+ functions->end_t = 1000;
+ G_warning(_("invalid number of iterations, 1000 will be used."));
+ }
+
/* debug help */
+
+ functions->path = path->answer; /* default/0 for no pathflag, but selected/1 to use Rk as next Ri if not mutually best neighbors. */
+
if (outband->answer == NULL)
files->out_band = NULL;
else {
@@ -178,11 +193,5 @@
G_fatal_error("Invalid output raster name for means.");
}
- if (endt->answer != NULL && endt->answer >= 0)
- functions->end_t = atoi(endt->answer);
- else {
- functions->end_t = 10000;
- G_warning(_("invalid number of iterations, 10000 will be used."));
- }
return TRUE;
}
Modified: grass-addons/grass7/imagery/i.segment/write_output.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/write_output.c 2012-07-19 23:17:08 UTC (rev 52418)
+++ grass-addons/grass7/imagery/i.segment/write_output.c 2012-07-20 05:36:54 UTC (rev 52419)
@@ -1,5 +1,5 @@
-/* transfer the segmented regions from the segmented data file to a raster file */
-/* close_files() function is at bottom */
+/* write_output(): transfer the segmented regions from the segmented data file to a raster file */
+/* close_files(): close SEG files and free memory */
#include <stdlib.h>
#include <grass/gis.h>
@@ -7,41 +7,38 @@
#include <grass/segment.h> /* segmentation library */
#include "iseg.h"
-/* TODO some time delay here with meanbuf, etc being processed. I only put if statements on the actual files
+/* 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, todo, could add array... */
+ int out_fd, mean_fd, row, col; /* mean_fd for validiating/debug of means */
CELL *outbuf;
DCELL *meanbuf;
- struct Colors colors;
-
- outbuf = Rast_allocate_c_buf(); /*hold one row of data to put into raster */
+ struct Colors colors;
+
+ outbuf = Rast_allocate_c_buf(); /* hold one row of data to put into raster */
meanbuf = Rast_allocate_d_buf();
- /* Todo: return codes are 1 for these, need to check and react to errors? programmer's manual didn't include it... */
-
/* force all data to disk */
- segment_flush(&files->bands_seg); /* TODO use IFDEF or just delete for all these parts? for debug/validation output */
+ segment_flush(&files->bands_seg);
- G_debug(1, "preparing output raster");
/* 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);
- G_debug(1, "start data transfer from segmentation file to raster");
/* transfer data from segmentation file to raster */
-
for (row = 0; row < files->nrows; row++) {
Rast_set_c_null_value(outbuf, files->ncols); /*set buffer to NULLs, only write those that weren't originally masked */
Rast_set_d_null_value(meanbuf, files->ncols);
for (col = 0; col < files->ncols; col++) {
segment_get(&files->bands_seg, (void *)files->bands_val, row,
col);
- if (!(flag_get(files->null_flag, row, col))) {
+ if (!(FLAG_GET(files->null_flag, row, col))) {
outbuf[col] = files->iseg[row][col];
meanbuf[col] = files->bands_val[0];
}
@@ -53,28 +50,27 @@
G_percent(row, files->nrows, 1);
}
- /* TODO: I don't understand the header/history/etc for raster storage. Can/should we save any information about the segmentation
- * settings used to create the raster? What the input image group was? Anything else the statistics module would need?
- * check it with r.info to see what we have by default.
+ /* TODO polish: update the data found by r.info
+ * current: | Data Description: |
+ * | generated by i.segment |
+ * It would be nice to save the segmentation settings (just the CL? Or something more human readable?)
*/
- /* TODO after we have a count of create segments... if(total segments == 0) G_warning(_("No segments were created. Verify threshold and region settings.")); */
-
/* close and save file */
Rast_close(out_fd);
if (files->out_band != NULL)
Rast_close(mean_fd);
- /* set colors */
- Rast_init_colors(&colors);
- Rast_make_random_colors(&colors, 1, files->nrows*files->ncols); /* TODO polish - number segments from 1 - max ? and then can use that max here. */
- Rast_write_colors(files->out_name, G_mapset(), &colors); /* TODO, OK to just use G_mapset() here, seems I don't use it anywhere else, and that is where the output has to be written. (Should the library allow changing colors to other mapsets???) */
-
+ /* set colors */
+ Rast_init_colors(&colors);
+ Rast_make_random_colors(&colors, 1, files->nrows * files->ncols); /* TODO polish - number segments from 1 - max ? and then can use that max here. */
+ Rast_write_colors(files->out_name, G_mapset(), &colors); /* TODO, OK to just use G_mapset() here, seems I don't use it anywhere else, and that is where the output has to be written. (Markus, should the library allow changing colors to other mapsets? I was suprised it was needed as a parameter.) */
+
/* free memory */
G_free(outbuf);
G_free(meanbuf);
- Rast_free_colors(&colors);
-
+ Rast_free_colors(&colors);
+
return TRUE;
}
@@ -86,9 +82,9 @@
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);
-
+ 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);
@@ -101,13 +97,10 @@
G_debug(1, "destroying flags");
flag_destroy(files->null_flag);
flag_destroy(files->candidate_flag);
-// flag_destroy(files->no_check);
G_debug(1, "close_files() before link_cleanup()");
link_cleanup(files->token);
G_debug(1, "close_files() after link_cleanup()");
- /* anything else left to clean up? */
-
return TRUE;
}
More information about the grass-commit
mailing list