龙空技术网

基于opencv图像识别的AI五子棋系列3—查找棋盘四个顶点

自动驾驶前沿 66

前言:

而今朋友们对“五子棋计算器在线”大概比较珍视,看官们都想要知道一些“五子棋计算器在线”的相关内容。那么小编同时在网络上收集了一些关于“五子棋计算器在线””的相关知识,希望你们能喜欢,小伙伴们一起来学习一下吧!

基于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;}

下一篇文章,我们根据寻找到的四个角点,进行透视变换,将侧视图投影成俯视图。

标签: #五子棋计算器在线