162 lines
5.8 KiB
Python
162 lines
5.8 KiB
Python
"""
|
|
Calibration and ROI helper tool.
|
|
Used to:
|
|
- Capture current game screen and save snapshots.
|
|
- Crop template snippets (buttons, banners) for assets/
|
|
- Test template matching against the active game screen with confidence score.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
import cv2
|
|
import numpy as np
|
|
from window_utils import WindowManager
|
|
|
|
CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config.json")
|
|
|
|
|
|
def load_config():
|
|
if os.path.exists(CONFIG_PATH):
|
|
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
|
|
return json.load(f)
|
|
return {}
|
|
|
|
|
|
def capture_snapshot(output_path: str = "assets/current_screen.png"):
|
|
config = load_config()
|
|
target_title = config.get("target_window", {}).get("title", "DirtyLeague")
|
|
wm = WindowManager(window_title=target_title)
|
|
|
|
hwnd = wm.find_window()
|
|
if not hwnd:
|
|
print(f"[ERROR] Window '{target_title}' not found!")
|
|
return None
|
|
|
|
frame = wm.capture_frame(client_only=True)
|
|
os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
|
|
cv2.imwrite(output_path, frame)
|
|
h, w = frame.shape[:2]
|
|
print(f"[OK] Screenshot saved to: {output_path} (Resolution: {w}x{h})")
|
|
return output_path
|
|
|
|
|
|
def crop_roi(source_path: str, output_name: str, x: int, y: int, w: int, h: int):
|
|
if not os.path.exists(source_path):
|
|
print(f"[ERROR] Source file not found: {source_path}")
|
|
return
|
|
|
|
img = cv2.imread(source_path)
|
|
if img is None:
|
|
print(f"[ERROR] Could not read image: {source_path}")
|
|
return
|
|
|
|
roi = img[y:y+h, x:x+w]
|
|
if roi.size == 0:
|
|
print(f"[ERROR] Crop dimensions resulted in empty image! (x={x}, y={y}, w={w}, h={h})")
|
|
return
|
|
|
|
out_path = os.path.join("assets", output_name if output_name.endswith(".png") else f"{output_name}.png")
|
|
cv2.imwrite(out_path, roi)
|
|
print(f"[OK] Cropped ROI saved to: {out_path} ({w}x{h})")
|
|
|
|
|
|
def test_match(template_name: str, threshold: float = 0.8):
|
|
config = load_config()
|
|
target_title = config.get("target_window", {}).get("title", "DirtyLeague")
|
|
wm = WindowManager(window_title=target_title)
|
|
|
|
template_path = os.path.join("assets", template_name if template_name.endswith(".png") else f"{template_name}.png")
|
|
if not os.path.exists(template_path):
|
|
print(f"[ERROR] Template not found: {template_path}")
|
|
return
|
|
|
|
tpl = cv2.imread(template_path)
|
|
if tpl is None:
|
|
print(f"[ERROR] Failed to load template: {template_path}")
|
|
return
|
|
|
|
frame = wm.capture_frame(client_only=True)
|
|
res = cv2.matchTemplate(frame, tpl, cv2.TM_CCOEFF_NORMED)
|
|
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
|
|
|
|
th, tw = tpl.shape[:2]
|
|
print(f"--- Template Match: {template_name} ---")
|
|
print(f"Max confidence: {max_val:.4f} (Threshold: {threshold})")
|
|
print(f"Best match client location: ({max_loc[0]}, {max_loc[1]}) -> Center: ({max_loc[0] + tw // 2}, {max_loc[1] + th // 2})")
|
|
|
|
if max_val >= threshold:
|
|
print("[RESULT] MATCH FOUND! Target is visible.")
|
|
else:
|
|
print("[RESULT] NOT FOUND (below threshold).")
|
|
|
|
|
|
def check_all_assets():
|
|
config = load_config()
|
|
target_title = config.get("target_window", {}).get("title", "DirtyLeague")
|
|
default_conf = config.get("bot", {}).get("confidence_threshold", 0.8)
|
|
wm = WindowManager(window_title=target_title)
|
|
|
|
assets_dir = "assets"
|
|
if not os.path.exists(assets_dir):
|
|
print("[ERROR] assets/ directory does not exist.")
|
|
return
|
|
|
|
files = [f for f in os.listdir(assets_dir) if f.endswith(".png") and not f.startswith("current_screen")]
|
|
if not files:
|
|
print("[INFO] No template images found in assets/ yet.")
|
|
return
|
|
|
|
frame = wm.capture_frame(client_only=True)
|
|
print(f"Testing {len(files)} asset templates against current game screen...")
|
|
for f in sorted(files):
|
|
tpl = cv2.imread(os.path.join(assets_dir, f))
|
|
if tpl is None:
|
|
continue
|
|
res = cv2.matchTemplate(frame, tpl, cv2.TM_CCOEFF_NORMED)
|
|
_, max_val, _, max_loc = cv2.minMaxLoc(res)
|
|
status = "MATCH" if max_val >= default_conf else "NO MATCH"
|
|
print(f" [{status}] {f:<25} confidence: {max_val:.3f} loc: {max_loc}")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Bot UI Calibration & Asset Tool")
|
|
subparsers = parser.add_subparsers(dest="command")
|
|
|
|
# snapshot command
|
|
snap_p = subparsers.add_parser("snapshot", help="Capture current game screen to file")
|
|
snap_p.add_argument("-o", "--output", default="assets/current_screen.png", help="Output PNG path")
|
|
|
|
# crop command
|
|
crop_p = subparsers.add_parser("crop", help="Crop ROI template from an image")
|
|
crop_p.add_argument("-s", "--source", default="assets/current_screen.png", help="Source screenshot")
|
|
crop_p.add_argument("-n", "--name", required=True, help="Output template name (e.g. btn_play.png)")
|
|
crop_p.add_argument("--roi", nargs=4, type=int, required=True, metavar=("X", "Y", "W", "H"), help="Crop region X Y W H")
|
|
|
|
# test command
|
|
test_p = subparsers.add_parser("test", help="Test match of a single template on live screen")
|
|
test_p.add_argument("-n", "--name", required=True, help="Template name in assets/")
|
|
test_p.add_argument("-t", "--threshold", type=float, default=0.8, help="Confidence threshold")
|
|
|
|
# scan command
|
|
subparsers.add_parser("scan", help="Scan all templates in assets/ against current screen")
|
|
|
|
args = parser.parse_args()
|
|
if args.command == "snapshot":
|
|
capture_snapshot(args.output)
|
|
elif args.command == "crop":
|
|
x, y, w, h = args.roi
|
|
crop_roi(args.source, args.name, x, y, w, h)
|
|
elif args.command == "test":
|
|
test_match(args.name, args.threshold)
|
|
elif args.command == "scan":
|
|
check_all_assets()
|
|
else:
|
|
parser.print_help()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|