#!/usr/bin/env python3 """Diagnostic: classify the 64 squares of samples/board.png and compare to the known FEN. Helps calibrate crop/normalization and understand the empty-class issue.""" import sys import cv2 as cv import numpy as np import onnxruntime as ort SIZE = 800 CORNERS = np.array([[520, 140], [1360, 150], [1430, 915], [452, 918]], dtype=np.float32) TRUE_FEN = "rn1qkb1r/pb2pppp/5n2/1ppp4/8/3P1NP1/PPP1PPBP/RNBQ1RK1" IDX2SYM = {0: "K", 1: "Q", 2: "R", 3: "B", 4: "N", 5: "P", 6: "k", 7: "q", 8: "r", 9: "b", 10: "n", 11: "p"} MEAN = np.array([0.485, 0.456, 0.406], np.float32) STD = np.array([0.229, 0.224, 0.225], np.float32) def preprocess(bgr): rgb = cv.cvtColor(cv.resize(bgr, (96, 96)), cv.COLOR_BGR2RGB).astype(np.float32) / 255.0 rgb = (rgb - MEAN) / STD return rgb.transpose(2, 0, 1)[None] def fen_grid(fen): grid = [] for row in fen.split("/"): r = [] for ch in row: if ch.isdigit(): r += ["."] * int(ch) else: r.append(ch) grid.append(r) return grid def main(): margin = float(sys.argv[1]) if len(sys.argv) > 1 else 0.6 # top margin (cell fractions) img = cv.imread("samples/board.png") dst = np.array([[0, 0], [SIZE, 0], [SIZE, SIZE], [0, SIZE]], np.float32) Minv = cv.getPerspectiveTransform(dst, CORNERS) # warped -> original sess = ort.InferenceSession("models_onnx/digitizer.int8.onnx", providers=["CPUExecutionProvider"]) cell = SIZE / 8 pred = [["." for _ in range(8)] for _ in range(8)] prob = [[0.0 for _ in range(8)] for _ in range(8)] for r in range(8): for c in range(8): # cell quad in warped space, extended upward by `margin` cells for the piece y0 = (r - margin) * cell quad = np.array([[[c * cell, y0], [(c + 1) * cell, y0], [(c + 1) * cell, (r + 1) * cell], [c * cell, (r + 1) * cell]]], np.float32) orig = cv.perspectiveTransform(quad, Minv)[0] xs, ys = orig[:, 0], orig[:, 1] x1, x2 = max(0, int(xs.min())), min(img.shape[1], int(xs.max())) y1, y2 = max(0, int(ys.min())), min(img.shape[0], int(ys.max())) crop = img[y1:y2, x1:x2] if crop.size == 0: continue out = sess.run(None, {"input": preprocess(crop)})[0].flatten() p = np.exp(out) / np.exp(out).sum() k = int(p.argmax()) pred[r][c] = IDX2SYM[k] prob[r][c] = float(p[k]) true = fen_grid(TRUE_FEN) print(f"margin={margin}\n") print("PRED (top row = rank8 assumed): TRUE:") hits = 0 for r in range(8): pr = " ".join(pred[r]) tr = " ".join(true[r]) hits += sum(1 for a, b in zip(pred[r], true[r]) if a == b) print(f" {pr} {tr}") print(f"\nexact-cell match (incl. empties): {hits}/64") print("avg max-prob per row (low on empties?):") for r in range(8): print(" " + " ".join(f"{prob[r][c]:.2f}" for c in range(8))) if __name__ == "__main__": main()