OpenCV# 18 数字识别(打印题)

该博客介绍了一个基于控制台的图像数字识别程序,主要流程包括读取Numbers目录下的png文件,对图像进行二值化、轮廓检测、最小外接矩形提取、重置大小及通道转换等预处理,然后使用KNN进行训练和识别。程序通过FindContours找到图像轮廓,裁剪并调整大小,最后用KNearest找到最近邻进行识别。

该程序实现打印体数字的图像识别

该程序是基于控制台的程序

其中Program文件为主程序,主要体现目的性的主流程;SimpleOCR文件为主要逻辑实现文件;ImageInfo文件定义了一个供查询的训练、测试图像数据结构。

程序流程如下:

1、调用了一个自定义类simpleOCR的构造函数,查询发现,程序并没有实现此构造函数,即啥也没做。

2、调用simpleOCR.ReadTrainingImages函数,读取Numbers目录下所有的png文件。

主要代码如下:

2.1 foreach (var dir in new DirectoryInfo(path).GetDirectories())遍历给定目录下的子目录

2.2 var groupId = int.Parse(dir.Name);得到目录名或该目录下文件的标签

2.3 foreach (var imageFile in dir.GetFiles(ext)) 得到该目录下的所有文件

2.4 var image = processTrainingImage(new Mat(imageFile.FullName, ImreadModes.GrayScale));

该代码将读取图片并将图片处理为同样大小和单通道。具体逻辑为

2.4.1 Cv2.Threshold(gray, threshImage, 80, 255, ThresholdTypes.BinaryInv); 对图片做二值化处理

2.4.2 查找图片的外轮廓

 Point[][] contours;
            HierarchyIndex[] hierarchyIndexes;
            Cv2.FindContours(
                threshImage,
                out contours,
                out hierarchyIndexes,
                mode: RetrievalModes.CComp,
                method: ContourApproximationModes.ApproxSimple);

2.4.3 根据轮廓得到图片的最小外接矩形,并复制图片

 var boundingRect = Cv2.BoundingRect(contour); 

var roi = new Mat(threshImage, boundingRect); 

2.4.4将图片重置大小为10*10

 var resizedImage = new Mat();

 Cv2.Resize(roi, resizedImage, new Size(10, 10)); //resize to 10X10

2.4.5 将图片设置为单通道

resizedImage.ConvertTo(resizedImageFloat, MatType.CV_32FC1); //convert to float
                result = resizedImageFloat.Reshape(1, 1);

2.5将该规范图片存入list的数据结构

var images = new List<ImageInfo>();

images.Add(new ImageInfo
                    {
                        Image = image,
                        ImageId = imageId++,
                        ImageGroupId = groupId
                    });

3、调用 var knn = simpleOCR.TrainData(trainingImages);开始训练

此函数和常见的cv  knn训练相同,不再分析,代码如下:

public KNearest TrainData(IList<ImageInfo> trainingImages)
        {
            var samples = new Mat();
            foreach (var trainingImage in trainingImages)
            {
                samples.PushBack(trainingImage.Image);
            }

            var labels = trainingImages.Select(x => x.ImageGroupId).ToArray();
            var responses = new Mat(labels.Length, 1, MatType.CV_32SC1, labels);
            var tmp = responses.Reshape(1, 1); //make continuous
            var responseFloat = new Mat();
            tmp.ConvertTo(responseFloat, MatType.CV_32FC1); // Convert  to float

            var kNearest = KNearest.Create();
            kNearest.Train(samples, SampleTypes.RowSample, responseFloat); // Train with sample and responses
            return kNearest;
        }

4、调用simpleOCR.DoOCR(knn, @"..\..\Samples\sample1.png");进行测试。

这个函数也比较有研究价值

首先依然是灰度和二值化处理

v2.CvtColor(src, gray, ColorConversionCodes.BGRA2GRAY);

            var threshImage = new Mat();
            Cv2.Threshold(gray, threshImage, Thresh, ThresholdMaxVal, ThresholdTypes.BinaryInv); 

接着用FindContours寻找要识别的图片的外轮廓

Point[][] contours;
            HierarchyIndex[] hierarchyIndexes;
            Cv2.FindContours(
                threshImage,
                out contours,
                out hierarchyIndexes,
                mode: RetrievalModes.CComp,
                method: ContourApproximationModes.ApproxSimple);

然后对每一个封闭的外轮廓找到rect并完成框图绘制

var contour = contours[contourIndex];

                var boundingRect = Cv2.BoundingRect(contour); //Find bounding rect for each contour

                Cv2.Rectangle(src,
                    new Point(boundingRect.X, boundingRect.Y),
                    new Point(boundingRect.X + boundingRect.Width, boundingRect.Y + boundingRect.Height),
                    new Scalar(0, 0, 255),
                    2);

接着选择该区域,进行大小和通道转换

var roi = new Mat(threshImage, boundingRect); //Crop the image

                var resizedImage = new Mat();
                var resizedImageFloat = new Mat();
                Cv2.Resize(roi, resizedImage, new Size(10, 10)); //resize to 10X10
                resizedImage.ConvertTo(resizedImageFloat, MatType.CV_32FC1); //convert to float
                var result = resizedImageFloat.Reshape(1, 1);

接着用knn的函数进行识别

var detectedClass = (int)kNearest.FindNearest(result, 1, results, neighborResponses, dists);

最后利用puttext函数将数字写在新的目标图片上面

Cv2.PutText(
                    dst,
                    detectedClass.ToString(CultureInfo.InvariantCulture),
                    new Point(boundingRect.X, boundingRect.Y + boundingRect.Height),
                    0,
                    1,
                    new Scalar(0, 255, 0),
                    2);

当然,待识别的可能有很多图片,即原先找到的外轮廓可能很多,下面继续识别就可以了。

 contourIndex = hierarchyIndexes[contourIndex].Next;

完毕

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

健忘的松鼠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值