Загрузка данных
import os
import cv2
import numpy as np
def prepare_features(gray_img):
"""
Улучшает контраст фигуры (CLAHE) и извлекает её контуры (Canny).
Возвращает сбалансированную комбинацию для точного сравнения форм.
"""
# 1. Адаптивное повышение контраста (проявляет детали на темном фоне)
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
enhanced = clahe.apply(gray_img)
# 2. Выделение граней (Canny edges)
edges = cv2.Canny(enhanced, 30, 120)
# 3. Объединяем текстуру и контуры в одно изображение
combined = cv2.addWeighted(enhanced, 0.6, edges, 0.4, 0)
return combined
def load_templates(templates_folder="extracted_pieces"):
"""Загрузка и подготовка шаблонов"""
symbol_map = {
"white_K": "K", "white_Q": "Q", "white_R": "R", "white_B": "B", "white_N": "N", "white_P": "P",
"black_k": "k", "black_q": "q", "black_r": "r", "black_b": "b", "black_n": "n", "black_p": "p",
}
templates = {"white": {}, "black": {}}
if not os.path.exists(templates_folder):
print(f"[!] Ошибка: Папка '{templates_folder}' не найдена!")
return templates
for filename in os.listdir(templates_folder):
if filename.endswith(".png"):
name_without_ext = os.path.splitext(filename)[0]
symbol = symbol_map.get(name_without_ext)
if not symbol:
continue
file_path = os.path.join(templates_folder, filename)
tpl_img = cv2.imread(file_path, cv2.IMREAD_GRAYSCALE)
if tpl_img is not None:
# Обрабатываем шаблон через CLAHE + Canny
processed_tpl = prepare_features(tpl_img)
color_group = "white" if name_without_ext.startswith("white") else "black"
templates[color_group][symbol] = processed_tpl
return templates
def analyze_cell(cell_gray):
"""Определяет статус клетки: empty, white или black."""
h, w = cell_gray.shape
center = cell_gray[int(h*0.2):int(h*0.8), int(w*0.2):int(w*0.8)]
std_dev = np.std(center)
# Если контраст совсем низкий — клетка пустая
if std_dev < 11.0:
return "empty"
median_val = np.median(center)
# Порог определения цвета
if median_val > 115:
return "white"
else:
return "black"
def generate_fen(board_matrix):
"""Генерация строки FEN"""
fen_rows = []
for row in board_matrix:
empty_count = 0
row_str = ""
for cell in row:
if cell == ".":
empty_count += 1
else:
if empty_count > 0:
row_str += str(empty_count)
empty_count = 0
row_str += cell
if empty_count > 0:
row_str += str(empty_count)
fen_rows.append(row_str)
return "/".join(fen_rows) + " w - - 0 1"
def scan_board(
board_path="board.png",
templates_folder="extracted_pieces",
inner_margin_pct=0.04
):
templates = load_templates(templates_folder)
if not templates["white"] and not templates["black"]:
print("[!] Ошибка: Нет шаблонов для сравнения.")
return
board_img = cv2.imread(board_path, cv2.IMREAD_GRAYSCALE)
if board_img is None:
print(f"[!] Ошибка: Не удалось открыть файл '{board_path}'.")
return
img_h, img_w = board_img.shape
cell_w = img_w / 8.0
cell_h = img_h / 8.0
board_matrix = []
print("Сканирование деталей и контуров фигур...\n")
for row in range(8):
row_symbols = []
for col in range(8):
# Точный вырез клетки
x1 = int(round(col * cell_w + cell_w * inner_margin_pct))
y1 = int(round(row * cell_h + cell_h * inner_margin_pct))
x2 = int(round((col + 1) * cell_w - cell_w * inner_margin_pct))
y2 = int(round((row + 1) * cell_h - cell_h * inner_margin_pct))
cell_crop = board_img[y1:y2, x1:x2]
# 1. Проверяем, есть ли фигура и какой у неё цвет
cell_status = analyze_cell(cell_crop)
if cell_status == "empty":
row_symbols.append(".")
else:
# 2. Выделяем детальные характеристики клетки
cell_features = prepare_features(cell_crop)
best_match_symbol = "."
best_score = -1.0
# 3. Сравниваем контуры с шаблонами соответствующего цвета
target_templates = templates[cell_status]
for symbol, tpl_features in target_templates.items():
tpl_h, tpl_w = tpl_features.shape
cell_resized = cv2.resize(cell_features, (tpl_w, tpl_h))
res = cv2.matchTemplate(cell_resized, tpl_features, cv2.TM_CCOEFF_NORMED)
_, max_val, _, _ = cv2.minMaxLoc(res)
if max_val > best_score:
best_score = max_val
best_match_symbol = symbol
row_symbols.append(best_match_symbol)
board_matrix.append(row_symbols)
# Отрисовка в консоли
divider = " +" + "---+" * 8
print(divider)
for i, row in enumerate(board_matrix):
rank_label = 8 - i
row_content = " | ".join(row)
print(f"{rank_label} | {row_content} |")
print(divider)
print(" a b c d e f g h\n")
# Вывод FEN
fen_string = generate_fen(board_matrix)
print(f"FEN: {fen_string}\n")
if __name__ == "__main__":
try:
scan_board()
except Exception as e:
print(f"\n[!] Ошибка: {e}")
input("\nНажмите Enter, чтобы закрыть программу...")