用OpenCV编程计算围棋棋盘黑白子总数
2017-05-23 15:05
447 查看
今天柯洁开始对战AlphaGo,虽然对围棋一窍不通,但这种大事我也在关注,知道胜负与最后棋盘上黑白子的数量有关。当我看到结局的棋盘时,黑白棋子那么多,一时也看不出来谁多谁少,就想着编个程序来计算一下。程序很简单,就给大家分享一下,对OpenCV初学者或许有些帮助吧。
先在网上找到了一个围棋图片,图片如下
由于黑白子颜色反差非常明显,只需要将原图转化为灰度图,再用合适的阈值进行二值化就可以。白子的灰度值都在200上以,我就以190为阈值就行二值化,黑子的灰度在80以下,便以90为阈值进行二值化。
以190为阈值进行二值化后,图片变成下图这个样子
这时画面噪点太多,我们需要腐蚀一下,用圆形算子对图片进行腐蚀,代码如下
腐蚀后的效果还是较为理想的,如下图所示
到了这一步,想必大家都知道下一步该怎么做了——直接找连通集,只要边缘点数满足条件,就可以计数为一个白子。相关代码如下
【备注一下】,程序中的lowLimit和topLimit指的是边缘点数的上下限,边缘点数也就是周长的意思,所以乘以3.1416。只有该连通集的边缘点数在lowLimit和topLimit之间,才认为是白子;mom.m10
/ mom.m00和mom.m01 / mom.m00是求质心坐标,-5和+10是将打印的数字移到中心处。
黑子的计算方法是一样的,只不过二值化时,需使用关键字CV_THRESH_BINARY_INV。完成后,最终的效果图如下
完整的代码如下
代码写得匆忙,可能很粗糙,请匆见怪!如果自己想实验,像阈值这些地方,可以自己想办法确定,不一定取上面例子中的阈值。还有腐蚀,也不一定是cvCreateStructuringElementEx(9, 9, 4, 4, CV_SHAPE_ELLIPSE); 同样可以根据不同情况,做相应调整。
先在网上找到了一个围棋图片,图片如下
由于黑白子颜色反差非常明显,只需要将原图转化为灰度图,再用合适的阈值进行二值化就可以。白子的灰度值都在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);
【备注一下】,程序中的lowLimit和topLimit指的是边缘点数的上下限,边缘点数也就是周长的意思,所以乘以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); 同样可以根据不同情况,做相应调整。
相关文章推荐
- OpenCV编程->双目匹配计算棋盘格角点计算及两轴位移计算
- 用opencv计算棋盘格内角点坐标(通过4个内角点获得转换矩阵),并同时用halcon和opnecv对图像进行透视变换
- OpenCV2编程手册笔记之 4.2计算图像的直方图(彩色)
- hadoop编程之mapreduce,计算总数和平均数
- OpenCV2编程手册笔记之 6.5计算图像的拉普拉斯变换
- OpenCV2编程手册笔记之 4.2计算图像的直方图
- OpenCV2编程手册笔记之 7.6计算连通区域的形状描述符
- 用opencv计算棋盘格内角点坐标(通过多个内角点获得转换矩阵),并同时用halcon和opnecv对图像进行透视变换
- javascript/HTML实现多个text控件自动/动态计算总数
- 编写“线围棋”程序-1-画棋盘
- TMP(Template metaprogramming)模板元编程的起手程序:编译期计算阶乘
- 多核处理器并行计算编程利器OpenPM
- 计算UCS2编码的长度以及注意事项(C编程实现)
- GPU精粹2——高性能图形芯片和通用计算编程技巧 流式编程 1
- OpenCV初次使用配置及编程示例
- OpenCV 编程入门
- 金山招聘题目: 编程计算从1到2008080808之间的整数有多少个含有数字7
- NVIDIA CUDA统一计算设备架构编程手册(1)
- 并行计算简介和多核CPU编程Demo
- 围棋棋盘的手工画法