File size: 5,636 Bytes
c871a64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import chess.svg
import chess
import re
import cv2
import random as rd
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.models import load_model
import chess, chess.svg
from pathlib import Path
import webbrowser
piece_symbols = 'prbnkqPRBNKQ-'
try:
    model = load_model('./chess_model.h5')
    print("Model loaded successfully")
except Exception as e:
    print(f"Error loading model: {e}")
    
def onehot_from_fen(fen):
    eye = np.eye(13)
    output = np.empty((0, 13))
    fen = re.sub('[-]', '', fen)

    for char in fen:
        if(char in '12345678'):
            output = np.append(output, np.tile(eye[12], (int(char), 1)), axis=0)
        else:
            idx = piece_symbols.index(char)
            output = np.append(output, eye[idx].reshape((1, 13)), axis=0)
    return output
import numpy as np
import re



def board_from_fen(fen):
    board = []
    fen = fen.split()[0]   # just the board part (ignore turn, castling, etc.)
    
    for row in fen.split('/'):
        row_out = []
        for char in row:
            if char.isdigit():
                # expand empty squares
                row_out.extend([0] * int(char))
            else:
                idx = piece_symbols.index(char) + 1  # +1 so 0 = empty
                row_out.append(idx)
        board.append(row_out)

    return np.array(board, dtype=np.int8)  # shape (8,8)

def fen_from_onehot(one_hot):
    output = ''
    for j in range(8):
        for i in range(8):
            if(one_hot[j][i] == 12):
                output += ' '
            else:
                output += piece_symbols[one_hot[j][i]]
        if(j != 7):
            output += '-'

    for i in range(8, 0, -1):
        output = output.replace(' ' * i, str(i))
    return output


def order_points(pts):
    # Order 4 points: top-left, top-right, bottom-right, bottom-left
    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)]
    return rect

def preprocess_chessboard(image_path, target_size=(30, 30)):
    img = cv2.imread(image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (5,5), 0)
    edges = cv2.Canny(blur, 50, 150)

    # Find contours
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    cropped_board = None
    warped = None
    squares = []

    if contours:
        cnt = max(contours, key=cv2.contourArea)
        epsilon = 0.02 * cv2.arcLength(cnt, True)
        approx = cv2.approxPolyDP(cnt, epsilon, True)

        # If we found 4 corners, warp
        if len(approx) == 4:
            pts = approx.reshape(4,2)
            rect = order_points(pts)
            board_size = 256  # fixed square board
            dst = np.array([
                [0, 0],
                [board_size-1, 0],
                [board_size-1, board_size-1],
                [0, board_size-1]
            ], dtype="float32")

            M = cv2.getPerspectiveTransform(rect, dst)
            warped = cv2.warpPerspective(img, M, (board_size, board_size))
            cropped_board = warped
        else:
            # fallback: just resize whole image
            warped = cv2.resize(img, (256, 256))
            cropped_board = warped
    else:
        # no contours, fallback
        warped = cv2.resize(img, (256, 256))
        cropped_board = warped

    # Split into 64 squares
    board_size = warped.shape[0]
    sq_size = board_size // 8
    for row in range(8):
        for col in range(8):
            square = warped[row*sq_size:(row+1)*sq_size,
                            col*sq_size:(col+1)*sq_size]
            square = cv2.resize(square, target_size)
            square = square / 255.0
            squares.append(square)

    return np.array(squares), cropped_board

def display_with_predicted_fen(image):
    squares,_ = preprocess_chessboard(image)
    pred = model.predict(squares).argmax(axis=1).reshape(8, 8)
    fen = fen_from_onehot(pred)
    return fen

def get_board_from_image(img_path):
    image = cv2.imread(img_path)
    if image is None:
        print('sorry image is null')
        return
    ext = img_path.split('.')[-1]
    if not (ext=='jpg' or ext == 'jpeg' or ext == 'png'):
        print('sorry image needs to be in jpg/jpeg/png format')
        return
    predicted_fen = display_with_predicted_fen(img_path)
    # Create board from predicted FEN
    board = chess.Board(predicted_fen.replace('-','/'))
    return predicted_fen
def get_board_from_fen(fen:str):
    print(type(fen))
    fen=fen.split('-')
    if len(fen)!=8:
        return [[]]
    answers = [[] for _ in range(8)]
    map={'p':'bp','r':'bR','n':'bN','k':'bK','q':'bQ','b':'bB','P':'wp','R':'wR','N':'wN','K':'wK','Q':'wQ','B':'wB'}
    for i,f in enumerate(fen):
        for char in f:
            
            if char in '12345678':
                answers[i].extend(['--']*(int(char)))
            elif char in piece_symbols:
                answers[i].append(map[char])
    return answers
                
    
def get_board(image_path):
    fen_notation = get_board_from_image(image_path)
    if fen_notation:
        board = get_board_from_fen(fen_notation)
        
    else:
        return [[]],""
    return board,fen_notation.replace('-','/')
    
if __name__ == "__main__":
    get_board()