OpenCV 玩九宫格数独(二):knn 数字识别

前言

首先需要说明,这里所说的数字识别不是手写数字识别!

但凡对机器学习有所了解的人,相信看到数字识别的第一反应就是MNIST。MNIST是可以进行数字识别,但是那是手写数字。我们现在要做的是要识别从九宫格图片中提取出来的印刷体的数字。手写数字集训练出来的模型用来识别印刷体数字,显然不太专业。而且手写体跟印刷体相差不小,我们最看重的正确率问题不能保证。

本文从零开始做一遍数字识别,展示了数字识别的完整流程。从收集数据开始,到数据预处理,再到训练KNN,最后进行数字识别。

我们一步一步来说。

数据收集

为了便于处理,我百度找到了10张下面这样按照1-9-0顺序排列的图片,作为我们的初始数据集。

有的图片可能本来除数字区域外,周围空白部分比较多。为了便于处理,首先用windows自带的画图软件把图片裁剪成上面这样只包含数字区域的样子。

这十张数据集基本涵盖了印刷数字体的不同样式、字体,而且颜色、背景甚至渐变方式都各不相同。

数据处理

显然,我们第一步要做的就是上一节的内容,那就是把图片中的数字分别提取出来。

训练knn,还有其他任何有监督的机器学习模型,不光要有样本数据,还要有知道每一个样本对应的标签。这也是为什么我要选择上面这样按顺序排列的数字图片。

提取数字之后,我们可以对每一个数字的位置进行排序,然后根据位置信息可以知道每一个数字是几。标签也就由此生成了。

这一部分的内容可以分两部分来说,第一部分就是提取数字,第二部分是提取数字之后的数据预处理。

1.提取数字

提取数字的处理流程与上一篇内容差不多:

1.遍历文件夹下的原始数字图片;

2.对每一张图片进行轮廓提取操作,只提取外围轮廓(参考上一节讲解);

img_path = gb.glob("numbers\\*") 

k = 0
labels = []
samples =  []

for path in img_path:
    img  = cv2.imread(path)       
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray,(5,5),0)
    thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2)    
    image, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

3.求轮廓外包矩形,并根据矩形大小信息筛选出所有的数字轮廓;

4.然后根据位置信息对数字框排序,显然第一排依次是12345,第二排依次是67890;

    height,width = img.shape[:2]
    w = width/5
    rect_list = []
    list1 = []
    list2 = []
    for cnt in contours:
        #if cv2.contourArea(cnt)>100:
        [x,y,w,h] = cv2.boundingRect(cnt)

        if w>30 and h > (height/4):        
            if y < (height/2):
                list1.append([x,y,w,h])
            else:
                list2.append([x,y,w,h])
    list1_sorted = sorted(list1,key = lambda t : t[0])
    list2_sorted = sorted(list2,key = lambda t : t[0])

5.提取出每一个数字所在的矩形框,作为ROI取出。

    for i in range(5):
        [x1,y1,w1,h1] = list1_sorted[i] 
        [x2,y2,w2,h2] = list2_sorted[i]                
        number_roi1 = gray[y1:y1+h1, x1:x1+w1] #Cut the frame to size
        number_roi2 = gray[y2:y2+h2, x2:x2+w2] #Cut the frame to size

数据预处理

为了加快训练速度,我们不用原图作为输入,而是对每一个数字原图做一定的处理。此处可选方案很多,提取特征有很多经典特征可选,也可以是自己设计的特征。

这里我用比较简单的方法,把每一张数字图片ROI转换为二值图像。大致流程是这样的:

1.把每一张ROI大小统一变换为40 x 20。

2.阈值分割。

        resized_roi1=cv2.resize(number_roi1,(20,40))
        thresh1 = cv2.adaptiveThreshold(resized_roi1,255,1,1,11,2)

        resized_roi2=cv2.resize(number_roi2,(20,40))
        thresh2 = cv2.adaptiveThreshold(resized_roi2,255,1,1,11,2)

3.把二值图像转换为0-1二值图像。

4.把处理完的数字图片保存到对应数字的文件夹中。(此为中间过程,可注释掉)

        number_path1 = "number\\%s\\%d" % (str(i+1),k) + '.jpg'
        j = i+6
        if j ==10:
            j = 0
        number_path2 = "number\\%s\\%d" % (str(j),k) + '.jpg'
        k+=1

        normalized_roi1 = thresh1/255.
        normalized_roi2 = thresh2/255.
        cv2.imwrite(number_path1,thresh1)
        cv2.imwrite(number_path2,thresh2)

处理完之后保存的文件夹如下:

每一个文件夹里面类似这样,可以看到背景有黑有白,数字也是有黑有白:

5.把处理完的二值图像展开成一行。

6.最后把展开成的一行行样本保存起来作为训练用的数据。

7.对应的,把数字标签按照数字的保存顺序对应保存成训练用的数据。

        sample1 = normalized_roi1.reshape((1,800))
        samples.append(sample1[0])
        labels.append(float(i+1))

        sample2 = normalized_roi2.reshape((1,800))
        samples.append(sample2[0])
        labels.append(float(j))
import numpy as np
samples = np.array(samples,np.float32)
labels = np.array(labels,np.float32)
labels = labels.reshape((labels.size,1))
np.save('samples.npy',samples)
np.save('label.npy',labels)

训练kNN识别数字

这里用opencv自带的knn算法实现。我同时尝试了opencv自带的神经网络和SVM,发现还是kNN的效果最好。有兴趣的可以自己去尝试一下。也可能是我参数没调好。

这里的流程是:

1.加载上面保存的样本和标签数据;

2.分别用80个作为训练数据,20个作为测试数据;

3.用opencv自带的knn训练模型;

4.用训练好的模型识别测试数据中的数字;

5.输出预测值和实际标签值。

import numpy as np
import cv2

samples = np.load('samples.npy')
labels = np.load('label.npy')

k = 80
train_label = labels[:k]
train_input = samples[:k]
test_input = samples[k:]
test_label = labels[k:]

model = cv2.ml.KNearest_create()
model.train(train_input,cv2.ml.ROW_SAMPLE,train_label)

retval, results, neigh_resp, dists = model.findNearest(test_input, 1)
string = results.ravel()

print(test_label.reshape(1,len(test_label))[0])
print(string)

下面是输出结果:

可以看到,预测值和实际值简直一模一样!

注意

1.opencv中的knn只能训练模型,不能保存和加载模型。所以只能用的时候训练,训练好直接用。

2.此次训练样本只有不到一百,暂时只能保证对于本系列文章自带的九宫格图片进行完美的数字识别。其他图片的数字识别准确率不敢保证。如果想要得到更好的效果,请按照机器学习的方法进行优化,或进行更好的数据与处理,或加大数据集等。

3.整个项目代码会在下一篇,也就是最终篇之后放出。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏王肖的UT

OpenGL中投影变换矩阵的反向推导

3108
来自专栏数据处理

scikit-learning小试牛刀

1322
来自专栏大数据挖掘DT机器学习

【LDA数学八卦-1】神奇的Gamma函数

1. 神奇的Gamma函数 1.1 Gamma 函数诞生记 学高等数学的时候,我们都学习过如下一个长相有点奇特的Gamma函数 Γ(x)=∫∞0tx−1e−t...

3255
来自专栏Python小屋

Python使用系统聚类算法对随机元素进行分类

系统聚类算法又称层次聚类或系谱聚类,首先把样本看作各自一类,定义类间距离,选择距离最小的一对元素合并成一个新的类,重复计算各类之间的距离并重复上面的步骤,直到将...

3516
来自专栏大数据文摘

手把手:使用OpenCV进行面部合成— C++ / Python

26412
来自专栏大数据挖掘DT机器学习

百度魅族深度学习大赛初赛冠军作品(图像识别.源码)

赛题以识别类似手写体的四则运算式为主题,参赛者需要在充满干扰信息的10万张图片中,设计算法识别图片上数学运算式并计算结果。决赛在初赛的基础上,引入分数和更加复杂...

3955
来自专栏程序生活

Leetcode-Easy 887. Projection Area of 3D Shapes

当时自己没有想到好办法,就是按部就班的分别求三个面的面积,注意求xy的面积的时候需要考虑grid[i][j]值是否为0

752
来自专栏天天P图攻城狮

iOS多边形马赛克的实现(下)

上一篇里我们详述了多边形马赛克的实现步骤,末尾提出了一个思考:如何在涂抹时让马赛克逐块显示呢? 再回顾一下多边形马赛克的实现。首先进行图片预处理,将原图转成bi...

28512
来自专栏Petrichor的专栏

opencv: 颜色空间转换(cv2.cvtColor) 探究(图示+源码)

我们从 OpenCV官网 的Miscellaneous Image Transformations 上,可查到 cv2.cvtColor 这个api的定义如下:

552
来自专栏QQ音乐前端团队专栏

前端图片主题色提取

对于需要根据用户“定制”、“生成”的图片,这样的方式就有了一个上传图片---->后端计算---->返回结果的时间,等待时间也许就比较长了。由此,我尝试着利用 c...

84813

扫码关注云+社区