Three fixes from running it against the live game:
1. It blind-pressed the open key. The grid was ALREADY open, so the press
CLOSED it and the scan read grass. Now it detects the grid via its bottom
hint and toggles only when needed, restoring the state it found. The hint
match is fuzzy because Tesseract renders it "PRESS FI-F8 TO BIRD A SKIN"
(BIND->BIRD, SKILL->SKIN).
2. The tooltip ROI was fixed, but the tooltip renders ABOVE the hovered cell
and moves with the row, so it was reading the game world. Now taken relative
to the cell. Skill identification is a fuzzy match against a known list
rather than a demand for clean OCR: real reads included "XI BLESSED HAMMER"
and one frame OCR'd Fist of the Heavens as "LNMEPULE".
3. Hotkey labels are read by TEMPLATE MATCH, not OCR. They are ~20x12px of
white glyph over whatever icon is behind them; brightness thresholding
cannot separate the two when the icon is also bright (F4's swirl, the row-4
weapons) and OCR managed 4/8. A top-hat isolates small bright features
regardless of background: 8/8 with zero false positives.
Matching a same-SIZED crop is 1px-brittle — int() rounding in the grid
geometry lands a pixel off the measured centre and the score collapses from
~1.0 to ~0.4. Exactly the three cells where truncation differed failed. A
4px search slack fixes it; 8/8 held at every threshold 0.60-0.78.
Adds assets/templates/skill_binds/f1..f8.png, cut from a frame with all eight
labels visible.
Verified live: correctly reported conviction=f5 as casting TELEPORT and
concentration=f8 as casting CONVICTION — both confirmed by hand beforehand —
and found Concentration sitting unbound.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>