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 删除。

编辑于

我来说两句

2 条评论
登录 后参与评论

相关文章

来自专栏用户2442861的专栏

相似图片检测:感知哈希算法之dHash的Python实现

某些情况下,我们需要检测图片之间的相似性,进行我们需要的处理:删除同一张图片、标记盗版等。 如何判断是同一张图片呢?最简单的方法是使用加密哈希(例如MD5, ...

441
来自专栏机器学习算法与理论

利用二维图像进行头部姿态估计

3D头部姿态估计(ubuntu操作系统,基于opencv3.2+Dlib19.4+python2.7)打开摄像头,可实现实时(realtime)姿态检测。 坐标...

3675
来自专栏机器之心

教程 | 在Python和TensorFlow上构建Word2Vec词嵌入模型

选自adventuresinmachinelearning 机器之心编译 参与:李诗萌、刘晓坤 本文详细介绍了 word2vector 模型的模型架构,以及 T...

3807
来自专栏null的专栏

机器学习算法实现解析——libFM之libFM的训练过程概述

本节主要介绍的是libFM源码分析的第四部分——libFM的训练。 FM模型的训练是FM模型的核心的部分。 4.1、libFM中训练过程的实现 在FM模型的训练...

38111
来自专栏素质云笔记

keras系列︱Sequential与Model模型、keras基本结构功能(一)

不得不说,这深度学习框架更新太快了尤其到了Keras2.0版本,快到Keras中文版好多都是错的,快到官方文档也有旧的没更新,前路坑太多。 到发文为止...

1.6K8
来自专栏量化投资与机器学习

【Python机器学习】系列之特征提取与处理篇(深度详细附源码)

第1章 机器学习基础 将机器学习定义成一种通过学习经验改善工作效果的程序研究与设计过程。其他章节都以这个定义为基础,后面每一章里介绍的机器学习模型都是按照这个...

5907
来自专栏CDA数据分析师

一篇文章教你如何用R进行数据挖掘

引言 R是一种广泛用于数据分析和统计计算的强大语言,于上世纪90年代开始发展起来。得益于全世界众多 爱好者的无尽努力,大家继而开发出了一种基于R但优于R基本文本...

2005
来自专栏李智的专栏

Deep learning基于theano的keras学习笔记(3)-网络层

1.3 Dropout层 为输入数据施加Dropout。Dropout将在训练过程中每次更新参数时随机断开一定百分比(p)的输入神经元连接,Dropout层...

1052
来自专栏追不上乌龟的兔子

[LeetCode Weekly Contest 88]849. 到最近的人的最大距离

3013
来自专栏CreateAMind

keras中文doc之三

前面介绍了keras文档一二 keras中文文档, keras中文-快速开始Sequential模型

692

扫码关注云+社区