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


import time
import cv2
import mss
import numpy as np


def verify_chessboard_grid(gray_crop):
    """Проверяет наличие линий сетки 8x8 внутри области."""
    h, w = gray_crop.shape
    if h < 64:
        return False, 0

    edges = cv2.Canny(gray_crop, 40, 120)
    col_sums = np.sum(edges, axis=0)
    row_sums = np.sum(edges, axis=1)

    cell_size = w / 8.0
    score_x = 0
    score_y = 0

    for i in range(1, 8):
        idx = int(round(i * cell_size))
        x_min, x_max = max(0, idx - 2), min(w, idx + 3)
        y_min, y_max = max(0, idx - 2), min(h, idx + 3)

        score_x += np.max(col_sums[x_min:x_max]) if x_max > x_min else 0
        score_y += np.max(row_sums[y_min:y_max]) if y_max > y_min else 0

    total_score = score_x + score_y
    return True, total_score


class BoardTracker:
    def __init__(self):
        self.sct = mss.mss()
        # Главный монитор
        self.monitor = self.sct.monitors[1]
        
        # Сохраняем координаты найденной доски: (x, y, size)
        self.last_board_rect = None
        self.last_check_time = 0.0

    def capture_screen(self, region=None):
        """Делает скриншот всего экрана или указанной области."""
        target_region = region if region else self.monitor
        sct_img = self.sct.grab(target_region)
        # Преобразуем из BGRA в BGR
        return np.array(sct_img)[:, :, :3]

    def get_board_image(self, passive_interval=1.5):
        """
        Возвращает только BGR-изображение вырезанной доски (cropped_board) или None.
        Идеально для передачи напрямую в ChessAnalyzer.
        """
        now = time.time()

        # --- РЕЖИМ 1: Пассивное отслеживание (доска уже найдена) ---
        if self.last_board_rect is not None:
            bx, by, size = self.last_board_rect
            roi = {
                "top": self.monitor["top"] + by,
                "left": self.monitor["left"] + bx,
                "width": size,
                "height": size,
            }

            # Быстрый захват кадра по известным координатам
            if now - self.last_check_time < passive_interval:
                return self.capture_screen(roi)

            # Проверка, что доска всё ещё на месте
            self.last_check_time = now
            cropped_board = self.capture_screen(roi)
            cropped_gray = cv2.cvtColor(cropped_board, cv2.COLOR_BGR2GRAY)

            is_grid, score = verify_chessboard_grid(cropped_gray)
            if is_grid and score > 0:
                return cropped_board

            # Если доска пропала / закрыта — сбрасываем координаты
            self.last_board_rect = None

        # --- РЕЖИМ 2: Полное сканирование экрана ---
        full_img = self.capture_screen()
        gray = cv2.cvtColor(full_img, cv2.COLOR_BGR2GRAY)

        edges = cv2.Canny(gray, 30, 150)
        contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

        best_board = None
        max_score = -1

        for cnt in contours:
            x, y, w, h = cv2.boundingRect(cnt)

            if w < 80 or h < 80:
                continue

            aspect_ratio = float(w) / h
            if not (0.98 <= aspect_ratio <= 1.02):
                continue

            size = min(w, h)
            crop = gray[y : y + size, x : x + size]

            is_grid, score = verify_chessboard_grid(crop)

            if is_grid and score > max_score:
                max_score = score
                best_board = (x, y, size)

        if best_board is not None:
            self.last_board_rect = best_board
            self.last_check_time = now

            bx, by, size = best_board
            return full_img[by : by + size, bx : bx + size]

        return None


# Глобальный экземпляр трекера
_tracker = BoardTracker()


def get_board_image():
    """Главная функция для вызова из logic.py"""
    return _tracker.get_board_image()


def find_chessboard_strictly_1to1(input_path=None):
    """Обертка для обратной совместимости: возвращает (None, cropped_board)"""
    board_img = _tracker.get_board_image()
    return None, board_img