[GRASS-SVN] r52253 - grass-addons/grass7/imagery/i.segment
svn_grass at osgeo.org
svn_grass at osgeo.org
Thu Jun 28 23:54:51 PDT 2012
Author: momsen
Date: 2012-06-28 23:54:51 -0700 (Thu, 28 Jun 2012)
New Revision: 52253
Modified:
grass-addons/grass7/imagery/i.segment/create_isegs.c
grass-addons/grass7/imagery/i.segment/iseg.h
grass-addons/grass7/imagery/i.segment/open_files.c
grass-addons/grass7/imagery/i.segment/parse_args.c
grass-addons/grass7/imagery/i.segment/times.txt
grass-addons/grass7/imagery/i.segment/write_output.c
Log:
moved flags and segment ID's into RAM (out of segmentation file) input bands are still in segmentation file. Significant speed improvement.
Modified: grass-addons/grass7/imagery/i.segment/create_isegs.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/create_isegs.c 2012-06-28 22:43:02 UTC (rev 52252)
+++ grass-addons/grass7/imagery/i.segment/create_isegs.c 2012-06-29 06:54:51 UTC (rev 52253)
@@ -4,7 +4,7 @@
#include <stdlib.h>
#include <float.h> /* to get value of LDBL_MAX -> change this if there is a more usual grass way */
- /* #include <math.h> *//* for sqrt() and pow() */
+ /* #include <math.h> *//* for sqrt() and pow() */
#include <grass/gis.h>
#include <grass/glocale.h>
#include <grass/raster.h>
@@ -13,7 +13,7 @@
#include "iseg.h"
#define LINKM
-#define REVERSE
+/* #define REVERSE */
int create_isegs(struct files *files, struct functions *functions)
{
@@ -80,9 +80,7 @@
/* files->out_val[0] = col + row; */
segment_get(&files->bands_seg, (void *)files->bands_val, row,
col);
- files->out_val[0] = files->bands_val[0] * 100; /*pushing DCELL into CELL */
- files->out_val[1] = 1; /*processing flag */
- segment_put(&files->out_seg, (void *)files->out_val, row, col);
+ files->iseg[row][col] = files->bands_val[0] * 100; /*pushing DCELL into CELL */
}
G_percent(row, files->nrows, 1);
}
@@ -232,9 +230,7 @@
for (row = 0; row < files->nrows; row++) {
for (col = 0; col < files->ncols; col++) {
/*files->out_val[0] = files->out_val[0]; *//*segment number *//* just copying the map for testing. */
- files->out_val[0] = col + row;
- files->out_val[1] = TRUE; /*processing flag */
- segment_put(&files->out_seg, (void *)files->out_val, row, col);
+ files->iseg[row][col] = col + row;
}
G_percent(row, files->nrows, 1);
}
@@ -300,7 +296,8 @@
* 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. */
+ * 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. */
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() */
@@ -333,36 +330,34 @@
if (files->bounds_map == NULL) { /*normal processing */
for (row = 0; row < files->nrows; row++) {
for (col = 0; col < files->ncols; col++) {
- segment_get(&files->out_seg, (void *)files->out_val, row, col); /*need to get, since we only want to change the flag, and not overwrite the segment value.
- TODO: consider splitting this, put flag in one segmentmentation file (or RAM), and segment assignment in another. */
/* TODO: if we are starting from seeds...and only allow merges between unassigned pixels
* and seeds/existing segments, then this needs an if (and will be very inefficient)
* maybe consider the sorted array, btree, map... but the number of seeds could still be high for a large map */
- files->out_val[1] = TRUE; /*candidate pixel flag */
- segment_put(&files->out_seg, (void *)files->out_val, row,
- col);
+ if (!(FLAG_GET(files->null_flag, row, col))) {
+ 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 */
+ 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++) {
- segment_get(&files->bounds_seg, &files->bounds_val, row,
- col);
- segment_get(&files->out_seg, (void *)files->out_val, row,
- col);
+ if (!(FLAG_GET(files->null_flag, 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.) */
- files->out_val[1] = TRUE; /*candidate pixel flag */
- else
- files->out_val[1] = FALSE;
+ segment_get(&files->bounds_seg, &files->bounds_val,
+ row, col);
- segment_put(&files->out_seg, (void *)files->out_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);
- files->candidate_count++; /*TODO this assumes full grid with no null or mask!! But need something to prevent "pathflag" infinite loop */
+ }
}
}
}
@@ -377,8 +372,8 @@
/* reverse order
*/
#ifdef REVERSE
- for (row = files->nrows; row >= 0; row--) {
- for (col = files->ncols; col >= 0; col--) {
+ for (row = files->nrows - 1; row >= 0; row--) {
+ for (col = files->ncols - 1; col >= 0; col--) {
#else
for (row = 0; row < files->nrows; row++) {
for (col = 0; col < files->ncols; col++) {
@@ -387,16 +382,13 @@
G_debug(4,
"Next starting pixel from next row/col, not from Rk");
- segment_get(&files->out_seg, (void *)files->out_val, row, col); /*TODO small time savings - if candidate_count reaches zero, bail out of these loops too? */
- if (files->out_val[1] == TRUE && files->out_val != NULL) { /* out_val[1] is the candidate pixel flag, want to process the 1's *//*TODO MASK handling - checking if we have a segment ID already, in open_files() we will put nulls in [0] slot... better/faster way to do this? */
- G_debug(4, "going to free memory on linked lists...");
+ 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? */
Rk_count = 0;
- G_debug(4, "finished free memory on linked lists...");
/* First pixel in Ri is current row/col pixel. We may add more later if it is part of a segment */
Ri_count = 1;
@@ -413,7 +405,7 @@
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.
+ /* Setting Ri to be not a candidate allows using "itself" when at edge of raster. TODO THIS NEEDS TO BE CHANGED!!!!
* Otherwise need to use a list/count/something to know the number of pixel neighbors */
set_candidate_flag(Ri_head, 0, files); /* TODO: error trap? */
G_debug(4, "line 165, \t\t\t\tcc = %d",
@@ -436,7 +428,7 @@
(&Ri_head, &Rin_head, &Ri_count, files,
functions) != TRUE) {
G_fatal_error("find_segment_neighbors() failed");
- } /* TODO - shouldn't be just fatal error - need to still close_files(). Just put that here then fatal error? */
+ }
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 */
@@ -495,10 +487,13 @@
Ri_similarity, Ri_bestn->row,
Ri_bestn->col);
- segment_get(&files->out_seg,
- (void *)files->out_val, Ri_bestn->row,
- Ri_bestn->col);
- if (files->out_val[1] == FALSE)
+ //~ 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_similarity = threshold + 1;
@@ -599,351 +594,363 @@
/* 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
+ }
+ }
+ /* to balance brackets in first ifdef statement */
+#endif
- /* finished one pass for processing candidate pixels */
+ } /*next row */
- G_debug(4, "Finished one pass, t was = %d", t);
- t++;
- } while (t <= functions->end_t && endflag == FALSE);
+ /* finished one pass for processing candidate pixels */
+
+ 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? */
/* free memory *//*TODO: anything ? */
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, m, Ri_seg_ID = -1;
- struct pixels *newpixel, *current, *to_check; /* need to check the pixel neighbors of to_check */
- int val_no_check = -1; /*value of the no_check flag for the particular pixel. */
- int pixel_neighbors[8][2]; /* TODO: data type? put in files to allocate memory once? */
+ int find_segment_neighbors(struct pixels **R_head,
+ struct pixels **neighbors_head, int *seg_count,
+ struct files *files,
+ struct functions *functions)
+ {
+ int n, Ri_seg_ID = -1;
+ struct pixels *newpixel, *current, *to_check; /* need to check the pixel neighbors of to_check */
+ int pixel_neighbors[8][2]; /* TODO: data type? put in files to allocate memory once? */
+ /* files->no_check is a FLAG structure, only used here but allocating memory 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...
- */
+ /* 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...
+ */
- /* 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 ?
- * files->out_seg is currently an array [0] for seg ID and [1] for "candidate pixel"
- * files->no_check is a segmentation data structure, if the pixel should no longer be checked on this current find_neighbors() run
- * functions->num_pn int, 4 or 8, for number of pixel neighbors
- * */
+ /* 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 ?
+ * files->out_seg is currently an array [0] for seg ID and [1] for "candidate pixel"
+ * files->no_check is a segmentation data structure, if the pixel should no longer be checked on this current find_neighbors() run
+ * functions->num_pn int, 4 or 8, for number of pixel neighbors
+ * */
- /* 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); */
+ /* 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); */
- /*initialize data.... TODO: maybe remember min max row/col that was looked at each time, initialize in open_files, and reset smaller region at end of this functions */
- for (n = 0; n < files->nrows; n++) {
- for (m = 0; m < files->ncols; m++) {
- val_no_check = FALSE;
- segment_put(&files->no_check, &val_no_check, n, m);
- }
- }
+ /*initialize data.... TODO: maybe remember min max row/col that was looked at each time, initialize in open_files, and reset smaller region at end of this functions */
+ /*todo, dlete this loop for (n = 0; n < files->nrows; n++) {
+ for (m = 0; m < files->ncols; m++) {
+ val_no_check = FALSE;
+ segment_put(&files->no_check, &val_no_check, n, m);
+ }
+ } */
+ flag_clear_all(files->no_check);
- to_check = NULL;
+ to_check = NULL;
- /* Copy R in to_check and no_check data structures (don't expand them if we find them again) */
- /* NOTE: in pseudo code also have a "current segment" list, but think we can just pass Ri and use it directly */
+ /* Copy R in to_check and no_check data structures (don't expand them if we find them again) */
+ /* NOTE: in pseudo code also have a "current segment" list, but think we can just pass Ri and use it directly */
- for (current = *R_head; current != NULL; current = current->next) {
+ for (current = *R_head; current != NULL; current = current->next) {
- 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. */
+ 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. */
- val_no_check = 1;
- segment_put(&files->no_check, &val_no_check, current->row,
- current->col);
- }
+ flag_set(files->no_check, current->row, current->col);
+ }
- /* empty "neighbor" list Note: this step is in pseudo code, but think we just pass in Rin - it was already initialized, and later could have Ri data available to start from */
+ /* empty "neighbor" list Note: this step is in pseudo code, but think we just pass in Rin - it was already initialized, and later could have Ri data available to start from */
- /* get Ri's segment ID */
- segment_get(&files->out_seg, (void *)files->out_val, (*R_head)->row,
- (*R_head)->col);
- Ri_seg_ID = files->out_val[0];
+ /* get Ri's segment ID */
+ Ri_seg_ID = files->iseg[(*R_head)->row][(*R_head)->col]; /* old data structure needed, this... keep for readability? */
- 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);
+ 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);
+ functions->find_pixel_neighbors(to_check->row,
+ to_check->col,
+ pixel_neighbors, files);
- /* Done using this to_check pixels coords, remove from list */
+ /* 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);
+ 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 */
- 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);
+ /*print out to_check */
+ 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);
- /*now check the pixel neighbors and add to the lists */
+ /*now check the pixel neighbors and add to the lists */
- /*debug what 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]);
- } */
+ /*debug what neighbors were found: */
+ /* for (n = 0; n < functions->num_pn; n++){
+ G_debug(5, "\tpixel_neighbors[n][0]: %d, pixel_neighbors[n][1]: %d", pixel_neighbors[n][0], pixel_neighbors[n][1]);
+ } */
- for (n = 0; n < functions->num_pn; n++) { /* with pixel neighbors */
+ for (n = 0; n < functions->num_pn; n++) { /* with pixel neighbors */
- segment_get(&files->no_check, &val_no_check,
- pixel_neighbors[n][0], pixel_neighbors[n][1]);
- G_debug(5,
- "\twith pixel neigh %d, row: %d col: %d, val_no_check = %d",
- n, pixel_neighbors[n][0], pixel_neighbors[n][1],
- val_no_check);
- if (val_no_check == FALSE) { /* want to check this neighbor */
- val_no_check = 1;
- segment_put(&files->no_check, &val_no_check, pixel_neighbors[n][0], pixel_neighbors[n][1]); /* don't check it again */
+ G_debug(5,
+ "\twith pixel neigh %d, row: %d col: %d, val_no_check = %d",
+ n, pixel_neighbors[n][0], pixel_neighbors[n][1],
+ flag_get(files->no_check, pixel_neighbors[n][0],
+ pixel_neighbors[n][1]));
+ if (flag_get(files->no_check, pixel_neighbors[n][0], pixel_neighbors[n][1]) == FALSE) { /* want to check this neighbor */
+ flag_set(files->no_check, pixel_neighbors[n][0], pixel_neighbors[n][1]); /* don't check it again */
- segment_get(&files->out_seg, (void *)files->out_val, pixel_neighbors[n][0], pixel_neighbors[n][1]); /*TODO : do I need a second "out_val" data structure? */
+ if (!(FLAG_GET(files->null_flag, pixel_neighbors[n][0], pixel_neighbors[n][1]))) { /* all pixels, not just valid pixels */
- if (files->out_val[1] == TRUE || files->out_val[1] == FALSE) { /* all pixels, not just valid pixels */
- /* TODO: use -1 for NULL/MASKED pixels? */
+ G_debug(5, "\tfiles->iseg[][] = %d Ri_seg_ID = %d",
+ files->
+ iseg[pixel_neighbors[n][0]][pixel_neighbors[n]
+ [1]], Ri_seg_ID);
+ if (files->
+ iseg[pixel_neighbors[n][0]][pixel_neighbors[n][1]]
+ == Ri_seg_ID) {
+ 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);
- G_debug(5, "\tfiles->out_val[0] = %d Ri_seg_ID = %d",
- files->out_val[0], Ri_seg_ID);
- if (files->out_val[0] == Ri_seg_ID) {
- 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);
+ /* 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. */
- /* 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. */
+ }
+ else { /* segment id's were different */
+ /* 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. */
- }
- else { /* segment id's were different */
- /* 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. */
+ }
+ } /*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]);
- }
- } /*end if valid candidate pixel */
- else
- G_debug(5,
- "pixel row: %d col: %d was not a valid candidate pixel",
- pixel_neighbors[n][0], pixel_neighbors[n][1]);
+ } /*end if for pixel_neighbor was in "don't check" list */
+ } /* end for loop - next pixel neighbor */
+ 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");
+ } /* while to_check has more elements */
- } /*end if for pixel_neighbor was in "don't check" list */
- } /* end for loop - next pixel neighbor */
- 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");
- } /* while to_check has more elements */
+ return TRUE;
+ }
- return TRUE;
-}
-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... */
- /* 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? */
+ 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... */
- /* 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;
+ /* 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? */
- /* 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;
+ /* 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;
- /* 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;
+ /* 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;
- /*TODO: seems there should be a more elegent way to do this... */
- return TRUE;
-}
+ /* 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;
-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);
+ /*TODO: seems there should be a more elegent way to do this... */
+ return TRUE;
+ }
- /* get the 4 diagonal neighbors */
- G_warning("Diagonal neighbors Not Implemented");
- /*TODO... continue as above */
- return TRUE;
-}
+ 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);
-/* 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;
-
- /* 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]);
+ /* get the 4 diagonal neighbors */
+ G_warning("Diagonal neighbors Not Implemented");
+ /*TODO... continue as above */
+ return TRUE;
}
- /* val = sqrt(val); *//* use squared distance, save the calculation time */
+ /* 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;
- return val;
+ /* 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]);
+ }
-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;
+ /* val = sqrt(val); *//* use squared distance, save the calculation time */
- /*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);
+ return val;
- 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 process flag ==0 */
+ 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;
- segment_get(&files->out_seg, (void *)files->out_val, Ri_head->row,
- Ri_head->col);
- files->out_val[1] = FALSE; /*candidate pixel flag, only one merge allowed per t iteration */
- /* if separate out candidate flag, can do all changes with helper function...otherwise remember: */
+ /*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);
+ 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);
+ }
- G_debug(4, "\t\tMerging, segment number: %d, including pixels:",
- files->out_val[0]);
+ /* update segment number and candidate flag ==0 */
- /* 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);
- segment_put(&files->out_seg, (void *)files->out_val, current->row,
- current->col);
- 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);
- segment_put(&files->out_seg, (void *)files->out_val, 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);
+ 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);
+
+ }
+
+ 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;
}
- 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. */
+ /* 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;
-/* TODO.. helper function, maybe make more general? */
-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) {
- segment_get(&files->out_seg, (void *)files->out_val, current->row, current->col); /* this may change... */
- files->out_val[1] = value; /*candidate pixel flag */
- segment_put(&files->out_seg, (void *)files->out_val, current->row,
- current->col);
- /* also increment how many pixels remain to be processed */
+ 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 == 0)
- files->candidate_count--;
- else if (value == 1)
- files->candidate_count++;
- G_debug(4, "line 544, \t\t\t\tcc = %d", files->candidate_count);
-
+ G_debug(4, "line 544, \t\t\t\tcc = %d", files->candidate_count);
+ }
+ return TRUE;
}
- 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;
+ /* 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;
- while ((*head) != NULL) {
- current = *head; /* rememer "old" head */
- *head = (*head)->next; /* move head to next pixel */
- link_dispose(token, (VOID_T *) current); /* remove "old" head */
+ while ((*head) != NULL) {
+ current = *head; /* rememer "old" head */
+ *head = (*head)->next; /* move head to next pixel */
+ link_dispose(token, (VOID_T *) current); /* remove "old" head */
+ }
+
+ return TRUE;
}
-
- return TRUE;
-}
Modified: grass-addons/grass7/imagery/i.segment/iseg.h
===================================================================
--- grass-addons/grass7/imagery/i.segment/iseg.h 2012-06-28 22:43:02 UTC (rev 52252)
+++ grass-addons/grass7/imagery/i.segment/iseg.h 2012-06-29 06:54:51 UTC (rev 52253)
@@ -14,6 +14,7 @@
#include <grass/segment.h>
#include <grass/linkm.h>
+#include "flag.h"
/* pixel stack */
struct pixels
@@ -38,15 +39,21 @@
char *seeds, *bounds_map, *bounds_mapset; /* optional segment seeds and polygon constraints/boundaries */
char *out_band; /* for debug */
- /* file processing */
+ /* 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. */
int nbands; /* number of rasters in the image group */
- SEGMENT bands_seg, out_seg, bounds_seg; /* bands is for input, normal application is landsat bands, but other input can be included in the group. */
+ 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 *out_val, bounds_val, current_bound; /* out_val is array, to hold the segment ID and processing flag(s) */
+ int bounds_val, current_bound;
- SEGMENT no_check; /* pixels that have already been checked during this neighbor finding routine */
+ /* 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 */
+
/* memory management, linked lists */
struct link_head *token; /* for linkm linked list memory management. */
Modified: grass-addons/grass7/imagery/i.segment/open_files.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/open_files.c 2012-06-28 22:43:02 UTC (rev 52252)
+++ grass-addons/grass7/imagery/i.segment/open_files.c 2012-06-29 06:54:51 UTC (rev 52253)
@@ -11,12 +11,17 @@
{
struct Ref Ref; /* group reference list */
int *in_fd, bounds_fd, null_check;
- int n, s, row, col, srows, scols, inlen, outlen, nseg;
+ int i, n, s, row, col, srows, scols, inlen, outlen, nseg;
DCELL **inbuf; /* buffer array, to store lines from each of the imagery group rasters */
CELL *boundsbuf;
struct FPRange *fp_range; /* for getting min/max values on each input raster */
DCELL *min, *max;
+ /*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);
+
G_debug(1, "Checking image group...");
/* references: i.cost r.watershed/seg and http://grass.osgeo.org/programming7/segmentlib.html */
@@ -94,23 +99,18 @@
scols, inlen, nseg) != TRUE)
G_fatal_error("Unable to create input temporary files");
- /* G_debug(1, "finished segment_open(...bands_seg...)"); */
-
/* TODO: signed integer gives a 2 billion segment limit, depending on how the initialization is done, this means 2 billion max input pixels. */
- if (segment_open
- (&files->out_seg, G_tempfile(), files->nrows, files->ncols, srows,
- scols, outlen, nseg) != TRUE)
- G_fatal_error("Unable to create output temporary files");
+ files->iseg = G_malloc(files->nrows * sizeof(int *));
+ for (i = 0; i < files->nrows; i++)
+ files->iseg[i] = G_malloc(files->ncols * sizeof(int));
- if (segment_open(&files->no_check, G_tempfile(), files->nrows, files->ncols, srows, scols, sizeof(int), nseg) != TRUE) /* todo could make this smaller ? just need 0 or 1 */
- G_fatal_error("Unable to create flag temporary files");
+ /* TODO, need error check for running out of memory, or does G_malloc included a G_fatal_error call? */
- /* load input bands to segment structure and initialize output segmentation file */
+ /* 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);
- files->out_val = (int *)G_malloc(2 * sizeof(int));
s = 1; /* initial segment ID */
for (row = 0; row < files->nrows; row++) {
@@ -118,10 +118,8 @@
Rast_get_d_row(in_fd[n], inbuf[n], row);
}
for (col = 0; col < files->ncols; col++) {
- /*tempval = 0; Doesn't work, no "null" for doubles in c *//* want a number, not null */
null_check = 1; /*Assume there is data */
for (n = 0; n < Ref.nfiles; n++) {
- /*tempval += inbuf[n][col]; *//* if mask/null, adding a null value should set tempval to NULL */
if (Rast_is_d_null_value(&inbuf[n][col]))
null_check = -1;
if (files->weighted == TRUE)
@@ -132,16 +130,14 @@
segment_put(&files->bands_seg, (void *)files->bands_val, row, col); /* store input bands */
if (null_check != -1) { /*good pixel */
- files->out_val[0] = s; /*starting segment number TODO: for seeds this will be different */
- files->out_val[1] = TRUE; /*flag */
+ files->iseg[row][col] = s; /*starting segment number TODO: for seeds this will be different */
+ FLAG_UNSET(files->null_flag, row, col); /*flag */
+ s++; /* sequentially number all pixels with their own segment ID */
}
else { /*don't use this pixel */
-
- files->out_val[0] = -1; /*starting segment number */
- files->out_val[1] = -1; /*flag */
+ files->iseg[row][col] = -1; /* place holder...TODO this could be a conflict if constraints included a -1 */
+ FLAG_SET(files->null_flag, row, col); /*flag */
}
- segment_put(&files->out_seg, (void *)files->out_val, row, col); /* initialize input */
- s++; /* sequentially number all pixels with their own segment ID */
}
}
Modified: grass-addons/grass7/imagery/i.segment/parse_args.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/parse_args.c 2012-06-28 22:43:02 UTC (rev 52252)
+++ grass-addons/grass7/imagery/i.segment/parse_args.c 2012-06-29 06:54:51 UTC (rev 52253)
@@ -103,7 +103,7 @@
G_fatal_error("Invalid output raster name.");
functions->threshold = atof(threshold->answer); /* Note: this threshold is scaled after we know more at the beginning of create_isegs() */
- if (weighted->answer == TRUE &&
+ if (weighted->answer == FALSE &&
(functions->threshold <= 0 || functions->threshold >= 1))
G_fatal_error(_("threshold should be >= 0 and <= 1")); /* TODO OK to have fatal error here, seems this would be an invalid entry. */
Modified: grass-addons/grass7/imagery/i.segment/times.txt
===================================================================
--- grass-addons/grass7/imagery/i.segment/times.txt 2012-06-28 22:43:02 UTC (rev 52252)
+++ grass-addons/grass7/imagery/i.segment/times.txt 2012-06-29 06:54:51 UTC (rev 52253)
@@ -1,5 +1,39 @@
+NC sample landsat images, lsat7_2000_10
+
+g.region rast=lsat7_2000_10 at landsat
+rows: 475
+cols: 527
+cells: 250325
+
+still running...
+
+
+g.region s=222000 e=637000
+cells: 56334
+< 5 minutes
+
+
+g.region s=220000 e=639000
+cells: 94783
+
+13 min (just one time test)
+
+
+
+
+typical i.segment options:
+time i.segment -w --overwrite group=landsat1 at user1 output=val threshold=5 method=region_growing endt=1000 final_mean=val_mean --verbose
+
+threshold of 3 ran faster, but visually seemed oversegmented.
+
+
+
+#########older data below
+
Using version from around June 20, SVN 52181, it does not remember fragment membership.
+After some updates...June 26, SVN 52232. (I don't recall changing anything to specifically improve things by this much ?!?)
+
50 x 50 = 2500 pixels
real 0m5.767s
@@ -20,13 +54,47 @@
140 x 140 = 19,600 pixels
+52181:
real 150m7.509s
user 149m27.656s
sys 0m9.025s
0.45 s/pixel
+52232:
+real 0m55.961s
+user 0m52.655s
+sys 0m3.040s
+
+
+
+10k pixels, but [Raster MASK present]
+52232:
+real 1m43.085s
+user 1m42.338s
+sys 0m0.152s
+10k pixels, but [Raster MASK present]
+
+basic flag implementation:
+real 0m47.079s
+user 0m46.651s
+sys 0m0.092s
+[Raster MASK present]
+
+
+19600 pixels
+52232:
+real 0m55.961s
+user 0m52.655s
+sys 0m3.040s
+
+basic implementation:
+real 1m4.718s
+user 1m0.016s
+sys 0m4.340s
+
+
Rough target:
10,000 x 10,000 = 100,000,000 pixels
Modified: grass-addons/grass7/imagery/i.segment/write_output.c
===================================================================
--- grass-addons/grass7/imagery/i.segment/write_output.c 2012-06-28 22:43:02 UTC (rev 52252)
+++ grass-addons/grass7/imagery/i.segment/write_output.c 2012-06-29 06:54:51 UTC (rev 52253)
@@ -22,7 +22,7 @@
/* Todo: return codes are 1 for these, need to check and react to errors? programmer's manual didn't include it... */
- segment_flush(&files->out_seg); /* force all data to disk */
+ /* force all data to disk */
segment_flush(&files->bands_seg); /* TODO use IFDEF or just delete for all these parts? for debug/validation output */
G_debug(1, "preparing output raster");
@@ -39,13 +39,11 @@
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->out_seg, (void *)files->out_val, row, col);
segment_get(&files->bands_seg, (void *)files->bands_val, row,
col);
- G_debug(5, "outval[0] = %i", files->out_val[0]);
- if (files->out_val[0] >= 0) { /* only write positive segment ID's, using -1 as indicator of Null/Masked pixels. TODO: OK to use -1 as flag for this? */
- outbuf[col] = files->out_val[0]; /*just want segment assignment, not the other processing flag(s) */
- meanbuf[col] = files->out_val[0];
+ if (!(flag_get(files->null_flag, row, col))) {
+ outbuf[col] = files->iseg[row][col];
+ meanbuf[col] = files->bands_val[0];
}
}
Rast_put_row(out_fd, outbuf, CELL_TYPE);
@@ -76,23 +74,27 @@
int close_files(struct files *files)
{
+ int i;
+
/* close segmentation files and output raster */
G_debug(1, "closing files");
segment_close(&files->bands_seg);
- segment_close(&files->out_seg);
- segment_close(&files->no_check);
segment_close(&files->bounds_seg);
G_free(files->bands_val);
G_free(files->second_val);
- G_free(files->out_val);
+ for (i = 0; i < files->nrows; i++)
+ G_free(files->iseg[i]);
+ G_free(files->iseg);
+
+ 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((struct link_head *)files->token); */
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