publish eve_rock_watcher.py

This commit is contained in:
brockdarnold 2026-06-15 05:12:55 +00:00
parent 11d569c8ac
commit a728b529c5

View file

@ -90,55 +90,57 @@ QUANTITY_RE = re.compile(r"quantit[yvſ]\s*[:.]?\s*([\d.,]{2,})\s*units?", re.I)
UNITS_RE = re.compile(r"([\d.,]{3,})\s*units?\b", re.I) UNITS_RE = re.compile(r"([\d.,]{3,})\s*units?\b", re.I)
def _ocr_window_text(cp): def read_selected_quantity(cp):
"""OCR the EVE window with sparse-text mode (psm 11) — far better than uniform-block """Return (units, ore) for the currently-selected rock from its floating info popup.
(psm 6) at picking scattered UI text off a busy nebula background. Returns flat text.""" Two-pass: (1) locate the word 'Units' anywhere in the window via sparse OCR, then
(2) crop just that line, zoom + grayscale it, and OCR the digits cleanly small text
over the nebula garbles in a full-window pass ('5b Units'), but reads fine zoomed in."""
import pytesseract import pytesseract
from PIL import Image # noqa: F401 (capture_window already returns a PIL image)
tcmd = cp.get("ocr", "tesseract_cmd", fallback="").strip() tcmd = cp.get("ocr", "tesseract_cmd", fallback="").strip()
if tcmd: if tcmd:
pytesseract.pytesseract.tesseract_cmd = tcmd pytesseract.pytesseract.tesseract_cmd = tcmd
img = w.capture_window() img = w.capture_window()
if img is None: if img is None:
return None
out = []
for psm in (11, 6): # sparse first, then block fallback
try:
out.append(pytesseract.image_to_string(img, config=f"--psm {psm}"))
except Exception:
pass
return " ".join(out).replace("\n", " ")
def read_selected_quantity(cp):
"""Return (units, ore) for the currently-selected rock from its floating info popup,
found anywhere in the EVE window. (None, None) if no rock is selected/visible."""
flat = _ocr_window_text(cp)
if flat is None:
return None, None return None, None
m = QUANTITY_RE.search(flat) try:
if not m: # "Quantity" garbled? take any "N Units" d = pytesseract.image_to_data(img, config="--psm 11",
for cand in UNITS_RE.finditer(flat): output_type=pytesseract.Output.DICT)
v = int(re.sub(r"\D", "", cand.group(1)) or 0) except Exception:
if 100 <= v <= 5_000_000: # plausible asteroid remaining
m = cand
break
if not m:
# leave a breadcrumb so we can see what OCR produced when it misses
try:
with open(os.path.join(w.HERE, "_rockocr.txt"), "w", encoding="utf-8") as fh:
fh.write(flat[:4000])
except Exception:
pass
return None, None return None, None
digits = re.sub(r"\D", "", m.group(1)) n = len(d["text"])
if len(digits) < 2: cands = [i for i in range(n) if d["text"][i].strip().lower().startswith("unit")]
return None, None dbg = []
# the rock's name sits just before its 'Quantity' line in the same popup block for i in cands:
# (e.g. "Omber Distance ... Quantity 47,765 Units") — match there, not the whole uy, uh, ux = d["top"][i], d["height"][i], d["left"][i]
# window (the hold's "Compressed Kernite" would otherwise win). # crop the line just LEFT of "Units" — that's "Quantity <number>"
ore = match_ore(flat[max(0, m.start() - 180):m.start()].lower()) \ box = img.crop((max(0, ux - 320), max(0, uy - 8), ux + 6, uy + uh + 8))
or match_ore(flat.lower()) or "rock" z = box.convert("L").resize((box.width * 4, box.height * 4))
return int(digits), ore line = pytesseract.image_to_string(
z, config="--psm 7 -c tessedit_char_whitelist=0123456789,QuantiyUns ")
dbg.append(repr(line.strip()))
mm = re.search(r"([\d,]{3,})", line)
if not mm:
continue
v = int(re.sub(r"\D", "", mm.group(1)) or 0)
if not (100 <= v <= 5_000_000): # plausible asteroid remaining
continue
# ore name = a token a little above the 'Units' line, same popup block
ore = "rock"
for j in range(n):
if d["text"][j].strip() and 0 < (uy - d["top"][j]) < 120 \
and abs(d["left"][j] - (ux - 160)) < 260:
o = match_ore(d["text"][j].strip().lower())
if o:
ore = o
break
return v, ore
try:
with open(os.path.join(w.HERE, "_rockocr.txt"), "w", encoding="utf-8") as fh:
fh.write(f"units-candidates={len(cands)} crops={dbg}\n")
except Exception:
pass
return None, None
def save_rock_region(cp, l, t, wd, ht, mode="survey"): def save_rock_region(cp, l, t, wd, ht, mode="survey"):