手写数字识别 [MNIST+CV+AdaBoost]

本文介绍了一种基于图像处理和机器学习的手写数字识别系统。系统首先通过边缘检测和角点检测对A4纸上的手写数字进行预处理,接着使用自适应二值化算法进行分割,最后通过adaBoost算法进行数字识别。实验结果表明,该系统在10张图片上的识别准确率约为80%,在81张图片上的识别准确率提高到了90%左右。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实验内容

输入图像:普通A4纸,上面有手写的如下信息:

  1. 学号
  2. 手机号
  3. 身份证号

所有输入格式一致,数字号码不粘连,但是拍照时可能角度不正。

输出:

  1. 根据标准流程输出每一个主要步骤的结果,包括A4纸张的矫正结果,行数据的切割,单个字符的切割结果。
  2. 对A4纸的四个角、学号、手机号、身份证号进行识别,识别结果保存到Excel表格。

实验步骤

  1. 边缘检测

    在之前的学习中采用了canny算子或者是图像分割来获得边缘。由于A4纸的边缘比较明显,这里可以采用更简单的prewitt算子,只计算像素上下和左右的梯度,加快运算速度。可以看到效果还是很好。

  2. 角点检测

    采用之前霍夫变换的代码,将图像变换到霍夫空间,检测出直线,再计算交点的坐标,输出到文件point.txt中。

  3. A4纸矫正

    获得角点坐标后,利用透视变换的公式对每个像素进行处理。最后结果保存在result/fixed文件夹中。

  4. 二值化

    由于图像的文字和背景的灰度值并没有很好的区分开,所以使用全局阈值是不行的。这里采用了wellner自适应二值化算法。 算法基本的细想就是遍历图像,计算一个移动的平均值。如果某个像素明显的低于这个平均值,则设置为黑色,否则设置为白色。
    在这里插入图片描述

  5. 行分割

    基于垂直方向的直方图,把原图分割为多个子图,每个子图包含一行数字。计算每一行黑色像素的数量,会出现峰和谷,谷的地方就是分割线的地方。结果保存在result/divideY文件夹中。

  6. 列分割

    同样的方法,基于水平方向的直方图,把行子图分割为多个子图,每个子图包含一个数字。结果保存在divide文件夹中。

  7. 膨胀

    在二值化的时候,有可能出现字符断裂的情况,使用滤波器进行膨胀操作来扩张字符。

    在这里插入图片描述

  8. 对每张子图,用连通区域标记的方法从左到右分割数字。同时还需要把图片取反,因为mnist训练集的数据是黑底白字的。结果保存在result/number文件夹中。每一行数字存在了一个文件夹中。

  9. 预测

    使用MNIST数据集进行测试,尝试了adaBoost和svm两种模型,发现还是adaBoost对自己的数据集的识别准确率高一点,而且训练速度较快。结果保存在number.xlsx中。

结果分析

10张图片

A4纸矫正和角点的检测全部正确,在行分割时对于倾斜的文字,如图9,有两行因为倾斜角度较大,通过垂直方向直方图没有办法很好区分开,其他全部正确。对于单个数字的切分基本正确,有个别数字因为两部分间隔较大被切分成了两个数字。

在数字识别的准确率上不太理想,单个字符的准确率在80%左右,没有能够实现一张图片完全识别正确。
在这里插入图片描述

81张图片

A4纸的矫正和角点的检测全部正确,但是部分图片的行分割出现了大于三行的情况,因为在二值化时,由于图片变化较大,有些图片还保留了一些除手写字符以外的黑色像素。还有一个原因是字符离边框较近,没有办法通过裁掉边框过滤一些无关的黑色像素。但是这些错误的行都出现在正确的行后面,在最后预测结果时只保留前面三列就可以了。

在数字的识别上面,由于这次的数字像素较小,resize后字迹更粗,与MNIST训练集更相似,所以识别率较之前更高,达到了90%左右,但是这样的准确率对于整张A4纸的识别来说准确率还是较低。
在这里插入图片描述

adaBoost

算法原理

具体说来,整个Adaboost 迭代算法就3步:

  1. 初始化训练数据的权值分布。如果有N个样本,则每一个训练样本最开始时都被赋予相同的权值:1/N。
  2. 训练弱分类器。具体训练过程中,如果某个样本点已经被准确地分类,那么在构造下一个训练集中,它的权值就被降低;相反,如果某个样本点没有被准确地分类,那么它的权值就得到提高。然后,权值更新过的样本集被用于训练下一个分类器,整个训练过程如此迭代地进行下去。
  3. 将各个训练得到的弱分类器组合成强分类器。各个弱分类器的训练过程结束后,加大分类误差率小的弱分类器的权重,使其在最终的分类函数中起着较大的决定作用,而降低分类误差率大的弱分类器的权重,使其在最终的分类函数中起着较小的决定作用。换言之,误差率低的弱分类器在最终分类器中占的权重较大,否则较小。

算法实现

使用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数据集的测试结果

在这里插入图片描述
在这里插入图片描述

代码地址

Github

参考资料

https://blog.youkuaiyun.com/qq_33000225/article/details/73123880
https://github.com/haidawyl/Mnist

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值