尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

OpenCV围棋识别实战:棋盘定位、棋子检测与参数调优全攻略

OpenCV围棋识别实战:棋盘定位、棋子检测与参数调优全攻略 简介这是一份基于Python与OpenCV的围棋棋盘棋子识别项目面向毕业设计、课程设计与项目开发场景适合需要完成图像处理或计算机视觉课题的本科、高职学生也适合OpenCV初学者学习棋盘定位、棋子检测与图像预处理的基本流程。项目包含可运行的识别源码与说明文档源码经过严格测试可在其基础上做功能延伸。压缩包共40个文件包括2个Python脚本、1个Markdown说明文档以及37张jpg、png图像素材两个脚本分别对应不同测试流程图片可用于观察不同棋盘状态和光照条件下的识别效果整体大小约4.83MB目录结构清晰便于按图像素材与代码模块对照学习。目前已有283人浏览学习。读者拿到后可快速了解完整的识别思路借助配套图片复现结果并结合文档进行二次开发节省从零搭建环境与调试算法的时间。1. 基于 Python 和 OpenCV 的围棋棋盘棋子识别这套源码到底怎么跑通如果你拿到的是一份名为21_0629_opencv_python_weiqi-master的压缩包里面躺着两个测试脚本、一堆img_temp.jpg和checkerboard目录那大概率就是这套围棋识别项目了。它解决的是一件很具体的事给定一张包含围棋棋盘的图片用 Python 和 OpenCV 判断棋盘在哪、棋子落在哪些交叉点上以及每颗子是黑还是白。这套源码的价值不在于算法多前沿而在于它把图像处理里最常用的几个手段——灰度化、边缘检测、霍夫变换、轮廓查找——串成了一个完整可跑的流程非常适合毕业设计、课程设计这类需要「能演示、能讲解、能交差」的场景。我拆过不少同类项目先说结论这份源码能不能直接用取决于你手里的照片是什么样的。如果是正对棋盘、光照均匀的图跑通的概率很高如果是斜着拍的、有反光的、背景杂乱的你就得动参数、加预处理甚至改检测逻辑。后面几章我会把两个测试脚本逐个拆开讲把它做了什么、参数怎么调、坑在哪都说明白。适合谁看打算做图像识别课设、需要快速搭一个棋盘检测 Demo、或者单纯想学 OpenCV 实战流程的开发者这篇都值得你跟着跑一遍。2. 先搞清楚识别流程从读图到输出棋盘点位分几步2.1 这个项目的整体架构两个测试脚本分别干什么压缩包里有21_0629_opencv_python_weiqi_test1.py和21_0629_opencv_python_weiqi_test2.py两个脚本这是拆解整个项目最直接的入口。按照我看到的代码组织方式第一个脚本通常承担「棋盘定位」的职责也就是从一张任意图片里找到棋盘区域并把棋盘网格的交叉点坐标算出来第二个脚本则是在棋盘已经定位的基础上进一步检测每个交叉点附近有没有棋子以及棋子的颜色。这两个脚本对应的处理思路不同也体现了 OpenCV 做这类任务的两条典型路线一条是几何驱动靠边缘检测和直线拟合找棋盘网格另一条是区域驱动靠灰度分布和圆检测找棋子。实际做课设答辩的时候你可以把这两段流程分别演示一个讲「我怎么把棋盘从图片里抠出来的」另一个讲「我怎么知道这个点上有黑子还是白子」逻辑非常清晰。两条脚本共享同一批测试图片比如img_temp.jpg、img_temp1.jpg、src.jpg、3.png这些说明作者在开发时是用多张不同光照、不同角度的图来验证的。你拿到手后第一次运行建议先用img_temp.jpg这张图大概率是作者调试得最顺的一张作为基线跑通后再换其他图验证泛化性。2.2 棋盘定位的核心链路灰度化、边缘检测、轮廓查找和直线拟合先看棋盘这一步。常见做法是先用cv2.cvtColor把彩色图转成灰度图因为后续的边缘检测和霍夫变换都只处理单通道数据转灰度既能减少计算量又能避免颜色干扰。接着用cv2.GaussianBlur做高斯模糊这一步非常必要拍照产生的噪点会在边缘检测阶段被放大成大量碎片边缘不提前平滑一下后面找直线会找到几十条杂乱线段。模糊之后是cv2.Canny边缘检测。Canny 的两个阈值低阈值和高阈值是这个环节的核心参数高阈值决定一条边缘必须强到一定程度才被保留低阈值用于连接断裂的边缘。对棋盘这类黑白格子交替、边缘锐利的物体我一般会把高阈值设在 150200 之间低阈值设在 50 左右具体数值要看图片的分辨率和光照。边缘图出来后就要找棋盘的外边界了。这里有两种实现思路一种是直接cv2.findContours找轮廓然后用cv2.approxPolyDP做多边形逼近去找四边形轮廓作为棋盘候选另一种是用cv2.HoughLinesP检测直线再把直线聚类成交点从而推导网格结构。从源码的文件命名习惯看大概率是轮廓优先因为后续还要配合角点检测来矫正透视而轮廓逼近正好能给出四个角点。从工程角度说这两种方案都有坑轮廓方案怕的是棋盘和背景颜色相近导致轮廓断裂直线方案怕的是棋子本身也产生大量直线干扰。对于课程设计来说先跑通轮廓方案是性价比最高的选择因为它代码量小、调试直观。2.3 棋盘网格的定位策略交叉点坐标是怎么算出来的检测到棋盘区域只是第一步真正的难点在于把 19×19 的网格交叉点坐标算出来。如果图片是正对棋盘拍的网格线是水平和垂直的那可以用霍夫变换找直线按角度聚类分成横竖两组再求交点。但如果图片带透视棋盘是倾斜的这个方案就不灵了。更稳妥的做法是拿到棋盘四个角点后用cv2.getPerspectiveTransform做透视矫正把棋盘区域拉正再去算网格点。具体来说矫正后棋盘变成标准的矩形你可以把四条边等间距切分来估算交叉点假设棋盘边长为 W 像素矫正后的那么第 i 行第 j 列的交叉点坐标大约是(W * j / 18, W * i / 18)。这个做法在矫正准确的情况下误差很小而且是纯几何计算不需要额外调参。源码里是否做了透视矫正可以从 ReadMe.md 里的说明判断。但我建议你拿到手后第一步就是确认这一点如果脚本里没有矫正环节那你用斜着拍的图测试时检测结果会严重偏移。这时候你需要补上角点检测和透视变换的代码这部分我会在最后一章给出一个可以直接用的扩展思路。3. 把源码跑起来环境配置、两个脚本的执行过程与输出解读3.1 环境准备Python 版本和 OpenCV 安装的注意事项这个项目基于 Python 和 OpenCV本身依赖不算复杂但环境搭不对会浪费大量时间。先说 Python 版本我用 Python 3.8 跑这类兼容 OpenCV 的老项目最稳3.10 以上版本偶尔会遇到 NumPy 接口兼容的坑如果源码里用了np.float这类旧接口在新版本里会直接报AttributeError。OpenCV 的安装是另一个高频翻车点。很多人执行pip install opencv-python之后import cv2报ModuleNotFoundError或者报cv2.error: OpenCV(4.4.0)这类 C 层错误。前者多半是装到了不同的 Python 环境里后者多半是包版本冲突。我的习惯是新建一个虚拟环境再装python -m venv weiqi_env source weiqi_env/bin/activate # Windows 下用 weiqi_env\Scripts\activate pip install opencv-python opencv-contrib-python numpy matplotlibopencv-contrib-python包含了cv2.xfeatures2d这类扩展模块虽然本项目不一定用到但毕设答辩时如果你要现场加一个特征匹配的演示有它就不需要重新装包。装完后用下面这段代码验证环境是否正常import cv2 import numpy as np print(cv2.__version__) print(np.__version__)能正常打印版本号说明环境没问题。如果打印cv2.__version__报错先执行pip list看opencv-python是否在列表里不在就重装在的话就检查当前 shell 使用的是不是虚拟环境的 Python。3.2 运行 test1 脚本棋盘检测的代码逻辑与输出文件第一个脚本21_0629_opencv_python_weiqi_test1.py是棋盘检测的主脚本。我拆解后的典型流程是这样的读取图片 → 灰度化 → 高斯模糊 → Canny 边缘检测 → 找轮廓 → 筛四边形 → 输出检测结果到临时文件。import cv2 import numpy as np # 读取图片filename 可以从命令行参数传入 img cv2.imread(img_temp.jpg) # 转灰度 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯模糊核大小 5x5抑制噪点 blurred cv2.GaussianBlur(gray, (5, 5), 0) # Canny 边缘检测 edges cv2.Canny(blurred, 50, 150) # 查找轮廓 contours, _ cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 按轮廓面积排序取最大的几个候选 contours sorted(contours, keycv2.contourArea, reverseTrue)[:5] for contour in contours: # 多边形逼近得到四边形的四个顶点 epsilon 0.02 * cv2.arcLength(contour, True) approx cv2.approxPolyDP(contour, epsilon, True) if len(approx) 4: cv2.drawContours(img, [approx], -1, (0, 255, 0), 2) break # 保存结果方便对照原图检查 cv2.imwrite(checkerboard_detected.jpg, img)这段代码里最重要的参数是epsilon 0.02 * cv2.arcLength(contour, True)它决定了多边形逼近的严格程度。0.02 的意思是允许轮廓周长 2% 的误差这个值偏小意味着逼近结果更贴近原始轮廓反过来也更容易出现五边形、六边形这类非四边形结果。如果你检测的图片里棋盘边缘有阴影干扰建议把系数放宽到 0.030.04代价是角点位置会略微偏离真实角点。另一个参数是cv2.RETR_EXTERNAL它只检索最外层轮廓。这个选择合理因为棋盘作为图片中最大的四边形物体最外层轮廓通常就是它的边界。但如果图片里棋盘旁边放了一个更大的矩形物体比如一本书、一个显示器边框那这个最大的轮廓就是错的。真遇到这种情况我会在排序前先加一个面积比例过滤只保留面积占整张图 20% 以上的轮廓。3.3 运行 test2 脚本棋子识别是依赖坐标前瞻还是独立检测第二个脚本21_0629_opencv_python_weiqi_test2.py负责棋子检测。从工程上分它有两种可能的设计第一种是接收 test1 输出的棋盘坐标逐点检查周围区域有没有棋子第二种是独立地对整张图做圆检测再和棋盘交叉点做匹配。这两种思路在代码复杂度上有明显差异从文件命名和测试图片的重复利用来看test2 极大概率是独立检测。独立检测的核心手段是cv2.HoughCircles这是 OpenCV 最常用的圆检测函数。它的参数比较多这里逐个说circles cv2.HoughCircles( blurred, # 输入图要求是灰度图 cv2.HOUGH_GRADIENT, # 检测方法目前只有这一种 dp1.2, # 累加器分辨率与图像分辨率的比值 minDist20, # 两个圆心之间的最小距离防止重复检测 param1100, # Canny 高阈值 param230, # 累加器阈值越小越容易检出圆也越容易误检 minRadius5, # 最小半径 maxRadius30 # 最大半径 )这里面minDist和param2是最需要调的。围棋棋子在 19 路棋盘上的间距大约是棋盘宽度的 1/18如果你的棋盘在图片里占了 1080 像素宽那么棋子间距就是 60 像素左右minDist设成棋间距的 0.71.0 倍比较合适。param2则是典型的「值越低越乱、值越高越漏」没有通用解必须对着实际图片反复试。圆检测出来之后还要区分黑白子。常见做法是取每个圆心的像素值看灰度是偏亮还是偏暗亮的是白子暗的是黑子。更稳的做法是取圆心周围一个小方块的灰度均值比如以圆心为中心取 10×10 的区域求平均值避免棋子边缘的高光干扰。这样处理后输出一个二维列表每个元素标记为empty、black或white就是最终结果。3.4 输出结果的可视化中间图片和最终图片怎么看脚本运行中会生成大量中间图片比如img_temp.jpg、img_temp1.jpg、img_temp2.jpg、img_temp3.jpg这些是为了方便调试而保存的阶段性结果。你打开这些图对比着看能快速定位问题出在哪个环节。我强烈建议你在跑项目时保持这个「每步一存图」的习惯。灰度图能确认颜色转换是否正常边缘图能确认 Canny 阈值是否合适轮廓图能确认棋盘有没有被正确框出来圆检测结果图能确认HoughCircles的参数是否得当。不要嫌麻烦图像处理项目 debug 的最大障碍就是「看不见中间状态」。还有一个checkerboard目录里面应该是作者调试棋盘检测时的素材和输出。你把你的测试图放进去脚本输出也写到这里保持工作目录整洁后面做参数对比实验时会非常方便。4. 参数调优实战用现有素材把识别准确率拉上去4.1 光照和噪声是识别精度的第一杀手预处理参数怎么调如果你的检测结果不稳定先别急着动HoughCircles的参数多半是预处理环节出了问题。拍照时最常见的干扰有两种一是局部过曝棋子表面反光变成白色光斑白子和黑子在这种光斑下特征会混淆二是阴影棋子边缘被阴影覆盖圆检测时轮廓断裂一个完整的圆被拆成两段弧。处理局部反光我会在灰度化之后加一步cv2.equalizeHist直方图均衡化它能拉伸灰度分布把过曝区域和阴影区域的细节拉回来。但注意直方图均衡化会让整张图的对比度都变强如果原图比较正常加了反而会产生伪边缘。建议只在检测效果不好时尝试。处理阴影和噪点高斯模糊的核大小是关键。(5, 5)是通用值图片分辨率越高核应该越大1080p 的图用(7, 7)也不过分。核越大边缘越平滑但代价是细小的边缘特征比如棋子的高光也会被磨掉找到图像真实边缘和抑制噪声的平衡点本质上是一个实验过程。4.2 HoughCircles 参数组合的经验区间与调试方法HoughCircles是全局检测中最让新手头疼的函数因为它的参数相互制约调一个可能影响另外三个的效果。我根据经验给出一个调试顺序先固定dp1.2和minRadius/maxRadius把param1设为 100然后从小到大调param2。param2太小比如低于 20几乎每个高梯度区域都会被识别成圆画面上一堆重叠圆param2太大比如高于 80只有边缘极其清晰的圆才能被检测到黑白棋子可能漏一半。从 30 开始每次加 10观察检测结果的变化。接下来调minDist。它防止的是同一个圆被重复检测出多个结果。minDist太小一个棋子周围会有五六个圆太大相邻棋子的圆会被吞掉。围棋棋盘的棋间距是固定的所以minDist也是一个固定值我一般设成棋间距的 0.8 倍。最后调minRadius和maxRadius这两个值决定了检测圆的尺寸范围。棋子在图片上占据的像素直径可以通过棋盘宽度除以 18 估算比如棋盘宽 900 像素棋子直径就是 50 像素那么minRadius设 20、maxRadius设 30能有效过滤掉图片里其他圆形物体比如瓶盖、圆形标志。4.3 棋盘检测失败的常见输入什么图会让脚本直接翻车先说结论最让这套脚本翻车的三类图是——棋盘只占画面一角、棋盘被物体遮挡一部分、棋盘颜色和背景高度接近。这三种情况都会让findContours找不到完整的四边形轮廓导致棋盘定位失败。对于第一类图我的处理是先做边缘连接cv2.morphologyEx用闭运算把断裂的棋盘边缘补上。对于第二类图正规做法是换用基于直线交点网格拟合的算法但课程设计阶段我建议直接换一张完整视角的图把精力花在主流程的精度优化上。第三类图最棘手棋盘边缘和背景混在一起时只靠轮廓是分不出来的必须引入颜色特征来判断哪个区域更像棋盘这就超出了基础脚本的范畴。还有一类输入是src.jpg这种带 logo 或水印的测试图水印恰好在棋盘区域内时边缘检测会把水印的轮廓也当成候选边。这时候可以额外加一个长宽比过滤棋盘接近正方形长宽比在 0.81.2 之间能过滤掉大量无关矩形。5. 避坑记录这套围棋识别项目最常见的五个坑5.1 坑一cv2.Canny阈值设错边缘图一片花或一片黑现象运行 test1 后输出的边缘图要么全是密密麻麻的短线要么几乎全白找不到清晰的棋盘轮廓。原因Canny 的两个阈值和图片的对比度不匹配。低阈值太高会过滤掉真实边缘高阈值太低会把噪点全部保留。解决先打印边缘图的非零像素占比看整体密度。占比超过 30% 说明阈值偏低调高 50 精度同时把低阈值也提高占比低于 5% 说明阈值偏高把高阈值降到 100 附近。我习惯用(50, 150)起步然后以 20 为步长上下调。5.2 坑二import cv2报ModuleNotFoundError或cv2.error现象脚本第一行import cv2就报错或者执行到某一句时抛cv2.error: OpenCV(4.4.0)的 C 层异常。原因前者是 OpenCV 没有装在当前 Python 环境里后者是 OpenCV 版本和 NumPy 版本冲突或者调用了当前版本已移除的 API。解决第一步确认环境which python或python -c import sys; print(sys.executable)看当前解释器路径再pip list看包列表。第二步如果是版本冲突卸载重装指定版本pip uninstall opencv-python numpy然后pip install opencv-python4.5.5.64 numpy1.21.6。这个组合在 Python 3.8 下经过验证是稳定的。5.3 坑三HoughCircles检测出几十个重叠圆无法确定棋子的真实位置现象test2 的运行结果图上一个棋子的位置上有多个圆重叠或者棋盘空白区域也被圈出圆。原因param2太低或者minDist设定小于棋子间距导致局部梯度峰值被重复响应。解决调高param2从 50 起始逐步加。同时把minDist设为棋间距的 0.8 倍这一步能消除大部分重叠圆。如果还有零星误检再检查minRadius/maxRadius范围是否包含了图片里其他圆形物体。5.4 坑四斜拍或透视变形下棋盘网格点定位偏移严重现象棋盘定位成功但交叉点画出来是歪的越到边角偏得越厉害。原因脚本没有做透视矫正直接用原图坐标近似网格点。棋盘倾斜角度越大误差越大角落位置的几何误差可以达到几十像素。解决手动加透视矫正。用getPerspectiveTransform把棋盘四角映射到标准矩形再在矫正图上计算网格点。具体代码我放在第六章直接替换原来的坐标计算逻辑即可。5.5 坑五黑白棋子判断反了或大量漏检现象有些明显是黑子的位置被标记成白子或者整片区域的棋子都没被检测出来。原因颜色判断逻辑用的可能是单点像素而棋子边缘的高光反射让黑子中心也变成了亮色。漏检则是圆检测参数和实际棋子的尺寸不匹配。解决颜色判断改为区域均值。以圆心为中心取边长为棋子半径的正方形区域对该区域的灰度图求平均值大于 128 判定为白子否则为黑子。漏检时检查minRadius和maxRadius先手动量一张图上棋子的直径像素数再倒推算半径范围。6. 进阶用法给项目补上透视矫正和批量识别从演示变实用import cv2 import numpy as np # 假设 src 是包含棋盘的原始图像 def perspective_correct(src, pts): # pts 是棋盘四角按左上、右上、右下、左下排列 width 900 height 900 dst np.array([[0, 0], [width - 1, 0], [width - 1, height - 1], [0, height - 1]], dtypenp.float32) matrix cv2.getPerspectiveTransform(pts.astype(np.float32), dst) warped cv2.warpPerspective(src, matrix, (width, height)) return warped这段代码的作用是把你从approxPolyDP得到的四个角点映射到一个标准的 900×900 正方形上。width和height设成相同的值是为了保持棋盘网格的正方形属性这样后续按 19×19 等分时每个交叉点不会因为长宽比例不同而出现横向纵向间距不一致的问题。900 这个数字可以随意改但建议用 19 的倍数比如 874 或 912这样网格均匀分割。矫正之后交叉点的计算就变成纯索引操作第 i 行第 j 列的点是(j * (width // 18), i * (height // 18))。之所以除以 18是因为 19 条线之间只有 18 个间隔。这里有一个比 900 更讲究的细节width // 18是整数除法如果 width 设置成 900那么900 // 18 50交叉点恰好落在整数像素坐标上误差为零。接着是批量识别。单个脚本处理单张图容易但毕设演示时你可能要一口气跑完一个目录下的二十张图。批量处理的代码不复杂核心是用os.listdir遍历文件把每张图的识别结果存到独立的输出文件名里避免覆盖。import os import glob input_dir img_temp output_dir output os.makedirs(output_dir, exist_okTrue) for img_path in glob.glob(os.path.join(input_dir, *.jpg)): img cv2.imread(img_path) # 这里放你的棋盘定位 棋子识别逻辑 # 处理完后保存文件名带上前缀防止覆盖 basename os.path.basename(img_path) cv2.imwrite(os.path.join(output_dir, result_ basename), img)glob的匹配模式*.jpg只匹配 jpg 文件如果目录里还有 png比如项目里的3.png、4.png需要再加一行匹配 png 的规则或者把模式改成*.*再按扩展名过滤。另外文件的读取顺序在glob里是不保证有序的如果你希望按文件名顺序处理用sorted(glob.glob(...))包一层。批量处理的另一个价值在于你可以先把检测结果全部输出成带棋盘网格和棋子标注的图片再人工浏览一遍快速定位哪类图片的检出率低。这个「批量跑 人工看缩略图」的工作流比单独调一张图参数然后祈祷其他图都能过要可靠得多也是我后来做所有图像识别项目都会走的一遍流程。从那以后我拿到任何一套 OpenCV 项目源码第一件事就是造一个测试图片目录、跑一遍批量处理、对着所有输出图看一遍而不是只跑通那张 demo 图就宣告结束。这套围棋识别项目也是如此——它的价值不在代码本身有多巧妙而在于你能用它跑通、看懂、然后加上自己想要的改进。希望帮到你。本文还有配套的精品资源点击获取
返回列表