如何用Pillow或imageio识别棋子并将国际象棋棋盘转为8x8布局列表
基于Pillow的无OpenCV棋子识别实现方案
核心前置逻辑
你已经完成完整棋盘区域的提取,整个棋盘是规则的正方形8x8网格,不需要做角点检测、滑动窗口这类复杂操作,仅需通过均匀切分+逐格像素相似度比对完成识别,全程不依赖OpenCV的模板匹配函数,仅用Pillow完成图像处理,imageio替代cv2做图像读取。
步骤1:棋盘格均匀切分
因为棋盘区域是已经裁剪好的完整正方形,直接按8等分拆分所有格子即可,不需要额外检测边界:
- 图像读取阶段直接用imageio替代原有的
cv2.imread,读入的图像默认是RGB格式,可直接转成Pillow的Image对象,省去通道转换步骤 - 计算单个棋盘格的边长:
square_size = 棋盘图宽度 // 8 - 逐行列裁剪每个独立格子,按行列索引存入对应位置,为后续匹配做准备
切分参考代码:
from PIL import Image import imageio.v2 as iio import numpy as np # 替代原有cv2.imread读取图像 full_img = iio.imread('checkmate_public/public/set/1/1.png') # 如果你已经用OpenCV裁剪了棋盘区域,可直接把裁剪后的BGR数组转成RGB格式再传入Image.fromarray board_img = Image.fromarray(full_img) square_size = board_img.width // 8 # 初始化8x8棋盘结果列表 chess_board = [["" for _ in range(8)] for _ in range(8)] # 存储所有切分好的格子图像 square_store = [] for row_idx in range(8): current_row = [] for col_idx in range(8): # 定义裁剪坐标:(左,上,右,下) crop_box = ( col_idx * square_size, row_idx * square_size, (col_idx + 1) * square_size, (row_idx + 1) * square_size ) sq_img = board_img.crop(crop_box) current_row.append(sq_img) square_store.append(current_row)
步骤2:实现轻量模板匹配
Pillow和imageio本身没有内置的模板匹配接口,由于当前场景下格子尺寸和素材尺寸完全对齐,不需要做滑动窗口搜索,仅需逐格计算图像和素材的相似度即可,计算量极低:
- 提前加载所有单独棋子、空棋盘格素材,统一缩放到和棋盘格一致的尺寸,为每个素材打对应的类型标签(比如白兵、黑车、深格空格、浅格空格)
- 定义相似度计算函数,用均方误差衡量两个图像的像素匹配度,误差最小的素材即为当前格子的匹配结果
匹配参考代码:
def calculate_similarity(img1: Image.Image, img2: Image.Image) -> float: # 统一转灰度图计算,降低颜色偏差带来的匹配误差 gray1 = np.array(img1.convert("L")) gray2 = np.array(img2.convert("L").resize(img1.size)) # 计算逐像素差的平方均值,数值越小代表两个图像越相似 return np.mean((gray1 - gray2) ** 2) # 加载所有模板素材,统一缩放到格子大小 template_library = {} # 示例:按实际素材路径加载 template_library["empty_dark"] = Image.open("templates/dark_blank.png").resize((square_size, square_size)) template_library["empty_light"] = Image.open("templates/light_blank.png").resize((square_size, square_size)) template_library["w_pawn"] = Image.open("templates/white_pawn.png").resize((square_size, square_size)) template_library["b_king"] = Image.open("templates/black_king.png").resize((square_size, square_size)) # 把所有需要识别的棋子素材全部加入字典即可 # 逐格子匹配填充结果列表 for row in range(8): for col in range(8): current_square = square_store[row][col] min_diff = float("inf") match_result = "" # 遍历所有模板找最相似的 for label, template_img in template_library.items(): diff = calculate_similarity(current_square, template_img) if diff < min_diff: min_diff = diff match_result = label chess_board[row][col] = match_result
匹配优化技巧
- 如果你的棋子素材是带透明通道的PNG,计算相似度时可以仅统计棋子覆盖的非透明像素,忽略背景部分,大幅降低棋盘底色带来的匹配干扰
- 可设置相似度阈值:当最小的像素差值超过预设阈值时,标记当前格子为识别异常,避免错配
- 统一把素材和切分出的格子做亮度归一化,降低拍摄/截图时的全局亮度偏差对匹配结果的影响
内容的提问来源于stack exchange,提问作者Marko Maksimovic
相关产品推荐
相关产品推荐

