基于3DOF机械臂的五子棋机器人–图像处理
文章目录
1. 前言
在前面的文章中,机械设计部分和下位机的程序设计思路业已完成。接下来该讲解上位机的设计思路了,上位机主要包括两个部分,分别是:图像处理部分,用于实现棋盘网格的分割以及棋子的识别,最终得到当前棋局的棋盘数据;人工智能部分,通过输入当前棋盘的数据,进行分析下一手的棋子位置。
上位机的类和头文件中的公有函数和公有数据结构如图
下面,我们先来讲解一下图像处理部分,实现棋盘数据的读取
2. 图像处理的思路
2.1 棋盘网格构建
棋盘网格识别主要的原理就是,
- 对图片进行预处理(旋转和高斯滤波)
- 在棋盘外围有一层绿色的框,通过颜色识别找出绿色区域(HSV获得绿色掩膜,与原图进行与运算即可得到边框)
- 对绿色区域进行轮廓识别。
- 找到识别得到的最大轮廓
- 对找到的最大轮廓做多边形拟合,当且仅当拟合边数为4的时候,认为图片是有效的,如果边数不为4,图片无效,会进行重新采集
- 找到四个边界点,通过仿射变换矫正图片,使得网格图片占满界面
- 因为棋盘线是均匀的,按等分法对图像进行分割,横竖线的交界处就是棋盘网格线
//图像处理
bool ImageProcessor(cv::Mat& src) //图片预处理函数,输出图片为放射变换之后的图片
{
// 一、图片旋转
Rotation(src);
//二、高斯滤波
cv::GaussianBlur(src, src, cv::Size(3, 3), 0);
//三、提取轮廓边界点
vector<cv::Point2f> 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 < 9; i++)
{
for (int j = 0; j < 9; j++)
{
int h, s, v; //记录格点的hsv值
int flag = 0; //用于判断是否连续两次检测都是蓝色棋子
for (int time = 0; time < 2; time++)
{
//获取网格点的hsv值,识别蓝色棋子,这是我们的棋子。但是棋盘可能出现误识别,所以识别了2次,必须连续2次都是蓝色,才
//被认为是我们下的棋子
h = hsv.at<cv::Vec3b>(P(i,j))[0];
s = hsv.at<cv::Vec3b>(P(i, j))[1];
v = hsv.at<cv::Vec3b>(P(i, j))[2];
if (h >= 110 && h <= 124 && s >= 43 && s <= 255 && v >= 43 && v <= 255)
{
flag++;
if (flag == 2)
{
if (find(BlackList.begin(), BlackList.end(), cv::Point(i, j)) == BlackList.end())//如果这个位置的黑棋还没有加入棋盘,就加入
{
chess.at<uchar>(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 棋子识别
一开始我是用的标准的黑白棋子来做的,因为棋盘背景也是白色的,非常不好实现。
比如用颜色识别的话,判断网格点周围一小块区域的平均灰度值,来判断是黑色、白色还是无子。但是因为网格不是非常准,有可能对着空白区域,加了很多矫正方案也不行,因此放弃了这种方案
也使用了轮廓识别+霍夫圆变换的方法识别棋子,因为背景是白色的,白色棋子轮廓区别度特别小。并且白色特别容易反光。后来在镜头端增加了一个偏振光滤光片,希望能够解决反光问题。但是发现偏振光滤光片对平面的反光效果非常好,但是对凹凸面的反光其实是没有效果的,因此也放弃了。
还用了多模板匹配的方法希望找到黑白棋,但是由于棋盘是白色的,还是与白棋比较像,模板匹配找白色棋子效果非常差。
因此最终得到一个结论,如果想非常好的识别棋子,那么,棋盘和棋子一定要具有比较好的对比度。而且如果有条件配光源的话,最后搞一个,配置好环境光,减少反光,在输入层面解决问题,比图像处理上容易多了。
展示一些中间的过程图吧。