import cv2
import numpy as np
def extract_dominant_piece_color(cell_bgr):
"""Извлекает медианный BGR-цвет центральной части клетки."""
h, w, _ = cell_bgr.shape
center = cell_bgr[int(h * 0.25):int(h * 0.75), int(w * 0.25):int(w * 0.75)]
return np.median(center, axis=(0, 1)).tolist()
def print_board(board_matrix, fen):
"""Визуальный вывод доски в консоль в виде таблицы."""
divider = " +" + "---+" * 8
print("\n a b c d e f g h")
print(divider)
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(divider)
print(" a b c d e f g h")
print(f"\nFEN: {fen}\n")
def scan_frame(cell_board, color_database, templates_edges, ALL_PIECES, empty_threshold=10):
"""
Сканирует изображение доски.
:param cell_board: Изображение доски (BGR)
:param color_database: База BGR-цветов фигур
:param templates_edges: База Canny-шаблонов фигур (36x36)
:param ALL_PIECES: Список всех символов фигур
:param empty_threshold: Порог пикселей Canny (чем ниже, тем чувствительнее к фигурам)
"""
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)
# Обрезка граней клетки (окно 36x36)
cell_edge = cv2.Canny(gray_cell[7:43, 7:43], 30, 100)
# 1. Проверка на пустую клетку (снизили порог до empty_threshold)
if np.count_nonzero(cell_edge) < empty_threshold:
row_pieces.append('.')
continue
cell_color = np.array(extract_dominant_piece_color(cell))
best_match, max_score = '.', -999.0
# 2. Поиск наилучшей фигуры
for piece_symbol in ALL_PIECES:
if piece_symbol not in templates_edges or piece_symbol not in color_database:
continue
# Сравнение формы Canny
res = cv2.matchTemplate(cell_edge, templates_edges[piece_symbol], cv2.TM_CCOEFF_NORMED)
_, shape_score, _, _ = cv2.minMaxLoc(res)
# Штраф за цвет
tpl_color = np.array(color_database[piece_symbol])
color_dist = np.linalg.norm(cell_color - tpl_color)
score = shape_score - (color_dist / 100.0)
if score > max_score:
max_score = score
best_match = piece_symbol
row_pieces.append(best_match)
board_matrix.append(row_pieces)
# 3. Генерация FEN
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)
fen_string = "/".join(fen_rows) + " w - - 0 1"
return board_matrix, fen_string