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


import cv2
import numpy as np
import os

TEMPLATES_DIR = "templates"

PIECES_CONFIG = {
    'r': ('black_r.png', (0, 0)),
    'n': ('black_n.png', (0, 1)),
    'b': ('black_b.png', (0, 2)),
    'q': ('black_q.png', (0, 3)),
    'k': ('black_k.png', (0, 4)),
    'p': ('black_p.png', (1, 0)),
    'R': ('white_R.png', (7, 0)),
    'N': ('white_N.png', (7, 1)),
    'B': ('white_B.png', (7, 2)),
    'Q': ('white_Q.png', (7, 3)),
    'K': ('white_K.png', (7, 4)),
    'P': ('white_P.png', (6, 0))
}


def get_edges(img):
    """Преобразует изображение клетки в контур, полностью удаляя цвет фона"""
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Срезаем 4 пикселя по краям, чтобы убрать рамки и подсветку доски
    h, w = gray.shape
    crop = gray[4:h-4, 4:w-4]
    edges = cv2.Canny(crop, 50, 150)
    return edges


def crop_board(image_path):
    img = cv2.imread(image_path)
    if img is None:
        raise FileNotFoundError(f"Не удалось открыть файл: {image_path}")

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (5, 5), 0)
    edges = cv2.Canny(blur, 50, 150)

    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    board_cnt = None
    max_area = 0

    for cnt in contours:
        area = cv2.contourArea(cnt)
        if area > 10000:
            peri = cv2.arcLength(cnt, True)
            approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)
            if len(approx) == 4 and area > max_area:
                board_cnt = approx
                max_area = area

    if board_cnt is not None:
        pts = board_cnt.reshape(4, 2)
        rect = np.zeros((4, 2), dtype="float32")
        s = pts.sum(axis=1)
        rect[0] = pts[np.argmin(s)]
        rect[2] = pts[np.argmax(s)]
        diff = np.diff(pts, axis=1)
        rect[1] = pts[np.argmin(diff)]
        rect[3] = pts[np.argmax(diff)]
        dst = np.array([[0, 0], [399, 0], [399, 399], [0, 399]], dtype="float32")
        M = cv2.getPerspectiveTransform(rect, dst)
        return cv2.warpPerspective(img, M, (400, 400))
    else:
        return cv2.resize(img, (400, 400))


def generate_templates_from_start_position(start_image_path="start_board.png"):
    os.makedirs(TEMPLATES_DIR, exist_ok=True)
    board = crop_board(start_image_path)
    cell_size = 50

    for piece_symbol, (filename, (row, col)) in PIECES_CONFIG.items():
        y1, y2 = row * cell_size, (row + 1) * cell_size
        x1, x2 = col * cell_size, (col + 1) * cell_size
        cell_img = board[y1:y2, x1:x2]
        
        save_path = os.path.join(TEMPLATES_DIR, filename)
        cv2.imwrite(save_path, cell_img)
    
    print(f"✓ Шаблоны обновлены под контурный поиск!")


def scan_board_with_templates(target_image_path="board.png"):
    if not os.path.exists(TEMPLATES_DIR) or len(os.listdir(TEMPLATES_DIR)) < 12:
        generate_templates_from_start_position("start_board.png")

    # Преобразуем все шаблоны в контуры
    templates_edges = {}
    for piece_symbol, (filename, _) in PIECES_CONFIG.items():
        t_path = os.path.join(TEMPLATES_DIR, filename)
        t_img = cv2.imread(t_path)
        templates_edges[piece_symbol] = get_edges(t_img)

    board = crop_board(target_image_path)
    cell_size = 50
    board_matrix = []

    for row in range(8):
        row_pieces = []
        for col in range(8):
            y1, y2 = row * cell_size, (row + 1) * cell_size
            x1, x2 = col * cell_size, (col + 1) * cell_size
            cell = board[y1:y2, x1:x2]

            cell_edge = get_edges(cell)

            # Если на клетке нет рисунка фигуры (контуров очень мало) — клетка точно пустая
            if np.count_nonzero(cell_edge) < 35:
                row_pieces.append('.')
                continue

            best_match = '.'
            max_score = -1.0

            # Ищем силуэт с максимальным совпадением
            for piece_symbol, t_edge in templates_edges.items():
                res = cv2.matchTemplate(cell_edge, t_edge, cv2.TM_CCOEFF_NORMED)
                _, max_val, _, _ = cv2.minMaxLoc(res)

                if max_val > max_score:
                    max_score = max_val
                    best_match = piece_symbol

            row_pieces.append(best_match)

        board_matrix.append(row_pieces)

    # Генерация FEN
    fen_rows = []
    for r in board_matrix:
        empty_count = 0
        row_str = ""
        for p in r:
            if p == '.':
                empty_count += 1
            else:
                if empty_count > 0:
                    row_str += str(empty_count)
                    empty_count = 0
                row_str += p
        if empty_count > 0:
            row_str += str(empty_count)
        fen_rows.append(row_str)

    final_fen = "/".join(fen_rows) + " w - - 0 1"
    
    print("\n===========================================")
    print("УСПЕХ! Точный FEN позиции:")
    print(final_fen)
    print("===========================================")


if __name__ == "__main__":
    try:
        scan_board_with_templates("board.png")
    except Exception as e:
        print("Ошибка:", e)
    
    input("\nНажми Enter, чтобы закрыть...")