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']}"