fix(extract): Brückenerkennung per Verschneidung, Mittellinien ohne Dopplung
Fix-Runde 1, drei Befunde: - road_polygons: Straßennetz ist ein zusammenhängendes Polygon über den ganzen Ausschnitt; eine Fensterprüfung um seine Bounding-Box sieht daher zwangsläufig Wasser und meldet fast alles als Brücke. Ersetzt durch Verschneidung jedes Straßenpolygons mit der Landfläche (Ruling 13). Die wörtlich vorgegebene Landberechnung (unary_union(land_polygons(labels))) besteht die bestehenden Tests nicht: eine Straße ist nie "wasser", also zählt land_polygons() sie selbst als Land, und ein Damm quer durchs Meer überlappt dann mit sich selbst statt als Brücke aufzufallen (siehe Fix-Bericht für den Nachweis). Land wird stattdessen ohne die Straßenklasse berechnet und um LAND_REACH_PX gewachsen, damit eine Straße, die direkt an Land beginnt, dort noch erkannt wird. Der Radius wurde am echten Mosaik kalibriert: unter 8 px verkettet sich eine gewöhnliche Küstenstraße mangels Berührung mit "Land" erneut zu einem kartenweiten Brückenstück; 10 px behebt das mit Sicherheitsabstand zur Wasserlücke des bestehenden Tests. - centerlines: eine Kontur um das ein Pixel breite Skelett lief auf beiden Seiten des Bandes hin und zurück und lieferte jede Linie doppelt; an Kreuzungen ergab das eine einzelne Schleife statt getrennter Äste (Ruling 14, neue Tests zuerst rot gesehen). Ersetzt durch einen Pixelgraphen (networkx): Skelettpixel als Knoten, 8er-Nachbarschaft als Kanten, Pfade zwischen Knoten mit Grad ungleich 2 je Zusammenhangskomponente, reine Ringe einmal umlaufen. - mask_polygons: Polygon(ring).buffer(0) kann bei selbstschneidenden Konturen ein MultiPolygon liefern; das schlug später mit AttributeError fehl. Beim Einsortieren in outers/holes wird jetzt nur noch ein sauberes Polygon akzeptiert (Ruling 15). 16/16 Tests grün. Auf dem echten Mosaik: 469 Straßenstücke (190 Brücken, größtes Brückenstück 26.294 m² auf einer lokalen Fläche von 643 x 564 m, nicht mehr kartenweit). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fb898b03b3
commit
47c3cb98da
2 files changed
+101
-30
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import numpy as np
|
||||
from leonida.classify import CLASSES
|
||||
from leonida.vectors import land_polygons, mask_polygons, road_polygons
|
||||
from leonida.vectors import centerlines, land_polygons, mask_polygons, road_polygons
|
||||
|
||||
def test_square_becomes_one_polygon_with_right_area():
|
||||
mask = np.zeros((100, 100), dtype=bool)
|
||||
@@ -31,3 +31,16 @@ def test_crossing_roads_form_a_single_polygon():
|
||||
labels[:, 28:32] = CLASSES["strasse"]
|
||||
roads = road_polygons(labels)
|
||||
assert len(roads) == 1 # Kreuzung, kein Zerfall in vier Arme
|
||||
|
||||
def test_centerline_runs_once_along_a_straight_road():
|
||||
labels = np.full((60, 300), CLASSES["land"], dtype=np.uint8)
|
||||
labels[28:38, 50:250] = CLASSES["strasse"] # 200 lang, 10 breit
|
||||
lines = centerlines(labels)
|
||||
assert len(lines) == 1
|
||||
assert 180 <= lines[0].length <= 215 # einmal entlang, nicht hin und zurück
|
||||
|
||||
def test_crossing_roads_yield_separate_centerlines():
|
||||
labels = np.full((120, 120), CLASSES["land"], dtype=np.uint8)
|
||||
labels[55:65, :] = CLASSES["strasse"]
|
||||
labels[:, 55:65] = CLASSES["strasse"]
|
||||
assert len(centerlines(labels)) >= 3 # Arme werden an der Kreuzung getrennt
|
||||
Reference in new issue
Block a user