89 lines
2.5 KiB
Python
89 lines
2.5 KiB
Python
"""
|
|
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
|