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 条评论
登录 后参与评论

相关文章

来自专栏专知

【论文推荐】最新五篇视频分类相关论文—细粒度行人识别、群组归一化、MLtuner、时序特征

1603
来自专栏AI研习社

都说 AllenNLP 好用,我们跑一遍看看究竟多好用

良好学习过程的关键原则之一,就是让学习的内容略高于当前的理解。如果该主题与你已知的内容太过于相似,那么你就不会有很大的进步。另一方面,如果这个主题太难的话,你就...

2282
来自专栏人工智能头条

LSTM实现详解

2003
来自专栏机器之心

学界 | 老论文没有源码?14年神经图灵机的复现被接收为大会论文

项目地址:https://github.com/MarkPKCollier/NeuralTuringMachine

982
来自专栏从流域到海域

Python机器学习中的特征选择

原文地址:https://machinelearningmastery.com/feature-selection-machine-learning-pytho...

1.1K7
来自专栏IT派

教程 | 用TensorFlow Estimator实现文本分类

本文选自介绍 TensorFlow 的 Datasets 和 Estimators 模块系列博文的第四部分。读者无需阅读所有之前的内容,如果想重温某些概念,可以...

1333
来自专栏Petrichor的专栏

论文阅读: Light-head R-CNN

“ Head ” 在文中指连接在basemodel后面的网络结构,包括以下两部分:

1194
来自专栏IT派

教程 | 用TensorFlow Estimator实现文本分类

本文选自介绍 TensorFlow 的 Datasets 和 Estimators 模块系列博文的第四部分。读者无需阅读所有之前的内容,如果想重温某些概念,可以...

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

【kaggle实战】从KNN,LR,SVM,RF到深度学习

@蜡笔小轩V 原文:http://blog.csdn.net/Dinosoft/article/details/50734539 纸上得来终觉浅,还是要多实践...

8846

TensorFlow中生成手写笔迹的Demo

这项操作现在在github上已经可以使用了。

4117

扫码关注云+社区