diff --git a/grass-gis-addons/m.analyse.trees/r.trees.mltrain/r.trees.mltrain.html b/grass-gis-addons/m.analyse.trees/r.trees.mltrain/r.trees.mltrain.html index df331a1..4326a05 100644 --- a/grass-gis-addons/m.analyse.trees/r.trees.mltrain/r.trees.mltrain.html +++ b/grass-gis-addons/m.analyse.trees/r.trees.mltrain/r.trees.mltrain.html @@ -25,14 +25,13 @@

Generation of training data for tree and non-tree classification

trees_pixel_ndvi=trees_pixel_ndvi \ trees_raw_rast=trees_raw_rast \ group=ml_input \ - save_model=ml_trees_randomforest.gz \ + save_model=ml_trees_randomforest.gz

SEE ALSO

m.analyse.trees, -r.geomorphon, r.learn.train, r.learn.predict diff --git a/grass-gis-addons/m.analyse.trees/r.trees.mltrain/r.trees.mltrain.py b/grass-gis-addons/m.analyse.trees/r.trees.mltrain/r.trees.mltrain.py index 88ea0d1..3345611 100644 --- a/grass-gis-addons/m.analyse.trees/r.trees.mltrain/r.trees.mltrain.py +++ b/grass-gis-addons/m.analyse.trees/r.trees.mltrain/r.trees.mltrain.py @@ -57,7 +57,7 @@ # % key: trees_pixel_ndvi # % label: raster with trees identified by NDVI value # % answer: trees_pixel_ndvi -# % guisection: Input # ---> ???? +# % guisection: Input # %end # %option G_OPT_R_INPUT @@ -109,6 +109,14 @@ # % guisection: Input # %end +# %option +# % key: num_samples +# % type: integer +# % required: no +# % label: Number of sample points for each class in training +# % guisection: Optional input +# %end + # %option # % key: group # % type: string @@ -127,10 +135,6 @@ # % guisection: Output # %end -# %option G_OPT_MEMORYMB -# % guisection: Parallel processing -# %end - # %option G_OPT_M_NPROCS # % label: Number of cores for multiprocessing, -2 is the number of available cores - 1 # % answer: -2 @@ -150,8 +154,6 @@ # initialize global vars rm_rasters = [] -rm_vectors = [] -rm_groups = [] tmp_mask_old = None @@ -161,24 +163,28 @@ def cleanup(): for rmrast in rm_rasters: if grass.find_file(name=rmrast, element="cell")["file"]: grass.run_command("g.remove", type="raster", name=rmrast, **kwargs) - for rmv in rm_vectors: - if grass.find_file(name=rmv, element="vector")["file"]: - grass.run_command("g.remove", type="vector", name=rmv, **kwargs) - for rmgroup in rm_groups: - if grass.find_file(name=rmgroup, element="group")["file"]: - grass.run_command("g.remove", type="group", name=rmgroup, **kwargs) grass.del_temp_region() +def numsamplecheck(number_samples, rastername): + raster_samples = int(grass.parse_command("r.univar", map=rastername, flags="g")["n"]) + if number_samples > raster_samples: + grass.warning( + _(f"The chosen number of pixels {number_samples} is exceeding the total number of ", + f"non-null pixels in the given rastermap {rastername}. ", + f"The number of pixels will be set to the maximal amount of {raster_samples}.") + ) + number_samples = raster_samples + return number_samples def main(): - global rm_rasters, tmp_mask_old, rm_vectors, rm_groups + global rm_rasters, tmp_mask_old path = get_lib_path(modname="m.analyse.trees", libname="analyse_trees_lib") if path is None: grass.fatal("Unable to find the analyse trees library directory") sys.path.append(path) try: - from analyse_trees_lib import set_nprocs, test_memory + from analyse_trees_lib import set_nprocs except Exception: grass.fatal("m.analyse.trees library is not installed") @@ -203,28 +209,7 @@ def main(): nprocs = int(options["nprocs"]) trees_pixel_ndvi = options["trees_pixel_ndvi"] - if options["trees_raw_v"]: - trees_raw_v_rast = f"trees_raw_v_rast_{os.getpid()}" - rm_rasters.append(trees_raw_v_rast) - - grass.run_command( - "v.to.rast", - input=options["trees_raw_v"], - output=trees_raw_v_rast, - use="value", - value=2, - ) - trees_basemap = trees_raw_v_rast - else: - trees_basemap = options["trees_raw_r"] - nprocs = set_nprocs(nprocs) - memmb = test_memory(options["memory"]) - # for some modules like r.neighbors and r.slope_aspect, there is - # no speed gain by using more than 100 MB RAM - memory_max100mb = 100 - if memmb < 100: - memory_max100mb = memmb grass.use_temp_region() @@ -240,55 +225,82 @@ def main(): f"{ndgb} = round(127.5 * (1.0 + float({green} - {blue}) / float({green} + {blue})))" ) - # extract training points - # extract 4000 cells - grass.run_command( - "r.random", - input=trees_basemap, - raster="trees_trainpnts", - npoints=4000, - flags="s", - ) - - rm_rasters.append("trees_trainpnts") + if options["trees_raw_v"]: + trees_raw_v_rast = f"trees_raw_v_rast_{os.getpid()}" + rm_rasters.append(trees_raw_v_rast) + + grass.run_command( + "v.to.rast", + input=options["trees_raw_v"], + output=trees_raw_v_rast, + use="value", + value=2, + ) + trees_basemap = trees_raw_v_rast + else: + trees_basemap = options["trees_raw_r"] # non trees # false trees # problem areas with high NDVI like shadows on roofs, solar panels # trees_object_filt_large = NULL and trees_pixel_ndvi != NULL - rm_rasters.append(trees_pixel_ndvi) grass.mapcalc( f"false_trees = if(isnull({trees_pixel_ndvi}), null(), if(isnull({trees_basemap}), 1, null()))" ) - grass.run_command( - "r.random", - input="false_trees", - raster="false_trees_trainpnts", - npoints=4000, - flags="s", - ) - rm_rasters.append("false_trees") - rm_rasters.append("false_trees_trainpnts") # other areas clearly not trees grass.mapcalc( f"notrees = if(isnull({trees_pixel_ndvi}) && isnull({trees_basemap}), 1, null())" ) - grass.run_command( - "r.random", - input="notrees", - raster="notrees_trainpnts", - npoints=4000, - flags="s", - ) + rm_rasters.append("false_trees") rm_rasters.append("notrees") - rm_rasters.append("notrees_trainpnts") + + # extract training points + # extract "num_samples" cells if given + if options["num_samples"]: + num_samples = int(options["num_samples"]) + + realsamples = numsamplecheck(num_samples, trees_basemap) + grass.run_command( + "r.random", + input=trees_basemap, + raster="trees_trainpnts", + npoints=realsamples, + flags="s", + ) + + # false trees + realsamples = numsamplecheck(num_samples, "false_trees") + grass.run_command( + "r.random", + input="false_trees", + raster="false_trees_trainpnts", + npoints=realsamples, + flags="s", + ) + + # other areas clearly not trees + realsamples = numsamplecheck(num_samples, "notrees") + grass.run_command( + "r.random", + input="notrees", + raster="notrees_trainpnts", + npoints=realsamples, + flags="s", + ) + rm_rasters.append("trees_trainpnts") + rm_rasters.append("false_trees_trainpnts") + rm_rasters.append("notrees_trainpnts") + + patch_list = ["trees_trainpnts", "false_trees_trainpnts", "notrees_trainpnts"] + else: + patch_list = [trees_basemap, "false_trees", "notrees"] # patch trees, false trees and non-trees grass.run_command( "r.patch", - input="trees_trainpnts,false_trees_trainpnts,notrees_trainpnts", + input=patch_list, output="ml_trainpnts", ) rm_rasters.append("ml_trainpnts") diff --git a/grass-gis-addons/m.analyse.trees/r.trees.traindata/r.trees.traindata.html b/grass-gis-addons/m.analyse.trees/r.trees.traindata/r.trees.traindata.html index 22f8482..4f80664 100644 --- a/grass-gis-addons/m.analyse.trees/r.trees.traindata/r.trees.traindata.html +++ b/grass-gis-addons/m.analyse.trees/r.trees.traindata/r.trees.traindata.html @@ -25,7 +25,8 @@

Generation of training data for tree and non-tree classification

nir_threshold=130 \ ndsm_threshold=1 \ slopep75_threshold=70 \ - area_threshold=5 + area_threshold=5 \ + trees_pixel_ndvi=trees_pixel_ndvi

SEE ALSO

diff --git a/grass-gis-addons/m.analyse.trees/r.trees.traindata/r.trees.traindata.py b/grass-gis-addons/m.analyse.trees/r.trees.traindata/r.trees.traindata.py index caee502..f208166 100644 --- a/grass-gis-addons/m.analyse.trees/r.trees.traindata/r.trees.traindata.py +++ b/grass-gis-addons/m.analyse.trees/r.trees.traindata/r.trees.traindata.py @@ -145,7 +145,7 @@ # % key: traindata_r # % label: primary tree map as raster # % description: Primary tree map in raster format to pass to mltrain -# % answer: traindata_r +# % required: no # % guisection: Output # %end @@ -153,7 +153,15 @@ # % key: traindata_v # % label: primary tree map as vector # % description: Primary tree map in vector format to export and edit -# % answer: traindata_v +# % required: no +# % guisection: Output +# %end + +# %option G_OPT_R_OUTPUT +# % key: trees_pixel_ndvi +# % label: Name of raster with nearest peak IDs filtered by NDVI +# % description: necessary intermediate result for mltrain +# % required: no # % guisection: Output # %end @@ -168,7 +176,6 @@ # %end # %rules -# % exclusive: traindata_r,traindata_v # % required: traindata_r,traindata_v # %end @@ -181,7 +188,6 @@ # initialize global vars rm_rasters = [] rm_vectors = [] -rm_groups = [] tmp_mask_old = None @@ -194,14 +200,11 @@ def cleanup(): for rmv in rm_vectors: if grass.find_file(name=rmv, element="vector")["file"]: grass.run_command("g.remove", type="vector", name=rmv, **kwargs) - for rmgroup in rm_groups: - if grass.find_file(name=rmgroup, element="group")["file"]: - grass.run_command("g.remove", type="group", name=rmgroup, **kwargs) grass.del_temp_region() def main(): - global rm_rasters, tmp_mask_old, rm_vectors, rm_groups + global rm_rasters, tmp_mask_old, rm_vectors path = get_lib_path(modname="m.analyse.trees", libname="analyse_trees_lib") if path is None: @@ -311,16 +314,22 @@ def main(): rm_rasters.append(f"{ndvi_split}_max1") rm_rasters.append(f"{ndvi_split}_max2") + if options["trees_pixel_ndvi"]: + trees_pixel_ndvi = options["trees_pixel_ndvi"] + else: + trees_pixel_ndvi = "trees_pixel_ndvi" + rm_rasters.append(trees_pixel_ndvi) + grass.mapcalc( - f"trees_pixel_ndvi = if({ndvi_split}_max2 < {ndvi_threshold}, null(), {nearest})" + f"{trees_pixel_ndvi} = if({ndvi_split}_max2 < {ndvi_threshold}, null(), {nearest})" ) - # rm_rasters.append("trees_pixel_ndvi") --> keep raster for mltrain + # cut to nir: all pixels below 100 are not vegetation # removes shadows with high ndvi e.g. on roofs # needed grass.mapcalc( - f"trees_pixel_nir = if({nir} < {nir_threshold}, null(), trees_pixel_ndvi)" + f"trees_pixel_nir = if({nir} < {nir_threshold}, null(), {trees_pixel_ndvi})" ) rm_rasters.append("trees_pixel_nir") @@ -491,10 +500,11 @@ def main(): # trees: trees_object_filt_large if options["traindata_r"]: - grass.mapcalc(f"{options["traindata_r"]} = if(isnull(trees_object_filt_large), null(), 2)") - rm_vectors.append("trees_object_filt_large") + grass.mapcalc(f"{options['traindata_r']} = if(isnull(trees_object_filt_large), null(), 2)") + if not options["traindata_v"]: + rm_vectors.append("trees_object_filt_large") - elif options["traindata_v"]: + if options["traindata_v"]: grass.run_command( "g.rename", vector=f"trees_object_filt_large,{options['traindata_v']}"