Skip to content

Commit ab7f1d8

Browse files
committed
UDP for parcel delineation.
1 parent 892495f commit ab7f1d8

File tree

6 files changed

+589
-0
lines changed

6 files changed

+589
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
The example focuses on the inference step, using a trained model. It demonstrates data loading and preprocessing,
5+
inference, and finally producing vector data as a result.
6+
7+
The example serves as a technology demonstration for openEO, and is not intended for use in another context.
8+
Please contact the authors in case you're interested in a field detector!
9+
10+
## Authors
11+
12+
- Kristof Van Tricht
13+
- Jeroen Dries
14+
- Victor Verhaert
15+
16+
Tuning by:
17+
- Kasper Bonte
18+
- Bart Driessen
19+
20+
[VITO Remote Sensing](https://remotesensing.vito.be)
21+
22+
## Running the example
23+
24+
Most openEO providers require an account to run the example. They offer various options for this, often providing you with some trial to test out the platform.
25+
You can find all known openEO providers on the [openEO hub](https://hub.openeo.org/).
26+
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
# TODO investigate setting max cloud cover and kernel size as parameters as well
9+
def generate() -> dict:
10+
# define spatial_extent
11+
spatial_extent = Parameter.bounding_box(
12+
name="spatial_extent",
13+
default={"west": 5.0, "south": 51.2, "east": 5.1, "north": 51.3})
14+
15+
# define temporal_extent
16+
temporal_extent = Parameter.temporal_interval(
17+
name="temporal_extent", default=["2021-01-01", "2021-12-31"]
18+
)
19+
20+
# backend to connect and load
21+
backend_url = "openeo.dataspace.copernicus.eu/"
22+
conn = openeo.connect(backend_url).authenticate_oidc()
23+
24+
# load the input data and filter based on cloud cover.
25+
# max cloud cover should be less than 10%
26+
s2_bands = conn.load_collection(
27+
collection_id="SENTINEL2_L2A",
28+
spatial_extent=spatial_extent,
29+
temporal_extent=temporal_extent,
30+
bands=["B04", "B08"],
31+
max_cloud_cover=10,
32+
)
33+
34+
# The delineation will be estimated based on the NDVI. The `ndvi` process can be used for these calculations.
35+
ndviband = s2_bands.ndvi(red="B04", nir="B08")
36+
37+
# Apply ML algorithm
38+
# We now apply a neural network, that requires 128x128 pixel 'chunks' input.
39+
segment_udf = openeo.UDF.from_file("udf_segmentation.py")
40+
# segment_udf = openeo.UDF.from_url("https://raw.githubusercontent.com/Open-EO/openeo-community-examples/main/python/ParcelDelineation/udf_segmentation.py")
41+
segmentationband = ndviband.apply_neighborhood(
42+
process=segment_udf,
43+
size=[{"dimension": "x", "value": 64, "unit": "px"},
44+
{"dimension": "y", "value": 64, "unit": "px"}],
45+
overlap=[{"dimension": "x", "value": 32, "unit": "px"},
46+
{"dimension": "y", "value": 32, "unit": "px"}],
47+
)
48+
49+
# We postprocess the output from the neural network using a sobel filter and
50+
# Felzenszwalb's algorithm, which are then merged. This time, we work on larger
51+
# chunks, to reduce the need for stitching the vector output.
52+
segment_postprocess_udf = openeo.UDF.from_file("udf_sobel_felzenszwalb.py")
53+
# segment_postprocess_udf = openeo.UDF.from_url("https://raw.githubusercontent.com/Open-EO/openeo-community-examples/refs/heads/main/python/ParcelDelineation/udf_sobel_felzenszwalb.py")
54+
sobel_felzenszwalb = segmentationband.apply_neighborhood(
55+
process=segment_postprocess_udf,
56+
size=[{"dimension": "x", "value": 2048, "unit": "px"},
57+
{"dimension": "y", "value": 2048, "unit": "px"}],
58+
overlap=[{"dimension": "x", "value": 0, "unit": "px"},
59+
{"dimension": "y", "value": 0, "unit": "px"}],
60+
)
61+
62+
return build_process_dict(
63+
process_graph=sobel_felzenszwalb,
64+
process_id= "Parcel Delineation",
65+
summary= "Parcel delineation using Sentinel-2 data retrived from the CDSE and processed on openEO.",
66+
description= "Parcel delineation using Sentinel-2",
67+
parameters= [spatial_extent, temporal_extent],
68+
returns=None, # TODO
69+
categories=None, # TODO
70+
)
71+
72+
73+
if __name__ == "__main__":
74+
# save the generated process to a file
75+
output_path = Path(__file__).parent
76+
print(output_path)
77+
output_path.mkdir(parents=True, exist_ok=True)
78+
79+
# Save the generated process to a file
80+
with open(output_path / "parcel_delineation.json", "w") as f:
81+
json.dump(generate(), f, indent=2)

0 commit comments

Comments
 (0)