Python实现识别手写数字 Python图片读入与处理


Posted in Python onMarch 23, 2020

写在前面

在上一篇文章Python徒手实现手写数字识别—大纲中,我们已经讲过了我们想要写的全部思路,所以我们不再说全部的思路。

我这一次将图片的读入与处理的代码写了一下,和大纲写的过程一样,这一段代码分为以下几个部分:

  • 读入图片;
  • 将图片读取为灰度值矩阵;
  • 图片背景去噪;
  • 切割图片,得到手写数字的最小矩阵;
  • 拉伸/压缩图片,得到标准大小为100x100大小矩阵;
  • 将图片拉为1x10000大小向量,存入训练矩阵中。

所以下面将会对这几个函数进行详解。

代码分析

基础内容

首先我们现在最前面定义基础变量

import os
from skimage import io
import numpy as np

##Essential vavriable 基础变量
#Standard size 标准大小
N = 100
#Gray threshold 灰度阈值
color = 100/255

其中标准大小指的是我们在最后经过切割、拉伸后得到的图片的尺寸为NxN。灰度阈值指的是在某个点上的灰度超过阈值后则变为1.

接下来是这图像处理的一部分的主函数

filenames = os.listdir(r"./num/")
pic = GetTrainPicture(filenames)

其中filenames得到在num目录下所有文件的名称组成的列表。pic则是通过函数GetTrainPicture得到所有训练图像向量的矩阵。这一篇文章主要就是围绕这个函数进行讲解。

GetTrainPicture函数

GetTrainPicture函数内容如下

#Read and save train picture 读取训练图片并保存
def GetTrainPicture(files):
 Picture = np.zeros([len(files), N**2+1])
 #loop all pictures 循环所有图片文件
 for i, item in enumerate(files):
 #Read the picture and turn RGB to grey 读取这个图片并转为灰度值
 img = io.imread('./num/'+item, as_grey = True)
 #Clear the noise 清除噪音
 img[img>color] = 1
 #Cut the picture and get the picture of handwritten number
 #将图片进行切割,得到有手写数字的的图像
 img = CutPicture(img)
 #Stretch the picture and get the standard size 100x100
 #将图片进行拉伸,得到标准大小100x100
 img = StretchPicture(img).reshape(N**2)
 #Save the picture to the matrix 将图片存入矩阵
 Picture[i, 0:N**2] = img
 #Save picture's name to the matrix 将图片的名字存入矩阵
 Picture[i, N**2] = float(item[0])
 return Picture

可以看出这个函数的信息量非常大,基本上今天做的所有步骤我都把封装到一个个函数里面了,所以这里我们可以看到图片处理的所有步骤都在这里。

提前准备

首先是创建了一个用来存放所有图像向量的矩阵Picture,大小为fx10001,其中f代表我们拥有的训练图片的数目,10001的前10000位代表图片展开后的向量长度,最后一维代表这一个向量的类别,比如说时2就代表这个图片上面写的数字是2.

接下来用的是一个for循环,将files里面每一个图片进行一次迭代,计算出向量后存入picture。

在循环中的内容就是对每一张图片进行的操作。

读入图片并清除背景噪音

首先是io.imread函数,这个函数是将图片导出成为灰度值的矩阵,每一个像素点是矩阵上的一个元素。

接下来是img[img>color]=1这一句。这一句运用了逻辑运算的技巧,我们可以将其分为两部分

point = img > color
img[point] = 1

首先是img>color,img是一个矩阵,color是一个数。意义就是对img中所有元素进行判断是否大于color这个数,并输出一个与img同等大小的矩阵,对应元素上是该值与color判断后的结果,有False与True。如果大于这个数,那么就是Ture,否则是False。下面举个例子,不再赘述。

a = np.array([1, 2, 3, 4])
print(a>2)

#以下为输出结果
[False False True True]

之后的img[point] = 1说明将所有True的值等于1。举个例子

a = np.array([1, 2, 3, 4])
p = a > 2
a[p] = 0
print(a)

#以下为输出结果
[1 2 0 0]

因此我通过这样的方法来清除掉了与数字颜色差别太大的背景噪音。

切割图像

首先切割图像的函数我写的是CutPicture。我们来说一下这个切割图像的意思。比如说有一个人写字写的特别小,另一个人写字写的特别大。就像是下图所示,所以我们进行这样的操作。沿着图片的边进行切割,得到了下面切割后的图片,让数字占满整个图片,从而具有可比性。

Python实现识别手写数字 Python图片读入与处理

所以下面贴出代码,详细解释一下我是怎么做的。

#Cut the Picture 切割图象
def CutPicture(img):
 #初始化新大小
 size = []
 #图片的行数
 length = len(img)
 #图片的列数
 width = len(img[0,:])
 #计算新大小
 size.append(JudgeEdge(img, length, 0, [-1, -1]))
 size.append(JudgeEdge(img, width, 1, [-1, -1]))
 size = np.array(size).reshape(4)
 print(size)
 return img[size[0]:size[1]+1, size[2]:size[3]+1]

首先函数导入过来的的参数只有一个原img。

我第一步做的是把新的大小初始化一下,size一共会放入四个值,第一个值代表原图片上的手写数字图案的最高行,第二个值代表的是最低行,第三个值代表数字图案的最左列,,第四个只代表最右列**。这个还看不明白的话就看上面的图示,就是沿着图片切割一下就好了。

接下来的length和width分别代表着原图片的行数与列数,作用在下面。我又创建了一个JudgeEdge函数,这个函数是输出它的行数或者列数的两位数字。第一个append是给size列表放入了两个行序号(最高行和最低行),第二个append是给size放进两个列序号(最左列和最右列)。所以接下来就看JudgeEdge函数是干什么的。

def JudgeEdge(img, length, flag, size):
 for i in range(length):
 #Cow or Column 判断是行是列
 if flag == 0:
  #Positive sequence 正序判断该行是否有手写数字
  line1 = img[img[i,:]<color]
  #Negative sequence 倒序判断该行是否有手写数字
  line2 = img[img[length-1-i,:]<color]
 else:
  line1 = img[img[:,i]<color]
  line2 = img[img[:,length-1-i]<color]
 #If edge, recode serial number 若有手写数字,即到达边界,记录下行
 if len(line1)>=1 and size[0]==-1:
  size[0] = i
 if len(line2)>=1 and size[1]==-1:
  size[1] = length-1-i
 #If get the both of edge, break 若上下边界都得到,则跳出
 if size[0]!=-1 and size[1]!=-1:
  break
 return size

JudgeEdge函数的参数flag就是用来判断是行还是列,当flag=0时,说明以行为基础开始循环;当flag=1时说明以列为基础进行循环。所以参数length传递的时候就是行数和列数。接下来的for循环就是根据length的大小看似是循环。

当是行时,我这里用line1和line2起到的是一个指针的作用,即在第i行时,line1的内容就是这一行拥有非白色底(数值为1)的像素的个数;line2的作用则是反序的,也就是他计算的是倒数i行非白色像素个数,这样做的目的是能够快一点,让上下同时开始进行寻找,而不用line1把整个图片循环一遍,line2把整个图片循环一遍,大大节省了时间。

寻找这一行拥有非白色底的像素的个数这一个语句同样的运用了逻辑判断,和上文中去噪的原理一模一样。

当line里面有数的时候,说明已经到达了手写数字的边缘。这时候就记录下来,然后就不再改变。当两个line都不等于初始值-1时,说明已经找到了两个边缘,这时候就可以跳出循环并且return了。

这个函数就是这样。所以说切割图像的完整代码就是这样子,下面就要把切割的大小不一的图像给拉伸成标准大小100x100了。

拉伸图像

因为切割以后的图像有大有小,一张图片的大小可能是21x39(比如说数字2),另一张可能是4x40(比如说数字1)。所以为了能够让他们统一大小称为100x100,我们就要把他们拉伸一下。

Python实现识别手写数字 Python图片读入与处理

大概就是像图上的一样。实际情况的图案可能会更复杂,所以我们下面展示一下代码

#Stretch the Picture 拉伸图像
def StretchPicture(img):
 newImg = np.ones(N**2).reshape(N, N)
 ##Stretch/Compress each cows/columns 对每一行/列进行拉伸/压缩
 #The length of each cows after stretching 每一行拉伸/压缩的步长
 step1 = len(img[0])/100
 #Each columns blabla 每一列拉伸/压缩的步长
 step2 = len(img)/100
 #Operate on each cows 对每一行进行操作
 for i in range(len(img)):
 for j in range(N):
  newImg[i, j] = img[i, int(np.floor(j*step1))]
 #Operate on each columns 对每一列进行操作
 for i in range(len(img[0])):
 for j in range(N):
  newImg[j, i] = img[int(np.floor(j*step2)), i]
 return newImg

首先初始化一个新的图片矩阵,这个大小就是标准大小100x100。接下来才是重头戏。我这里用的方法是比较简单基础的方法,但是可能依旧比较难。

首先定义两个步长step1和step2,分别代表拉伸/压缩行与列时的步长。这里的原理就是把原来的长度给他平均分成100份,然后将这100个像素点分别对应上原本的像素点。

如下图所示,图像1我们假设为原图像,图像2我们假设为标准图像,我们需要把图像1转化为图像2,其中每一个点代表一个像素点,也就是图像1有五个像素点,图像2有四个像素点。

Python实现识别手写数字 Python图片读入与处理

所以我的思想就是直接让图像2的像素点的值等于距离它最近的图像1的像素点。

我们为了方便起见,在这里定义一个语法:图像2第三个数据点我们可以写为2_3.

所以2_1对应的就是1_1,2_2对应的就是1_2,2_3对应的是1_4,2_4对应的是1_5。就这样我们就能够得到了图像2所有的数据点。

利用数学的形式表现出来,就是假设图像1长度为l_1,图像2长度为l_2,所以令图像2的步长为l_1/l_2,也就是说当图像2的第一个像素点对应图像1第一个像素点,图像2的最后一个像素点对应图像1最后一个像素点。然后图像2第二个像素点位置就是2*l_1/l_2,对应图像1第floor(2*l_1/l_2)个像素点。以此类推就行。因此再回头看一下那一段代码,这一段是不是就好理解了?

之后对行与列分别进行这个操作,所以就可以得到标准的图片大小。然后再返回到GetTrainPicture即可。

再GetTrainPicture函数中,我用了reshape函数把原本100x100大小的图片拉伸成为1x10000大小的向量,然后存入矩阵当中,并将这一张图片的类别存入矩阵最后一个。

以上就是图片处理的所有内容。

小结

以上就是把一张图片经过处理后存入矩阵的内容。

本文已被收录到专题《python图片处理操作》 ,欢迎大家点击学习更多精彩内容。

本文中的所有算法、代码均是我自己构思的,所以可能存在一些不足之处,我没有系统的学习过图像的相关知识,也并不是计算机专业,因此可能在理论上有一些不合乎情况,所以如果有错误的话欢迎一起讨论,谢谢。

Python 相关文章推荐
Python __setattr__、 __getattr__、 __delattr__、__call__用法示例
Mar 06 Python
Python中属性和描述符的正确使用
Aug 23 Python
python自动发微信监控报警
Sep 06 Python
解决Tensorflow 使用时cpu编译不支持警告的问题
Feb 03 Python
Python图像处理库PIL的ImageGrab模块介绍详解
Feb 26 Python
Python matplotlib修改默认字体的操作
Mar 05 Python
Python参数传递机制传值和传引用原理详解
May 22 Python
pandas DataFrame运算的实现
Jun 14 Python
Jupyter Notebook 安装配置与使用详解
Jan 06 Python
python 实现体质指数BMI计算
May 26 Python
Python Flask搭建yolov3目标检测系统详解流程
Nov 07 Python
Python matplotlib 利用随机函数生成变化图形
Apr 26 Python
Python实现PS滤镜特效Marble Filter玻璃条纹扭曲效果示例
Jan 29 #Python
Python实现识别手写数字大纲
Jan 29 #Python
django文档学习之applications使用详解
Jan 29 #Python
Python实现PS滤镜Fish lens图像扭曲效果示例
Jan 29 #Python
python实现识别手写数字 python图像识别算法
Mar 23 #Python
Python实现简易版的Web服务器(推荐)
Jan 29 #Python
python实现图像识别功能
Jan 29 #Python
You might like
PHP4实际应用经验篇(9)
2006/10/09 PHP
PHP常量使用的几个需要注意的地方(谨慎使用PHP中的常量)
2014/09/12 PHP
PHP目录与文件操作技巧总结(创建,删除,遍历,读写,修改等)
2016/09/11 PHP
不使用浏览器运行javascript代码的方法
2013/07/24 Javascript
JS 实现BASE64_ENCODE和BASE64_DECODE(实例代码)
2013/11/13 Javascript
Window.Open如何在同一个标签页打开
2014/06/20 Javascript
jQuery中大家不太了解的几个方法
2015/03/04 Javascript
JavaScript中string对象
2015/06/12 Javascript
jquery实现表单验证并阻止非法提交
2015/07/09 Javascript
剖析Node.js异步编程中的回调与代码设计模式
2016/02/16 Javascript
微信小程序 JS动态修改样式的实现代码
2017/02/10 Javascript
浅谈微信小程序之官方UI框架we-ui使用教程
2018/08/20 Javascript
Vue响应式原理Observer、Dep、Watcher理解
2019/06/06 Javascript
解决vue 表格table列求和的问题
2019/11/06 Javascript
JavaScript Window窗口对象属性和使用方法
2020/01/19 Javascript
element-ui 实现响应式导航栏的示例代码
2020/05/08 Javascript
[44:04]OG vs Mineski 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
[01:02:09]Liquid vs TNC 2019国际邀请赛淘汰赛 胜者组 BO3 第二场 8.21
2020/07/19 DOTA
Python设计模式编程中解释器模式的简单程序示例分享
2016/03/02 Python
Python切片知识解析
2016/03/06 Python
Ruby使用eventmachine为HTTP服务器添加文件下载功能
2016/04/20 Python
Pycharm学习教程(3) 代码运行调试
2017/05/03 Python
神经网络理论基础及Python实现详解
2017/12/15 Python
对python使用http、https代理的实例讲解
2018/05/07 Python
selenium+python 对输入框的输入处理方法
2018/10/11 Python
python使用wxpy实现微信消息防撤回脚本
2019/04/29 Python
详解用python生成随机数的几种方法
2019/08/04 Python
python requests抓取one推送文字和图片代码实例
2019/11/04 Python
Python 实现顺序高斯消元法示例
2019/12/09 Python
pymysql 插入数据 转义处理方式
2020/03/02 Python
BudgetAir印度:预订航班、酒店和汽车租赁
2019/07/07 全球购物
俄罗斯运动、健康和美容产品在线商店:Lactomin.ru
2020/07/23 全球购物
2015年大学生村官工作总结
2015/04/21 职场文书
运动会广播稿50字
2015/08/19 职场文书
职业规划从高考志愿专业选择开始
2019/08/08 职场文书
导游词之河北邯郸
2019/09/12 职场文书