Загрузка данных


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()