1 /* PURPOSE:      Parse and validate the input */
2 
3 #include <stdlib.h>
4 #include <string.h>
5 #include <grass/gis.h>
6 #include <grass/glocale.h>
7 #include <grass/raster.h>
8 #include "iseg.h"
9 
parse_args(int argc,char * argv[],struct globals * globals)10 int parse_args(int argc, char *argv[], struct globals *globals)
11 {
12     struct Option *group, *seeds, *bounds, *output,
13                   *method, *similarity, *threshold, *min_segment_size,
14 		  *hs, *hr, *bsuf,
15 #ifdef _OR_SHAPE_
16 		  *shape_weight, *smooth_weight,
17 #endif
18 		   *mem;
19     struct Flag *diagonal, *weighted, *ms_a, *ms_p;
20     struct Option *gof, *endt;
21     int bands;
22 
23 
24     /* required parameters */
25     group = G_define_standard_option(G_OPT_R_INPUTS);
26     group->key = "group";
27     group->description = _("Name of input imagery group or raster maps");
28 
29     output = G_define_standard_option(G_OPT_R_OUTPUT);
30 
31     bsuf = G_define_standard_option(G_OPT_R_OUTPUT);
32     bsuf->key = "band_suffix";
33     bsuf->required = NO;
34     bsuf->label = _("Suffix for output bands with modified band values");
35 
36     threshold = G_define_option();
37     threshold->key = "threshold";
38     threshold->type = TYPE_DOUBLE;
39     threshold->required = YES;
40     threshold->label = _("Difference threshold between 0 and 1");
41     threshold->description = _("Threshold = 0 merges only identical segments; threshold = 1 merges all");
42 
43     /* optional parameters */
44 
45     hs = G_define_option();
46     hs->key = "radius";
47     hs->type = TYPE_DOUBLE;
48     hs->required = NO;
49     hs->answer = "1.5";
50     hs->label = _("Spatial radius in number of cells");
51     hs->description = _("Must be >= 1, only cells within spatial bandwidth are considered for mean shift");
52 
53     hr = G_define_option();
54     hr->key = "hr";
55     hr->type = TYPE_DOUBLE;
56     hr->required = NO;
57     hr->label = _("Range (spectral) bandwidth [0, 1]");
58     hr->description = _("Only cells within range (spectral) bandwidth are considered for mean shift. Range bandwidth is used as conductance parameter for adaptive bandwidth");
59 
60     method = G_define_option();
61     method->key = "method";
62     method->type = TYPE_STRING;
63     method->required = NO;
64     method->answer = "region_growing";
65     method->options = "region_growing,mean_shift";
66     /*
67       Watershed method disabled, it's not implemented yet, see
68       https://trac.osgeo.org/grass/ticket/3181
69     method->options = "region_growing,mean_shift,watershed";
70     */
71     method->description = _("Segmentation method");
72     method->guisection = _("Settings");
73 
74     similarity = G_define_option();
75     similarity->key = "similarity";
76     similarity->type = TYPE_STRING;
77     similarity->required = NO;
78     similarity->answer = "euclidean";
79     similarity->options = "euclidean,manhattan";
80     similarity->description = _("Similarity calculation method");
81     similarity->guisection = _("Settings");
82 
83     min_segment_size = G_define_option();
84     min_segment_size->key = "minsize";
85     min_segment_size->type = TYPE_INTEGER;
86     min_segment_size->required = NO;
87     min_segment_size->answer = "1";
88     min_segment_size->options = "1-100000";
89     min_segment_size->label = _("Minimum number of cells in a segment");
90     min_segment_size->description =
91 	_("The final step will merge small segments with their best neighbor");
92     min_segment_size->guisection = _("Settings");
93 
94 #ifdef _OR_SHAPE_
95     radio_weight = G_define_option();
96     radio_weight->key = "radio_weight";
97     radio_weight->type = TYPE_DOUBLE;
98     radio_weight->required = NO;
99     radio_weight->answer = "1";
100     radio_weight->options = "0-1";
101     radio_weight->label =
102 	_("Importance of radiometric (input raster) values relative to shape");
103     radio_weight->guisection = _("Settings");
104 
105     smooth_weight = G_define_option();
106     smooth_weight->key = "smooth_weight";
107     smooth_weight->type = TYPE_DOUBLE;
108     smooth_weight->required = NO;
109     smooth_weight->answer = "0.5";
110     smooth_weight->options = "0-1";
111     smooth_weight->label =
112 	_("Importance of smoothness relative to compactness");
113     smooth_weight->guisection = _("Settings");
114 #endif
115 
116     mem = G_define_standard_option(G_OPT_MEMORYMB);
117 
118     /* TODO input for distance function */
119 
120     /* debug parameters */
121     endt = G_define_option();
122     endt->key = "iterations";
123     endt->type = TYPE_INTEGER;
124     endt->required = NO;
125     endt->description = _("Maximum number of iterations");
126     endt->guisection = _("Settings");
127 
128     /* Using raster for seeds
129      * Low priority TODO: allow vector points/centroids seed input. */
130     seeds = G_define_standard_option(G_OPT_R_INPUT);
131     seeds->key = "seeds";
132     seeds->required = NO;
133     seeds->description = _("Name for input raster map with starting seeds");
134     seeds->guisection = _("Settings");
135 
136     /* Polygon constraints. */
137     bounds = G_define_standard_option(G_OPT_R_INPUT);
138     bounds->key = "bounds";
139     bounds->required = NO;
140     bounds->label = _("Name of input bounding/constraining raster map");
141     bounds->description =
142 	_("Must be integer values, each area will be segmented independent of the others");
143     bounds->guisection = _("Settings");
144 
145     gof = G_define_standard_option(G_OPT_R_OUTPUT);
146     gof->key = "goodness";
147     gof->required = NO;
148     gof->description =
149 	_("Name for output goodness of fit estimate map");
150     gof->guisection = _("Settings");
151 
152     diagonal = G_define_flag();
153     diagonal->key = 'd';
154     diagonal->description =
155 	_("Use 8 neighbors (3x3 neighborhood) instead of the default 4 neighbors for each pixel");
156     diagonal->guisection = _("Settings");
157 
158     weighted = G_define_flag();
159     weighted->key = 'w';
160     weighted->description =
161 	_("Weighted input, do not perform the default scaling of input raster maps");
162     weighted->guisection = _("Settings");
163 
164     ms_a = G_define_flag();
165     ms_a->key = 'a';
166     ms_a->label = _("Use adaptive bandwidth for mean shift");
167     ms_a->description = _("Range (spectral) bandwidth is adapted for each moving window");
168     ms_a->guisection = _("Settings");
169 
170     ms_p = G_define_flag();
171     ms_p->key = 'p';
172     ms_p->label = _("Use progressive bandwidth for mean shift");
173     ms_p->description =
174 	_("Spatial bandwidth is increased, range (spectral) bandwidth is decreased in each iteration");
175     ms_p->guisection = _("Settings");
176 
177     if (G_parser(argc, argv))
178 	exit(EXIT_FAILURE);
179 
180     /* Check and save parameters */
181 
182     for (bands = 0; group->answers[bands] != NULL; bands++) ;
183 
184     I_init_group_ref(&globals->Ref);
185     if (bands > 1 || !I_find_group(group->answers[0])) {
186 	/* create group from input is raster map(s) */
187 	char name[GNAME_MAX];
188 	const char *mapset;
189 
190 	for (bands = 0; group->answers[bands] != NULL; bands++) {
191 	    /* strip @mapset, do not modify opt_in->answers */
192 	    strcpy(name, group->answers[bands]);
193 	    mapset = G_find_raster(name, "");
194 	    if (!mapset)
195 		G_fatal_error(_("Raster map <%s> not found"),
196 			      group->answers[bands]);
197 	    /* Add input to group. */
198 	    I_add_file_to_group_ref(name, mapset, &globals->Ref);
199 	}
200 
201 	globals->image_group = NULL;
202     }
203     else {
204 	/* input is group. Try to read group file */
205 	if (!I_get_group_ref(group->answers[0], &globals->Ref))
206 	    G_fatal_error(_("Group <%s> not found in the current mapset"),
207 			  group->answers[0]);
208 
209 	if (globals->Ref.nfiles <= 0)
210 	    G_fatal_error(_("Group <%s> contains no raster maps"),
211 			  globals->image_group);
212 
213 	globals->image_group = group->answers[0];
214     }
215 
216     if (G_legal_filename(output->answer) == TRUE)
217 	globals->out_name = output->answer;
218     else
219 	G_fatal_error("Invalid output raster name");
220 
221     globals->bsuf = bsuf->answer;
222 
223     globals->alpha = atof(threshold->answer);
224     if (globals->alpha <= 0 || globals->alpha >= 1)
225 	G_fatal_error(_("Threshold should be > 0 and < 1"));
226 
227     globals->hs = -1;
228     if (hs->answer) {
229 	globals->hs = atof(hs->answer);
230 	if (globals->hs < 1) {
231 	    G_fatal_error(_("Option '%s' must be >= 1"), hs->key);
232 	}
233     }
234 
235     globals->hr = -1;
236     if (hr->answer) {
237 	globals->hr = atof(hr->answer);
238 	if (globals->hr < 0) {
239 	    G_warning(_("Negative value %s for option '%s': disabling"),
240 	              hr->answer, hr->key);
241 	    globals->hr = -1;
242 	}
243 	if (globals->hr >= 1) {
244 	    G_warning(_("Value %s for option '%s' is >= 1: disabling"),
245 	              hr->answer, hr->key);
246 	    globals->hr = -1;
247 	}
248     }
249     globals->ms_adaptive = ms_a->answer;
250     globals->ms_progressive = ms_p->answer;
251 
252     /* segmentation methods */
253     if (strcmp(method->answer, "region_growing") == 0) {
254 	globals->method = ORM_RG;
255 	globals->method_fn = region_growing;
256     }
257     else if (strcmp(method->answer, "mean_shift") == 0) {
258 	globals->method = ORM_MS;
259 	globals->method_fn = mean_shift;
260     }
261     else if (strcmp(method->answer, "watershed") == 0) {
262 	globals->method = ORM_WS;
263 	globals->method_fn = watershed;
264     }
265     else
266 	G_fatal_error(_("Unable to assign segmentation method"));
267 
268     G_debug(1, "segmentation method: %s (%d)", method->answer, globals->method);
269 
270     /* distance methods for similarity measurement */
271     if (strcmp(similarity->answer, "euclidean") == 0)
272 	globals->calculate_similarity = calculate_euclidean_similarity;
273     else if (strcmp(similarity->answer, "manhattan") == 0)
274 	globals->calculate_similarity = calculate_manhattan_similarity;
275     else
276 	G_fatal_error(_("Invalid similarity method"));
277 
278 #ifdef _OR_SHAPE_
279     /* consider shape */
280     globals->radio_weight = atof(radio_weight->answer);
281     if (globals->radio_weight <= 0)
282 	G_fatal_error(_("Option '%s' must be > 0"), radio_weight->key);
283     if (globals->radio_weight > 1)
284 	G_fatal_error(_("Option '%s' must be <= 1"), radio_weight->key);
285     globals->smooth_weight = atof(smooth_weight->answer);
286     if (globals->smooth_weight < 0)
287 	G_fatal_error(_("Option '%s' must be >= 0"), smooth_weight->key);
288     if (globals->smooth_weight > 1)
289 	G_fatal_error(_("Option '%s' must be <= 1"), smooth_weight->key);
290 #else
291     globals->radio_weight = 1;
292     globals->smooth_weight = 0.5;
293 #endif
294 
295     globals->min_segment_size = atoi(min_segment_size->answer);
296 
297     if (diagonal->answer == FALSE) {
298 	globals->find_neighbors = find_four_neighbors;
299 	globals->nn = 4;
300 	G_debug(1, "four pixel neighborhood");
301     }
302     else if (diagonal->answer == TRUE) {
303 	globals->find_neighbors = find_eight_neighbors;
304 	globals->nn = 8;
305 	G_debug(1, "eight (3x3) pixel neighborhood");
306     }
307 
308     /* default/0 for performing the scaling
309      * selected/1 if scaling should be skipped. */
310     globals->weighted = weighted->answer;
311 
312     globals->seeds = seeds->answer;
313     if (globals->seeds) {
314 	if (G_find_raster(globals->seeds, "") == NULL) {
315 	    G_fatal_error(_("Seeds raster map not found"));
316 	}
317 	if (Rast_map_type(globals->seeds, "") !=
318 	    CELL_TYPE) {
319 	    G_fatal_error(_("Seeeds raster map must be CELL type (integers)"));
320 	}
321     }
322 
323     if (bounds->answer == NULL) {
324 	globals->bounds_map = NULL;
325     }
326     else {
327 	globals->bounds_map = bounds->answer;
328 	if ((globals->bounds_mapset = G_find_raster(globals->bounds_map, "")) == NULL) {
329 	    G_fatal_error(_("Segmentation constraint/boundary raster map not found"));
330 	}
331 	if (Rast_map_type(globals->bounds_map, globals->bounds_mapset) !=
332 	    CELL_TYPE) {
333 	    G_fatal_error(_("Segmentation constraint raster map must be CELL type (integers)"));
334 	}
335     }
336 
337     /* other data */
338     globals->nrows = Rast_window_rows();
339     globals->ncols = Rast_window_cols();
340 
341     if (sizeof(LARGEINT) < 8) {
342 	int i;
343 
344 	LARGEINT intmax;
345 
346 	intmax = ((LARGEINT)1 << (sizeof(LARGEINT) * 8 - 2)) - 1;
347 	intmax += ((LARGEINT)1 << (sizeof(LARGEINT) * 8 - 2));
348 
349 	globals->ncells = globals->ncols;
350 	for (i = 1; i < globals->nrows; i++) {
351 	    if (globals->ncols > intmax - globals->ncells)
352 		G_fatal_error(_("Integer overflow: too many cells in current region"));
353 
354 	    globals->ncells += globals->ncols;
355 	}
356     }
357 
358     /* debug help */
359     if (gof->answer == NULL)
360 	globals->gof = NULL;
361     else {
362 	if (G_legal_filename(gof->answer) == TRUE)
363 	    globals->gof = gof->answer;
364 	else
365 	    G_fatal_error(_("Invalid output raster name for goodness of fit"));
366     }
367 
368     if (!endt->answer) {
369 	globals->end_t = 50;
370 	if (globals->method == ORM_MS)
371 	    globals->end_t = 10;
372 	G_message(_("Maximum number of iterations set to %d"),
373 		  globals->end_t);
374     }
375     else {
376 	if (atoi(endt->answer) > 0)
377 	    globals->end_t = atoi(endt->answer);
378 	else {
379 	    globals->end_t = 50;
380 	    if (globals->method == ORM_MS)
381 		globals->end_t = 10;
382 	    G_warning(_("Invalid number of iterations, %d will be used"),
383 	              globals->end_t);
384 	}
385     }
386 
387     if (mem->answer && atoi(mem->answer) > 10)
388 	globals->mb = atoi(mem->answer);
389     else {
390 	globals->mb = 300;
391 	G_warning(_("Invalid number of MB, 300 will be used"));
392     }
393 
394     return TRUE;
395 }
396