Загрузка данных
import cv2
import numpy as np
import os
import json
import time
import keyboard
from mss import mss
from config import (TEMPLATES_DIR, COLORS_FILE, CONFIG_FILE,
PIECES_CONFIG, ALL_PIECES, extract_dominant_piece_color,
generate_templates_and_colors)
is_active = False
board_roi = None
need_selection = False # Флаг для вызова выделения в главном потоке
def clear_console():
os.system('cls' if os.name == 'nt' else 'clear')
def toggle_scanner():
global is_active
is_active = not is_active
print(f"\n[СТАТУС] Сканирование: {'ВКЛ' if is_active else 'ПАУЗА'}")
def request_selection():
"""Только взводит флаг из потока клавиатуры"""
global need_selection
need_selection = True
def select_board_region(sct_instance):
"""Безопасное выделение доски в главном потоке"""
global board_roi, is_active
was_active = is_active
is_active = False
print("\n[ВЫБОР] Делаем скриншот... Выделите доску мышкой и нажмите ENTER")
monitor = sct_instance.monitors[1]
frame = np.array(sct_instance.grab(monitor))[:, :, :3]
win_name = "Выделите доску (ENTER - ок, ESC - отмена)"
cv2.namedWindow(win_name, cv2.WINDOW_NORMAL)
roi = cv2.selectROI(win_name, frame, fromCenter=False, showCrosshair=True)
cv2.waitKey(1)
cv2.destroyWindow(win_name)
cv2.waitKey(1)
x, y, w, h = roi
if w > 50 and h > 50:
board_roi = {"top": int(y), "left": int(x), "width": int(w), "height": int(h)}
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
json.dump(board_roi, f, indent=4)
print(f"[УСПЕХ] Область доски успешно сохранена!")
else:
print("[ОТМЕНА] Выделение отменено.")
time.sleep(0.5)
is_active = was_active
def print_visual_board(board_matrix, fen):
clear_console()
status = "[ВКЛ]" if is_active else "[ПАУЗА]"
print("=" * 60)
print(f" {status} | F7: Старт/Пауза | F8: Выделить доску мышкой")
print("=" * 60)
print(" a b c d e f g h")
print(" +---------------------------------+")
for idx, row in enumerate(board_matrix):
rank = 8 - idx
row_str = " ".join([f"[{p}]" if p != '.' else " . " for p in row])
print(f"{rank} | {row_str} | {rank}")
print(" +---------------------------------+")
print(" a b c d e f g h")
print("=" * 60)
print("FEN:", fen)
def scan_frame(cell_board, color_database, templates_edges):
board = cv2.resize(cell_board, (400, 400))
board_matrix = []
for row in range(8):
row_pieces = []
for col in range(8):
cell = board[row*50:(row+1)*50, col*50:(col+1)*50]
gray_cell = cv2.cvtColor(cell, cv2.COLOR_BGR2GRAY)
cell_edge = cv2.Canny(gray_cell[7:43, 7:43], 50, 150)
if np.count_nonzero(cell_edge) < 25:
row_pieces.append('.')
continue
cell_color = np.array(extract_dominant_piece_color(cell))
best_match, max_score = '.', -999.0
for piece_symbol in ALL_PIECES:
res = cv2.matchTemplate(cell_edge, templates_edges[piece_symbol], cv2.TM_CCOEFF_NORMED)
_, shape_score, _, _ = cv2.minMaxLoc(res)
color_dist = np.linalg.norm(cell_color - np.array(color_database[piece_symbol]))
score = shape_score - (color_dist / 100.0)
if score > max_score:
max_score, best_match = score, piece_symbol
row_pieces.append(best_match)
board_matrix.append(row_pieces)
fen_rows = []
for r in board_matrix:
empty, row_str = 0, ""
for p in r:
if p == '.': empty += 1
else:
if empty > 0: row_str += str(empty); empty = 0
row_str += p
if empty > 0: row_str += str(empty)
fen_rows.append(row_str)
return board_matrix, "/".join(fen_rows) + " w - - 0 1"
def main():
global is_active, board_roi, need_selection
if not os.path.exists(TEMPLATES_DIR) or not os.path.exists(COLORS_FILE):
generate_templates_and_colors("start_board.png")
if os.path.exists(CONFIG_FILE):
try:
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
board_roi = json.load(f)
except: board_roi = None
with open(COLORS_FILE, 'r', encoding='utf-8') as f:
color_database = json.load(f)
templates_edges = {}
for sym, (filename, _) in PIECES_CONFIG.items():
t_img = cv2.imread(os.path.join(TEMPLATES_DIR, filename))
gray_t = cv2.cvtColor(t_img, cv2.COLOR_BGR2GRAY)
templates_edges[sym] = cv2.Canny(gray_t[7:43, 7:43], 50, 150)
sct = mss()
keyboard.add_hotkey('f7', toggle_scanner)
keyboard.add_hotkey('f8', request_selection) # Регистрируем безопасный триггер
last_matrix, last_fen = [['.']*8 for _ in range(8)], "---"
while True:
start_time = time.time()
# Обрабатываем выбор области в главном потоке, если нажали F8
if need_selection:
need_selection = False
select_board_region(sct)
if is_active:
if board_roi is None:
clear_console()
print("⚠️ Область доски не задана! Нажмите [F8] для выбора мышкой.")
time.sleep(1)
continue
try:
frame = np.array(sct.grab(board_roi))[:, :, :3]
last_matrix, last_fen = scan_frame(frame, color_database, templates_edges)
except Exception as e:
print(f"Ошибка захвата: {e}")
board_roi = None
print_visual_board(last_matrix, last_fen)
else:
print_visual_board(last_matrix, last_fen)
time.sleep(max(0.01, 0.4 - (time.time() - start_time)))
if __name__ == "__main__":
main()