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


import time
import cv2
import mss
import numpy as np


def check_cell_has_piece(cell_gray, threshold=30.0):
    """Проверяет наличие фигуры в клетке по контрастности центрального креста."""
    h, w = cell_gray.shape
    if h < 8 or w < 8:
        return False

    mid_y, mid_x = h // 2, w // 2
    margin_x = int(w * 0.15)
    margin_y = int(h * 0.15)

    row_pixels = cell_gray[mid_y, margin_x : w - margin_x]
    col_pixels = cell_gray[margin_y : h - margin_y, mid_x]
    cross_pixels = np.concatenate([row_pixels, col_pixels])

    std_dev = float(np.std(cross_pixels))
    return std_dev > threshold


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
        img = np.array(sct_img)[:, :, :3]
        return img

    def analyze_board_cells(self, cropped_gray, size):
        """Нарезает доску на 64 клетки и определяет заполненность."""
        cell_size = size / 8.0
        board_matrix = []

        for row in range(8):
            row_status = []
            for col in range(8):
                x1 = int(round(col * cell_size))
                y1 = int(round(row * cell_size))
                x2 = int(round((col + 1) * cell_size))
                y2 = int(round((row + 1) * cell_size))

                cell_crop = cropped_gray[y1:y2, x1:x2]
                has_piece = check_cell_has_piece(cell_crop, threshold=45.0)
                row_status.append(has_piece)

            board_matrix.append(row_status)

        return board_matrix

    def get_board(self, passive_interval=1.5):
        """Возвращает (board_matrix, cropped_board).
        
        - Если доска известна: проверяет её раз в passive_interval секунд.
        - Если доска потеряна: переходит в режим полного сканирования экрана.
        """
        now = time.time()

        # --- РЕЖИМ 1: Пассивное отслеживание (доска уже была найдена) ---
        if self.last_board_rect is not None:
            # Если прошло меньше времени, чем интервал проверки — не тратим ресурсы
            if now - self.last_check_time < passive_interval:
                # Берем снимки строго по известным координатам
                bx, by, size = self.last_board_rect
                roi = {
                    "top": self.monitor["top"] + by,
                    "left": self.monitor["left"] + bx,
                    "width": size,
                    "height": size,
                }
                cropped_board = self.capture_screen(roi)
                cropped_gray = cv2.cvtColor(cropped_board, cv2.COLOR_BGR2GRAY)

                board_matrix = self.analyze_board_cells(cropped_gray, size)
                return board_matrix, cropped_board

            # Настало время полной проверки (убедиться, что доска всё ещё там)
            self.last_check_time = now
            bx, by, size = self.last_board_rect
            roi = {
                "top": self.monitor["top"] + by,
                "left": self.monitor["left"] + bx,
                "width": size,
                "height": size,
            }
            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:
                board_matrix = self.analyze_board_cells(cropped_gray, size)
                return board_matrix, 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
            cropped_board = full_img[by : by + size, bx : bx + size]
            cropped_gray = gray[by : by + size, bx : bx + size]

            board_matrix = self.analyze_board_cells(cropped_gray, size)
            return board_matrix, cropped_board

        # Если ничего не нашли
        return None, None


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


def find_chessboard_strictly_1to1(input_path=None):
    """Функция-обертка для сохранения совместимости с logic.py."""
    return _tracker.get_board(passive_interval=1.5)