0

We're hoping to download many Sentinel-2 mosaics in order to deploy a deep learning model on a large region. We currently use the following steps to construct and download images:

dltile = dl.scenes.DLTile.from_latlon(
    lat=lat,
    lon=lon,
    tilesize=250,
    resolution=5,
    pad=0
)
scenes, ctx = dl.scenes.search(
    aoi=dltile,
    products=["sentinel-2:L1C"],
    start_datetime="2019-05-01",
    end_datetime="2020-08-01",
    sort_field="acquired",
    sort_order="asc",
    cloud_fraction=0.01,
    limit=100
)
summer_scenes = scenes.filter(lambda scene: 5 <= scene.properties.date.month <= 7)
arr = summer_scenes.mosaic(
    bands="red green blue",
    ctx=ctx,
    bands_axis=-1,
    processing_level="surface",
    scaling="display",
    resampler="cubic"
)
img = Image.fromarray(arr.data)
img.save(image_filename)

The whole pipeline can be slow, particularly the call to mosaic. Our understanding is that the cubic resampling and conversion to surface reflectance slow things down, but we want to keep these operations ideally. Is there any way to speed this up without removing that processing?

0

The mosaic call is likely slower than you would expect due to the number of Scenes in the SceneCollection rather than your specification of surface processing or cubic resampling. In this case, the way to speed mosaicking up is to mosaic fewer Scenes. You can do so by checking the geometry of the Scene against the geometry of the AOI and only keep Scenes which will contribute to the mosaic. In this case, with 250x250 tiles at 5 meter resolution, a single Scene will very frequently cover the entire AOI.

Here is a function to limit the number of Scenes based on their geometries and the coverage needed:

import descarteslabs as dl

def minimal_scenes_to_cover(
    scene_collection: dl.scenes.SceneCollection,
    ctx: dl.scenes.GeoContext,
    raise_if_uncovered: bool = False,
) -> dl.scenes.SceneCollection:
    "Starting from the end, pick the fewest Scenes from a SceneCollection needed to fully cover the GeoContext"
    if len(scene_collection) == 0:
        if raise_if_uncovered:
            raise ValueError("Empty SceneCollection")
        return scene_collection

    ctx_geom = ctx.geometry
    combined_geom = None
    used_scenes = []
    
    for scene in reversed(scene_collection):
        if combined_geom is None:
            combined_geom = scene.geometry
            used_scenes.append(scene)
            
        if combined_geom.contains(ctx_geom):
            break
        
        if not combined_geom.contains(scene.geometry):
            combined_geom = combined_geom.union(scene.geometry)
            used_scenes.append(scene)
    else:
        # the `break` wasn't reached
        if raise_if_uncovered:
            raise ValueError(
                f"Could not fully cover the GeoContext with these scenes. "
                f"Only {(combined_geom.intersection(ctx_geom)).area/ctx_geom.area:.1%} covered. "
                f"(Use `raise_if_uncovered=False` to suppress this error.)"
            )
            
    return dl.scenes.SceneCollection(reversed(used_scenes))

For instance, using the coordinates 34.519900, -105.870100:

lat, lon = 34.519900, -105.870100

# ... create tile, search, and filter

print(summer_scenes)
sc = minimal_scenes_to_cover(summer_scenes, ctx)
print(sc)

This is returned:

SceneCollection of 11 scenes
  * Dates: May 04, 2019 to Jul 07, 2020
  * Products: sentinel-2:L1C: 11

SceneCollection of 1 scene
  * Dates: Jul 07, 2020 to Jul 07, 2020
  * Products: sentinel-2:L1C: 1

So we reduced the incoming SceneCollection of 11 Scenes down to the single Scene that will contribute to the mosaic. Calling sc.mosaic(bands, ctx) will produce the same result* as summer_scenes.mosaic(bands, ctx) but more quickly.

*This is not always the case, as mosaic also takes nodata values into consideration when mosaicking data. In this example we ignore possible missing data within the image.

(Disclosure: I am a member of the Customer Success team at Descartes Labs)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.