## 实现步骤

1. 图片如上，骰子容器是透明的，骰子数量固定为3颗，骰子本体颜色为白色，点数颜色有黑色和红色；

2. 骰子可能会出现紧靠、轻微反光和倾斜的情况；

3. 在正常情况下(倾斜和反光不严重)，准确识别每个骰子点数，单张图片识别不超过300ms。

【1】 通过HSV阈值提取骰子本体；

`int h_min = 0, s_min = 0, v_min = 200;int h_max = 180, s_max = 80, v_max = 255;CvInvoke.CvtColor(img2, hsv_img, ColorConversion.Bgr2Hsv);//Bgr转HsvScalarArray hsv_min = new ScalarArray(new MCvScalar(h_min, s_min, v_min));ScalarArray hsv_max = new ScalarArray(new MCvScalar(h_max, s_max, v_max));CvInvoke.InRange(hsv_img, hsv_min, hsv_max, mask);CvInvoke.MedianBlur(mask, mask, 5);CvInvoke.Imwrite("mask.jpg", mask);`

【2】先通过找轮廓方法，填充孔洞，然后做距离变换+阈值处理进一步定位骰子本体；

`VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();CvInvoke.FindContours(mask, contours, null, RetrType.External, ChainApproxMethod.ChainApproxNone);CvInvoke.DrawContours(mask, contours, -1, new MCvScalar(255, 255, 255), -1);Mat dist_transform = new Mat();Mat sure_fg = new Mat();CvInvoke.DistanceTransform(mask, dist_transform, null, DistType.L2, 5);CvInvoke.ConvertScaleAbs(dist_transform, dist_transform, 1, 0); //32F转8UCvInvoke.Imwrite("dist_transform.jpg", dist_transform);double[] minValues, maxValues;Point[] minLoc, maxLoc;dist_transform.MinMax(out minValues, out maxValues, out minLoc, out maxLoc);CvInvoke.Threshold(dist_transform, sure_fg, maxValues[0] * 0.7, 255, ThresholdType.Binary); //二值化CvInvoke.Imwrite("sure_fg.jpg", sure_fg);`

【3】找轮廓，按照固定大小截取ROI：

`Mat zero_img = sure_fg.Clone();VectorOfVectorOfPoint contours2 = new VectorOfVectorOfPoint();CvInvoke.FindContours(zero_img, contours2, null, RetrType.External, ChainApproxMethod.ChainApproxNone);for (int i = 0; i < contours2.Size; i++){    RotatedRect rotatedRect = CvInvoke.MinAreaRect(contours2[i]);    int x0 = (int)rotatedRect.Center.X;    int y0 = (int)rotatedRect.Center.Y;    Mat ROI = new Mat(imgInput, new Rectangle(x0-150, y0-150, 300, 300)).Clone();    CvInvoke.Imshow("ROI", ROI);    //CvInvoke.WaitKey(0);    int num = DiceRecognize(ROI);    CvInvoke.Circle(resultImg, new Point(x0, y0), 150, new MCvScalar(0, 0, 255), 10);    CvInvoke.PutText(resultImg, num.ToString(), new Point(x0-150, y0-150), FontFace.HersheyComplexSmall, 6,                        new MCvScalar(0, 255, 0), 10);}`

【4】对每个ROI图像分别识别点数，可以使用Blob检测来判断点数，也可以用模板匹配来判断，下面以模板匹配为例：

`private int DiceRecognize(Mat imgROI){    int diceNum = 0;    int diceNum1 = 0, diceNum2 = 0, diceNum3 = 0, diceNum4 = 0;    Mat black_img = Mat.Zeros(imgROI.Rows, imgROI.Cols, DepthType.Cv8U, 1);    Mat black_img2 = Mat.Zeros(imgROI.Rows, imgROI.Cols, DepthType.Cv8U, 1);    Mat black_img3 = Mat.Zeros(imgROI.Rows, imgROI.Cols, DepthType.Cv8U, 1);    Mat black_img4 = Mat.Zeros(imgROI.Rows, imgROI.Cols, DepthType.Cv8U, 1);    int w1 = template1.Cols, h1 = template1.Rows;    int w2 = template2.Cols, h2 = template2.Rows;    int w3 = template3.Cols, h3 = template3.Rows;    int w4 = template4.Cols, h4 = template4.Rows;    int matchImg1_rows = imgROI.Rows - h1 + 1;    int matchImg1_cols = imgROI.Cols - w1 + 1;    int matchImg2_rows = imgROI.Rows - h2 + 1;    int matchImg2_cols = imgROI.Cols - w2 + 1;    int matchImg3_rows = imgROI.Rows - h3 + 1;    int matchImg3_cols = imgROI.Cols - w3 + 1;    int matchImg4_rows = imgROI.Rows - h4 + 1;    int matchImg4_cols = imgROI.Cols - w4 + 1;    double matchThres = 0.62;    Mat matchImg1 = new Mat(matchImg1_rows, matchImg1_cols, DepthType.Cv32F, 1);    Mat matchImg2 = new Mat(matchImg2_rows, matchImg2_cols, DepthType.Cv32F, 1);    Mat matchImg3= new Mat(matchImg3_rows, matchImg3_cols, DepthType.Cv32F, 1);    Mat matchImg4 = new Mat(matchImg4_rows, matchImg4_cols, DepthType.Cv32F, 1);    CvInvoke.MatchTemplate(imgROI, template1, matchImg1, TemplateMatchingType.CcoeffNormed);    CvInvoke.MatchTemplate(imgROI, template2, matchImg2, TemplateMatchingType.CcoeffNormed);    CvInvoke.MatchTemplate(imgROI, template3, matchImg3, TemplateMatchingType.CcoeffNormed);    CvInvoke.MatchTemplate(imgROI, template4, matchImg4, TemplateMatchingType.CcoeffNormed);    Image<Gray, Single> ImgMatch = matchImg1.ToImage<Gray, Single>();    Image<Gray, Single> ImgMatch2 = matchImg2.ToImage<Gray, Single>();    Image<Gray, Single> ImgMatch3 = matchImg3.ToImage<Gray, Single>();    Image<Gray, Single> ImgMatch4 = matchImg4.ToImage<Gray, Single>();    int tempW = 0, tempH = 0;    for (int i = 0; i < matchImg1_rows; i++)    {        for (int j = 0; j < matchImg1_cols; j++)        {            float matchValue = ImgMatch.Data[i, j, 0];            //if (matchValue >= matchThres && (Math.Abs(j - tempW) > 50) && (Math.Abs(i - tempH) > 50))            if (matchValue >= matchThres )            {                diceNum1++;                tempW = j;                tempH = i;                CvInvoke.Circle(ImgMatch, new Point(j, i), 50, new MCvScalar(0), -1);                //CvInvoke.Imshow("change", ImgMatch);            }        }    }    for (int i = 0; i < matchImg2_rows; i++)    {        for (int j = 0; j < matchImg2_cols; j++)        {            float matchValue = ImgMatch2.Data[i, j, 0];            //if (matchValue >= matchThres && (Math.Abs(j - tempW) > 50) && (Math.Abs(i - tempH) > 50))            if (matchValue >= matchThres)            {                diceNum2++;                tempW = j;                tempH = i;                CvInvoke.Circle(ImgMatch2, new Point(j, i), 50, new MCvScalar(0), -1);                //CvInvoke.Imshow("change", ImgMatch);            }        }    }    for (int i = 0; i < matchImg3_rows; i++)    {        for (int j = 0; j < matchImg3_cols; j++)        {            float matchValue = ImgMatch3.Data[i, j, 0];            //if (matchValue >= matchThres && (Math.Abs(j - tempW) > 50) && (Math.Abs(i - tempH) > 50))            if (matchValue >= matchThres)            {                diceNum3++;                tempW = j;                tempH = i;                CvInvoke.Circle(ImgMatch3, new Point(j, i), 50, new MCvScalar(0), -1);                //CvInvoke.Imshow("change", ImgMatch);            }        }    }    for (int i = 0; i < matchImg4_rows; i++)    {        for (int j = 0; j < matchImg4_cols; j++)        {            float matchValue = ImgMatch4.Data[i, j, 0];            //if (matchValue >= matchThres && (Math.Abs(j - tempW) > 50) && (Math.Abs(i - tempH) > 50))            if (matchValue >= matchThres)            {                diceNum4++;                tempW = j;                tempH = i;                CvInvoke.Circle(ImgMatch4, new Point(j, i), 50, new MCvScalar(0), -1);                //CvInvoke.Imshow("change", ImgMatch);            }        }    }    Console.WriteLine("------");    Console.WriteLine(diceNum1.ToString());    Console.WriteLine(diceNum2.ToString());    Console.WriteLine(diceNum3.ToString());    Console.WriteLine(diceNum4.ToString());    Console.WriteLine("------");    diceNum = Math.Max(Math.Max(diceNum1, diceNum2), Math.Max(diceNum3, diceNum4));    return diceNum;}`

【5】更多图片验证效果请查看下面视频：

—THE END—