现在很多场景需要使用的数字识别,比如银行卡识别,以及车牌识别等,在AI领域有很多图像识别算法,大多是居于opencv 或者谷歌开源的tesseract 识别.

由于公司业务需要,需要开发一个客户端程序,同时需要在xp这种老古董的机子上运行,故研究了如下几个数字识别方案,如果大家有更好的方案可以留言告知我,大家一起学习借鉴,不过需要支持XP系统,万分感谢!

ocr 识别的不同选择方案

•tesseract

•放弃:谷歌的开源tesseract ocr识别目前最新版本不支持xp系统

•云端ocr 识别接口(不适用)

•费用比较贵:•场景不同,我们的需求是可能毫秒级别就需要调用一次ocr 识别

•opencv

•概念:OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。

以上几种ocr 识别比较,最后选择了opencv 的方式进行ocr 数字识别,下面讲解通过ocr识别的基本流程和算法.

opencv 数字识别流程及算法解析

要通过opencv 进行数字识别离不开训练库的支持,需要对目标图片进行大量的训练,才能做到精准的识别出目标数字;下面我会分别讲解图片训练的过程及识别的过程.

opencv 识别算法原理

1.比如下面一张图片,需要从中识别出正确的数字,需要对图片进行灰度、二值化、腐蚀、膨胀、寻找数字轮廓、切割等一系列操作.

原图

灰度化图

二值化图

寻找轮廓

识别后的结果图

以上就是简单的图片进行灰度化、二值化、寻找数字轮廓得到的识别结果(==这是基于我之前训练过的数字模型下得到的识别结果==) 有些图片比较赋值,比如存在背景斜杠等的图片则需要一定的腐蚀或者膨胀等处理,才能寻找到正确的数字轮廓.

上面的说到我这里使用的是opencv 图像处理库进行的ocr 识别,那我这里简单介绍下C# 怎么使用opencv 图像处理看;

为了在xp上能够运行 我这里通过nuget 包引用了 OpenCvSharp-AnyCPU 第三方库,它使用的是opencv 2410 版本,你们如果不考虑xp系统的情况下开源使用最新的版本,最新版本支持了更多的识别算法.

右击你的个人项目,选择“管理Nuget程序包”。在包管理器页面中,点击“浏览”选项,然后在搜索框中键入“OpenCvSharp-AnyCPU”。选择最顶端的正确项目,并在右侧详情页中点击“安装”,等待安装完成即可。

以上的核心代码如下:

      private void runSimpleOCR(string pathName)       {            //构造opcvOcr 库,这里的是我单独对opencv 库进行的一次封装,加载训练库模板            var opencvOcr = new OpencvOcr($"{path}Template\\Traindata.xml", opencvOcrConfig: new OCR.Model.OpencvOcrConfig()            {                ErodeLevel = 2.5,                ThresholdType = OpenCvSharp.ThresholdType.Binary,                ZoomLevel = 2,            });            var img = new Bitmap(this.txbFilaName.Text);            var mat = img.ToMat();            //核心识别方法            var str = opencvOcr.GetText(mat, isDebug: true);            this.labContent.Content = str;        }

opencvOcr 的核心代码如下

        #region 属性        const double Thresh = 80;        const double ThresholdMaxVal = 255;        const int _minHeight = 35;        bool _isDebug = false;        CvKNearest _cvKNearest = null;        OpencvOcrConfig _config = new OpencvOcrConfig() { ZoomLevel = 2, ErodeLevel = 3 };        #endregion        ///         /// 构造函数        ///         /// 训练库完整路径        /// OCR相关配置信息        public OpencvOcr(string path, OpencvOcrConfig opencvOcrConfig = null)        {            if (string.IsNullOrEmpty(path))                throw new ArgumentNullException("path is not null");            if (opencvOcrConfig != null)                _config = opencvOcrConfig;            this.LoadKnearest(path);        }        ///         /// 加载Knn 训练库模型        ///         ///         ///         private CvKNearest LoadKnearest(string dataPathFile)        {            if (_cvKNearest == null)            {                using (var fs = new FileStorage(dataPathFile, FileStorageMode.Read))                {                    var samples = fs["samples"].ReadMat();                    var responses = fs["responses"].ReadMat();                    this._cvKNearest = new CvKNearest();                    this._cvKNearest.Train(samples, responses);                }            }            return _cvKNearest;        }        ///         /// OCR 识别,仅仅只能识别单行数字         ///         /// 训练库        /// 要识别的图片路径        public override string GetText(Mat src, bool isDebug = false)        {            this._isDebug = isDebug;            #region 图片处理            var respMat = MatProcessing(src, isDebug);            if (respMat == null)                return "";            #endregion            #region 查找轮廓            var sortRect = FindContours(respMat.FindContoursMat);            #endregion            return GetText(sortRect, respMat.ResourcMat, respMat.RoiResultMat);        }         ///         /// 查找轮廓        ///         ///         ///         private ListFindContours(Mat src)        {            try            {                #region 查找轮廓                Point[][] contours;                HierarchyIndex[] hierarchyIndexes;                Cv2.FindContours(                    src,                    out contours,                    out hierarchyIndexes,                    mode: OpenCvSharp.ContourRetrieval.External,                    method: OpenCvSharp.ContourChain.ApproxSimple);                if (contours.Length == 0)                    throw new NotSupportedException("Couldn't find any object in the image.");                #endregion                #region 单行排序(目前仅仅支持单行文字,多行文字顺序可能不对,按照x坐标进行排序)                var sortRect = GetSortRect(contours, hierarchyIndexes);                sortRect = sortRect.OrderBy(item => item.X).ToList();                #endregion                return sortRect;            }            catch { }            return null;        }        ///         /// 获得切割后的数量列表        ///         ///         ///         ///         private ListGetSortRect(Point[][] contours, HierarchyIndex[] hierarchyIndex)        {            var sortRect = new List();            var _contourIndex = 0;            while ((_contourIndex >= 0))            {                var contour = contours[_contourIndex];                var boundingRect = Cv2.BoundingRect(contour); //Find bounding rect for each contour                sortRect.Add(boundingRect);                _contourIndex = hierarchyIndex[_contourIndex].Next;            }            return sortRect;        }        ///         /// 是否放大        ///         ///         ///         private bool IsZoom(Mat src)        {            if (src.Height <= _minHeight)                return true;            return false;        }        private ListGetAlgoritmList(Mat src)        {            var result = new List();            var algorithm = this._config.Algorithm;            #region 自定义的算法            try            {                if (algorithm.Contains("|"))                {                    result = algorithm.Split('|').ToList()                        .Select(item => (EnumMatAlgorithmType)Convert.ToInt32(item))                        .ToList();                    if (!IsZoom(src))                        result.Remove(EnumMatAlgorithmType.Zoom);                    return result;                }            }            catch { }            #endregion            #region 默认算法            if (IsZoom(src))            {                result.Add(EnumMatAlgorithmType.Zoom);            }            if (this._config.ThresholdType == ThresholdType.Binary)            {                //result.Add(EnumMatAlgorithmType.Blur);                result.Add(EnumMatAlgorithmType.Gray);                result.Add(EnumMatAlgorithmType.Thresh);                if (this._config.DilateLevel > 0)                    result.Add(EnumMatAlgorithmType.Dilate);                result.Add(EnumMatAlgorithmType.Erode);                return result;            }            //result.Add(EnumMatAlgorithmType.Blur);            result.Add(EnumMatAlgorithmType.Gray);            result.Add(EnumMatAlgorithmType.Thresh);            if (this._config.DilateLevel > 0)                result.Add(EnumMatAlgorithmType.Dilate);            result.Add(EnumMatAlgorithmType.Erode);            return result;            #endregion        }        ///         /// 对查找的轮廓数据进行训练模型匹配,这里使用的是KNN 匹配算法        ///         private string GetText(List sortRect, Mat source, Mat roiSource)        {            var response = "";            try            {                if ((sortRect?.Count ?? 0) <= 0)                    return response;                var contourIndex = 0;                using (var dst = new Mat(source.Rows, source.Cols, MatType.CV_8UC3, Scalar.All(0)))                {                    sortRect.ForEach(boundingRect =>                    {                        try                        {                            #region 绘制矩形                            if (this._isDebug)                            {                                Cv2.Rectangle(source, new Point(boundingRect.X, boundingRect.Y),                                new Point(boundingRect.X + boundingRect.Width, boundingRect.Y + boundingRect.Height),                                new Scalar(0, 0, 255), 1);                                Cv2.Rectangle(roiSource, new Point(boundingRect.X, boundingRect.Y),                                   new Point(boundingRect.X + boundingRect.Width, boundingRect.Y + boundingRect.Height),                                   new Scalar(0, 0, 255), 1);                            }                            #endregion                            #region 单个ROI                            var roi = roiSource.GetROI(boundingRect); //Crop the image                            roi = roi.Compress();                            var result = roi.ConvertFloat();                            #endregion                            #region KNN 匹配                            var results = new Mat();                            var neighborResponses = new Mat();                            var dists = new Mat();                            var detectedClass = (int)this._cvKNearest.FindNearest(result, 1, results, neighborResponses, dists);                            var resultText = detectedClass.ToString(CultureInfo.InvariantCulture);                            #endregion                            #region 匹配                            var isDraw = false;                            if (detectedClass >= 0)                            {                                response += detectedClass.ToString();                                isDraw = true;                            }                            if (detectedClass == -1 && !response.Contains("."))                            {                                response += ".";                                resultText = ".";                                isDraw = true;                            }                            #endregion                            #region 绘制及输出切割信息库                            try                            {                                //if (this._isDebug)                                //{                                Write(contourIndex, detectedClass, roi);                                //}                            }                            catch { }                            if (this._isDebug && isDraw)                            {                                Cv2.PutText(dst, resultText, new Point(boundingRect.X, boundingRect.Y + boundingRect.Height), 0, 1, new Scalar(0, 255, 0), 2);                            }                            #endregion                            result?.Dispose();                            results?.Dispose();                            neighborResponses?.Dispose();                            dists?.Dispose();                            contourIndex++;                        }                        catch (Exception ex)                        {                            TextHelper.Error("GetText ex", ex);                        }                    });                    #region 调试模式显示过程                    source.IsDebugShow("Segmented Source", this._isDebug);                    dst.IsDebugShow("Detected", this._isDebug);                    dst.IsDebugWaitKey(this._isDebug);                    dst.IsDebugImWrite("dest.jpg", this._isDebug);                    #endregion                }            }            catch            {                throw;            }            finally            {                source?.Dispose();                roiSource?.Dispose();            }            return response;        }        ///         /// 图片处理算法        ///         ///         ///         ///         public ImageProcessModel MatProcessing(Mat src, bool isDebug = false)        {            src.IsDebugShow("原图", isDebug);            var list = GetAlgoritmList(src);            var resultMat = new Mat();            src.CopyTo(resultMat);            var isZoom = IsZoom(src);            list?.ForEach(item =>            {                switch (item)                {                    case EnumMatAlgorithmType.Dilate:                        resultMat = resultMat.ToDilate(Convert.ToInt32(this._config.DilateLevel));                        resultMat.IsDebugShow(EnumMatAlgorithmType.Dilate.GetDescription(), isDebug);                        break;                    case EnumMatAlgorithmType.Erode:                        var eroderLevel = isZoom ? this._config.ErodeLevel * this._config.ZoomLevel : this._config.ErodeLevel;                        resultMat = resultMat.ToErode(eroderLevel);                        resultMat.IsDebugShow(EnumMatAlgorithmType.Erode.GetDescription(), isDebug);                        break;                    case EnumMatAlgorithmType.Gray:                        resultMat = resultMat.ToGrey();                        resultMat.IsDebugShow(EnumMatAlgorithmType.Gray.GetDescription(), isDebug);                        break;                    case EnumMatAlgorithmType.Thresh:                        var thresholdValue = this._config.ThresholdValue <= 0 ? resultMat.GetMeanThreshold() : this._config.ThresholdValue;                        resultMat = resultMat.ToThreshold(thresholdValue, thresholdType: this._config.ThresholdType);                        resultMat.IsDebugShow(EnumMatAlgorithmType.Thresh.GetDescription(), isDebug);                        break;                    case EnumMatAlgorithmType.Zoom:                        resultMat = resultMat.ToZoom(this._config.ZoomLevel);                        src = resultMat;                        resultMat.IsDebugShow(EnumMatAlgorithmType.Zoom.GetDescription(), isDebug);                        break;                    case EnumMatAlgorithmType.Blur:                        resultMat = resultMat.ToBlur();                        src = resultMat;                        resultMat.IsDebugShow(EnumMatAlgorithmType.Blur.GetDescription(), isDebug);                        break;                }            });            var oldThreshImage = new Mat();            resultMat.CopyTo(oldThreshImage);            return new ImageProcessModel()            {                ResourcMat = src,                FindContoursMat = oldThreshImage,                RoiResultMat = resultMat            };        }

opencv 图片处理开放出去的配置对象实体如下:

 public class OpencvOcrConfig    {        ///         /// 放大程度级别 默认2        ///         public double ZoomLevel { set; get; }        ///         /// 腐蚀级别 默认2.5        ///         public double ErodeLevel { set; get; }        ///         /// 膨胀        ///         public double DilateLevel { set; get; }        ///         /// 阀值        ///         public double ThresholdValue { set; get; }        ///         /// 图片处理算法,用逗号隔开        ///         public string Algorithm { set; get; }        ///         /// 二值化方式        ///         public ThresholdType ThresholdType { set; get; } = ThresholdType.BinaryInv;        ///         /// 通道模式        ///         public OcrChannelTypeEnums ChannelType { set; get; } = OcrChannelTypeEnums.BlackBox;    }

opencv 图片处理算法扩展方法如下:

 public static partial class OpenCvExtensions    {        private const int Thresh = 200;        private const int ThresholdMaxVal = 255;        ///         /// Bitmap Convert Mat        ///         ///         ///         public static Mat ToMat(this System.Drawing.Bitmap bitmap)        {            return OpenCvSharp.Extensions.BitmapConverter.ToMat(bitmap);        }        ///         /// Bitmap Convert Mat        ///         ///         ///         public static System.Drawing.Bitmap ToBitmap(this Mat mat)        {            return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat);        }        public static bool MatIsEqual(this Mat mat1, Mat mat2)        {            try            {                if (mat1.Empty() && mat2.Empty())                {                    return true;                }                if (mat1.Cols != mat2.Cols || mat1.Rows != mat2.Rows || mat1.Dims() != mat2.Dims() ||                    mat1.Channels() != mat2.Channels())                {                    return false;                }                if (mat1.Size() != mat2.Size() || mat1.Type() != mat2.Type())                {                    return false;                }                var nrOfElements1 = mat1.Total() * mat1.ElemSize();                if (nrOfElements1 != mat2.Total() * mat2.ElemSize())                    return false;                return MatPixelEqual(mat1, mat2);            }            catch (Exception ex)            {                TextHelper.Error("MatIsEqual 异常", ex);                return true;            }        }        ///         /// 灰度        ///         ///         ///         public static Mat ToGrey(this Mat mat)        {            try            {                Mat grey = new Mat();                Cv2.CvtColor(mat, grey, OpenCvSharp.ColorConversion.BgraToGray);                return grey;            }            catch            {                return mat;            }        }        ///         /// 二值化        ///         ///         ///         public static Mat ToThreshold(this Mat data, double threshValue = 0, ThresholdType thresholdType = ThresholdType.BinaryInv)        {            Mat threshold = new Mat();            if (threshValue == 0)                threshValue = Thresh;            Cv2.Threshold(data, threshold, threshValue, ThresholdMaxVal, thresholdType);            if (threshold.IsBinaryInv())            {                Cv2.Threshold(threshold, threshold, threshValue, ThresholdMaxVal, ThresholdType.BinaryInv);            }            return threshold;        }        ///         /// 是否调试显示        ///         ///         ///         ///         public static void IsDebugShow(this Mat src, string name, bool isDebug = false)        {            if (!isDebug)                return;            Cv2.ImShow(name, src);        }        public static void IsDebugWaitKey(this Mat src, bool isDebug = false)        {            if (!isDebug)                return;            Cv2.WaitKey();        }        public static void IsDebugImWrite(this Mat src, string path, bool isDebug = false)        {            if (!isDebug)                return;            try            {                Cv2.ImWrite(path, src);            }            catch { }        }        ///         /// Mat 转成另外一种存储矩阵方式        ///         ///         ///         public static Mat ConvertFloat(this Mat roi)        {            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);            return result;        }        ///         /// 腐蚀        ///         ///         ///         public static Mat ToErode(this Mat mat, double level)        {            #region 自动会判断是否需要腐蚀            if (level < 1)            {                return mat;            }            #endregion            var erode = new Mat();            var copyMat = new Mat();            mat.CopyTo(copyMat);            Cv2.Erode(mat, erode, Cv2.GetStructuringElement(StructuringElementShape.Ellipse, new Size(level, level)));            return erode;        }        ///         /// 膨胀        ///         ///         ///         public static Mat ToDilate(this Mat mat, int level)        {            if (level <= 0)                return mat;            var dilate = new Mat();            Cv2.Dilate(mat, dilate, Cv2.GetStructuringElement(StructuringElementShape.Ellipse, new Size(level, level)));            return dilate;        }        ///         /// mat 转Roi        ///         ///         ///         ///         public static Mat GetROI(this Mat image, Rect boundingRect)        {            try            {                return new Mat(image, boundingRect); //Crop the image            }            catch{}            return null;        }        ///         /// 获取平均阀值        ///         ///         ///         public static int GetMeanThreshold(this Mat mat)        {            var width = mat.Width;            var height = mat.Height;            var m = mat.Reshape(1, width * height);            return (int)m.Sum() / (width * height);        }        ///         /// 获得二值化阀值        ///         ///         ///         public static int GetMeanThreshold(this System.Drawing.Bitmap bitmap)        {            using (var mat = bitmap.ToMat())            using (var grap = mat.ToGrey())            {                return grap.GetMeanThreshold();            }        }        public static bool IsErode(this System.Drawing.Bitmap bitmap)        {            using (var mat = bitmap.ToMat())            using (var grap = mat.ToGrey())            {                var thresholdValue = grap.GetMeanThreshold();                using (var threshold = grap.ToThreshold(thresholdValue, ThresholdType.BinaryInv))                {                    return threshold.IsErode();                }            }        }        ///         /// 放大        ///         ///         ///         ///         public static Mat ToZoom(this Mat img, double times)        {            if (times <= 0)                return img;            var width = img.Width * times;            var height = img.Height * times;            img = img.Resize(new Size(width, height), 0, 0, Interpolation.NearestNeighbor);            return img;        }        ///         /// 均值滤波        ///         ///         ///         public static Mat ToBlur(this Mat img)        {            return img.Blur(new Size(3, 3));        }        public static Mat Compress(this Mat img)        {            var width = 28.0 * img.Width / img.Height;            var fWidth = width / img.Width;            var fHeight = 28.0 / img.Height;                        img = img.Resize(new Size(width, 28), fWidth, fHeight, Interpolation.NearestNeighbor);            return img;        }        public static bool MatPixelEqual(this Mat src, Mat are)        {            var width = src.Width;            var height = src.Height;            var sum = width * height;            for (int row = 0; row < height; row++)            {                for (int col = 0; col < width; col++)                {                    byte p = src.At(row, col); //获对应矩阵坐标的取像素                    byte pAre = are.At(row, col);                    if (p != pAre)                        return false;                }            }            return true;        }        public static int GetSumPixelCount(this Mat threshold)        {            var width = threshold.Width;            var height = threshold.Height;            var sum = width * height;            var value = 0;            for (int row = 0; row < height; row++)            {                for (int col = 0; col < width; col++)                {                    byte p = threshold.At(row, col); //获对应矩阵坐标的取像素                    value++;                }            }            return value;        }        public static int GetPixelCount(this Mat threshold, System.Drawing.Color color)        {            var width = threshold.Width;            var height = threshold.Height;            var sum = width * height;            var value = 0;            for (int row = 0; row < height; row++)            {                for (int col = 0; col < width; col++)                {                    byte p = threshold.At(row, col); //获对应矩阵坐标的取像素                    if (Convert.ToInt32(p) == color.R)                    {                        value++;                    }                }            }            return value;        }        ///         /// 是否需要二值化反转        ///         ///         ///         public static bool IsBinaryInv(this Mat threshold)        {            var width = threshold.Width;            var height = threshold.Height;            var sum = Convert.ToDouble(width * height);            var black = GetPixelCount(threshold, System.Drawing.Color.Black);            return (Convert.ToDouble(black) / sum) < 0.5;        }        ///         /// 是否需要腐蚀        ///         ///         ///         public static bool IsErode(this Mat mat)        {            var percent = mat.GetPercent();            return percent >= 0.20;        }        ///         /// 获得白色像素占比        ///         ///         ///         public static double GetPercent(this Mat threshold)        {            var width = threshold.Width;            var height = threshold.Height;            var sum = Convert.ToDouble(width * height);            var white = GetPixelCount(threshold, System.Drawing.Color.White);            return (Convert.ToDouble(white) / sum);        }        ///         /// 根据模板查找目标图片的在原图标中的开始位置坐标        ///         ///         ///         ///         ///         public static Point FindTemplate(this Mat source, Mat template, MatchTemplateMethod matchTemplateMethod = MatchTemplateMethod.SqDiffNormed)        {            if (source == null)                return new OpenCvSharp.CPlusPlus.Point();            var result = new Mat();            Cv2.MatchTemplate(source, template, result, matchTemplateMethod);            Cv2.MinMaxLoc(result, out OpenCvSharp.CPlusPlus.Point minVal, out OpenCvSharp.CPlusPlus.Point maxVal);            var topLeft = new OpenCvSharp.CPlusPlus.Point();            if (matchTemplateMethod == MatchTemplateMethod.SqDiff || matchTemplateMethod == MatchTemplateMethod.SqDiffNormed)            {                topLeft = minVal;            }            else            {                topLeft = maxVal;            }            return topLeft;        }    }

以上代码中开源对图片进行轮廓切割,同时会生成切割后的图片代码如下

#region 绘制及输出切割信息库    try    {        Write(contourIndex, detectedClass, roi);    }    catch { }#endregionprivate void Write(int contourIndex, int detectedClass, Mat roi){    Task.Factory.StartNew(() =>    {        try        {            var templatePath = $"{AppDomain.CurrentDomain.BaseDirectory}template";            FileHelper.CreateDirectory(templatePath);            var templatePathFile = $"{templatePath}/{contourIndex}_{detectedClass.ToString()}.png";            Cv2.ImWrite(templatePathFile, roi);            if (!roi.IsDisposed)            {                roi.Dispose();            }        }        catch {}   });}

切割后的图片如下:

这里我已经对数字进行切割好了,接下来就是需要对0-9 这些数字进行分类(建立文件夹进行数字归类),如下:

图中的每一个分类都是我事先切割好的数字图片,图中有-1 和-2 这两个特殊分类,-1 里面我是放的是“.”好的分类,用于训练“.”的图片,这样就可以识别出小数点的数字支持. -2 这个分类主要是其他一些无关紧要的图片,也就是不是数字和点的都归为这一类中.

现在训练库分类已经建立好了,接下来我们需要对这些分类数字进行归一化处理,生成训练模型. 代码如下:

        private void Button_Click_1(object sender, RoutedEventArgs e)        {            var opencvOcr = new OpencvOcr($"{path}Template\\Traindata.xml", opencvOcrConfig: null);            opencvOcr.Save($"{path}Template\\NumberWrite", outputPath: $"{path}Template\\Traindata.xml");            MessageBox.Show("生成训练库成功");            //var img = new Bitmap(this.txbFilaName.Text);            //var str = opencvOcr.GetText(img.ToMat(), isDebug: true);            //this.labContent.Content = str;        }        ///         /// 保存训练模型        ///         ///         ///         ///         public void Save(string dataPath, string trainExt = "*.png", string outputPath = "")        {            if (string.IsNullOrEmpty(outputPath))                throw new ArgumentNullException("save dataPath is not null");            var trainingImages = this.ReadTrainingImages(dataPath, trainExt);            var samples = GetSamples(trainingImages);            var response = GetResponse(trainingImages);            //写入到训练库中            using (var fs = new FileStorage(outputPath, FileStorageMode.WriteText))            {                fs.Write("samples", samples);                fs.Write("responses", response);            }        }        ///         /// 根据目录加载文件        ///         ///         ///         ///         private IList ReadTrainingImages(string path, string ext)        {            var images = new List();            var imageId = 1;            foreach (var dir in new DirectoryInfo(path).GetDirectories())            {                var groupId = int.Parse(dir.Name);                foreach (var imageFile in dir.GetFiles(ext))                {                    var srcMat = new Mat(imageFile.FullName, OpenCvSharp.LoadMode.GrayScale);                    var image = srcMat.ConvertFloat();                    if (image == null)                    {                        continue;                    }                    images.Add(new ImageInfo                    {                        Image = image,                        ImageId = imageId++,                        ImageGroupId = groupId                    });                }            }            return images;        }        ///         /// Mat 转成另外一种存储矩阵方式        ///         ///         ///         public static Mat ConvertFloat(this Mat roi)        {            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);            return result;        }        ///         /// 获取Samples        ///         ///         ///         private Mat GetSamples(IList trainingImages)        {            var samples = new Mat();            foreach (var trainingImage in trainingImages)            {                samples.PushBack(trainingImage.Image);            }            return samples;        }        private Mat GetResponse(IList trainingImages)        {            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            return responses;        }

到这里ocr 训练模型以及建立好了,会在目录中生成一个Traindata.xml 的训练模型库,我们来打开这个训练模型库文件探索它的神秘的容颜.

如果大家有更好的数字识别方案可以留言告知我,这样可以更好的应用到实际场景中需求场景:

  1. 客户端调用,不走api方式

  2. 必须要xp这种老爷机的支持

c# opencv车牌识别_opencv +数字识别相关推荐

  1. python信用卡识别_python opencv实现信用卡的数字识别

    本项目利用python以及opencv实现信用卡的数字识别 前期准备 导入工具包 定义功能函数 模板图像处理 读取模板图像 cv2.imread(img) 灰度化处理 cv2.cvtColor(img ...

  2. 【实战】python以及opencv实现信用卡的数字识别

    本项目利用python以及opencv实现信用卡的数字识别 前期准备 导入工具包 定义功能函数 模板图像处理 读取模板图像 cv2.imread(img) 灰度化处理 cv2.cvtColor(img ...

  3. matlab 职坐标,机器人之【机器视觉与图像处理】基于MATLAB的圆检测、颜色识别、数字识别...

    本文主要向大家介绍了机器人之[机器视觉与图像处理]基于MATLAB的圆检测.颜色识别.数字识别,通过具体的内容向大家展现,希望对大家学习机器人有所帮助. 对产品中心的检测:设置好路径之后,包含关系是在 ...

  4. Opencv项目实战-信用卡数字识别

    Opencv项目实战:信用卡数字识别 导入库,定义展示函数 import cv2 import numpy as np from imutils import contours import myut ...

  5. java opencv 物体检测_OpenCV.物体识别

    1.度娘:"OpenCV 物体识别" ZC:主看这个,讲的比较细致,操作一般都是使用的 OpenCV里面的exe,一些代码是 java的 可以搞定,最后一段测试代码 是Python ...

  6. 基于OpenCV的气体泵扫描仪数字识别系统

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 综述 2012年iOS应用商店中发布了一个名为FuelMate的G ...

  7. android led数字识别,opencv 学习之 液晶数字识别

    算法采用纵向及横向扫描方式,对清晰图片进行了实验,效果较好. 算法未检测小数点,感兴趣的同学可以Y方向腐蚀检测小数点,按照X坐标排序即可. 当然,数字及字符识别,SVM.K近邻.神经网络等等这些才是正 ...

  8. opencv进阶篇---银行卡数字识别

    执行结果: 主要思想:对模板图像以及待检测图像进行外轮廓检测,并得到各自外接矩形,将模板图像的外接矩形做resize()操作,使其外接矩形的大小与待检测图像外接矩形的大小相一致,然后与待检测图形做模板 ...

  9. 一文详解opencv摄像头数字识别

    OpenCV数字识别 一.数字识别的两种方式 1.1 轮廓提取法 1.2 行列扫描法 二.提取图像中的ROI区域 2.1 读取摄像头图像 2.2 对图像进行二值化处理 2.3 形态学处理 2.4 设置 ...

最新文章

  1. Docker部署配置相关使用总结
  2. saltstack安装
  3. 【数据分析】33个热门数据分析软件,你都用过哪些?
  4. python数据库优化_python | Mysql性能优化一
  5. 条码生成 SDK - Zint 教程及示例
  6. javascript中基本类型和引用类型复制变量的值
  7. 在git 服务器挂载、创建新的项目、克隆新的项目
  8. 数据库 linux 编译,部署mariadb数据库到linux(源码编译安装)
  9. python widget_python 图形界面
  10. libuv在cocos2d-x中的使用
  11. 服务器修改lang值,golang设置http response响应头与填坑记录
  12. CMMI认证难度大吗?
  13. 【Arduino实验16 步进电机的控制】
  14. 单例模式、Single
  15. 视频教程-WebGL 可视化3D绘图框架:Three.js 零基础上手实战-其他
  16. 2019.10.15
  17. 全球首个冰冻环境下的海上风电场完工
  18. php中define是啥意思
  19. 【已解决】IDEA创建Maven多模块项目子模块引用不到父模块的pom
  20. 浅谈K8S的容器管理

热门文章

  1. Python - 深度学习系列13- 显卡与CPU计算对比
  2. 《碟中谍6》阿汤哥:自律是时间的杀手
  3. 关于软件行业的工种划分浅析
  4. OpenCV每日函数 专栏简述(陆续整理中)
  5. UTF-8,UTF-16,UTF-32编码方式都是UNICODE,但只是他们的保存方式不同。
  6. Solr调研总结(很详细很全面)
  7. “能找到工作,全靠我简历造假”
  8. 07 C++简单应用 编写一个摄氏度转华氏摄氏度的函数
  9. APT组织BlackEnergy继任者:GreyEnergy,台湾研华公司证书被其盗取
  10. php 中遍历数组时使用引用出现的问题