在前面的文章中,机械设计部分和下位机的程序设计思路业已完成。接下来该讲解上位机的设计思路了,上位机主要包括两个部分,分别是:图像处理部分,用于实现棋盘网格的分割以及棋子的识别,最终得到当前棋局的棋盘数据;人工智能部分,通过输入当前棋盘的数据,进行分析下一手的棋子位置。 上位机的类和头文件中的公有函数和公有数据结构如图
下面,我们先来讲解一下图像处理部分,实现棋盘数据的读取
2. 图像处理的思路 2.1 棋盘网格构建棋盘网格识别主要的原理就是,
对图片进行预处理(旋转和高斯滤波)在棋盘外围有一层绿色的框,通过颜色识别找出绿色区域(HSV获得绿色掩膜,与原图进行与运算即可得到边框)对绿色区域进行轮廓识别。找到识别得到的最大轮廓对找到的最大轮廓做多边形拟合,当且仅当拟合边数为4的时候,认为图片是有效的,如果边数不为4,图片无效,会进行重新采集找到四个边界点,通过仿射变换矫正图片,使得网格图片占满界面因为棋盘线是均匀的,按等分法对图像进行分割,横竖线的交界处就是棋盘网格线 //图像处理bool ImageProcessor(cv::Mat& src) //图片预处理函数,输出图片为放射变换之后的图片{// 一、图片旋转Rotation(src);//二、高斯滤波cv::GaussianBlur(src, src, cv::Size(3, 3), 0);//三、提取轮廓边界点vector rect_Point;if (!GetRectPoint(src, rect_Point))return false; //如果没有提取边界点成功,返回false//四、放射变换warp(src, rect_Point);return true;} //网格分割ChessPad chp;chp.ReloadGrid(src); //输入仿射变换之后的图片,并且进行等分分割,得到网格 2.2 棋子的识别因为棋子是红色和蓝色的,通过在网格点处进行颜色识别,即可判断棋子类型,并加入到棋盘中 默认蓝色棋子代表黑色,是人工下的。红色代表白色棋子,是计算机下的。
bool ChessPad::addBlackList() //加入黑色棋子{//01 仿射变换以后的图片转转hsv图片cv::Mat hsv;cv::cvtColor(src, hsv, CV_BGR2HSV);//02 识别黑棋(实际使用的是蓝色的)for (int i = 0; i int h, s, v; //记录格点的hsv值int flag = 0; //用于判断是否连续两次检测都是蓝色棋子for (int time = 0; time flag++;if (flag == 2){if (find(BlackList.begin(), BlackList.end(), cv::Point(i, j)) == BlackList.end())//如果这个位置的黑棋还没有加入棋盘,就加入{chess.at(j, i) = BLACK;BlackList.push_back(cv::Point(i, j));return true; //本次有插入棋子}}}else{break;}}}}return false;} 3. 盘点上位机设计中的一些问题 3.1 命名空间在写开始写库函数的时候,因为直接开放的命名空间,发现串口库中调用的一些windows函数和opencv中的函数是冲突的。而不开放命名空间,能够确认函数的来源,会更少的发生重名冲突的事件。
3.2 网格识别也使用过边缘检测+霍夫变换的方法识别过网格。但是霍夫变换会出现很多的重线,需要用最小二乘的方法进行重线合并,并且识别效率比较差。 这种方法可能开始只需要识别一次网格获得坐标,后期就不需要继续识别了(后面下了棋子了,也没有办法通过这种方法继续识别了)。不过,因为后面需要判断我什么时候下好了棋子,通过检测棋盘轮廓是否为四边形来实现的,因此需要进行实时的图片检测,因此不能只做一次网格识别。
3.3 棋子识别一开始我是用的标准的黑白棋子来做的,因为棋盘背景也是白色的,非常不好实现。 比如用颜色识别的话,判断网格点周围一小块区域的平均灰度值,来判断是黑色、白色还是无子。但是因为网格不是非常准,有可能对着空白区域,加了很多矫正方案也不行,因此放弃了这种方案 也使用了轮廓识别+霍夫圆变换的方法识别棋子,因为背景是白色的,白色棋子轮廓区别度特别小。并且白色特别容易反光。后来在镜头端增加了一个偏振光滤光片,希望能够解决反光问题。但是发现偏振光滤光片对平面的反光效果非常好,但是对凹凸面的反光其实是没有效果的,因此也放弃了。 还用了多模板匹配的方法希望找到黑白棋,但是由于棋盘是白色的,还是与白棋比较像,模板匹配找白色棋子效果非常差。 因此最终得到一个结论,如果想非常好的识别棋子,那么,棋盘和棋子一定要具有比较好的对比度。而且如果有条件配光源的话,最后搞一个,配置好环境光,减少反光,在输入层面解决问题,比图像处理上容易多了。 展示一些中间的过程图吧。