Загрузка данных
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)