您的位置:首页 > 编程语言

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

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

    先在网上找到了一个围棋图片,图片如下



由于黑白子颜色反差非常明显,只需要将原图转化为灰度图,再用合适的阈值进行二值化就可以。白子的灰度值都在200上以,我就以190为阈值就行二值化,黑子的灰度在80以下,便以90为阈值进行二值化。
     以190为阈值进行二值化后,图片变成下图这个样子



    这时画面噪点太多,我们需要腐蚀一下,用圆形算子对图片进行腐蚀,代码如下
IplConvKernel* elem = cvCreateStructuringElementEx(7, 7, 3, 3, CV_SHAPE_ELLIPSE);
cvErode(bw, bw, elem, 1);
cvReleaseStructuringElement(&elem);

    腐蚀后的效果还是较为理想的,如下图所示



    到了这一步,想必大家都知道下一步该怎么做了——直接找连通集,只要边缘点数满足条件,就可以计数为一个白子。相关代码如下
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.m00
mom.m01 / mom.m00是求质心坐标,-5和+10是将打印的数字移到中心处。

    黑子的计算方法是一样的,只不过二值化时,需使用关键字CV_THRESH_BINARY_INV。完成后,最终的效果图如下



    完整的代码如下
#include <cv.h>
#include <highgui.h>
#include <iostream>
#include <string>
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); 同样可以根据不同情况,做相应调整。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: