"""read_marker_id: template-correlation read of a marker's own small "#" id label (see map_vision.read_marker_id's own docstring for why this is template correlation, not OCR -- same reasoning as read_cell_label). Synthetic image, real font, no fixture screenshot or the (slow) detection pipeline needed -- just render the label the way the game does and check it round-trips. """ import numpy as np from PIL import Image, ImageDraw, ImageFont from fenigma import map_vision def _render_label(text: str, height: int) -> Image.Image: """Cream glyph, heavy dark outline, same style glyph_template expects to correlate against -- see glyph_template's own docstring.""" font = ImageFont.truetype(str(map_vision.FONT_PATH), height) pad = height im = Image.new("L", (height * 4 + pad, height * 2 + pad), 30) # dark "photo" background ImageDraw.Draw(im).text((pad // 2, pad // 4), text, font=font, fill=230, stroke_width=max(1, int(height * 0.10)), stroke_fill=0) return im def test_reads_a_clean_id_label(): # A marker box roughly where a real one measures (see read_marker_id's # own calibration note), with a rendered "#8" sitting where the game # draws it: above-left of the box. box_w, box_h = 40, 40 label_h = int(0.45 * box_h) label_im = _render_label("#8", label_h) canvas = Image.new("L", (200, 200), 60) label_x, label_y = 60, 60 canvas.paste(label_im, (label_x, label_y)) gray = np.array(canvas) box_x = label_x + int(1.0 * box_w) - 5 # box sits to the right of/below the label box_y = label_y + int(0.45 * box_h) box = (box_x, box_y, box_w, box_h) assert map_vision.read_marker_id(gray, box) == "8" def test_returns_none_on_a_blank_patch(): gray = np.full((200, 200), 60, dtype=np.uint8) box = (100, 100, 40, 40) assert map_vision.read_marker_id(gray, box) is None def test_returns_none_on_a_degenerate_box_at_the_image_edge(): gray = np.full((200, 200), 60, dtype=np.uint8) box = (0, 0, 2, 2) # crop region collapses to nothing usable assert map_vision.read_marker_id(gray, box) is None