Test files and initial data gathering completed
This commit is contained in:
+127
@@ -0,0 +1,127 @@
|
||||
"""Lightweight image analysis for live capture feedback.
|
||||
|
||||
All stats are computed on a small downsampled copy so the demo loop stays
|
||||
fast at full 1080p capture.
|
||||
"""
|
||||
|
||||
from collections import Counter
|
||||
|
||||
from PIL import Image
|
||||
|
||||
ANALYSIS_SIZE = (320, 180)
|
||||
_TOP_COLORS = 5
|
||||
|
||||
# Harvesting is detected from the single pixel at this window coordinate.
|
||||
# Harvesting shows a light tan (~#f2c285, luma ~201); idle shows dark brown
|
||||
# (~#664c33, luma ~81).
|
||||
HARVEST_PIXEL = (1710, 1068)
|
||||
HARVEST_LUMA_THRESHOLD = 140
|
||||
|
||||
# The small numeric readout just before each bar at the bottom of the panel.
|
||||
# Regions are (x0, y0, x1, y1) in captured-window pixel coordinates. y0 starts
|
||||
# below the panel's top border line (y=1032) which would confuse OCR.
|
||||
NUMBER_REGIONS = {
|
||||
"mana": (0, 1033, 62, 1048),
|
||||
"food": (195, 1033, 226, 1048),
|
||||
"health": (355, 1033, 389, 1048),
|
||||
"inventory": (520, 1033, 552, 1048),
|
||||
"action_points": (610, 1033, 714, 1048),
|
||||
}
|
||||
|
||||
_OCR_SCALE = 3
|
||||
|
||||
try:
|
||||
import pytesseract
|
||||
except ImportError:
|
||||
pytesseract = None
|
||||
|
||||
|
||||
def _luma(pixel):
|
||||
r, g, b = pixel
|
||||
return round(0.299 * r + 0.587 * g + 0.114 * b)
|
||||
|
||||
|
||||
def load_image(path):
|
||||
"""Load a PNG as RGB; return None if the file is unreadable/corrupt."""
|
||||
try:
|
||||
return Image.open(path).convert("RGB")
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
|
||||
def is_harvesting(image):
|
||||
"""Return whether the player is harvesting, based on the harvest pixel."""
|
||||
rgb = image.convert("RGB")
|
||||
color = rgb.getpixel(HARVEST_PIXEL)
|
||||
lum = _luma(color)
|
||||
return {
|
||||
"is_harvesting": lum >= HARVEST_LUMA_THRESHOLD,
|
||||
"color": "#%02x%02x%02x" % color,
|
||||
"luma": lum,
|
||||
}
|
||||
|
||||
|
||||
def _ocr_number(image, region):
|
||||
"""OCR a single number region; return int or None if it can't be read."""
|
||||
if pytesseract is None:
|
||||
return None
|
||||
crop = image.convert("L").crop(region)
|
||||
crop = crop.resize((crop.width * _OCR_SCALE, crop.height * _OCR_SCALE))
|
||||
crop = crop.point(lambda p: 0 if p < 140 else 255)
|
||||
try:
|
||||
text = pytesseract.image_to_string(
|
||||
crop,
|
||||
config="--psm 7 -c tessedit_char_whitelist=0123456789",
|
||||
)
|
||||
except Exception:
|
||||
return None
|
||||
digits = "".join(ch for ch in text if ch.isdigit())
|
||||
return int(digits) if digits else None
|
||||
|
||||
|
||||
def read_bar_values(image):
|
||||
"""Return the numeric readout before each bar, or None if unreadable."""
|
||||
rgb = image.convert("RGB")
|
||||
return {
|
||||
name: _ocr_number(rgb, region)
|
||||
for name, region in NUMBER_REGIONS.items()
|
||||
}
|
||||
|
||||
|
||||
def _pixels(image):
|
||||
px = image.load()
|
||||
w, h = image.size
|
||||
return [px[x, y] for y in range(h) for x in range(w)]
|
||||
|
||||
|
||||
def summarize(image):
|
||||
"""Return size, mean RGB, and dominant colors of a captured frame."""
|
||||
small = image.convert("RGB").resize(ANALYSIS_SIZE)
|
||||
pixels = _pixels(small)
|
||||
n = len(pixels)
|
||||
total = [0, 0, 0]
|
||||
for r, g, b in pixels:
|
||||
total[0] += r
|
||||
total[1] += g
|
||||
total[2] += b
|
||||
mean = tuple(round(c / n) for c in total)
|
||||
top = Counter(pixels).most_common(_TOP_COLORS)
|
||||
dominant = [
|
||||
{"hex": "#%02x%02x%02x" % color, "fraction": round(count / n, 3)}
|
||||
for color, count in top
|
||||
]
|
||||
return {"size": (image.width, image.height), "mean_rgb": mean, "dominant": dominant}
|
||||
|
||||
|
||||
def difference(a, b):
|
||||
"""Sum of absolute channel differences between two same-size RGB images."""
|
||||
if a.size != b.size:
|
||||
return None
|
||||
pa, pb = a.load(), b.load()
|
||||
w, h = a.size
|
||||
diff = 0
|
||||
for y in range(h):
|
||||
for x in range(w):
|
||||
ca, cb = pa[x, y], pb[x, y]
|
||||
diff += abs(ca[0] - cb[0]) + abs(ca[1] - cb[1]) + abs(ca[2] - cb[2])
|
||||
return diff
|
||||
Reference in New Issue
Block a user