Files
2026-05-14 13:06:40 +02:00

204 lines
6.5 KiB
Python
Executable File

# aruco_svg_batch.py
from __future__ import annotations
import os
from typing import List, Tuple
OUT_DIR = "svgAruco"
FILL_COLOR = "#031740"
DICT_NAME = "DICT_5X5_250"
MARKER_SIZE_BITS = 5
BORDER_BITS = 1
SIDE_PIXELS_PER_MODULE = 8
START_INDEX = 0
PDF_NAME = "aruco_5x5_250_A4.pdf"
MARKER_SIZE_MM = 10.0
def _ensure_deps():
try:
import cv2
from cv2 import aruco
except Exception as e:
raise SystemExit("Install opencv-contrib-python: " + str(e))
try:
import reportlab
except Exception as e:
raise SystemExit("Install reportlab: " + str(e))
def _get_dictionary(dict_name: str):
from cv2 import aruco
if hasattr(aruco, "getPredefinedDictionary"):
return aruco.getPredefinedDictionary(getattr(aruco, dict_name))
return aruco.Dictionary_get(getattr(aruco, dict_name))
def _draw_marker(dictionary, marker_id: int, modules: int, spp: int):
from cv2 import aruco
side_px = modules * spp
if hasattr(aruco, "generateImageMarker"):
img = aruco.generateImageMarker(dictionary, marker_id, side_px, borderBits=BORDER_BITS)
else:
img = aruco.drawMarker(dictionary, marker_id, side_px, borderBits=BORDER_BITS)
return img
def _modules_from_image(img) -> List[Tuple[int, int]]:
import numpy as np
h, w = img.shape[:2]
modules = h // SIDE_PIXELS_PER_MODULE
step = SIDE_PIXELS_PER_MODULE
black_modules = []
bw = (img < 128).astype(np.uint8)
for r in range(modules):
rs = r * step
re = rs + step
for c in range(modules):
cs = c * step
ce = cs + step
block = bw[rs:re, cs:ce]
if block.mean() > 0.5:
black_modules.append((c, r))
return black_modules
def _svg_for_modules(black_modules: List[Tuple[int, int]], modules: int, fill: str) -> str:
parts = [
f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {modules} {modules}" shape-rendering="crispEdges">',
f'<g fill="{fill}">'
]
for (x, y) in black_modules:
parts.append(f'<rect x="{x}" y="{y}" width="1" height="1"/>')
parts.append('</g></svg>')
return ''.join(parts)
# PDF helpers
def mm(v):
return v * 72.0 / 25.4
from reportlab.pdfgen import canvas as _c
from reportlab.lib.pagesizes import A4
from reportlab.lib.colors import HexColor, Color
from reportlab.pdfbase import pdfmetrics
def _hex_to_reportlab(c):
try:
return HexColor(c)
except Exception:
return Color(0, 0, 0)
def _layout_grid_vars(page_w, page_h):
MARKER_MM = MARKER_SIZE_MM
MARGIN_MM = 4.0
GUTTER_MM = 7.0
marker = mm(MARKER_MM)
margin = mm(MARGIN_MM)
gutter = mm(GUTTER_MM)
usable_w = page_w - 2 * margin
usable_h = page_h - 2 * margin
# Anzahl Marker berechnen, die reinpassen
cols = int((usable_w + gutter) // (marker + gutter))
rows = int((usable_h + gutter) // (marker + gutter))
return dict(
margin=margin,
gutter=gutter,
cols=cols,
rows=rows,
cell_w=marker,
cell_h=marker
)
def _draw_marker_into_cell(c, cell_x, cell_y, cell_w, cell_h,
black_modules, modules_per_side,
module_fill_hex, index_label: str):
# label area
EXTRA_BOTTOM_MM = 10.0
label_h = mm(4.5 + EXTRA_BOTTOM_MM)
marker_side = cell_w # statt min(...)
module_size = marker_side / float(modules_per_side)
mx = cell_x + (cell_w - marker_side) / 2.0
my = cell_y + (cell_h - label_h - marker_side) / 2.0 + label_h
c.saveState()
c.setFillColor(_hex_to_reportlab(module_fill_hex))
for (x, y) in black_modules:
rx = mx + x * module_size
#ry = my + y * module_size
ry = my + (modules_per_side - 1 - y) * module_size
c.rect(rx, ry, module_size, module_size, stroke=0, fill=1)
c.restoreState()
# orientation dot: 2mm inside
dot_d_mm = 0.4
edge_gap_mm = 0.4
r = mm(dot_d_mm) / 2.0
gap = mm(edge_gap_mm)
dot_cx = mx + gap + r
dot_cy = my + marker_side - (gap + r)
c.saveState()
c.setFillColor(_hex_to_reportlab('#510000'))
c.circle(dot_cx, dot_cy, r, stroke=0, fill=1)
c.restoreState()
# label: exact 2mm gap using font ascent
c.saveState()
font_name = 'Helvetica'
font_size = 7
c.setFont(font_name, font_size)
c.setFillColor(Color(0.7, 0.7, 0.7))
tx = mx + marker_side / 2.0
ascent = pdfmetrics.getAscent(font_name) * (font_size/1000.0)
baseline = my - mm(1.3) - ascent
c.drawCentredString(tx, baseline, index_label)
c.restoreState()
def build_a4_pdf(pdf_path: str = PDF_NAME):
_ensure_deps()
dictionary = _get_dictionary(DICT_NAME)
modules = MARKER_SIZE_BITS + 2 * BORDER_BITS
PAGE_W, PAGE_H = A4
c = _c.Canvas(pdf_path, pagesize=A4)
grid = _layout_grid_vars(PAGE_W, PAGE_H)
COLS, ROWS = grid['cols'], grid['rows']
per_page = COLS * ROWS
total = 250
for idx in range(total):
if idx % per_page == 0:
if idx > 0:
c.showPage()
i_on_page = idx % per_page
col = i_on_page % COLS
row = i_on_page // COLS
x = grid['margin'] + col * (grid['cell_w'] + grid['gutter'])
y = grid['margin'] + (grid['rows'] - 1 - row) * (grid['cell_h'] + grid['gutter'])
img = _draw_marker(dictionary, marker_id=idx, modules=modules, spp=SIDE_PIXELS_PER_MODULE)
black_modules = _modules_from_image(img)
label = f"{idx + START_INDEX:03d}"
_draw_marker_into_cell(c, x, y, grid['cell_w'], grid['cell_h'], black_modules, modules, FILL_COLOR, label)
c.save()
return pdf_path
def main():
_ensure_deps()
os.makedirs(OUT_DIR, exist_ok=True)
dictionary = _get_dictionary(DICT_NAME)
modules = MARKER_SIZE_BITS + 2 * BORDER_BITS
total = 250
for idx in range(total):
img = _draw_marker(dictionary, marker_id=idx, modules=modules, spp=SIDE_PIXELS_PER_MODULE)
black_modules = _modules_from_image(img)
svg = _svg_for_modules(black_modules, modules, FILL_COLOR)
fname = f"aruco_{idx + START_INDEX:03d}.svg"
with open(os.path.join(OUT_DIR, fname), 'w', encoding='utf-8') as f:
f.write(svg)
pdf_path = build_a4_pdf(PDF_NAME)
print('PDF saved to:', pdf_path)
if __name__ == '__main__':
main()