基于OpenCV的围棋棋盘棋子识别:传统视觉定位网格与黑白子
发布时间:2026/9/28 4:47:47来源:尧图网络
简介这是一份基于Python语言和OpenCV库的围棋棋盘棋子识别项目资源面向毕业设计、课程设计以及计算机视觉方向的项目开发参考。资源共40个文件压缩包约4.83MB以两个Python测试脚本、一个Markdown说明文档为主体辅以多组jpg/png测试图像与效果图既可作为输入样本也能直观展示识别中间过程和结果。项目源码经过严格测试可直接运行参考适合在现有框架上进一步扩展棋盘分割、棋子落子检测等应用。预览信息显示两组测试脚本分别覆盖棋盘图像处理、棋子定位与识别等关键环节多张实拍与仿真棋盘图像为算法调试验证提供了充足素材配套Markdown文档则可用于快速了解资源结构。目前已有280人学习下载对于需要快速上手视觉识别项目的学生开发者而言是一份完整的可直接复现的参考样例。1. 围棋棋盘棋子识别不训模型也能把棋盘和黑白子捞出来把手机拍的一张围棋棋盘丢给传统 OpenCV不训练任何深度学习模型也能把 19 路网格线和黑白棋子的位置拎得清清楚楚——这正是这份以 python opencv 为基础的围棋棋盘棋子识别项目在做的事。它不带目标检测那套重装备纯靠边缘检测、霍夫直线、霍夫圆和 HSV 颜色空间这几板斧跑完两个测试脚本就能在屏幕上看到棋盘网格线和一个个被圈出来的棋子。对毕业设计和课程设计来说最实用的点也在这代码量小、推理链路短、每一步都有中间图可以对照写论文时好讲原理答辩时好现场演示。想快速接手一个能跑、能改、能讲出细节的视觉项目这份源码是挺合适的起点。2. 先摸清源码结构test1 和 test2 在分工干什么拿到压缩包先别急着装环境把目录摊开看一遍再动手能省不少弯路。这套项目里最值得先读的是 ReadMe.md其次是两个测试脚本21_0629_opencv_python_weiqi_test1.py和21_0629_opencv_python_weiqi_test2.py剩下的 jpg、png 都是配套输入图和调试用的中间产物。2.1 解压后都有哪些文件解开围棋识别.zip后主目录是21_0629_opencv_python_weiqi-master里面混着脚本、测试图、中间结果目录和一份说明文档。我按用途整理了一张表文件/目录用途21_0629_opencv_python_weiqi_test1.py棋盘定位与网格检测测试脚本21_0629_opencv_python_weiqi_test2.py棋子识别与颜色判定测试脚本src.jpg / img.jpg含完整棋盘的输入图img_temp1.jpg / img_temp2.jpg / img_temp3.jpg中间过程输出图用来核对每一步处理结果checkerboard / screen / temp 目录中间结果存放目录2.png / 3.png / 4.png额外测试图覆盖不同拍摄角度和光照lena_new_sz.png通用图像处理测试图ReadMe.md项目说明与运行方法从文件命名能看出这套代码的开发习惯输入图、临时图、输出目录分开管理img_temp*.jpg这类文件就是调试时落盘的过程图。这个习惯对复现非常友好跑完一次脚本后你可以在 temp 和 checkerboard 目录里看到算法每一步在做什么而不是只给一张最终结果图。2.2 两个测试脚本分别管哪一段按文件名和配套图的逻辑推断test1 聚焦棋盘网格的提取与定位test2 聚焦棋子的检测与黑白分类。两个脚本共用同一批输入图但侧重点不同我一般会这么分工脚本重点对应输出test1棋盘在哪、网格线在什么位置img_temp系列中的边缘图和网格叠加图test2棋子圆心在哪、是黑还是白画了圆框和颜色标签的结果图这个分法对毕设很友好论文里可以自然分成「棋盘定位」和「棋子识别」两个模块来写先讲怎么从图里找到棋盘再讲怎么在棋盘区域里找棋子、判颜色。答辩时按这条线讲逻辑是顺的。2.3 把它跑起来环境与首次运行复现这套源码的依赖很轻核心就是 OpenCV 和 NumPy没有 torch、没有 tensorflow。我习惯先新建虚拟环境再装依赖避免和系统 Python 环境的包版本互相干扰cd 21_0629_opencv_python_weiqi-master python -m venv .venv source .venv/bin/activate # Windows 下换成 .venv\Scripts\activate pip install opencv-python numpy python 21_0629_opencv_python_weiqi_test1.py装依赖时有个点值得注意opencv-python 默认会拉取当前最新的 4.x 系列numpy 装最新稳定版即可Python 3.7 到 3.11 都能正常跑。首次运行后去 temp 和 checkerboard 目录看有没有生成新的图片如果有说明脚本按相对路径读到了src.jpg链路是通的。如果报找不到文件多半是脚本里的相对路径跟你的工作目录对不上把cv2.imread的路径改成绝对路径就好。提示跑通之前别急着调算法参数。先确认环境能出中间图再谈优化。3. 棋盘定位与网格切分直线检测和透视矫正怎么落地围棋识别最关键的环节不是棋子识别而是先把棋盘从图里「端」出来。棋盘在画面里通常是倾斜的不等距、带透视形变如果不做矫正直接数格子后续棋子坐标全都会错位。传统视觉路线处理这件事有一套成熟的组合拳Canny 边缘检测 霍夫直线 多边形近似 透视变换。3.1 定位思路为什么是 Canny 加 HoughLinesP棋盘是结构化极强的图案横竖直线规则排列天然适合用直线检测来定位。相比深度学习目标检测霍夫直线的好处是参数透明、可解释性强出了问题你知道是阈值问题还是线段过滤问题。核心代码长这样我一般会写成独立函数方便调试import cv2 import numpy as np img cv2.imread(src.jpg) if img is None: raise FileNotFoundError(src.jpg 没找到先确认脚本工作目录) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray cv2.GaussianBlur(gray, (5, 5), 0) # Canny 双阈值低阈值 60、高阈值 160边缘太碎就往高调 edges cv2.Canny(gray, 60, 160) # HoughLinesP检测直线段而不是无限长直线 lines cv2.HoughLinesP(edges, 1, np.pi / 180, threshold180, minLineLength100, maxLineGap20) if lines is None: print(没检测到直线把 Canny 高阈值降低或 minLineLength 调小) else: lines lines[:, 0, :] # 摊平成 N×4 的数组每行是 x1,y1,x2,y2Canny 的低阈值和高阈值控制边缘的敏感度阈值调低了木纹、噪点、阴影全都会变成边缘调高了棋盘线可能断成一段一段。霍夫直线的四个参数里threshold180是最少投票数值越大要求线段越长越直minLineLength100过滤掉短线段棋盘线在图上通常超过 150 像素可以放心过滤maxLineGap20允许线段中间有最多 20 像素的断裂这个参数专门对付棋盘线被棋子或反光打断的情况。3.2 从一堆直线里找出棋盘外框再做透视矫正有了边缘和直线后下一步是找出棋盘外轮廓的四角。常见做法是先取最大外轮廓再用多边形近似逼出四边形cnts, _ cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts sorted(cnts, keycv2.contourArea, reverseTrue) board_contour cnts[0] peri cv2.arcLength(board_contour, True) approx cv2.approxPolyDP(board_contour, 0.02 * peri, True)approxPolyDP的第二个参数0.02 * peri是近似精度值越小拟合越严。棋盘外框在图上通常占最大面积所以直接取cnts[0]。注意 OpenCV 4.x 的findContours只返回两个值不要按旧版教程解包成三个否则会在这一行直接报错。拿到四角后做透视变换把倾斜的棋盘压成一个标准正方形方便后面按 19 路等距切分def order_points(pts): 把四角按 左上、右上、右下、左下 排序 rect np.zeros((4, 2), dtypefloat32) s pts.sum(axis1) # 左上角 xy 最小右下角 xy 最大 diff np.diff(pts, axis1) # 右上角 x-y 最小左下角 x-y 最大 rect[0] pts[np.argmin(s)] rect[2] pts[np.argmax(s)] rect[1] pts[np.argmin(diff)] rect[3] pts[np.argmax(diff)] return rect rect order_points(approx.reshape(4, 2).astype(float32)) size 512 dst np.array([[0, 0], [size - 1, 0], [size - 1, size - 1], [0, size - 1]], dtypefloat32) M cv2.getPerspectiveTransform(rect, dst) warped cv2.warpPerspective(img, M, (size, size)) cv2.imwrite(checkerboard/warped.jpg, warped)四角排序是透视变换最容易翻车的地方。直接拿轮廓点当角点顺序可能乱getPerspectiveTransform会映射出一张拧巴的图。order_points用坐标的加减关系排序不依赖轮廓输出的顺序稳得多。size 512不是随便定的19 路棋盘有 18 个间隔512 能整除 18 吗不能所以后面切网格时不能直接整除要用间隔数来算步长这个坑在下一节处理。3.3 网格切分按间隔算步长别按格子数整除19 路围棋是 19 条横线、19 条竖线交叉点是 19×19 361 个。相邻线之间的间隔是 18 段不是 19 段。直接从 512 整除 19 再乘交叉点索引边缘处会偏出好几像素。n 19 cell (size - 1) / (n - 1) # 512 / 18约 28.4 像素 grid_points [] for row in range(n): for col in range(n): x int(round(col * cell)) y int(round(row * cell)) grid_points.append((x, y)) # 可视化验证把交叉点画在矫正后的棋盘图上 vis warped.copy() for pt in grid_points: cv2.circle(vis, pt, 3, (0, 0, 255), -1) cv2.imwrite(checkerboard/grid_overlay.jpg, vis)这一步输出的grid_overlay.jpg是判断棋盘定位准不准的关键图。把 361 个红点叠在矫正后的棋盘上如果红点都落在横竖线的交叉位置说明透视矫正和网格计算是对的如果红点整体偏移或者边缘处偏出回头检查 3.2 的四角排序。很多项目做到这里就摆烂直接手动硬编码坐标换一张图就废。按间隔算步长才是通用的写法。4. 避坑指南复现这套围棋识别最容易翻车的五个点传统视觉项目七成时间花在调参和排错上这套围棋识别也逃不过。我把复现过程中最容易踩的坑按「现象 → 原因 → 解决」整理出来按优先级排好。4.1 环境与代码兼容的坑坑一findContours 解包报错。现象test1 跑到轮廓提取那行直接崩报TypeError: not enough values to unpack (expected 3, got 2)。原因网上大部分教程是 OpenCV 3.x 时代的写法findContours返回三个值image、contours、hierarchyOpenCV 4.x 只返回两个值contours、hierarchy照抄旧代码必然炸。解决统一写成contours, _ cv2.findContours(...)。如果项目要在不同机器上跑也可以先读cv2.__version__做兼容分支但更省事的做法是直接按 4.x 写因为现在 pip 默认装的都是 4.x。坑二numpy 和 opencv 版本不对付。现象import cv2报ImportError: numpy.core.multiarray failed to import或类似_ARRAY_API not found的提示。原因新版 opencv-python 对 numpy 的 C 级 ABI 有要求和机器上很老的 numpy 配对import 阶段就会崩。解决不要直接往系统 Python 里pip install opencv-python我建议强制走一遍虚拟环境流程python -m venv .venv激活后重新pip install opencv-python numpy让 pip 自己拉版本匹配的依赖。只要跑通一次后面换机器照搬这个流程就不会再栽。4.2 图像检测效果上的坑坑三用 lena_new_sz.png 当输入网格全乱。现象临时拿 lena_new_sz.png 测试输出图上全是杂乱的短线和假轮廓棋盘网格根本拉不出来。原因Lena 图上的人脸、帽檐纹理太丰富边缘检测后到处都是短线段霍夫直线被这些纹理干扰最大外轮廓也不是棋盘。解决测棋盘定位只认准 src.jpg、img.jpg、3.png 这类正经棋盘图。lenna_new_sz.png 是给通用图像处理流程验算法用的不是给本项目当输入用的。这个坑很隐性因为不少人看到测试图就随手换一张跑一跑就怀疑代码有问题。坑四HoughCircles 把棋盘交叉点当棋子一颗子被检出多个圆。现象test2 输出图上棋盘网格交叉点被画了圆框一颗白子有时候被圈出三四个同心圆。原因HoughCircles的param2圆心累加器阈值设得过低、minDist设得比棋子直径还小导致同一颗子被反复投票。解决把param2提到 3545minDist至少设为图像中棋子直径的 1.5 倍。检完圆再做一轮轮廓面积过滤面积落在棋子正常范围内的才保留把明显偏小的高光点、噪点直接剔除。参数不是越大越好要配合你的实际图像分辨率来调核心思路就是「宁可漏检不要误检」误检比漏检难清理。坑五换个光源黑子全部消失白子变灰子。现象室内自然光下跑得好好的换到台灯直射或逆光场景黑子一个都检不出来白子也被判成灰色棋。原因HSV 颜色空间里固定写死的阈值对亮度变化太敏感。黑子的 V明度分量一旦超过阈值区间就被排除白子的 S饱和度稍高一点就被踢出白色集合。解决不要在原始图上直接做 HSV 分割先转灰度做 CLAHE 自适应直方图均衡化再转回 HSV让亮度分布相对稳定。阈值不要写死我一般会取棋盘中心区域的亮度统计值作为基准再上下偏移出黑白的 V 阈值区间。这样做以后换环境基本不用再改代码顶多微调偏移量。5. 进阶玩法把两个脚本串成一条实时落子识别管线test1 和 test2 分开跑只是验证阶段的事真正想用在课程设计展示或实际复盘可以把它们串成一条完整流水线输入一帧 → 找棋盘 → 透视矫正 → 切网格 → 找棋子 → 映射到 361 个交叉点 → 输出落子序列。用摄像头实时跑也成立核心就是加一个连续帧稳定性判断。import cv2 def run_pipeline(frame): 整合定位矫正棋子识别返回网格坐标上的黑白子集合 warped perspective_fix(frame) # 第 3 章的逻辑 black, white detect_stones(warped) # 第 4 章的 HSV HoughCircles return warped, map_to_grid(black, white) cap cv2.VideoCapture(0) while True: ok, frame cap.read() if not ok: break warped, black_count, white_count run_pipeline(frame) # 棋盘稳定才更新结果连续两帧的外框角点偏移 5 像素 if warped is not None: cv2.imshow(result, warped) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()实时识别和静态图最大的区别是画面会抖。手持手机或摄像头对不准棋盘角点每帧都在跳。常见的做法是加一个位姿稳定性检查拿上一帧的外框四角和当前帧的算偏移量小于阈值才更新棋子结果这样输出不会一闪一闪。另一个实用技巧是把每次的中间图按时间戳落盘复现时对比img_temp*.jpg就能定位是哪一步出了问题。验证阶段我强烈建议做一个双盲对比程序输出的黑白子数量和你在原图上肉眼数的数量对一遍。19 路棋盘理论上有 361 个交叉点99% 的情况不会有 361 颗子只要数量对上、位置偏差不超过半个格子这个识别算法就在可用范围内。数值对不上的时候优先怀疑网格切分偏移其次才是棋子漏检。最后说一条我自己的习惯拿到任何视觉项目第一件事是把输入图、中间图、结果图分目录放好调参时固定一组基准图每次只改一个变量。这套围棋识别代码里temp和checkerboard目录的划分其实就是这个思路。从那以后我每次调这类传统视觉的参数都强制走一遍固定基准图对比的流程改完一眼能看出哪一步变了、哪一步坏了。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网