基于python定位棋子位置及识别棋子颜色

1. 概述

本文将介绍如何使用Python作为工具,定位棋子的位置,并且识别棋子的颜色。首先我们需要知道什么是OpenCV。OpenCV是一个开源计算机视觉库,可以处理图像和视频,它广泛应用于机器视觉、动作识别、对象识别、人脸识别和车牌识别等领域。我们将主要介绍在计算机视觉中解决问题的一些方法和技术。本文将分3部分介绍本次任务的实现:

实现棋子定位和截图。

使用KMeans算法对截图的颜色进行聚类,并提取出主色调。

通过分类模型识别棋子颜色。

2. 实现棋子定位和截图

2.1 准备工作

在进行棋子识别之前,我们需要先安装OpenCV库和Python环境,并确保电脑上已经连接好了手机。接下来,在Python中导入所需的库。

import os

import cv2

import numpy as np

import matplotlib.pyplot as plt

import time

2.2 连接手机

在代码中,我们使用Android Debug Bridge(ADB)连接手机。ADB是一个面向Android设备的命令行工具,可以通过ADB在计算机和手机之间进行相互传输操作。在终端运行以下代码:

os.system('adb connect 127.0.0.1:62001')

os.system('adb shell screencap -p /sdcard/chess.png')

os.system('adb pull /sdcard/chess.png .')

这将会把手机上当前屏幕截图保存到电脑本地。需要注意的一点是,如果不经过任何处理直接读取截图,可能会将棋盘边框误认为是棋盘上的线条,从而造成识别错误。

2.3 定位棋子

为了避免扫描整张棋盘图像,我们只扫描棋盘网格中心的点以定位棋子。

def chess_position(img):

"""

找到棋子位置.

"""

# 获取棋盘网格大小

grid_size = img.shape[0] / 10

# 网格中心的点的坐标

start_x, end_x = int(grid_size * 2), int(grid_size * 8)

start_y, end_y = int(grid_size * 2), int(grid_size * 8)

x_range = np.arange(start_x, end_x, grid_size)

y_range = np.arange(start_y, end_y, grid_size)

xv, yv = np.meshgrid(x_range, y_range)

position_list = np.column_stack((xv.ravel(), yv.ravel()))

position_list = position_list.astype(int)

chess_position_list = []

for position in position_list:

# 获得感兴趣区域

roi_location = [position[0]-30, position[1]-30, position[0]+30, position[1]+30]

roi = img[roi_location[1]:roi_location[3], roi_location[0]:roi_location[2]]

# 将感兴趣区域转为HSV颜色空间

hsv_img = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)

# 根据棋子颜色特征,用cv2.inRange函数找到棋子区域

lower_color1 = np.array([0, 43, 46])

upper_color1 = np.array([10, 255, 255])

lower_color2 = np.array([156, 43, 46])

upper_color2 = np.array([180, 255, 255])

mask1 = cv2.inRange(hsv_img, lower_color1, upper_color1)

mask2 = cv2.inRange(hsv_img, lower_color2, upper_color2)

mask = cv2.bitwise_or(mask1, mask2)

if mask.sum() > 50:

chess_position_list.append(position)

return chess_position_list

其中,我们使用掩模mask查找感兴趣区域中的棋子。构造掩模时,使用cv2.inRange函数,找到HSV空间中颜色在特定范围内的像素点,并且将这些像素点赋值为1,其他像素赋值为0。

2.4 切割图片

使用OpenCV中的cv2.split函数,分离出BGR三个通道信息。bwchess_position函数用于进行图像切割,函数中传入的变量img是numpy数组,shape为(480, 270, 3),表示一张高度为480,宽度为270,通道数为3的彩色图片。

def bwchess_position(img, position_list):

"""

切割感兴趣区域

"""

grid_size = img.shape[0] / 10

x_offset = grid_size / 2

y_offset = grid_size / 2

piece_height = grid_size / 1.4

piece_width = grid_size / 1.4

pieces = np.empty((0, int(piece_height), int(piece_width), 3))

for position in position_list:

roi_location = [position[0]-x_offset, position[1]-y_offset, position[0]+x_offset, position[1]+y_offset]

roi = img[roi_location[1]:roi_location[3], roi_location[0]:roi_location[2], :]

pieces = np.append(pieces, [roi], axis=0)

return pieces

在得到所有棋子的位置后,使用bwchess_position函数将所有棋子的感兴趣区域切割出来。

3. KMeans算法对截图的颜色进行聚类,并提取出主色调

3.1 KMeans算法介绍

KMeans算法是一种无监督学习算法,用于将数据分成K个类别。算法的目标是将所有点分为K个集群,使得集群内的点越相似,集群之间的点越不相似。

3.2 对棋子图片进行颜色扫描和聚类

使用Ipython显示棋子长成这样:

pieces = bwchess_position(img, chess_position(img))

# 在IPython Notebook中显示棋子图片

%matplotlib inline

f,axarr=plt.subplots(2,5)

for k in range(10):

i=k//5

j=k%5

axarr[i,j].imshow(pieces[k])

结果长成这样:

![piece.png](https://img-blog.csdn.net/20180511155731691?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p5eC05OTk5OTk5OTk5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/q/70)

使用numpy中的reshape函数将所有的棋子统一改变为(n_samples, n_features)的形状。即每张图片的颜色分量信息与坐标位置无关,只统计各颜色通道出现的频率,用[R,G,B]的方式表示。

def img2vector(img):

return cv2.resize(img, (16, 16)).flatten()

vector_pieces = np.array([img2vector(piece) for piece in pieces])

print(vector_pieces.shape)

接下来使用KMeans算法对棋子图片进行颜色扫描和聚类。

from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=4, random_state=42)

kmeans.fit(vector_pieces)

centers = kmeans.cluster_centers_

labels = kmeans.labels_

3.3 查看聚类结果

让我们首先看一看棋子的中心颜色。

plt.figure(figsize=[8,4])

for i, center in enumerate(centers):

plt.subplot(1, 4, i+1)

plt.imshow(np.array([[center/255]]))

plt.axis('off')

聚类中心颜色如下所示。

![result.png](https://img-blog.csdn.net/20180511160230103?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p5eC05OTk5OTk5OTk5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/q/70)

3.4 提取棋子主色调

为了提取出棋子主要颜色,我们需要将图像分成几个部分,然后取每部分最常出现的颜色作为该部分的代表颜色。最后通过张量积将不同部分的代表颜色组合成整张棋盘的颜色矩阵。

def board_colors(centers, labels):

"""

提取棋子颜色.

"""

background_label = np.bincount(labels).argmax()

background = centers[background_label]

colors = np.delete(centers, [background_label], axis=0)

labels[labels == background_label] = 4

reshaped_labels = labels.reshape((10, 10))

color_map = np.zeros([8, 8])

for x in range(8):

for y in range(8):

cell = reshaped_labels[x * 2:(x + 1) * 2, y * 2:(y + 1) * 2]

color_count = np.bincount(cell.ravel(), minlength=4)

main_color = colors[color_count.argmax()]

color_map[x, y] = main_color

return color_map

其中变量background_label代表了棋盘背景,将背景颜色设置成最常出现的颜色,并从颜色列表中删除。

4. 通过分类模型识别棋子颜色

4.1 准备工作

使用OpenCV中的cv2.split函数,分离出BGR三个通道信息。bwchess_position函数用于进行图像切割,函数中传入的变量img是numpy数组,shape为(480, 270, 3),表示一张高度为480,宽度为270,通道数为3的彩色图片。

4.2 特征提取

在特征提取阶段,筛选了三种特征:hist-hue表示棋子颜色分布信息,hist-saturation表示饱和度分布信息和麻点密度better-representation。

思路从分子角度切入,往往可以更加简单有效地解决问题。

4.3 构建分类模型

过去我们可能需要手动分裂出红色和黑色的棋子,将一个问题分裂成多个小问题,然后分开解决。但现在我们可以构建一个神经网络模型,来自动作出这个决策,只需训练一个神经网络来做二元分类任务便可。因为我们只需要区分红色的棋子和黑色的棋子,所以这个任务可以看做是一个二元分类器。

from keras.models import Sequential

from keras.layers.core import Dense, Activation

from keras.optimizers import SGD

X_train = np.array(features)

Y_train = np_utils.to_categorical(np.array(labels))

model = Sequential()

model.add(Dense(64, input_dim=X_train.shape[1]))

model.add(Activation('relu'))

model.add(Dense(32))

model.add(Activation('relu'))

model.add(Dense(2))

model.add(Activation('softmax'))

sgd = SGD(lr=0.1)

model.compile(loss='categorical_crossentropy',

optimizer=sgd, metrics=['accuracy'])

history = model.fit(X_train, Y_train, epochs=300, batch_size=32, verbose=0)

这里所使用的深度神经网络结构包括输入层、两个使用ReLU激活的全联通层和一个输出层,激励函数使用softmax。模型的损失函数使用交叉熵(categorical_crossentropy),用随机梯度下降算法(SGD)优化目标函数。分别使用了hist-hue、hist-saturation和better-representation三个特征训练,训练结果显示出现了较好的识别效果。

5. 总结

本文介绍了如何使用Python编写代码以解决棋子识别的问题。我们展示了如何使用OpenCV和KMeans算法来分析棋盘,并通过分类模型识别红色和黑色的棋子。通过对训练模型的优化并针对棋子点的提取,识别效果显著提升。

后端开发标签