实验内容
输入图像:普通A4纸,上面有手写的如下信息:
- 学号
- 手机号
- 身份证号
所有输入格式一致,数字号码不粘连,但是拍照时可能角度不正。
输出:
- 根据标准流程输出每一个主要步骤的结果,包括A4纸张的矫正结果,行数据的切割,单个字符的切割结果。
- 对A4纸的四个角、学号、手机号、身份证号进行识别,识别结果保存到Excel表格。
实验步骤
-
边缘检测
在之前的学习中采用了canny算子或者是图像分割来获得边缘。由于A4纸的边缘比较明显,这里可以采用更简单的prewitt算子,只计算像素上下和左右的梯度,加快运算速度。可以看到效果还是很好。
-
角点检测
采用之前霍夫变换的代码,将图像变换到霍夫空间,检测出直线,再计算交点的坐标,输出到文件point.txt中。
-
A4纸矫正
获得角点坐标后,利用透视变换的公式对每个像素进行处理。最后结果保存在result/fixed文件夹中。
-
二值化
由于图像的文字和背景的灰度值并没有很好的区分开,所以使用全局阈值是不行的。这里采用了wellner自适应二值化算法。 算法基本的细想就是遍历图像,计算一个移动的平均值。如果某个像素明显的低于这个平均值,则设置为黑色,否则设置为白色。
-
行分割
基于垂直方向的直方图,把原图分割为多个子图,每个子图包含一行数字。计算每一行黑色像素的数量,会出现峰和谷,谷的地方就是分割线的地方。结果保存在result/divideY文件夹中。
-
列分割
同样的方法,基于水平方向的直方图,把行子图分割为多个子图,每个子图包含一个数字。结果保存在divide文件夹中。
-
膨胀
在二值化的时候,有可能出现字符断裂的情况,使用滤波器进行膨胀操作来扩张字符。
-
对每张子图,用连通区域标记的方法从左到右分割数字。同时还需要把图片取反,因为mnist训练集的数据是黑底白字的。结果保存在result/number文件夹中。每一行数字存在了一个文件夹中。
-
预测
使用MNIST数据集进行测试,尝试了adaBoost和svm两种模型,发现还是adaBoost对自己的数据集的识别准确率高一点,而且训练速度较快。结果保存在number.xlsx中。
结果分析
10张图片
A4纸矫正和角点的检测全部正确,在行分割时对于倾斜的文字,如图9,有两行因为倾斜角度较大,通过垂直方向直方图没有办法很好区分开,其他全部正确。对于单个数字的切分基本正确,有个别数字因为两部分间隔较大被切分成了两个数字。
在数字识别的准确率上不太理想,单个字符的准确率在80%左右,没有能够实现一张图片完全识别正确。
81张图片
A4纸的矫正和角点的检测全部正确,但是部分图片的行分割出现了大于三行的情况,因为在二值化时,由于图片变化较大,有些图片还保留了一些除手写字符以外的黑色像素。还有一个原因是字符离边框较近,没有办法通过裁掉边框过滤一些无关的黑色像素。但是这些错误的行都出现在正确的行后面,在最后预测结果时只保留前面三列就可以了。
在数字的识别上面,由于这次的数字像素较小,resize后字迹更粗,与MNIST训练集更相似,所以识别率较之前更高,达到了90%左右,但是这样的准确率对于整张A4纸的识别来说准确率还是较低。
adaBoost
算法原理
具体说来,整个Adaboost 迭代算法就3步:
- 初始化训练数据的权值分布。如果有N个样本,则每一个训练样本最开始时都被赋予相同的权值:1/N。
- 训练弱分类器。具体训练过程中,如果某个样本点已经被准确地分类,那么在构造下一个训练集中,它的权值就被降低;相反,如果某个样本点没有被准确地分类,那么它的权值就得到提高。然后,权值更新过的样本集被用于训练下一个分类器,整个训练过程如此迭代地进行下去。
- 将各个训练得到的弱分类器组合成强分类器。各个弱分类器的训练过程结束后,加大分类误差率小的弱分类器的权重,使其在最终的分类函数中起着较大的决定作用,而降低分类误差率大的弱分类器的权重,使其在最终的分类函数中起着较小的决定作用。换言之,误差率低的弱分类器在最终分类器中占的权重较大,否则较小。
算法实现
使用Python sklearn包中的AdaBoostClassifier函数进行adaboost的训练和识别。
AdaBoostClassifier参数:
- base_estimator:弱分类器的类型,默认为CART决策树
- DecisionTreeClassifier algorithm: scikit-learn实现了两种Adaboost分类算法,SAMME和SAMME.R。两者的主要区别是弱学习器权重的度量,默认为SAMME.R
- n_estimators: 最大迭代次数。一般来说n_estimators太小,容易欠拟合,n_estimators太大,又容易过拟合,一般选择一个适中的数值。默认是50。
- learning_rate:每个弱学习器的权重缩减系数。对于同样的训练集拟合效果,较小的νν意味着我们需要更多的弱学习器的迭代次数。通常我们用步长和迭代最大次数一起来决定算法的拟合效果。默认为1。
DecisionTreeClassifier参数:
- max_features: 划分时考虑的最大特征数。默认是"None",意味着划分时考虑所有的特征数
- max_depth:决策树最大深度。默认可以不输入,如果不输入的话,决策树在建立子树的时候不会限制子树的深度。
- min_samples_split:内部节点再划分所需最小样本数
- min_samples_leaf: 叶子节点最少样本数
t = time()
model = AdaBoostClassifier(
base_estimator=DecisionTreeClassifier(splitter='random', max_features=90, max_depth=50, min_samples_split=6,
min_samples_leaf=3), n_estimators=1200, learning_rate=0.005)
# 拟合训练数据集
model.fit(train_X, train_Y)
joblib.dump(model, 'save/adaboost.pkl')
# 预测训练集
train_Y_hat = model.predict(train_X[idx])
print("训练集精确度: ", accuracy_score(train_Y[idx], train_Y_hat))
# 预测测试集
test_Y_hat = model.predict(test_X)
print("测试集精确度: ", accuracy_score(test_Y, test_Y_hat))
print("总耗时:", time() - t, "秒")
# 绘制ROC曲线
n_class = len(np.unique(train_Y))
roc.drawROC(n_class, test_Y, test_Y_hat)
对MNIST数据集的测试结果
代码地址
参考资料
https://blog.youkuaiyun.com/qq_33000225/article/details/73123880
https://github.com/haidawyl/Mnist