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