parse_args.c 12 KB

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