前言:
而今朋友们对“五子棋计算器在线”大概比较珍视,看官们都想要知道一些“五子棋计算器在线”的相关内容。那么小编同时在网络上收集了一些关于“五子棋计算器在线””的相关知识,希望你们能喜欢,小伙伴们一起来学习一下吧!基于opencv图像识别的AI五子棋系列3—查找棋盘四个顶点
该篇文章是基于opencv图像识别的AI五子棋系列的第三篇文章。通过该系列文章,你可以进阶opencv图像识别,熟悉图像识别与处理的大致流程,熟悉AI智能......
上一篇文章对五子棋棋盘进行了棋盘轮廓的查找,该篇文章,则在上一篇文章的基础上进行棋盘四个顶点的查找,查找到四个顶点后方便我们进行后续处理。
首先看一下处理的效果图
程序具体实现过程
在上篇文章基础上,我们查找到了棋盘的轮廓,如下图:
这里的轮廓是保存在
vector<vector<Point>> RectContours_fainal ;
里的。我们要在一个不规则的四边形中找到四个顶点还是需要一些技巧的。
这里我写了一个FindConnor() 函数来实现寻找到四个角点。函数实现如下:
/***** 2、输入单个轮廓,返回逆时针排序的4个角点*****/vector<Index_Point> FindConnor(vector<Point> RectContours){ ///**(1).根据距离条件找出三个角点 float distance = 0, distanceMax = 0; vector<Index_Point> connorPoint(4), connor_order(4); int i = 0; distance = 0; distanceMax = 0; for (i = 0; i < RectContours.size(); i++) {//找第一个角点 distance = getDistance(RectContours[i], RectContours[0]); if (distance>distanceMax) { distanceMax = distance; connorPoint[0].point = RectContours[i]; connorPoint[0].index = i; } } distance = 0; distanceMax = 0; for (i = 0; i < RectContours.size(); i++) {//找第二个角点 distance = getDistance(RectContours[i], connorPoint[0].point); if (distance>distanceMax) { distanceMax = distance; connorPoint[1].point = RectContours[i]; connorPoint[1].index = i; } } distance = 0; distanceMax = 0; for (i = 0; i < RectContours.size(); i++) {//找第三个角点 distance = getDistance(RectContours[i], connorPoint[0].point) + getDistance(RectContours[i], connorPoint[1].point); if (distance>distanceMax) { distanceMax = distance; connorPoint[2].point = RectContours[i]; connorPoint[2].index = i; } } ///**(2).对已经找到的角点排列 connor_order = ListConnor_0(connorPoint);// connorPoint = connor_order; ///**(3).找出3个怀疑是第四角点的点 vector<Index_Point> connor4_Doubt(3); distance = 0; distanceMax = 0; for (i = connorPoint[1].index; i < connorPoint[0].index; i++) {//1,0号角点之间找到怀疑是4角点的点 distance = getDistance(RectContours[i], connorPoint[0].point) + getDistance(RectContours[i], connorPoint[1].point); if (distance>distanceMax) { distanceMax = distance; connor4_Doubt[0].point = RectContours[i]; connor4_Doubt[0].index = i; } } distance = 0; distanceMax = 0; for (i = connorPoint[2].index; i < connorPoint[1].index; i++) {//2,1号角点之间找到怀疑是4角点的点 distance = getDistance(RectContours[i], connorPoint[2].point) + getDistance(RectContours[i], connorPoint[1].point); if (distance>distanceMax) { distanceMax = distance; connor4_Doubt[1].point = RectContours[i]; connor4_Doubt[1].index = i; } } distance = 0; distanceMax = 0; for (i = connorPoint[0].index; i < RectContours.size() + connorPoint[2].index; i++) {//0,2号角点之间找到怀疑是4角点的点 if (i< RectContours.size()) { distance = getDistance(RectContours[i], connorPoint[0].point) + getDistance(RectContours[i], connorPoint[2].point); if (distance>distanceMax) { distanceMax = distance; connor4_Doubt[2].point = RectContours[i]; connor4_Doubt[2].index = i; } } else { distance = getDistance(RectContours[i - RectContours.size()], connorPoint[0].point) + getDistance(RectContours[i - RectContours.size()], connorPoint[2].point); if (distance>distanceMax) { distanceMax = distance; connor4_Doubt[2].point = RectContours[i - RectContours.size()]; connor4_Doubt[2].index = i; } } } ///**(4).通过点到直线的距离找到第四个角点 if (getDist_P2L(connor4_Doubt[0].point, connorPoint[0].point, connorPoint[1].point)>10) { connorPoint[3] = connor4_Doubt[0]; } else if (getDist_P2L(connor4_Doubt[1].point, connorPoint[1].point, connorPoint[2].point)>10) { connorPoint[3] = connor4_Doubt[1]; } else if (getDist_P2L(connor4_Doubt[2].point, connorPoint[0].point, connorPoint[2].point)>10) { connorPoint[3] = connor4_Doubt[2]; } ///**(5).对四个角点按顺时针排序 connor_order = ListConnor(connorPoint); return connor_order;}
具体思路如下:
首先我们观察四边形,哪些点会是角点呢?显然是对角线这一特征,根据这一个特性,我们遍历所有的点,并一 一求出两点之间距离,在两个点距离最远的时候,肯定是四个角点中的两个。
找第一个顶点——第一个顶点怎么求,首先随意找轮廓中一个点,然后求任意两点距离,距离最大的那个肯定是个顶点,然后同理,以第一个点为基准,找第二个顶点。第三个顶点就是,距离第一个顶点和第二个顶点距离最远的那个点,然后对前三个点进行排序,最后再找第四个顶点,第四个顶点肯定在三个已知任意两个顶点的中间,求出来,然后再根据距离,就可以找到了。
然后我们利用ListConnor函数对找到的四个顶点进行排序,使它顺时针排序。方便后续我们透视变换用——透视变换需要指定顺序才能进行透视变换。
下面语句是将得到的四个角点画出来。
if (ConnorPoint_Output[0].size()>0) { vector<vector<Point>> RectContours; vector<Point> pts; for (int i = 0; i < ConnorPoint_Output[0].size(); i++) { Point one_ConnorGroup[4]; for (int j = 0; j < 4; j++) {//为单独一组角点赋值 one_ConnorGroup[j] = ConnorPoint_Output[j][i]; circle(srcImage0, one_ConnorGroup[j], 10, Scalar(0, 255, 0), -1); //第五个参数我设为-1,表明这是个实点。 } namedWindow("四个角点", 0); imshow("四个角点", srcImage0); waitKey(2); } } else { //没找到,返回下帧继续找 printf("[ALG ERROR][函数:%s][行号:%d],当前帧未找到方形仪表四边形四个角点,返回,下帧继续找 \n", __FUNCTION__, __LINE__); return ERROR; }完整源码如下:
/*****************************************************************************************五子棋棋盘棋子识别检测1、灰度化,二值化2、查找棋盘最外边轮廓3、找到棋盘四个顶点*****************************************************************************************/#include<opencv2/opencv.hpp>#include <iostream> #include <fstream> #include <stdlib.h> //srand()和rand()函数 #include <time.h> //time()函数 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/objdetect/objdetect.hpp> #include <opencv2/ml/ml.hpp> #include<opencv2\opencv.hpp>#include <opencv2\imgproc\types_c.h>#include<windows.h>using namespace std;using namespace cv;using namespace ml;struct Index_Point{//带引索的点 Point point; int index;};/********************************************************************************************函数功能 : 找每个轮廓的中心坐标*输入参数 :轮廓或者凸包*返 回 值 : 点向量*编写时间 : 2018.8.9*作 者 : diyun********************************************************************************************/vector< Point2f> find_lunkuo_zhongxin(vector<vector<Point>> dangban_RectContours){ vector<Point2f> zhongxin_zuobiao; /// 计算矩 vector<Moments> mu(dangban_RectContours.size()); for (int i = 0; i < dangban_RectContours.size(); i++) { mu[i] = moments(dangban_RectContours[i], false); } /// 计算中心矩: vector<Point2f> mc(dangban_RectContours.size()); for (int i = 0; i < dangban_RectContours.size(); i++) { mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00); zhongxin_zuobiao.push_back(mc[i]); } return zhongxin_zuobiao;}/***** 求两点间距离*****/float getDistance(Point pointO, Point pointA){ float distance; distance = powf((pointO.x - pointA.x), 2) + powf((pointO.y - pointA.y), 2); distance = sqrtf(distance); return distance;}//两点之间距离float getDistance_1(Point pointO, Point pointA){ float distance; distance = powf((pointO.x - pointA.x), 2) + powf((pointO.y - pointA.y), 2); distance = sqrtf(distance); return distance;}/***** 点到直线的距离:P到AB的距离*****/float getDist_P2L(Point pointP, Point pointA, Point pointB){ //求直线方程 int A = 0, B = 0, C = 0; A = pointA.y - pointB.y; B = pointB.x - pointA.x; C = pointA.x*pointB.y - pointA.y*pointB.x; //代入点到直线距离公式 float distance = 0; distance = ((float)abs(A*pointP.x + B*pointP.y + C)) / ((float)sqrtf(A*A + B*B)); return distance;}//辅助函数:/***** 输入角点组的前3个值,返回按Index由大到小排好的角点组*****/vector<Index_Point> ListConnor_0(vector<Index_Point> Point_list){ int i, j; vector<Index_Point> Point_Ordered(Point_list.size()); for (j = 0; j < Point_Ordered.size() - 1; j++) { if (j == 0) { for (i = 0; i < Point_list.size(); i++) { if (Point_list[i].index>Point_Ordered[j].index) { Point_Ordered[j].index = Point_list[i].index; Point_Ordered[j].point = Point_list[i].point; } } } else { for (i = 0; i < Point_list.size(); i++) { if (Point_list[i].index<Point_Ordered[j - 1].index&&Point_list[i].index>Point_Ordered[j].index) { Point_Ordered[j].index = Point_list[i].index; Point_Ordered[j].point = Point_list[i].point; } } } if (Point_list[j].index == 0) { Point_Ordered[2].index = Point_list[j].index; Point_Ordered[2].point = Point_list[j].point; } } return Point_Ordered;}//辅助函数:/***** 输入角点组,返回按Index由大到小排好的角点组*****/vector<Index_Point> ListConnor(vector<Index_Point> Point_list){ int i, j; vector<Index_Point> Point_Ordered(Point_list.size()); for (j = 0; j < Point_Ordered.size(); j++) { if (j == 0) { for (i = 0; i < Point_list.size(); i++) { if (Point_list[i].index>Point_Ordered[j].index) { Point_Ordered[j].index = Point_list[i].index; Point_Ordered[j].point = Point_list[i].point; } } } else { for (i = 0; i < Point_list.size(); i++) { if (Point_list[i].index<Point_Ordered[j - 1].index&&Point_list[i].index>Point_Ordered[j].index) { Point_Ordered[j].index = Point_list[i].index; Point_Ordered[j].point = Point_list[i].point; } } } if (Point_list[j].index == 0) { Point_Ordered[3].index = Point_list[j].index; Point_Ordered[3].point = Point_list[j].point; } } return Point_Ordered;}/***** 2、输入单个轮廓,返回逆时针排序的4个角点*****/vector<Index_Point> FindConnor(vector<Point> RectContours){ ///**(1).根据距离条件找出三个角点 float distance = 0, distanceMax = 0; vector<Index_Point> connorPoint(4), connor_order(4); int i = 0; distance = 0; distanceMax = 0; for (i = 0; i < RectContours.size(); i++) {//找第一个角点 distance = getDistance(RectContours[i], RectContours[0]); if (distance>distanceMax) { distanceMax = distance; connorPoint[0].point = RectContours[i]; connorPoint[0].index = i; } } distance = 0; distanceMax = 0; for (i = 0; i < RectContours.size(); i++) {//找第二个角点 distance = getDistance(RectContours[i], connorPoint[0].point); if (distance>distanceMax) { distanceMax = distance; connorPoint[1].point = RectContours[i]; connorPoint[1].index = i; } } distance = 0; distanceMax = 0; for (i = 0; i < RectContours.size(); i++) {//找第三个角点 distance = getDistance(RectContours[i], connorPoint[0].point) + getDistance(RectContours[i], connorPoint[1].point); if (distance>distanceMax) { distanceMax = distance; connorPoint[2].point = RectContours[i]; connorPoint[2].index = i; } } ///**(2).对已经找到的角点排列 connor_order = ListConnor_0(connorPoint);// connorPoint = connor_order; ///**(3).找出3个怀疑是第四角点的点 vector<Index_Point> connor4_Doubt(3); distance = 0; distanceMax = 0; for (i = connorPoint[1].index; i < connorPoint[0].index; i++) {//1,0号角点之间找到怀疑是4角点的点 distance = getDistance(RectContours[i], connorPoint[0].point) + getDistance(RectContours[i], connorPoint[1].point); if (distance>distanceMax) { distanceMax = distance; connor4_Doubt[0].point = RectContours[i]; connor4_Doubt[0].index = i; } } distance = 0; distanceMax = 0; for (i = connorPoint[2].index; i < connorPoint[1].index; i++) {//2,1号角点之间找到怀疑是4角点的点 distance = getDistance(RectContours[i], connorPoint[2].point) + getDistance(RectContours[i], connorPoint[1].point); if (distance>distanceMax) { distanceMax = distance; connor4_Doubt[1].point = RectContours[i]; connor4_Doubt[1].index = i; } } distance = 0; distanceMax = 0; for (i = connorPoint[0].index; i < RectContours.size() + connorPoint[2].index; i++) {//0,2号角点之间找到怀疑是4角点的点 if (i< RectContours.size()) { distance = getDistance(RectContours[i], connorPoint[0].point) + getDistance(RectContours[i], connorPoint[2].point); if (distance>distanceMax) { distanceMax = distance; connor4_Doubt[2].point = RectContours[i]; connor4_Doubt[2].index = i; } } else { distance = getDistance(RectContours[i - RectContours.size()], connorPoint[0].point) + getDistance(RectContours[i - RectContours.size()], connorPoint[2].point); if (distance>distanceMax) { distanceMax = distance; connor4_Doubt[2].point = RectContours[i - RectContours.size()]; connor4_Doubt[2].index = i; } } } ///**(4).通过点到直线的距离找到第四个角点 if (getDist_P2L(connor4_Doubt[0].point, connorPoint[0].point, connorPoint[1].point)>10) { connorPoint[3] = connor4_Doubt[0]; } else if (getDist_P2L(connor4_Doubt[1].point, connorPoint[1].point, connorPoint[2].point)>10) { connorPoint[3] = connor4_Doubt[1]; } else if (getDist_P2L(connor4_Doubt[2].point, connorPoint[0].point, connorPoint[2].point)>10) { connorPoint[3] = connor4_Doubt[2]; } ///**(5).对四个角点按顺时针排序 connor_order = ListConnor(connorPoint); return connor_order;}/***** 3、输入逆时针排序的4个角点,把0号点赋给右下角那个,最后逆时针输出*****/vector<Point> FindFirstPoint(vector<Index_Point> Point_list){ int i, Ymax, Y; float Kl, Kr; vector<Index_Point> Connor_Point_Ready; for (i = 0; i < Point_list.size(); i++) { Point_list[i].index = i; } for (i = 0; i < Point_list.size(); i++) {//找到所有上一级斜比下一级小的角点 if (i>0 && i<Point_list.size() - 1) { Kl = abs(((float)(Point_list[i].point.y - Point_list[i - 1].point.y)) / ((float)(Point_list[i].point.x - Point_list[i - 1].point.x))); Kr = abs(((float)(Point_list[i].point.y - Point_list[i + 1].point.y)) / ((float)(Point_list[i].point.x - Point_list[i + 1].point.x))); if (Kl<Kr) { Connor_Point_Ready.push_back(Point_list[i]); } } else if (i == 0) { Kl = abs(((float)(Point_list[i].point.y - Point_list[Point_list.size() - 1].point.y)) / ((float)(Point_list[i].point.x - Point_list[Point_list.size() - 1].point.x))); Kr = abs(((float)(Point_list[i].point.y - Point_list[i + 1].point.y)) / ((float)(Point_list[i].point.x - Point_list[i + 1].point.x))); if (Kl<Kr) { Connor_Point_Ready.push_back(Point_list[i]); } } else if (i == Point_list.size() - 1) { Kl = abs(((float)(Point_list[i].point.y - Point_list[i - 1].point.y)) / ((float)(Point_list[i].point.x - Point_list[i - 1].point.x))); Kr = abs(((float)(Point_list[i].point.y - Point_list[0].point.y)) / ((float)(Point_list[i].point.x - Point_list[0].point.x))); if (Kl<Kr) { Connor_Point_Ready.push_back(Point_list[i]); } } } Y = 0; Ymax = 0; Index_Point Point_First; Point_First.point = Point(0, 0); Point_First.index = 0; for (i = 0; i < Connor_Point_Ready.size(); i++) {//以y最大的那个点作为first点 if (Connor_Point_Ready[i].point.y >= Ymax) { Point_First.point = Connor_Point_Ready[i].point; Point_First.index = Connor_Point_Ready[i].index; Ymax = Connor_Point_Ready[i].point.y; } } vector<Point> PointOrder(Point_list.size()); if (Point_list.size()>0) { for (i = 0; i < Point_list.size(); i++) { if (Point_First.index + i< Point_list.size()) { PointOrder[i] = Point_list[Point_First.index + i].point; } else { PointOrder[i] = Point_list[Point_First.index + i - Point_list.size()].point; } } } return PointOrder;}int main(){ float ret = 0; Mat srcImage0 = imread("1.jpg");//读取图片 //Mat srcImage0 = imread("app/1.jpg");//读取图片 if (srcImage0.empty()) { cout << " 待预测图像不存在: " << endl; printf("[ALG ERROR][函数:%s][行号:%d],图片未正常读取,请检查输入路径十分正确 \n", __FUNCTION__, __LINE__, 1); cout << " 待预测图像不存在: " << endl; } Mat srcImage, srcImage1; resize(srcImage0, srcImage0, Size(1920, 1080)); cvtColor(srcImage0, srcImage1, CV_BGR2GRAY); namedWindow("灰度化", 0); imshow("灰度化", srcImage1); waitKey(2); srcImage = srcImage1 > 150; // 二值化 namedWindow("二值化", 0); imshow("二值化", srcImage); waitKey(2); /*****1、输入二值化图像返回有用的轮廓*****/ /*一般通过长度滤除剩下两个,选择内部那个*/ vector<vector<Point>> contours, RectContours, RectContours_fainal; int height = srcImage.rows; findContours(srcImage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);//找轮廓 CV_RETR_EXTERNAL,表示最外层轮廓 vector<vector<Point>> hull(contours.size());//用于存放凸包 vector<float> length(contours.size()); vector<float> Area_contours(contours.size()), Area_hull(contours.size()), Rectangularity(contours.size()), circularity(contours.size()); Mat drawing(srcImage.size(), CV_8UC3, cv::Scalar(255, 255, 255)); for (int i = 0; i < contours.size(); i++) {//历遍所有的轮廓 length[i] = arcLength(contours[i], true); if (length[i] >2000 && length[i] <12000) {//通过长度匹配滤除小轮廓 convexHull(Mat(contours[i]), hull[i], false);//把凸包找出来 Area_contours[i] = contourArea(contours[i]); Area_hull[i] = contourArea(hull[i]); Rectangularity[i] = Area_contours[i] / Area_hull[i]; printf("过滤后轮廓长度为 length=%5f\n", length[i]); circularity[i] = (4 * 3.1415*Area_contours[i]) / (length[i] * length[i]); //drawContours(drawing, contours, i, (255, 0, 0), 2);//得到方框 //if (Rectangularity[i]>0.9&&circularity[i]<0.8&&Area_hull[i]>8000 && Area_hull[i]<50000) {//通过凸包面积滤除不对的凸包,找到最终的四边形 RectContours.push_back(hull[i]);//把提取出来的方框导入到新的轮廓组 drawContours(drawing, hull, i, Scalar(0, 0, 0), 1);//得到方框 } } } if (0 == RectContours.size()) { printf("[ALG ERROR][函数:%s][行号:%d],RectContours.size=0,当前帧未找到轮廓,检查仪表二值化是否正常\n", __FUNCTION__, __LINE__); } /*识别结果显示,调试用*/ //namedWindow("找轮廓结果", 0); //imshow("找轮廓结果", drawing); //waitKey(2); /*将多个轮廓,弄成一个*/ cvtColor(drawing, drawing, CV_BGR2GRAY); drawing = drawing<150; // 二值化 //imshow("二值化", drawing); //将线连接起来 vector<Point2f> zhongxin_zuobiao; zhongxin_zuobiao = find_lunkuo_zhongxin(RectContours); if (zhongxin_zuobiao.size() >= 2) { for (int i = 0; i < zhongxin_zuobiao.size() - 1; i++) { for (int j = i + 1; j < zhongxin_zuobiao.size(); j++) { float dis_juxin = getDistance_1(zhongxin_zuobiao[i], zhongxin_zuobiao[j]); //if (dis_juxin <65) { line(drawing, zhongxin_zuobiao[i], zhongxin_zuobiao[j], Scalar(0), 40, LINE_AA); } } } } vector<vector<Point>> contours1; findContours(drawing, contours1, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//找轮廓 CV_RETR_EXTERNAL,表示最外层轮廓 Mat drawing1(drawing.size(), CV_8UC3, cv::Scalar(255, 255, 255)); vector<vector<Point>> hull1(contours1.size());//用于存放凸包 for (int i = 0; i < contours1.size(); i++) {//历遍所有的轮廓 //drawContours(drawing1, contours1, i, Scalar(0, 255, 0), 1);//得到方框 convexHull(Mat(contours1[i]), hull1[i], false);//把凸包找出来 RectContours_fainal.push_back(hull1[i]);//把提取出来的方框导入到新的轮廓组 drawContours(drawing1, hull1, i, Scalar(0, 0, 0), 4);//得到方框 } namedWindow("最终轮廓", 0); imshow("最终轮廓", drawing1); vector<Index_Point> ConnorPoint(4); vector<Point> ConnorPoint_ordered(4); vector<vector<Point>> ConnorPoint_Output(4); int i; for (i = 0; i < RectContours_fainal.size(); i++) { ConnorPoint = FindConnor(RectContours_fainal[i]); ConnorPoint_ordered = FindFirstPoint(ConnorPoint); ConnorPoint_Output[0].push_back(ConnorPoint_ordered[2]); ConnorPoint_Output[1].push_back(ConnorPoint_ordered[1]); ConnorPoint_Output[2].push_back(ConnorPoint_ordered[0]); ConnorPoint_Output[3].push_back(ConnorPoint_ordered[3]); } //ConnorPoint_Output[0][i]代表第i个轮廓的0号角点,顺时针输出 //return ConnorPoint_Output; if (ConnorPoint_Output[0].size()>0) { vector<vector<Point>> RectContours; vector<Point> pts; for (int i = 0; i < ConnorPoint_Output[0].size(); i++) { Point one_ConnorGroup[4]; for (int j = 0; j < 4; j++) {//为单独一组角点赋值 one_ConnorGroup[j] = ConnorPoint_Output[j][i]; circle(srcImage0, one_ConnorGroup[j], 10, Scalar(0, 255, 0), -1); //第五个参数我设为-1,表明这是个实点。 } namedWindow("四个角点", 0); imshow("四个角点", srcImage0); waitKey(2); } } else { //没找到,返回下帧继续找 printf("[ALG ERROR][函数:%s][行号:%d],当前帧未找到方形仪表四边形四个角点,返回,下帧继续找 \n", __FUNCTION__, __LINE__); return ERROR; } waitKey(0); return 0;}
下一篇文章,我们根据寻找到的四个角点,进行透视变换,将侧视图投影成俯视图。
标签: #五子棋计算器在线