Now we works and we still have type hints so thats nice
This commit is contained in:
parent
0f702640a3
commit
417538d768
|
@ -1,6 +1,6 @@
|
||||||
## Test Adapter:
|
## Test Adapter:
|
||||||
```bash
|
```bash
|
||||||
base64 --wrap=0 < whole_boards/board_118_1554110522.620303_.png| python3 adapter.py
|
base64 --wrap=0 < whole_boards/boards_for_empty/board_1554285167.655788_rank_5.png | python3 adapter.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build OpenCV
|
## Build OpenCV
|
||||||
|
|
14
adapter.py
14
adapter.py
|
@ -5,21 +5,29 @@ import sys
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from runner import find_homography
|
from main import find_occupied_squares
|
||||||
|
from runner import find_homography, warp_board
|
||||||
# Load base64 encoded image from stdin
|
# Load base64 encoded image from stdin
|
||||||
|
from tensor_classifier import predict_board
|
||||||
|
|
||||||
stdin = sys.stdin.readline()
|
stdin = sys.stdin.readline()
|
||||||
stdin_decoded = base64.b64decode(stdin)
|
stdin_decoded = base64.b64decode(stdin)
|
||||||
img_array = np.frombuffer(stdin_decoded, dtype=np.uint8)
|
img_array = np.frombuffer(stdin_decoded, dtype=np.uint8)
|
||||||
camera_img = cv2.imdecode(img_array, flags=cv2.COLOR_BGR2RGB)
|
camera_img = cv2.imdecode(img_array, flags=cv2.COLOR_BGR2RGB)
|
||||||
camera_img = cv2.cvtColor(camera_img, cv2.COLOR_BGR2RGB)
|
camera_img = cv2.cvtColor(camera_img, cv2.COLOR_BGR2RGB)
|
||||||
|
|
||||||
# Find keypoints in image and pass them back to unity
|
|
||||||
|
# def do_everything:
|
||||||
homography = find_homography(camera_img)
|
homography = find_homography(camera_img)
|
||||||
|
warped_board = warp_board(camera_img, homography)
|
||||||
|
occupied_squares = find_occupied_squares(warped_board)
|
||||||
|
board = predict_board(occupied_squares)
|
||||||
|
|
||||||
|
|
||||||
# Finally, output to stdout for unity to read
|
# Finally, output to stdout for unity to read
|
||||||
result = {
|
result = {
|
||||||
"homography": homography.tolist(),
|
"homography": homography.tolist(),
|
||||||
|
"board": board.to_array,
|
||||||
}
|
}
|
||||||
|
|
||||||
print(json.dumps(result))
|
print(json.dumps(result))
|
||||||
|
|
Binary file not shown.
Binary file not shown.
13
main.py
13
main.py
|
@ -102,6 +102,7 @@ def predict_empty(square: np.ndarray, position: POSITION) -> PIECE:
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def remove_most_empties(warped):
|
def remove_most_empties(warped):
|
||||||
empty = 0
|
empty = 0
|
||||||
non_empties = []
|
non_empties = []
|
||||||
|
@ -123,12 +124,6 @@ def remove_most_empties(warped):
|
||||||
|
|
||||||
y, x = np.where(segment == i)
|
y, x = np.where(segment == i)
|
||||||
|
|
||||||
|
|
||||||
if position == POSITION.E8:
|
|
||||||
print(np.max(segment))
|
|
||||||
print(len(y))
|
|
||||||
|
|
||||||
|
|
||||||
pls.append(len(y))
|
pls.append(len(y))
|
||||||
top, bottom, left, right = min(y), max(y), min(x), max(x)
|
top, bottom, left, right = min(y), max(y), min(x), max(x)
|
||||||
|
|
||||||
|
@ -146,13 +141,13 @@ def remove_most_empties(warped):
|
||||||
return non_empties
|
return non_empties
|
||||||
|
|
||||||
|
|
||||||
def find_occupied_squares(warped: np.ndarray) -> List[Tuple[POSITION, np.ndarray]]:
|
def find_occupied_squares(warped: np.ndarray) -> Squares:
|
||||||
non_empties = remove_most_empties(warped)
|
non_empties = remove_most_empties(warped)
|
||||||
|
|
||||||
completely_non_empties = []
|
completely_non_empties = {}
|
||||||
for position, square in non_empties:
|
for position, square in non_empties:
|
||||||
if predict_empty(square, position) != PIECE.EMPTY:
|
if predict_empty(square, position) != PIECE.EMPTY:
|
||||||
completely_non_empties.append((position, square))
|
completely_non_empties[position] = square
|
||||||
|
|
||||||
return completely_non_empties
|
return completely_non_empties
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,11 @@ cap = cv2.VideoCapture(0)
|
||||||
color = COLOR.BLACK
|
color = COLOR.BLACK
|
||||||
rank = RANK.EIGHT
|
rank = RANK.EIGHT
|
||||||
pieces = {
|
pieces = {
|
||||||
PIECE.rook: [POSITION(FILE.A, rank), POSITION(FILE.F, rank)],
|
PIECE.rook: [POSITION((FILE.A, rank)), POSITION((FILE.F, rank))],
|
||||||
PIECE.knight: [POSITION(FILE.E, rank), POSITION(FILE.H, rank)],
|
PIECE.knight: [POSITION((FILE.E, rank)), POSITION((FILE.H, rank))],
|
||||||
PIECE.bishop: [POSITION(FILE.C, rank), POSITION(FILE.D, rank)],
|
PIECE.bishop: [POSITION((FILE.C, rank)), POSITION((FILE.D, rank))],
|
||||||
PIECE.queen: [POSITION(FILE.B, rank)],
|
PIECE.queen: [POSITION((FILE.B, rank))],
|
||||||
PIECE.king: [POSITION(FILE.G, rank)],
|
PIECE.king: [POSITION((FILE.G, rank))],
|
||||||
}
|
}
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
|
@ -89,6 +89,7 @@ def train_empty_or_piece_hist() -> None:
|
||||||
X = []
|
X = []
|
||||||
Y = []
|
Y = []
|
||||||
for piece in OUR_PIECES + (PIECE.EMPTY,):
|
for piece in OUR_PIECES + (PIECE.EMPTY,):
|
||||||
|
print(f"training training_images/{piece}/{square_color}_square/*.png")
|
||||||
for filename in glob.glob(f"training_images/{piece}/{square_color}_square/*.png"):
|
for filename in glob.glob(f"training_images/{piece}/{square_color}_square/*.png"):
|
||||||
img = cv2.imread(filename)
|
img = cv2.imread(filename)
|
||||||
y, x = np.histogram(img.ravel(), bins=32, range=[0, 256])
|
y, x = np.histogram(img.ravel(), bins=32, range=[0, 256])
|
||||||
|
@ -209,7 +210,8 @@ def find_homography(camera_image: np.ndarray,
|
||||||
|
|
||||||
def warp_board(camera_image: np.ndarray, homography: np.ndarray = None, debug=False) -> np.ndarray:
|
def warp_board(camera_image: np.ndarray, homography: np.ndarray = None, debug=False) -> np.ndarray:
|
||||||
baseline = cv2.imread(str(here.joinpath("new_baseline_board.png")))
|
baseline = cv2.imread(str(here.joinpath("new_baseline_board.png")))
|
||||||
homography = homography or find_homography(camera_image, baseline, debug=debug)
|
if homography is None:
|
||||||
|
homography = find_homography(camera_image, baseline, debug=debug)
|
||||||
|
|
||||||
height, width, channels = baseline.shape
|
height, width, channels = baseline.shape
|
||||||
return cv2.warpPerspective(camera_image, homography, (width, height))
|
return cv2.warpPerspective(camera_image, homography, (width, height))
|
||||||
|
@ -289,4 +291,5 @@ def train_nn():
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
train_nn()
|
#train_nn()
|
||||||
|
train_empty_or_piece_hist()
|
||||||
|
|
|
@ -1,71 +1,27 @@
|
||||||
#from __future__ import absolute_import, division, print_function, unicode_literals
|
|
||||||
|
|
||||||
import tensorflow as tf
|
|
||||||
from tensorflow.python.keras import datasets, layers, models
|
|
||||||
import glob
|
|
||||||
import numpy as np
|
|
||||||
import cv2
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
from tensorflow.python.keras import models
|
||||||
|
|
||||||
import runner
|
from util import PIECE, Squares, Board
|
||||||
from main import find_occupied_squares
|
|
||||||
from util import POSITION
|
|
||||||
|
|
||||||
new_model = models.load_model('chess_model_3_pieces.h5')
|
new_model = models.load_model('chess_model_3_pieces.h5')
|
||||||
new_model.summary()
|
#new_model.summary()
|
||||||
|
|
||||||
#board = cv2.imread("whole_boards/boards_for_empty/board_1554286488.605142_rank_3.png")
|
#board = cv2.imread("whole_boards/boards_for_empty/board_1554286488.605142_rank_3.png")
|
||||||
#board = cv2.imread("whole_boards/boards_for_empty/board_1554285167.655788_rank_5.png")
|
#board = cv2.imread("whole_boards/boards_for_empty/board_1554285167.655788_rank_5.png")
|
||||||
board = cv2.imread("whole_boards/boards_for_empty/board_1554288891.129901_rank_8.png")
|
board = cv2.imread("whole_boards/boards_for_empty/board_1554288891.129901_rank_8.png")
|
||||||
|
|
||||||
|
|
||||||
warped = runner.warp_board(board)
|
def predict_board(occupied_squares: Squares) -> Board:
|
||||||
|
board = Board()
|
||||||
occupied = find_occupied_squares(warped)
|
for pos, square in occupied_squares.items():
|
||||||
|
square = cv2.cvtColor(square, cv2.COLOR_BGR2GRAY)
|
||||||
pos_1 = POSITION.C5
|
width, height = square.shape
|
||||||
pos_2 = POSITION.D5
|
square = square / 255.0
|
||||||
pos_3 = POSITION.G5
|
test = new_model.predict(np.array(square).reshape((-1, width, height, 1)))
|
||||||
pos_4 = POSITION.H5
|
|
||||||
|
|
||||||
square_1 = runner.get_square(warped, pos_1)
|
|
||||||
square_2 = runner.get_square(warped, pos_2)
|
|
||||||
square_3 = runner.get_square(warped, pos_3)
|
|
||||||
square_4 = runner.get_square(warped, pos_4)
|
|
||||||
|
|
||||||
square_1 = cv2.cvtColor(square_1, cv2.COLOR_BGR2GRAY)
|
|
||||||
square_2 = cv2.cvtColor(square_2, cv2.COLOR_BGR2GRAY)
|
|
||||||
square_3 = cv2.cvtColor(square_3, cv2.COLOR_BGR2GRAY)
|
|
||||||
square_4 = cv2.cvtColor(square_4, cv2.COLOR_BGR2GRAY)
|
|
||||||
|
|
||||||
width, height = square_1.shape
|
|
||||||
square_1 = square_1 / 255.0
|
|
||||||
square_2 = square_2 / 255.0
|
|
||||||
square_3 = square_3 / 255.0
|
|
||||||
square_4 = square_4 / 255.0
|
|
||||||
|
|
||||||
|
|
||||||
pieces = ['knight', 'rook', 'bishop']
|
|
||||||
|
|
||||||
for pos, square in occupied:
|
|
||||||
square = cv2.cvtColor(square, cv2.COLOR_BGR2GRAY)
|
|
||||||
width, height = square.shape
|
|
||||||
square = square / 255.0
|
|
||||||
test = new_model.predict(np.array(square).reshape((-1, width, height, 1)))
|
|
||||||
text_color = 255
|
|
||||||
cv2.putText(square, f"{pos} {pieces[int(np.argmax(test))]}", (0, 50), cv2.FONT_HERSHEY_SIMPLEX, fontScale=1,
|
|
||||||
color=(text_color,) * 3, thickness=3)
|
|
||||||
cv2.imwrite("lel", square)
|
|
||||||
|
|
||||||
cv2.waitKey(0)
|
|
||||||
|
|
||||||
"""
|
|
||||||
for pos, square in [(pos_1, square_1), (pos_2, square_2), (pos_3, square_3), (pos_4, square_4)]:
|
|
||||||
test = new_model.predict(np.array(square).reshape((-1, width, height, 1)))
|
|
||||||
print(f"{pos}: {np.argmax(test)}")
|
|
||||||
text_color = 255
|
|
||||||
cv2.putText(square, f"{pos} {pieces[int(np.argmax(test))]}", (0, 50), cv2.FONT_HERSHEY_SIMPLEX, fontScale=1,
|
|
||||||
color=(text_color,) * 3, thickness=3)
|
|
||||||
cv2.imshow(f"{pos}", square)
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
#cv2.putText(square, f"{pos} {PIECE(int(np.argmax(test)))}", (0, 50), cv2.FONT_HERSHEY_SIMPLEX, fontScale=1, color=(255,) * 3, thickness=3)
|
||||||
|
#cv2.imwrite("lel", square)
|
||||||
|
board[pos] = PIECE(int(np.argmax(test)))
|
||||||
|
|
||||||
|
return board
|
||||||
|
|
|
@ -1,52 +1,35 @@
|
||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||||
|
|
||||||
import tensorflow as tf
|
|
||||||
import glob
|
import glob
|
||||||
import numpy as np
|
|
||||||
import cv2
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import tensorflow as tf
|
||||||
|
|
||||||
#(train_images, train_labels), (test_images, test_labels) = datasets.mnist.load_data()
|
# exit()
|
||||||
|
from util import OUR_PIECES
|
||||||
|
|
||||||
#print(train_images[0])
|
# (train_images, train_labels), (test_images, test_labels) = datasets.mnist.load_data()
|
||||||
|
# print(train_images[0])
|
||||||
|
|
||||||
#exit()
|
|
||||||
training_img = []
|
training_img = []
|
||||||
training_labels = []
|
training_labels = []
|
||||||
|
|
||||||
test_img = []
|
test_img = []
|
||||||
test_labels_ = []
|
test_labels_ = []
|
||||||
|
|
||||||
|
for piece in OUR_PIECES:
|
||||||
|
# training set
|
||||||
|
for _ in range(10):
|
||||||
|
for filename in glob.glob(f"../training_images/{piece}/*_square/*.png")[:-50]:
|
||||||
|
training_img.append(cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2GRAY))
|
||||||
|
training_labels.append(piece)
|
||||||
|
|
||||||
for _ in range(10):
|
# test set
|
||||||
for filename in glob.glob(f"../training_images/rook/*_square/*.png")[:-50]:
|
for _ in range(5):
|
||||||
training_img.append(cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2GRAY))
|
for filename in glob.glob(f"../training_images/{piece}/*_square/*.png")[-50:]:
|
||||||
training_labels.append(1)
|
test_img.append(cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2GRAY))
|
||||||
|
test_labels_.append(piece)
|
||||||
|
|
||||||
for _ in range(10):
|
|
||||||
for filename in glob.glob(f"../training_images/knight/*_square/*.png")[:-50]:
|
|
||||||
training_img.append(cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2GRAY))
|
|
||||||
training_labels.append(0)
|
|
||||||
|
|
||||||
for _ in range(10):
|
|
||||||
for filename in glob.glob(f"../training_images/bishop/*_square/*.png")[:-50]:
|
|
||||||
training_img.append(cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2GRAY))
|
|
||||||
training_labels.append(2)
|
|
||||||
|
|
||||||
for _ in range(5):
|
|
||||||
for filename in glob.glob(f"../training_images/rook/*_square/*.png")[-50:]:
|
|
||||||
test_img.append(cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2GRAY))
|
|
||||||
test_labels_.append(1)
|
|
||||||
|
|
||||||
for _ in range(5):
|
|
||||||
for filename in glob.glob(f"../training_images/bishop/*_square/*.png")[-50:]:
|
|
||||||
test_img.append(cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2GRAY))
|
|
||||||
test_labels_.append(2)
|
|
||||||
|
|
||||||
for _ in range(5):
|
|
||||||
for filename in glob.glob(f"../training_images/knight/*_square/*.png")[-50:]:
|
|
||||||
test_img.append(cv2.cvtColor(cv2.imread(filename), cv2.COLOR_BGR2GRAY))
|
|
||||||
test_labels_.append(0)
|
|
||||||
|
|
||||||
|
|
||||||
width, height = training_img[0].shape
|
width, height = training_img[0].shape
|
||||||
|
|
26
util.py
26
util.py
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import NamedTuple, Dict, Tuple
|
from typing import NamedTuple, Dict, Tuple, List
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -19,23 +19,24 @@ class COLOR(Enum):
|
||||||
|
|
||||||
|
|
||||||
class PIECE(Enum):
|
class PIECE(Enum):
|
||||||
PAWN = "pawn"
|
KNIGHT = 0
|
||||||
ROOK = "rook"
|
ROOK = 1
|
||||||
KNIGHT = "knight"
|
BISHOP = 2
|
||||||
BISHOP = "bishop"
|
PAWN = 3
|
||||||
QUEEN = "queen"
|
QUEEN = 4
|
||||||
KING = "king"
|
KING = 5
|
||||||
EMPTY = "empty"
|
EMPTY = 6
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.value
|
return self.name.lower()
|
||||||
|
|
||||||
|
|
||||||
PieceAndColor = Tuple[PIECE, COLOR]
|
PieceAndColor = Tuple[PIECE, COLOR]
|
||||||
|
|
||||||
OUR_PIECES = (
|
OUR_PIECES = (
|
||||||
PIECE.ROOK,
|
|
||||||
PIECE.KNIGHT,
|
PIECE.KNIGHT,
|
||||||
|
PIECE.ROOK,
|
||||||
|
PIECE.BISHOP,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,6 +86,11 @@ Squares = Dict[POSITION, np.ndarray]
|
||||||
class Board(Dict[POSITION, PIECE]):
|
class Board(Dict[POSITION, PIECE]):
|
||||||
"""Board is a dict mapping positions to a piece, i.e. a board configuration after all image processing"""
|
"""Board is a dict mapping positions to a piece, i.e. a board configuration after all image processing"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def to_array(self) -> List[List[int]]:
|
||||||
|
return [[self.get(POSITION((file, rank)), PIECE.EMPTY).value for file in FILE]
|
||||||
|
for rank in RANK]
|
||||||
|
|
||||||
|
|
||||||
def imwrite(*args, **kwargs):
|
def imwrite(*args, **kwargs):
|
||||||
Path(args[0]).parent.mkdir(parents=True, exist_ok=True)
|
Path(args[0]).parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user