feat(extract): Straßennetz als Graph aus dem Skelett

centerlines() verwirft alles unter 15 m und zerlegt das Netz damit an jeder
Kreuzung: von 3.038 Linienenden treffen sich nur 81. Der neue Graph lässt
das Skelett ungefiltert, macht aus den 5.970 Pixeln mit Grad >= 3 durch
Verschmelzen im Umkreis von 8 m die 862 Kreuzungen und hängt die Kanten
dort ein.

_skeleton_graph/_to_world in vectors.py sind jetzt öffentliche Schnittstelle
für roads.py (skeleton_graph/to_world); inpaint.py zieht seinen einzigen
Aufruf von skeleton_graph mit, sonst bräche dessen Import.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
knsoandClaude Sonnet 5 committed 2026-09-17 00:36:40 +02:00
1 parent 505b1969ec
commit 3fe3cd5c45
4 files changed
+182 -7

No files matched your search

+5 -5
View File
@@ -12,7 +12,7 @@ from .coords import SLICE
ROAD_CLASSES = ("strasse", "weg", "spekulativ")
def _to_world(contour: np.ndarray) -> list[tuple[float, float]]:
def to_world(contour: np.ndarray) -> list[tuple[float, float]]:
"""Konturpunkte (Zeile, Spalte) → Weltkoordinaten in Metern."""
return [(SLICE["x0"] + c, SLICE["y1"] - r) for r, c in contour]
@@ -22,7 +22,7 @@ def mask_polygons(mask: np.ndarray, min_area: float, simplify: float) -> list[Po
outers, holes = [], []
for contour in find_contours(padded.astype(float), 0.5):
local = contour - 1
ring = _to_world(local)
ring = to_world(local)
if len(ring) < 4:
continue
poly = Polygon(ring).buffer(0)
@@ -128,16 +128,16 @@ def centerlines(labels: np.ndarray) -> list[LineString]:
(Enden und Kreuzungen trennen die Äste); ein reiner Ring ohne solche Knoten wird
einmal umlaufen.
"""
graph = _skeleton_graph(skeletonize(road_mask(labels)))
graph = skeleton_graph(skeletonize(road_mask(labels)))
lines = []
for path in _skeleton_paths(graph):
line = LineString(_to_world(path)).simplify(1.5)
line = LineString(to_world(path)).simplify(1.5)
if line.length >= 15:
lines.append(line)
return lines
def _skeleton_graph(skeleton: np.ndarray) -> nx.Graph:
def skeleton_graph(skeleton: np.ndarray) -> nx.Graph:
"""Skelettpixel als Knoten, 8er-Nachbarschaft als Kanten."""
pixels = set(zip(*np.nonzero(skeleton)))
graph = nx.Graph()