74 lines
2.3 KiB
Python
74 lines
2.3 KiB
Python
"""Vectorization pipeline — converts preprocessed binary images to SVG."""
|
|
|
|
import numpy as np
|
|
import potrace
|
|
|
|
|
|
def potrace_trace(
|
|
binary_img: np.ndarray,
|
|
turdsize: int = 2,
|
|
alphamax: float = 1.0,
|
|
opticurve: bool = True,
|
|
opttolerance: float = 0.2,
|
|
) -> str:
|
|
"""Trace a binary image using Potrace and return an SVG string.
|
|
|
|
Args:
|
|
binary_img: 2D numpy array — nonzero pixels are foreground.
|
|
turdsize: Despeckle threshold; curves with enclosed area below this are removed.
|
|
alphamax: Corner detection threshold (0.0 = polygon, 1.3333 = no corners).
|
|
opticurve: Whether to optimize curves by reducing Bezier segments.
|
|
opttolerance: Tolerance for curve optimization.
|
|
|
|
Returns:
|
|
Well-formed SVG string.
|
|
"""
|
|
if binary_img.ndim != 2:
|
|
raise ValueError(f"Expected 2D binary image, got shape {binary_img.shape}")
|
|
|
|
h, w = binary_img.shape
|
|
|
|
# Potrace interprets nonzero pixels as foreground.
|
|
# Convert to uint32 — pypotrace needs values that fit in a C int.
|
|
data = (binary_img > 0).astype(np.uint32)
|
|
|
|
bmp = potrace.Bitmap(data)
|
|
path = bmp.trace(
|
|
turdsize=turdsize,
|
|
alphamax=alphamax,
|
|
opticurve=int(opticurve),
|
|
opttolerance=opttolerance,
|
|
)
|
|
|
|
return _path_to_svg(path, w, h)
|
|
|
|
|
|
def _path_to_svg(path, width: int, height: int) -> str:
|
|
"""Convert a potrace Path object to an SVG string."""
|
|
parts = []
|
|
for curve in path:
|
|
sx, sy = curve.start_point
|
|
parts.append(f"M {sx:.3f},{sy:.3f}")
|
|
for segment in curve.segments:
|
|
if segment.is_corner:
|
|
cx, cy = segment.c
|
|
ex, ey = segment.end_point
|
|
parts.append(f"L {cx:.3f},{cy:.3f} L {ex:.3f},{ey:.3f}")
|
|
else:
|
|
c1x, c1y = segment.c1
|
|
c2x, c2y = segment.c2
|
|
ex, ey = segment.end_point
|
|
parts.append(
|
|
f"C {c1x:.3f},{c1y:.3f} {c2x:.3f},{c2y:.3f} {ex:.3f},{ey:.3f}"
|
|
)
|
|
parts.append("Z")
|
|
|
|
d = " ".join(parts)
|
|
|
|
return (
|
|
f'<svg xmlns="http://www.w3.org/2000/svg" '
|
|
f'width="{width}" height="{height}" '
|
|
f'viewBox="0 0 {width} {height}">'
|
|
f'<path d="{d}" fill="black" fill-rule="evenodd" stroke="none"/>'
|
|
f"</svg>"
|
|
)
|