前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C# OpenCV | 手把手教你用传统方法实现骰子识别

C# OpenCV | 手把手教你用传统方法实现骰子识别

作者头像
Color Space
发布2022-02-08 17:00:57
9940
发布2022-02-08 17:00:57
举报

导读

本文给大家分享一个用C# OpenCV传统方法实现骰子识别的小案例。

背景介绍

这个案例是2018年初我给别人做的一个小项目,当时还没有现在这么多可以即拿即用的目标检测网络(比如SSD/Yolo等),所以当时是用传统的图像处理方法实现的。后来我的TensorFlow视频教程中也拿它来做目标检测案例讲解过:

TensorFlow基础与应用实战高清视频教程

接下来我们介绍用传统图像处理方法实现骰子识别的详细步骤!

实现步骤

一、需求分析

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

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

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

二、实现步骤

测试图片:

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

代码语言:javascript
复制
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转Hsv
ScalarArray 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】先通过找轮廓方法,填充孔洞,然后做距离变换+阈值处理进一步定位骰子本体;

代码语言:javascript
复制
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转8U
CvInvoke.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:

代码语言:javascript
复制
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检测来判断点数,也可以用模板匹配来判断,下面以模板匹配为例:

模板图:

识别结果与代码:

代码语言:javascript
复制
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;
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 OpenCV与AI深度学习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景介绍
  • 实现步骤
相关产品与服务
图像识别
腾讯云图像识别基于深度学习等人工智能技术,提供车辆,物体及场景等检测和识别服务, 已上线产品子功能包含车辆识别,商品识别,宠物识别,文件封识别等,更多功能接口敬请期待。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档