Files
aza/APP/logo-tool/core/vectorizer.py

89 lines
2.5 KiB
Python
Raw Permalink Normal View History

2026-03-30 07:59:11 +02:00
"""
Vektorisierung: PNG Pfade via potrace.
Zerlegt das Bitmap in Konturen, die dann als SVG-Pfade manipuliert werden können.
"""
from __future__ import annotations
import numpy as np
try:
import potracer
HAS_POTRACER = True
except ImportError:
HAS_POTRACER = False
from svgpathtools import Path as SvgPath, CubicBezier, Line
from core.logo_model import GlyphGroup
def _rgba_to_bitmap(pixels: np.ndarray, threshold: int = 128) -> np.ndarray:
"""Konvertiert RGBA in ein Schwarz/Weiß-Bitmap für potrace."""
gray = np.mean(pixels[:, :, :3], axis=2)
alpha = pixels[:, :, 3] / 255.0
effective = gray * alpha + 255 * (1 - alpha)
return (effective < threshold).astype(np.uint8)
def _curve_to_svg_segment(segment):
"""Konvertiert ein potrace-Kurvensegment in ein svgpathtools-Segment."""
tag = segment.tag
c = segment.c
end = segment.end_point
if tag == potracer.CORNER:
return [
Line(complex(c[0][0], c[0][1]), complex(c[1][0], c[1][1])),
Line(complex(c[1][0], c[1][1]), complex(end[0], end[1])),
]
else:
start_pt = complex(c[0][0], c[0][1])
cp1 = complex(c[0][0], c[0][1])
cp2 = complex(c[1][0], c[1][1])
end_pt = complex(end[0], end[1])
return [CubicBezier(start_pt, cp1, cp2, end_pt)]
def trace_to_paths(
pixels: np.ndarray,
threshold: int = 128,
turd_size: int = 2,
alpha_max: float = 1.0,
) -> list[GlyphGroup]:
"""
Hauptfunktion: Konvertiert ein RGBA-Bild in eine Liste von GlyphGroups.
Jede zusammenhängende Kontur wird eine eigene Gruppe.
"""
if not HAS_POTRACER:
raise ImportError(
"potracer ist nicht installiert. "
"Bitte 'pip install potracer' ausführen."
)
bitmap = _rgba_to_bitmap(pixels, threshold)
bmp = potracer.Bitmap(bitmap)
path_result = bmp.trace(
turdsize=turd_size,
alphamax=alpha_max,
)
groups: list[GlyphGroup] = []
for i, curve in enumerate(path_result):
svg_segments = []
for segment in curve.segments:
svg_segments.extend(_curve_to_svg_segment(segment))
if not svg_segments:
continue
svg_path = SvgPath(*svg_segments)
bbox = svg_path.bbox() # xmin, xmax, ymin, ymax
group = GlyphGroup(
label=f"Pfad_{i}",
paths=[svg_path],
bbox=(bbox[0], bbox[2], bbox[1] - bbox[0], bbox[3] - bbox[2]),
)
groups.append(group)
return groups