Skip to main content
  • »
  • NOWATORSKA TECHNOLOGIA »
  • Monitorowanie zasięgu pożarów na obszarach mokradeł z wykorzystaniem sztucznej inteligencji i danych satelitarnych Sentinel-2

Monitorowanie zasięgu pożarów na obszarach mokradeł z wykorzystaniem sztucznej inteligencji i danych satelitarnych Sentinel-2

Postępy w dziedzinie sztucznej inteligencji (AI) oraz teledetekcji satelitarnej otwierają nowe możliwości monitorowania i ochrony cennych ekosystemów. Tereny podmokłe są mają kluczowe znacznie, gdyż magazynują znaczne ilości węgla, regulują cykle hydrologiczne oraz są siedliskiem dla licznych gatunków zwierząt. W ciągu wieków działalność człowieka, głównie w postaci osuszania podmokłych terenów na cele rolnicze, doprowadziła do dramatycznego zmniejszenia się ich powierzchni na całym świecie, co sprawia, że ich ochrona staje się coraz bardziej istotna.

Pożary stanowią poważne zagrożenie dla obszarów mokradeł, zwłaszcza na początku lub pod koniec sezonu wegetacyjnego, gdy roślinność jest sucha i wysoce łatwopalna. Pożary mają znaczący wpływ na populacje ptaków poprzez redukcję dostępnych siedlisk, szczególnie dla gatunków rzadkich i zagrożonych. Przyczyniają się również do spadku bioróżnorodności ekosystemów, promując rozprzestrzenianie się gatunków inwazyjnych kosztem rzadkich gatunków endemicznych.

Jednym z największych kompleksów obszarów mokradeł w Europie jest Biebrzańsi Park Narodowy znajdujący w północno-wschodniej Polsce, objęty ochroną na mocy Konwencji Ramsarskiej. 20 kwietnia 2025 r. na tym obszarze wybuchł pożar, gdzie ze względu na teren podmokły i brak dostępnych dróg, prowadzenie działań gaśniczych było utrudnione. W takich sytuacjach dostęp do aktualnych danych satelitarnych jest kluczowy dla monitorowania rozmiarów pożaru oraz wspierania decyzji operacyjnych.

Aby wspomóc ocenę skutków pożaru połączyliśmy aktualne obrazy Sentinel-2 uzyskane za pośrednictwem serwisu NSIS Cloud z nowoczesnym podejściem segmentacyjnym opartym na sztucznej inteligencji. Zamiast polegać wyłącznie na wizualnej inspekcji czy metodach opartych na progowaniu, zastosowaliśmy model Segment Anything Model (SAM), będący modelem bazowym zdolnym do segmentacji obszarów obrazów na podstawie danych multimodalnych.

Porównanie obrazów Sentinel-2 L2A w kompozycji RGB (pasma: B04, B03, B02) oraz SWIR (pasma: B12, B8A, B04) pozyskanych przed i po pożarze na obszarze Biebrzańskich mokradeł.

Ryc. 1. Porównanie obrazów Sentinel-2 L2A w kompozycji RGB (pasma: B04, B03, B02) oraz SWIR (pasma: B12, B8A, B04) pozyskanych przed i po pożarze na obszarze Biebrzańskich mokradeł.


SAM wykorzystuje architekturę z podwójnym enkoderem, składającą się z enkodera tekstu i enkodera obrazu, które wspólnie przetwarzają dane wejściowe. W niniejszym badaniu obrazy Sentinel-2 zostały przycięte do badanego obszaru, a następnie wstępnie przetworzone i znormalizowane, aby zapewnić zgodność z wymaganiami modelu.

Do implementacji SAM użyto pakietu SamGeo, gdzie wejściem obrazowym było zdjęcie po pożarze, a wejściem tekstowym do modelu zostało wywołany prostym zapytaniem: „burn scars” (ślady pogorzeliska). Dane obrazowe i tekstowe zostały przetworzone przez odpowiednie enkodery, co skutkowało wygenerowaniem maski binarnej wyznaczającej obszar dotknięty pożarem. Tego rodzaju segmentacja wspomagana przez AI umożliwiła szybkie, powtarzalne i skalowalne mapowanie śladów pożaru bez konieczności ręcznej anotacji czy treningu modelu. Na podstawie wygenerowanej maski przeprowadzono analizy przestrzenne w celu oszacowania powierzchni spalonego terenu.

Schemat metodyki pracy ilustrujący proces działania modelu Segment Anything Model (SAM) przy użyciu obrazów satelitarnych oraz zapytania tekstowego.

Ryc. 2. Schemat metodyki pracy ilustrujący proces działania modelu Segment Anything Model (SAM) przy użyciu obrazów satelitarnych oraz zapytania tekstowego.


Analizy wykazały, że pożar objął powierzchnię około 1,83 km² Biebrzańskiego Parku Narodowego, przy szacunkowych wymiarach około 2,5 km × 1,2 km. Choć obszar spalony był znacznie mniejszy niż podczas dużego pożaru w 2020 r., który objął około 55 km², powtarzalność i rosnąca częstotliwość pożarów w regionie budzi niepokój. Nawet pożary o niewielkiej skali przyczyniają się do degradacji siedlisk, spadku bioróżnorodności oraz długoterminowego osłabienia ekosystemu. Ciągłe monitorowanie z wykorzystaniem zaawansowanych danych satelitarnych i metod AI jest niezbędne do skutecznego zarządzania i ochrony cennych ekosystemów.

Wykryty spalony obszar przy użyciu Segment Anything Model (SAM) na obrazie Sentinel-2C pozyskanym 23 kwietnia 2025 r.

Ryc. 3. Wykryty spalony obszar przy użyciu Segment Anything Model (SAM) na obrazie Sentinel-2C pozyskanym 23 kwietnia 2025 r.


Część I - wstępne przetworzenie obrazu

Zaimportowanie bibliotek

import rasterio
import numpy as np
from rasterio.mask import mask
import geopandas as gpd
from shapely.geometry import box
import os

Ustawienie zmiennych środowiskowych

os.environ['AWS_S3_ENDPOINT'] = "eodata.cloudferro.com"
os.environ['AWS_ACCESS_KEY_ID'] = "<INSERT YOUR PUBLIC KEY>"
os.environ['AWS_SECRET_ACCESS_KEY'] = "<INSERT YOUR PRIVATE KEY>"
os.environ['AWS_HTTPS'] = "YES"
os.environ['AWS_VIRTUAL_HOSTING'] = "FALSE"
os.environ['GDAL_HTTP_TCP_KEEPALIVE'] = "YES"
os.environ['GDAL_HTTP_UNSAFESSL'] = "YES"
os.environ["GDAL_HTTP_MAX_RETRY"] = "5"
os.environ["GDAL_HTTP_RETRY_DELAY"] = "30"
os.environ["GDAL_HTTP_MAX_CONNECTIONS"] = "10"

Zdefiniowanie ścieżek wejściowych i wyjściowych

paths = [
    's3://EODATA/Sentinel-2/MSI/L2A/2025/04/23/S2C_MSIL2A_20250423T094051_N0511_R036_T34UFE_20250423T151018.SAFE/GRANULE/L2A_T34UFE_A003295_20250423T094446/IMG_DATA/R10m/T34UFE_20250423T094051_B02_10m.jp2',
    's3://EODATA/Sentinel-2/MSI/L2A/2025/04/23/S2C_MSIL2A_20250423T094051_N0511_R036_T34UFE_20250423T151018.SAFE/GRANULE/L2A_T34UFE_A003295_20250423T094446/IMG_DATA/R10m/T34UFE_20250423T094051_B03_10m.jp2',
    's3://EODATA/Sentinel-2/MSI/L2A/2025/04/23/S2C_MSIL2A_20250423T094051_N0511_R036_T34UFE_20250423T151018.SAFE/GRANULE/L2A_T34UFE_A003295_20250423T094446/IMG_DATA/R10m/T34UFE_20250423T094051_B04_10m.jp2',
]

clipped_output_path = 'RGB_burn_scars.tif'

Zdefiniowanie i utworzenie pola ograniczającego

bbox = [622908.376, 5941055.448, 626316.237, 5944113.115]
min_x, min_y, max_x, max_y = bbox
polygon = box(min_x, min_y, max_x, max_y)
gdf = gpd.GeoDataFrame({"geometry": [polygon]}, crs="EPSG:32634")

Przycięcie i przetworzenie pasmów Sentinel-2

with rasterio.open(paths[0]) as b02, rasterio.open(paths[1]) as b03, rasterio.open(paths[2]) as b04:
    raster_crs = b02.crs
    if gdf.crs != raster_crs:
        gdf = gdf.to_crs(raster_crs)

    clipped_bands = []
    out_transform = None
    for src in [b04, b03, b02]:
        out_image, out_transform = mask(src, gdf.geometry, crop=True, nodata=0)
        clipped_bands.append(out_image[0])

    out_image = np.stack(clipped_bands, axis=0)

    out_image = out_image.astype(np.float32)
    out_image = np.clip(out_image, 0, None)
    out_image -= out_image.min(axis=(1, 2), keepdims=True)
    out_image /= out_image.max(axis=(1, 2), keepdims=True) + 1e-10
    out_image *= 255
    out_image = out_image.astype(np.uint8)

    with rasterio.open(
        clipped_output_path,
        'w',
        driver='GTiff',
        height=out_image.shape[1],
        width=out_image.shape[2],
        count=3,
        dtype=out_image.dtype,
        crs=raster_crs,
        transform=out_transform,
        nodata=0
    ) as dst:
        dst.write(out_image)

Sprawdzenie poprawności danych wyjściowych

if os.path.exists(clipped_output_path):
    print(f"Clipped and converted RGB image saved to: {clipped_output_path}")
else:
    print("Failed to save the image.")

with rasterio.open(clipped_output_path) as src:
    print(f"Output shape: {src.shape}")
    print(f"Output bands: {src.count}")
    print(f"Output CRS: {src.crs}")
    print(f"Output bounds: {src.bounds}")

Część II - wykrycie śladów pogorzeliska

Zaimportowanie wymaganego modułu

from samgeo.text_sam import LangSAM

Zdefiniowane parametrów

bbox = [622908.376, 5941055.448, 626316.237, 5944113.115]
image = "RGB_burn_scars.tif"
text_prompt = "burn scars"

Wykonanie segmentacji

sam = LangSAM()

sam.predict(
    image=image,
    text_prompt=text_prompt,
    box_threshold=0.24,
    text_threshold=0.24
)

Wizualizacja

sam.show_anns(
    cmap="Greys_r",
    add_boxes=False,
    alpha=1,
    title="Automatic Segmentation of Trees",
    blend=False,
    output=output_raster
)
sam.show_anns(
    cmap="Greens",
    add_boxes=False,
    alpha=0.5,
    title="Automatic Segmentation of Trees"
)

Zapisanie wyników i obliczenie powierzchni

output_raster = "burnscars.tif"
output_vector = "burnscars.gpkg"

sam.raster_to_vector(output_raster, output_vector)

gdf = gpd.read_file(output_vector)

if gdf.crs.is_geographic:
    raise ValueError("The CRS is geographic (lat/lon). Please reproject to a projected CRS (e.g., UTM) for accurate area calculations.")

gdf['area_m2'] = gdf.geometry.area
total_area_km2 = gdf['area_m2'].sum() / 1_000_000

print(f"Total area of segmented features: {total_area_km2:.2f} km²")