ArucoGenerator
This commit is contained in:
203
data/ArucoGenerator/generate_5x5.py
Executable file
203
data/ArucoGenerator/generate_5x5.py
Executable file
@@ -0,0 +1,203 @@
|
||||
# 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()
|
||||
Reference in New Issue
Block a user