用OpenCV编程计算围棋棋盘黑白子总数

    今天柯洁开始对战AlphaGo,虽然对围棋一窍不通,但这种大事我也在关注,知道胜负与最后棋盘上黑白子的数量有关。当我看到结局的棋盘时,黑白棋子那么多,一时也看不出来谁多谁少,就想着编个程序来计算一下。程序很简单,就给大家分享一下,对OpenCV初学者或许有些帮助吧。

    先在网上找到了一个围棋图片,图片如下
用OpenCV编程计算围棋棋盘黑白子总数_第1张图片
由于黑白子颜色反差非常明显,只需要将原图转化为灰度图,再用合适的阈值进行二值化就可以。白子的灰度值都在200上以,我就以190为阈值就行二值化,黑子的灰度在80以下,便以90为阈值进行二值化。
     以190为阈值进行二值化后,图片变成下图这个样子
用OpenCV编程计算围棋棋盘黑白子总数_第2张图片
    这时画面噪点太多,我们需要腐蚀一下,用圆形算子对图片进行腐蚀,代码如下
IplConvKernel* elem = cvCreateStructuringElementEx(7, 7, 3, 3, CV_SHAPE_ELLIPSE);
cvErode(bw, bw, elem, 1);
cvReleaseStructuringElement(&elem);
    腐蚀后的效果还是较为理想的,如下图所示
用OpenCV编程计算围棋棋盘黑白子总数_第3张图片
    到了这一步,想必大家都知道下一步该怎么做了——直接找连通集,只要边缘点数满足条件,就可以计数为一个白子。相关代码如下
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
cvFindContours(bw, storage, &cont, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, cvPoint(0, 0));
int white = 0;
float lowLimit = src->height / 50 * 3.1416;
float topLimit = src->height / 20 * 3.1416;
cout << lowLimit << '\t' << topLimit << endl;
for (; cont; cont = cont->h_next)
{
	int count = cont->total;
	if (count < lowLimit || count > topLimit)
		continue;
	white++;
	CvMoments mom;
	cvMoments(cont, &mom, true);
	CvPoint p;
	p.x = mom.m10 / mom.m00 - 5;
	p.y = mom.m01 / mom.m00 + 10;
	CvFont font = cvFont(0.7, 0.8);
	cvPutText(src, to_string(white).c_str(), p, &font, cvScalar(0, 0, 0));
}
cout << white << endl;
cvReleaseMemStorage(&storage);	
    【备注一下】,程序中的 lowLimittopLimit指的是边缘点数的上下限,边缘点数也就是周长的意思,所以乘以3.1416。只有该连通集的边缘点数在lowLimit和topLimit之间,才认为是白子; mom.m10 / mom.m00mom.m01 / mom.m00是求质心坐标,-5和+10是将打印的数字移到中心处。

    黑子的计算方法是一样的,只不过二值化时,需使用关键字 CV_THRESH_BINARY_INV。完成后,最终的效果图如下
用OpenCV编程计算围棋棋盘黑白子总数_第4张图片

    完整的代码如下
#include 
#include 
#include 
#include 
using namespace std;
#define THRESH1	190
#define THRESH2	100

void showImg(IplImage* img, string str)
{
	cvNamedWindow(str.c_str(), CV_WINDOW_AUTOSIZE);
	cvShowImage(str.c_str(), img);
}

int main()
{
	IplImage *src = cvLoadImage("E:/qipan.jpg");	
	IplImage* gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);	
	cvCvtColor(src, gray, CV_RGB2GRAY);	
	IplImage* bw = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
	//以下部分是计算白棋
	cvThreshold(gray, bw, THRESH1, 255, CV_THRESH_BINARY);	
	IplConvKernel* elem = cvCreateStructuringElementEx(7, 7, 3, 3, CV_SHAPE_ELLIPSE);
	cvErode(bw, bw, elem, 1);
	cvReleaseStructuringElement(&elem);
	/*showImg(bw, "二值化");*/
	/*cvSaveImage("E:/result2.jpg", bw);*/
	CvMemStorage* storage = cvCreateMemStorage(0);
	CvSeq* cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	cvFindContours(bw, storage, &cont, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, cvPoint(0, 0));
	int white = 0;
	float lowLimit = src->height / 50 * 3.1416;
	float topLimit = src->height / 20 * 3.1416;
	cout << lowLimit << '\t' << topLimit << endl;
	for (; cont; cont = cont->h_next)
	{
		int count = cont->total;
		if (count < lowLimit || count > topLimit)
			continue;
		white++;
		CvMoments mom;
		cvMoments(cont, &mom, true);
		CvPoint p;
		p.x = mom.m10 / mom.m00 - 5;
		p.y = mom.m01 / mom.m00 + 10;
		CvFont font = cvFont(0.7, 0.8);
		cvPutText(src, to_string(white).c_str(), p, &font, cvScalar(0, 0, 0));
	}
	cout << white << endl;
	cvReleaseMemStorage(&storage);	
	//以下部分是计算黑棋
	cvThreshold(gray, bw, THRESH2, 255, CV_THRESH_BINARY_INV);
	elem = cvCreateStructuringElementEx(9, 9, 4, 4, CV_SHAPE_ELLIPSE);
	//cvDilate(bw, bw, elem, 1);
	cvErode(bw, bw, elem, 1);
	cvReleaseStructuringElement(&elem);
	/*showImg(bw, "二值化1");*/
	storage = cvCreateMemStorage(0);
	CvSeq* cont1 = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
	cvFindContours(bw, storage, &cont1, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, cvPoint(0, 0));
	int black = 0;
	for (; cont1; cont1 = cont1->h_next)
	{
		int count = cont1->total;
		if (count < 10 || count > 100)
			continue;
		black++;
		CvMoments mom;
		cvMoments(cont1, &mom, true);
		CvPoint p;
		p.x = mom.m10 / mom.m00 - 10;
		p.y = mom.m01 / mom.m00;
		CvFont font = cvFont(0.7, 0.8);
		cvPutText(src, to_string(black).c_str(), p, &font, cvScalar(255, 255, 255));
	}
	cvReleaseMemStorage(&storage);
	cout << black << endl;
	CvFont font = cvFont(1, 0.5);
	string text = "Black chessman: " + to_string(black);
	cvPutText(src, text.c_str(), cvPoint(10, 20), &font, cvScalar(255, 0, 0));
	text = "White chessman: " + to_string(white);
	cvPutText(src, text.c_str(), cvPoint(10, 45), &font, cvScalar(0, 0, 255));
	cvSaveImage("E:/result.jpg", src);
	/*showImg(src, "对比");*/		
	cvReleaseImage(&src);
	cvReleaseImage(&gray);
	cvReleaseImage(&bw);
	
	cvWaitKey(0);
	return 0;
}

    代码写得匆忙,可能很粗糙,请匆见怪!如果自己想实验,像阈值这些地方,可以自己想办法确定,不一定取上面例子中的阈值。还有腐蚀,也不一定是 cvCreateStructuringElementEx(9, 9, 4, 4, CV_SHAPE_ELLIPSE); 同样可以根据不同情况,做相应调整。

你可能感兴趣的:(OpenCV)