Загрузка данных
import cv2
import numpy as np
import onnxruntime as ort
CLASS_NAMES = [
"board", # 0
"white_king", # 1
"white_queen", # 2
"white_rook", # 3
"white_bishop", # 4
"white_knight", # 5
"white_pawn", # 6
"black_king", # 7
"black_queen", # 8
"black_rook", # 9
"black_bishop", # 10
"black_knight", # 11
"black_pawn" # 12
]
# Маппинг названий в стандартные символы FEN
FEN_MAP = {
"white_king": "K",
"white_queen": "Q",
"white_rook": "R",
"white_bishop": "B",
"white_knight": "N",
"white_pawn": "P",
"black_king": "k",
"black_queen": "q",
"black_rook": "r",
"black_bishop": "b",
"black_knight": "n",
"black_pawn": "p"
}
class ChessAnalyzer:
def __init__(self, model_path="best.onnx", conf_threshold=0.35):
self.conf_threshold = conf_threshold
self.session = ort.InferenceSession(model_path, providers=['CPUExecutionProvider'])
self.input_name = self.session.get_inputs()[0].name
def preprocess(self, img):
h, w = img.shape[:2]
resized = cv2.resize(img, (640, 640), interpolation=cv2.INTER_AREA)
rgb = cv2.cvtColor(resized, cv2.COLOR_BGR2RGB)
tensor = rgb.transpose(2, 0, 1).astype(np.float32) / 255.0
tensor = np.expand_dims(tensor, axis=0)
return tensor, w, h
def analyze(self, image_input):
if isinstance(image_input, str):
img = cv2.imread(image_input)
else:
img = image_input
if img is None:
print("Ошибка: Не удалось загрузить изображение.")
return []
tensor, orig_w, orig_h = self.preprocess(img)
outputs = self.session.run(None, {self.input_name: tensor})[0]
predictions = np.squeeze(outputs).T
boxes, confidences, class_ids = [], [], []
x_factor = orig_w / 640.0
y_factor = orig_h / 640.0
for pred in predictions:
scores = pred[4:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence >= self.conf_threshold:
cx, cy, w, h = pred[0], pred[1], pred[2], pred[3]
x1 = int((cx - w / 2) * x_factor)
y1 = int((cy - h / 2) * y_factor)
x2 = int((cx + w / 2) * x_factor)
y2 = int((cy + h / 2) * y_factor)
boxes.append([x1, y1, x2 - x1, y2 - y1])
confidences.append(float(confidence))
class_ids.append(class_id)
indices = cv2.dnn.NMSBoxes(boxes, confidences, self.conf_threshold, 0.45)
results = []
if len(indices) > 0:
for i in indices.flatten():
x, y, w, h = boxes[i]
cid = class_ids[i]
name = CLASS_NAMES[cid] if cid < len(CLASS_NAMES) else f"class_{cid}"
if name != "board": # Игнорируем класс самой доски
results.append({
"name": name,
"box": [x, y, x + w, y + h],
"confidence": confidences[i]
})
# Генерируем FEN и записываем в human.txt
fen = self.export_to_fen(results, orig_w, orig_h)
self.save_human_txt(fen)
return results
def export_to_fen(self, detections, img_w, img_h):
"""Преобразует список детектированных фигур в FEN-строку"""
grid = [[None for _ in range(8)] for _ in range(8)]
cell_w = img_w / 8.0
cell_h = img_h / 8.0
for det in detections:
x1, y1, x2, y2 = det["box"]
# Центр фигуры по горизонтали, ближе к основанию по вертикали
cx = (x1 + x2) / 2.0
cy = y2 - (y2 - y1) * 0.2 # 80% высоты рамки (ближе к нижней части фигуры)
col = int(cx // cell_w)
row = int(cy // cell_h)
# Ограничиваем индексы от 0 до 7
col = max(0, min(7, col))
row = max(0, min(7, row))
piece_char = FEN_MAP.get(det["name"])
if piece_char:
# Если клетка уже занята, сохраняем фигуру с большей уверенностью
grid[row][col] = piece_char
# Сборка FEN
fen_rows = []
for row in grid:
empty_count = 0
row_str = ""
for cell in row:
if cell is None:
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)
def save_human_txt(self, fen_string, filepath="human.txt"):
"""Сохраняет FEN в файл в формате [FEN]"""
content = f"[{fen_string}]"
with open(filepath, "w", encoding="utf-8") as f:
f.write(content)
print(f"Сохранено в {filepath}: {content}")
# === Запуск ===
if __name__ == "__main__":
analyzer = ChessAnalyzer("best.onnx")
# Передаем вырезанную доску от crop.py
analyzer.analyze("chess_board.jpg")