FOSSGIS-Konferenz 2025
Marius Maryniak
aviary
Ein generisches Python-Framework
zur KI-Inferenz
für georeferenzierte Daten
Hans-Jürgen Landes
Forschungsteam
für Künstliche Intelligenz
und kommunale Geoinformationen
an der Westfälischen Hochschule
Prof. Dr. Christian
Kuhlmann
Alexander
Roß
Stefan
Küpper
Marius
Maryniak
Hintergrund
Warum entwickeln wir aviary?
Standards statt Insellösungen
Mehr Flexibilität
Weniger Redundanz
Kürzere Implementierungszeiten in KI-Projekten
Höhere Qualität der Software
Gefördert im Rahmen von URBAN.KI (Projektidee des Kreises Unna)
Anforderungen
Was soll aviary können?
Generisch
Modular
Einfach anpass- und erweiterbar
Task- und KI-Framework-unabhängig
Effizient und skalierbar
Nutzungsfreundlich
Konzepte
Wie führt man KI-Modelle auf georeferenzierten Daten aus?
Kachelweise Verarbeitung
Area of Interest definieren
Datenquellen definieren
Datenverarbeitung definieren
Area of Interest definieren
Welche Kacheln sollen verarbeitet werden?
grid = aviary.Grid(
coordinates=np.array(...),
tile_size=256,
)
aviary.Grid
coordinates
: Koordinaten der Kacheln (x_min, y_min)
tile_size
: Größe der Kacheln
bounding_box = aviary.BoundingBox(
x_min=403691,
y_min=5756873,
x_max=405739,
y_max=5758921,
)
grid = aviary.Grid.from_bounding_box(
bounding_box=bounding_box,
tile_size=256,
snap=False,
)
Grid
mit einer
BoundingBox
erstellen
bounding_box = aviary.BoundingBox(
x_min=403691,
y_min=5756873,
x_max=405739,
y_max=5758921,
)
grid = aviary.Grid.from_bounding_box(
bounding_box=bounding_box,
tile_size=256,
snap=True,
)
Grid
mit einer
BoundingBox
erstellen
gpkg_path = 'gemarkungsgrenze_muenster.gpkg'
gdf = gpd.read_file(gpkg_path)
grid = aviary.Grid.from_gdf(
gdf=gdf,
tile_size=256,
snap=True,
)
Grid
mit einem
gpd.GeoDataFrame
erstellen
grid = grid_1 - grid_2
grid = grid_1 + grid_2
grid = grid_1 & grid_2
Vereinigung zweier Grids
mit +
bilden
Differenz zweier Grids
mit -
bilden
Schnittmenge zweier Grids
mit &
bilden
grid = grid.append(coordinates=(405739, 5758921))
grid = grid.remove(coordinates=(403691, 5756873))
Koordinaten zu einem Grid
hinzufügen mit append
Koordinaten von einem Grid
entfernen mit remove
grids = grid.chunk(num_chunks=4)
Grid
in mehrere Grids
unterteilen mit chunk
Datenquellen definieren
Woher sollen die Daten der Kacheln bezogen werden?
aviary.tile.
Protocol
TileFetcher
coordinates
: Koordinaten der Kachel (x_min, y_min)
tile_fetcher = aviary.tile.TileFetcher(...)
tile = tile_fetcher(coordinates=(404587, 5757769))
url = (
'https://www.wms.nrw.de/geobasis/wms_nw_dop'
)
rgb_wms_fetcher = aviary.tile.WMSFetcher(
url=url,
version=aviary.WMSVersion.V1_3_0,
layer='nw_dop_rgb',
epsg_code=25832,
response_format='image/png',
channel_keys=[
aviary.ChannelName.R,
aviary.ChannelName.G,
aviary.ChannelName.B,
],
tile_size=256,
ground_sampling_distance=.2,
)
nir_wms_fetcher = aviary.tile.WMSFetcher(
url=url,
version=aviary.WMSVersion.V1_3_0,
layer='nw_dop_nir',
epsg_code=25832,
response_format='image/png',
channel_keys=[
aviary.ChannelName.NIR,
aviary.ChannelName.NIR,
aviary.ChannelName.NIR,
],
tile_size=256,
ground_sampling_distance=.2,
)
aviary.tile.
WMSFetcher
Rasterdaten von einem
Web Map Service laden
url = (
'https://www.wms.nrw.de/geobasis/wms_nw_dop'
)
rgb_wms_fetcher = aviary.tile.WMSFetcher(
url=url,
version=aviary.WMSVersion.V1_3_0,
layer='nw_dop_rgb',
epsg_code=25832,
response_format='image/png',
channel_keys=[
aviary.ChannelName.R,
aviary.ChannelName.G,
aviary.ChannelName.B,
],
tile_size=256,
ground_sampling_distance=.2,
)
nir_wms_fetcher = aviary.tile.WMSFetcher(
url=url,
version=aviary.WMSVersion.V1_3_0,
layer='nw_dop_nir',
epsg_code=25832,
response_format='image/png',
channel_keys=[
aviary.ChannelName.NIR,
None,
None,
],
tile_size=256,
ground_sampling_distance=.2,
)
aviary.tile.
WMSFetcher
Rasterdaten von einem
Web Map Service laden
rgb_tile = rgb_wms_fetcher(coordinates=(404587, 5757769))
nir_tile = nir_wms_fetcher(coordinates=(404587, 5757769))
Tile
von einem Web Map Service
(RGB-Layer) laden
Tile
von einem Web Map Service
(NIR-Layer) laden
url = (
'https://www.wms.nrw.de/geobasis/wms_nw_dop'
)
rgb_wms_fetcher = aviary.tile.WMSFetcher(
url=url,
version=aviary.WMSVersion.V1_3_0,
layer='nw_dop_rgb',
epsg_code=25832,
response_format='image/png',
channel_keys=[
aviary.ChannelName.R,
aviary.ChannelName.G,
aviary.ChannelName.B,
],
tile_size=256,
ground_sampling_distance=.2,
)
nir_wms_fetcher = aviary.tile.WMSFetcher(
url=url,
version=aviary.WMSVersion.V1_3_0,
layer='nw_dop_nir',
epsg_code=25832,
response_format='image/png',
channel_keys=[
aviary.ChannelName.NIR,
aviary.ChannelName.NIR,
aviary.ChannelName.NIR,
],
tile_size=256,
ground_sampling_distance=.2,
)
aviary.tile.
WMSFetcher
Rasterdaten
mit Buffer von einem
Web Map Service laden
url = (
'https://www.wms.nrw.de/geobasis/wms_nw_dop'
)
rgb_wms_fetcher = aviary.tile.WMSFetcher(
url=url,
version=aviary.WMSVersion.V1_3_0,
layer='nw_dop_rgb',
epsg_code=25832,
response_format='image/png',
channel_keys=[
aviary.ChannelName.R,
aviary.ChannelName.G,
aviary.ChannelName.B,
],
tile_size=256,
ground_sampling_distance=.2,
buffer_size=32,
)
nir_wms_fetcher = aviary.tile.WMSFetcher(
url=url,
version=aviary.WMSVersion.V1_3_0,
layer='nw_dop_nir',
epsg_code=25832,
response_format='image/png',
channel_keys=[
aviary.ChannelName.NIR,
aviary.ChannelName.NIR,
aviary.ChannelName.NIR,
],
tile_size=256,
ground_sampling_distance=.2,
buffer_size=32,
)
aviary.tile.
WMSFetcher
Rasterdaten
mit Buffer von einem
Web Map Service laden
rgb_tile = rgb_wms_fetcher(coordinates=(404587, 5757769))
nir_tile = nir_wms_fetcher(coordinates=(404587, 5757769))
Tile
von einem Web Map Service
(RGB-Layer) laden
Tile
von einem Web Map Service
(NIR-Layer) laden
CompositeFetcher
VRTFetcher
WMSFetcher
Implementierte TileFetcher
tile = aviary.Tile(
channels=[
aviary.Channel(...),
aviary.Channel(...),
aviary.Channel(...),
aviary.Channel(...),
]
coordinates=(404587, 5757769),
tile_size=256,
)
aviary.Tile(s)
channels
: Kanäle der Kachel
coordinates
: Koordinaten der Kachel (x_min, y_min)
tile_size
: Größe der Kachel
Tile(s)
und Channel
Wie strukturiert aviary georeferenzierte Daten?
channel = aviary.Channel(
data=...,
name='my_channel',
buffer_size=.125,
time_step=None,
)
aviary.Channel
ABC
data
: Daten des Kanals
name
: Name des Kanals
buffer_size
: Buffer-Größe des Kanals
time_step
: Zeitschritt des Kanals
Tile(s)
und Channel
Wie strukturiert aviary georeferenzierte Daten?
r_channel = aviary.RasterChannel(
data=np.array(...),
name=aviary.ChannelName.R,
buffer_size=.125,
time_step=None,
)
g_channel = aviary.RasterChannel(
data=np.array(...),
name=aviary.ChannelName.G,
buffer_size=.125,
time_step=None,
)
b_channel = aviary.RasterChannel(
data=np.array(...),
name=aviary.ChannelName.B,
buffer_size=.125,
time_step=None,
)
nir_channel = aviary.RasterChannel(
data=np.array(...),
name=aviary.ChannelName.NIR,
buffer_size=.125,
time_step=None,
)
RasterChannel
VectorChannel
Implementierte Channel
Datenverarbeitung definieren
Wie sollen die Daten der Kacheln verarbeitet werden?
aviary.tile.
Protocol
TilesProcessor
tiles
: Batch von Kacheln
tiles_processor = aviary.tile.TilesProcessor(...)
tiles = tiles_processor(tiles=aviary.Tiles(...))
NormalizeProcessor
NormalizeProcessor
NormalizeProcessor
NormalizeProcessor
Adois
RemoveBufferProcessor
VectorizeProcessor
VectorExporter
GridExporter
Rasterdaten normalisieren
adois ausführen
Buffer entfernen
Rasterdaten vektorisieren
Vektordaten exportieren
Koordinaten exportieren
Tiles
Tiles
Beispiel 1:
adois
NormalizeProcessor
NormalizeProcessor
NormalizeProcessor
NormalizeProcessor
MyModel1
RemoveBufferProcessor
VectorizeProcessor
VectorExporter
GridExporter
Rasterdaten normalisieren
KI-Modell ausführen
Buffer entfernen
Rasterdaten vektorisieren
Vektordaten exportieren
Koordinaten exportieren
Tiles
Tiles
Beispiel 2:
Zwei KI-Modelle
MyModel1
MyModel1
RemoveBufferProcessor
VectorizeProcessor
VectorExporter
MyModel2
RasterizeProcessor
RasterExporter
RasterExporter
GridExporter
Vektordaten rasterisieren
Rasterdaten exportieren
Koordinaten exportieren
Tiles
Tiles
Beispiel 3:
Trainingsdaten
CopyProcessor
NormalizeProcessor
ParallelCompositeProcessor
RemoveBufferProcessor
RemoveProcessor
SelectProcessor
SequentialCompositeProcessor
StandardizeProcessor
VectorizeProcessor
Implementierte TilesProcessor
Adois
Implementierte TilesProcessor
GridExporter
VectorExporter
aviary verwenden
Wie installiert man aviary?
pip install --pre geospaitial-lab-aviary
uv pip install --pre geospaitial-lab-aviary
docker pull ghcr.io/geospaitial-lab/aviary
Option 1:
pip
Option 2:
uv
Option 3:
Docker
aviary verwenden
Welche Nutzungsmöglichkeiten bietet aviary?
3
Optionen:
Python API verwenden
Python API mit Pipelines verwenden
CLI mit Pipelines verwenden
tile_pipeline = aviary.pipeline.TilePipeline(
grid=aviary.Grid(...),
tile_fetcher=aviary.tile.TileFetcher(...),
tiles_processor=aviary.tile.TilesProcessor(...),
)
tile_pipeline()
Option 2:
Python API mit Pipelines
grid_config:
bounding_box_coordinates:
- 403691
- 5756873
- 405739
- 5758921
tile_size: 256
snap: true
tile_fetcher_config:
name: 'CompositeFetcher'
config:
tile_fetcher_configs:
- name: 'WMSFetcher'
config:
url: 'https://www.wms.nrw.de/geobasis/wms_nw_dop'
version: '1.3.0'
layer: 'nw_dop_rgb'
epsg_code: 25832
response_format: 'image/png'
channel_keys:
- 'r'
- 'g'
- 'b'
tile_size: 256
ground_sampling_distance: .2
buffer_size: 32
- name: 'WMSFetcher'
config:
url: 'https://www.wms.nrw.de/geobasis/wms_nw_dop'
version: '1.3.0'
layer: 'nw_dop_nir'
epsg_code: 25832
response_format: 'image/png'
channel_keys:
- 'nir'
- null
- null
tile_size: 256
ground_sampling_distance: .2
buffer_size: 32
tile_loader_config:
batch_size: 4
num_prefetched_tiles: 1
tiles_processor_config:
name: 'SequentialCompositeProcessor'
config:
tiles_processor_configs:
- name: 'NormalizeProcessor'
config:
channel_key: 'r'
min_value: 0
max_value: 255
- name: 'NormalizeProcessor'
config:
channel_key: 'g'
min_value: 0
max_value: 255
- name: 'NormalizeProcessor'
config:
channel_key: 'b'
min_value: 0
max_value: 255
- name: 'NormalizeProcessor'
config:
channel_key: 'nir'
min_value: 0
max_value: 255
- name: 'Adois'
config:
- name: 'RemoveBufferProcessor'
config:
- name: 'VectorizeProcessor'
config:
channel_key: 'adois'
- name: 'VectorExporter'
config:
channel_key: 'adois'
epsg_code: 25832
dir_path: 'output'
gpkg_name: 'adois.gpkg'
- name: 'GridExporter'
config:
dir_path: 'output'
json_name: 'grid.json'
aviary tile-pipeline config.yaml
Option 3:
CLI mit Pipelines
aviary erweitern
Wie implementiert man eigene Komponenten?
Komponenten:
TileFetcher
TilesProcessor
2 Optionen:
Eigene Komponente in der Python API verwenden
Eigene Komponente in der CLI als Plugin verwenden
class MyTilesProcessor:
def __init__(self) -> None:
...
def __call__(
self,
tiles: Tiles,
) -> Tiles:
...
Option 1:
Python API
Protocol implementieren
MyTilesProcessor
wie aviary’s Komponenten verwenden
class MyTilesProcessorConfig(pydantic.BaseModel):
...
@register_tiles_processor(config_class=MyTilesProcessorConfig)
class MyTilesProcessor:
def __init__(self) -> None:
...
@classmethod
def from_config(
cls,
config: MyTilesProcessorConfig,
) -> MyTilesProcessor:
...
def __call__(
self,
tiles: Tiles,
) -> Tiles:
...
Option 2:
CLI
Protocol implementieren
Config
implementieren
from_config
implementieren
Komponente registrieren
MyTilesProcessor
wie aviary’s Komponenten verwenden
Backlog
Woran arbeiten wir als nächstes?
Web-App für eine interaktive Konfiguration entwickeln
How-to-Guides erstellen
Neue Komponenten implementieren
TileFetcher
:
COGFetcher
GPKGFetcher
TilesProcessor
:
RasterizeProcessor
GraphProcessor
aviary.vector
Neue KI-Modelle integrieren
Neue KI-Modelle zur Auswertung von Luftbildern entwickeln
Basis-Modell trainieren
Fine-Tuning für konkrete Use-Cases durchführen
Gefördert im Rahmen von URBAN.KI (Projektidee des Kreises Recklinghausen)
Neue KI-Modelle
Woran arbeiten wir parallel?
Use-Case 1:
Gebäude
Geneigtes Dach ohne Begrünung
Geneigtes Dach mit Begrünung
Flachdach ohne Begrünung
Flachdach mit Begrünung
Flachdach mit Kiesschüttung
Use-Case 2:
Versiegelte Flächen
Verkehrsflächen
Zuwegungen
Schottergärten
Pools
Sonstige
Use-Case 3:
Nicht versiegelte Flächen
Niedrige Vegetation
Mittlere Vegetation
Hohe Vegetation
Agrarflächen
Gewässer
Sonstige
Use-Case 4:
Solaranlagen
Photovoltaik
Solarthermie
Feature Requests und Bug Reports erstellen
Feedback geben und Support erhalten
Community
Wie kannst du beitragen?