OpenCVSharp4 识别物体系列之学习对象分类

前量篇文章提及了计算机识别的整体步骤 预处理->分割->特征提取->机器学习分类->后期处理,并详细阐述了预处理与分割。本文阐述 特征提取,机器学习分类。
在预处理,分割之后,我们已经得到了一个个独立的感兴趣的对象。接下来就是要从感兴趣的对象提取特征集,这些特征集又称为特征向量,用于描述对象,可以是轮廓,面积,纹理等等。
在有了大量的已知标签的特征集之后,接下来就是通过训练生成模型。
当拥有模型之后,就可以将未知标签的新特征向量输入进去,进行预测标签。


ml.jpg

本文用OpenCV自带的\opencv\sources\samples\data\digits.png作为样本来训练0123456789的识别,权当作AI的hello word程序。

  • 准备数据,即分割digits.png得到一个个独立的样本数据。


    digits
        static void SplitDigits(string path)
        {
            Mat gray = new Mat(path, ImreadModes.Grayscale);

            int imgName = 0;
            int imgIndex = 0;

            int step = 20;
            int rowsCount = gray.Rows / step;   //原图为1000*2000
            int colsCount = gray.Cols / step;   //裁剪为5000个20*20的小图块
            for (int i = 0; i < rowsCount; i++)
            {
                if (i % 5 == 0 && i != 0)
                {
                    imgName++;
                    imgIndex = 0;
                }

                int offsetRow = i * step;  //行上的偏移量
                for (int j = 0; j < colsCount; j++)
                {
                    int offsetCol = j * step; //列上的偏移量
                    Mat temp = gray.SubMat(offsetRow, offsetRow + step, offsetCol, offsetCol + step);
                    if (!System.IO.Directory.Exists($"svm/digits/{imgName}"))// OpenCV不会自动创建目录,此处需要代码额外处理下
                    {
                        System.IO.Directory.CreateDirectory($"digits/{imgName}");
                    }
                    temp.ImWrite($"digits/{imgName}/{imgIndex}.png");
                    imgIndex++;
                }
            }

            Console.WriteLine("split complete");
        }
目录

0
  • 准备数据
        static void PrepareData(out Mat tTrainData, out Mat tTrainLabel, out Mat tTestData, out Mat tTestLabel)
        {
            tTrainData = new Mat();
            tTrainLabel = new Mat();
            tTestData = new Mat();
            tTestLabel = new Mat();
            for (int i = 0; i < 10; i++)
            {
                for (int j = 0; j < 500; j++)
                {
                    Mat temp = new Mat($"digits/{i}/{j}.png", ImreadModes.Grayscale);
                    temp = temp.Reshape(1, 1);
                    if (j < 400)
                    {
                        tTrainData.PushBack(temp);
                        tTrainLabel.PushBack(i);
                    }
                    else
                    {
                        tTestData.PushBack(temp);
                        tTestLabel.PushBack(i);
                    }
                }
            }
            tTrainData.ConvertTo(tTrainData, MatType.CV_32F);
            tTestData.ConvertTo(tTestData, MatType.CV_32F);
            tTestLabel.ConvertTo(tTestLabel, MatType.CV_32F);
        }
  • 训练,存储训练出来的模型
            KNearest knn = KNearest.Create();
            knn.Train(tTrainData, SampleTypes.RowSample, tTrainLabel);

            knn.Save("digits/knn.xml");
            /*knn.Read(new FileStorage("digits/knn.xml", FileStorage.Mode.Read).GetFirstTopLevelNode()); 下一次可以直接读取存储的模型,而不用再次训练*/

  • 测试验证模型准确率
           Mat testPredict = new Mat();
           knn.Predict(tTestData, testPredict);
           Mat errorImg = tTestLabel.NotEquals(testPredict);
           float errorPercent = 100f * errorImg.CountNonZero() / testPredict.Rows;
           Console.WriteLine($"错误率:{errorPercent}");
  • 实际使用
        static void Predict(KNearest knn)
        {
            Mat tTestData = new Mat();
            Mat temp = new Mat($"digits/6/30.png", ImreadModes.Grayscale);
            temp = temp.Reshape(1, 1);
            tTestData.PushBack(temp);
            temp = new Mat($"digits/8/355.png", ImreadModes.Grayscale);
            temp = temp.Reshape(1, 1);
            tTestData.PushBack(temp);
            tTestData.ConvertTo(tTestData, MatType.CV_32F);

            Mat testPredict = new Mat();
            knn.Predict(tTestData, testPredict);
            testPredict.ConvertTo(testPredict, MatType.CV_8U);
            byte num = testPredict.At<byte>(0, 0);
            byte num1 = testPredict.At<byte>(1, 0);
            Console.WriteLine($"图片的数值{num} {num1}");
        }
结果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。