【问题标题】:How to find the empty squares in a chess board image?如何在棋盘图像中找到空方格?
【发布时间】:2021-06-03 14:30:31
【问题描述】:

我目前正在为我的国际象棋项目编写一个图像识别脚本,该脚本从棋盘图像中生成一个二维数组。但是,我发现很难找到哪些方格是空的:

到目前为止,我在应用高斯模糊后对我的图像使用了 Canny 边缘检测,得到了以下结果:

我使用的代码是:

sigma = 0.33
v = np.median(img) 
img = cv2.GaussianBlur(img, (7, 7), 2) # we use gaussian blur on the image to make it clear.
lower = int(max(0, (1.0 - sigma) * v)) # we find the lower threshold.
upper = int(min(255, (1.0 + sigma) * v)) # we find the higher threshold.
img_edge = cv2.Canny(img, 50, 50) # we use the canny function to edge canny the image.
cv2.imshow('question', img_edge) # we show the image.
cv2.waitKey(0)

(你可能注意到我没有使用我得到的阈值,那是因为我发现它不准确。如果有人有任何提示,我会喜欢的!)

现在,在完成这些步骤之后,我尝试了许多其他方法,例如寻找轮廓、霍夫变换等。但我似乎无法弄清楚如何继续前进并实际找出一个正方形是否为空.

感谢任何帮助!

【问题讨论】:

  • 有些方块被部分覆盖,因为 a) 一块在两个方块之间或 b) 透视图使部分看起来覆盖了一个方块 - 您想如何处理这种情况?
  • 嗨!感谢您阅读我的帖子。我最初想获得每个正方形的平均颜色,并检查它是否“黑色”/“棕色”足以为空。我不确定这是否现实,或者我是否应该这样做。如果您有任何建议,欢迎提出!
  • 不幸的是,棋子的颜色和棋盘格的颜色非常相似......那样会非常困难
  • 我明白了。但我指的是在图像上完成精巧边缘之后,我们得到白色的碎片和黑色的棋盘。无论哪种方式,假设我有一块颜色与棋盘格颜色不相似的棋盘,那我该怎么办?
  • 说,如果棋盘是黑白的,而棋子是绿蓝的(四种颜色都不同),那么您将图像划分为 8*8 数组并检查平均值的方法颜色会给你很好的结果。但是,请注意,为此,电路板的照片必须与您示例中的照片几乎一样精确。

标签: python image opencv


【解决方案1】:

假设您有某种方形输入图像覆盖整个棋盘(如示例所示),您可以通过舍入宽度和高度来调整图像大小to the next smaller multiple of 8。因此,您可以从图像中导出 64 个大小相同的图块。对于每个图块,count the number of unique colors。设置一些阈值来区分两个类(空与非空方块),可能使用Otsu's method

这就是我的代码(其中一半只是可视化的东西):

import cv2
import matplotlib.pyplot as plt
import numpy as np
from skimage.filters import threshold_otsu


# Round to next smaller multiple of 8
# https://www.geeksforgeeks.org/round-to-next-smaller-multiple-of-8/
def round_down_to_next_multiple_of_8(a):
    return a & (-8)


# Read image, and shrink to quadratic shape with width and height of
# next smaller multiple of 8
img = cv2.imread('m0fAx.png')
wh = np.min(round_down_to_next_multiple_of_8(np.array(img.shape[:2])))
img = cv2.resize(img, (wh, wh))

# Prepare some visualization output
out = img.copy()
plt.figure(1, figsize=(18, 6))
plt.subplot(1, 3, 1), plt.imshow(img)

# Blur image
img = cv2.blur(img, (5, 5))

# Iterate tiles, and count unique colors inside
# https://stackoverflow.com/a/56606457/11089932
wh_t = wh // 8
count_unique_colors = np.zeros((8, 8))
for x in np.arange(8):
    for y in np.arange(8):
        tile = img[y*wh_t:(y+1)*wh_t, x*wh_t:(x+1)*wh_t]
        tile = tile[3:-3, 3:-3]
        count_unique_colors[y, x] = np.unique(tile.reshape(-1, tile.shape[-1]), axis=0).shape[0]

# Mask empty squares using cutoff from Otsu's method
val = threshold_otsu(count_unique_colors)
mask = count_unique_colors < val

# Some more visualization output
for x in np.arange(8):
    for y in np.arange(8):
        if mask[y, x]:
            cv2.rectangle(out, (x*wh_t+3, y*wh_t+3),
                          ((x+1)*wh_t-3, (y+1)*wh_t-3), (0, 255, 0), 2)
plt.subplot(1, 3, 2), plt.imshow(count_unique_colors, cmap='gray')
plt.subplot(1, 3, 3), plt.imshow(out)
plt.tight_layout(), plt.show()

而且,这就是输出:

如您所见,它并不完美。一个问题是相机位置,特别是那个角度,但你已经在 cmets 中提到过,你可以纠正它。另一个问题,正如 cmets 中已经讨论的那样,是一些棋子放置在两个方格之间的事实。这取决于你,如何处理。 (我会简单地正确放置碎片。)

----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.19041-SP0
Python:        3.9.1
PyCharm:       2021.1.1
Matplotlib:    3.4.2
NumPy:         1.20.3
OpenCV:        4.5.2
scikit-image:  0.18.1
----------------------------------------

【讨论】:

  • 非常感谢!你对我帮助太大了!我真的很感激。感谢您抽出宝贵时间帮助我。
  • 一个很好的方法!如果可能的话,会投票两次
【解决方案2】:

不像给定的原始图像,但是如果您有一个棋盘,其中的颜色与棋盘的颜色不同(如 cmets 中所讨论的),那么您可以做一些事情像这样:

import cv2
import numpy

img = cv2.imread("Chesss.PNG")             # read image using cv2

for x in range(0,img.shape[0] - 8, img.shape[0]//8):
    for y in range(0,img.shape[1] - 8, img.shape[1]//8):      
        square = img[x:x+img.shape[0]//8, y:y+img.shape[1]//8, :]          # creating 8*8 squares of image
        avg_colour_per_row = numpy.average(square, axis=0)
        avg_colour = numpy.array(list(map(int, numpy.average(avg_colour_per_row, axis=0))))//8         # finding average colour of the square
        
        if list(avg_colour) == list(numpy.array([0, 0, 0])) or list(avg_colour) == list(numpy.array([31, 31, 31])):         # if average colour  of the squareis black or white, then print the coordinates of the square
            print(x//(img.shape[0]//8), y//(img.shape[1]//8))

我的示例图像(我现在没有棋盘,所以我使用了渲染图像):

输出:

0 0
0 1
0 2
0 3
0 4
0 5
0 6
0 7
1 1
1 3
1 4
1 6
2 0
2 1
2 2
2 3
2 4
2 5
2 6
2 7
3 0
3 1
3 2
3 3
3 5
3 6
3 7
4 0
4 1
4 2
4 3
4 4
4 6
4 7
5 0
5 1
5 2
5 3
5 4
5 5
5 6
5 7
6 0
6 3
6 4
6 5
6 6
7 0
7 1
7 2
7 3
7 4
7 5
7 6
7 7

请注意,我已将平均颜色值除以 8。这是因为我们只会将 [0, 0, 0] 和 [1, 1, 1] (以及类似地)视为黑色。

【讨论】:

  • 非常感谢您的帮助!非常感谢您花时间帮助我!
【解决方案3】:

你可以找到棋盘,甚至可以找到像here 这样的姿势。然后你就可以估计片基的椭圆形状了。 查找省略号,例如使用this project

使用姿势知识过滤掉垃圾椭圆,您将获得碎片位置。然后你可以找到空闲的单元格。

【讨论】:

    猜你喜欢
    • 2017-02-22
    • 1970-01-01
    • 2022-11-04
    • 1970-01-01
    • 2014-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多