Skip to content

Commit 73f2fc7

Browse files
authored
Merge pull request #197 from ESA-APEx/parceldelineation
Add parcel delineation UDP
2 parents 2a9d474 + a07a875 commit 73f2fc7

File tree

7 files changed

+771
-0
lines changed

7 files changed

+771
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[
2+
{
3+
"id": "parcel_delineation",
4+
"type": "openeo",
5+
"description": "Parcel Delineation based on ML using Sentinal-2",
6+
"backend": "openeo.dataspace.copernicus.eu",
7+
"process_graph": {
8+
"parcel_delineation1": {
9+
"process_id": "parcel_delineation",
10+
"namespace": "https://raw.githubusercontent.com/ESA-APEx/apex_algorithms/b828eecc13f1bd4d012576d746329041cf840c61/algorithm_catalog/vito/parcel_delineation/openeo_udp/parcel_delineation.json",
11+
"arguments": {
12+
"spatial_extent": {
13+
"west": 5.0,
14+
"south": 51.2,
15+
"east": 5.1,
16+
"north": 51.3
17+
},
18+
"temporal_extent": [
19+
"2021-01-01",
20+
"2021-12-31"
21+
]
22+
},
23+
"result": true
24+
}
25+
},
26+
"reference_data": {}
27+
}
28+
]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Parcel delineation
2+
This is an [openEO](https://openeo.org/) example for delineating agricultural parcels based on a neural network, using Sentinel-2 input data.
3+
4+
[VITO Remote Sensing](https://remotesensing.vito.be)
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import json
2+
from pathlib import Path
3+
import openeo
4+
from openeo.api.process import Parameter
5+
from openeo.rest.udp import build_process_dict
6+
7+
8+
def generate() -> dict:
9+
# DEFINE PARAMETERS
10+
# define spatial_extent
11+
spatial_extent = Parameter.bounding_box(
12+
name="spatial_extent", default={"west": 5.0, "south": 51.2, "east": 5.1, "north": 51.3}
13+
)
14+
# define temporal_extent
15+
temporal_extent = Parameter.temporal_interval(name="temporal_extent", default=["2021-01-01", "2021-12-31"])
16+
17+
# backend to connect and load
18+
backend_url = "openeo.dataspace.copernicus.eu/"
19+
conn = openeo.connect(backend_url).authenticate_oidc()
20+
21+
# Compute cloud mask, and filter input data based on cloud mask.
22+
# compute cloud mask using the SCL band
23+
scl = conn.load_collection(
24+
"SENTINEL2_L2A",
25+
temporal_extent=temporal_extent,
26+
spatial_extent=spatial_extent,
27+
bands=["SCL"],
28+
max_cloud_cover=10,
29+
)
30+
cloud_mask = scl.process(
31+
"to_scl_dilation_mask",
32+
data=scl,
33+
kernel1_size=17,
34+
kernel2_size=77,
35+
mask1_values=[2, 4, 5, 6, 7],
36+
mask2_values=[3, 8, 9, 10, 11],
37+
erosion_kernel_size=3,
38+
)
39+
40+
# Load s2 bands and set max cloud cover to be less than 10%
41+
s2_bands = conn.load_collection(
42+
collection_id="SENTINEL2_L2A",
43+
spatial_extent=spatial_extent,
44+
temporal_extent=temporal_extent,
45+
bands=["B04", "B08"],
46+
max_cloud_cover=10,
47+
)
48+
# mask data with cloud mask
49+
s2_bands_masked = s2_bands.mask(cloud_mask)
50+
51+
# The delineation will be estimated based on the NDVI. The `ndvi` process can be used for these calculations.
52+
ndviband = s2_bands_masked.ndvi(red="B04", nir="B08")
53+
54+
# Apply ML algorithm
55+
# apply a neural network, requires 128x128 pixel 'chunks' as input.
56+
segment_udf = openeo.UDF.from_file("udf_segmentation.py")
57+
segmentationband = ndviband.apply_neighborhood(
58+
process=segment_udf,
59+
size=[{"dimension": "x", "value": 64, "unit": "px"}, {"dimension": "y", "value": 64, "unit": "px"}],
60+
overlap=[{"dimension": "x", "value": 32, "unit": "px"}, {"dimension": "y", "value": 32, "unit": "px"}],
61+
)
62+
63+
# Postprocess the output from the neural network using a sobel filter and
64+
# Felzenszwalb's algorithm, which are then merged.
65+
segment_postprocess_udf = openeo.UDF.from_file("udf_sobel_felzenszwalb.py")
66+
sobel_felzenszwalb = segmentationband.apply_neighborhood(
67+
process=segment_postprocess_udf,
68+
size=[{"dimension": "x", "value": 2048, "unit": "px"}, {"dimension": "y", "value": 2048, "unit": "px"}],
69+
overlap=[{"dimension": "x", "value": 0, "unit": "px"}, {"dimension": "y", "value": 0, "unit": "px"}],
70+
)
71+
job_options = {
72+
"udf-dependency-archives": [
73+
"https://artifactory.vgt.vito.be/auxdata-public/openeo/onnx_dependencies.zip#onnx_deps",
74+
"https://artifactory.vgt.vito.be/artifactory/auxdata-public/openeo/parcelDelination/BelgiumCropMap_unet_3BandsGenerator_Models.zip#onnx_models",
75+
],
76+
"driver-memory": "500m",
77+
"driver-memoryOverhead": "1000m",
78+
"executor-memory": "1000m",
79+
"executor-memoryOverhead": "500m",
80+
"python-memory": "4200m",
81+
}
82+
83+
# Build the process dictionary
84+
return build_process_dict(
85+
process_graph=sobel_felzenszwalb,
86+
process_id="parcel_delineation",
87+
summary="Parcel delineation using Sentinel-2 data retrieved from the CDSE and processed on openEO.",
88+
description="Parcel delineation using Sentinel-2",
89+
parameters=[spatial_extent, temporal_extent],
90+
default_job_options=job_options,
91+
)
92+
93+
94+
if __name__ == "__main__":
95+
# save the generated process to a file
96+
output_path = Path(__file__).parent
97+
print(output_path)
98+
output_path.mkdir(parents=True, exist_ok=True)
99+
100+
# Save the generated process to a file
101+
with open(output_path / "parcel_delineation.json", "w") as f:
102+
json.dump(generate(), f, indent=2)

0 commit comments

Comments
 (0)