|
| 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