# 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'', f'' ] for (x, y) in black_modules: parts.append(f'') parts.append('') 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()