手写数字识别(识别纸上手写的数字)

这篇具有很好参考价值的文章主要介绍了手写数字识别(识别纸上手写的数字)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

说明

使用pytorch框架,实现对MNIST手写数字数据集的训练和识别。重点是,自己手写数字,手机拍照后传入电脑,使用你自己训练的权重和偏置能够识别。数据预处理过程的代码是重点。

分析

要识别自己用手在纸上写的数字,从特征上来看,手写数字相比于普通的电脑上的数字最大的 不同就是数字的边缘会发生不同幅度的抖动。而且,在MNIST数据集中的数字是边缘为黑色的,然后数字是不同灰度的白色的,如下所示:
手写数字识别(识别纸上手写的数字)
在数据集中,每个数据都是 28 ∗ 28 28*28 2828的灰度图,并且黑色部分都是零,其余白色的灰度值并不统一。因为如果训练时背景都是统一的时候我们测试用的图片背景也必须是统一的,否则基本无法识别出来。除非训练的时候换各种不同的背景大数据进行训练,这样特征就不会依托着背景而存在,剩下的就是要识别的物体自己所拥有的特征了。所以在这里我要做的就是在图片预处理的时候尽量让图片处理成接近测试图片的样子。

训练网络

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable
from torch.utils.data import DataLoader

# 下载训练集
train_dataset = datasets.MNIST(root='./data/',
				train=True,
				transform=transforms.ToTensor(),
				download=False)
# 下载测试集
test_dataset = datasets.MNIST(root='./data/',
				train=False,
				transform=transforms.ToTensor(),
				download=False)

# 设置批次数
batch_size = 100

# 装载训练集
train_loader = torch.utils.data.DataLoader(dataset = train_dataset,
						batch_size = batch_size,
						shuffle=True)
# 装载测试集
test_loader = torch.utils.data.DataLoader(dataset = test_dataset,
						batch_size = batch_size,
						shuffle = True)

# 自定义手写数字识别网络
class net(nn.Module):
	def __init__(self):
		super(net, self).__init__()

		self.Conn_layers = nn.Sequential(
				nn.Linear(784, 100),
				nn.Sigmoid(),
				nn.Linear(100, 10),
				nn.Sigmoid()
			)

	def forward(self, input):
		output = self.Conn_layers(input)

		return output

# 定义学习率
LR = 0.1

# 定义一个网络对象
net = net()

# 损失函数使用交叉熵
loss_function = nn.CrossEntropyLoss()

# 优化函数使用 SGD
optimizer = optim.SGD(
	net.parameters(),
	lr = LR,
	momentum = 0.9,
	weight_decay = 0.0005
)

# 定义迭代次数
epoch = 20

# 进行迭代训练
for epoch in range(epoch):
	for i, data in enumerate(train_loader):
		inputs, labels = data
		
		# 转换下输入形状
		inputs = inputs.reshape(batch_size, 784)

		inputs, labels = Variable(inputs), Variable(labels)
		outputs = net(inputs)
		loss = loss_function(outputs, labels)
		optimizer.zero_grad()
		loss.backward()
		optimizer.step()
	
	# 初始化正确结果数为0
	test_result = 0
	
	# 用测试数据进行测试
	for data_test in test_loader:
		images, labels = data_test
		
		# 转换下输入形状
		images = images.reshape(batch_size, 784)

		images, labels = Variable(images), Variable(labels)
		output_test = net(images)
		
		# 对一个批次的数据的准确性进行判断
		for i in range(len(labels)):
		
			# 如果输出结果的最大值的索引与标签内正确数据相等,准确个数累加
			if torch.argmax(output_test[i]) == labels[i]:
				test_result += 1
				
	# 打印每次迭代后正确的结果数
	print("Epoch {} : {} / {}".format(epoch, test_result, len(test_dataset)))
	
# 保存权重模型
torch.save(net, 'weight/test.pkl')

至此,对手写数字网络的训练已经结束,且训练的准确性为:
手写数字识别(识别纸上手写的数字)
这个网络比较粗糙,所以准确性也只是一般,但如果要精确起来后面有很多文章可做。

图像预处理

因为我们手机拍的照片和训练集的图片有很大的区别,所以无法将手机上拍的照片直接丢到训练好的网络模型中进行识别,需要先对图片进行预处理。有几点需要对原图进行改变:

  1. 图片的大小:肯定得将拍摄到的图片转换成 28 ∗ 28 28*28 2828尺寸大小的图片。
  2. 图片的通道数:由于MNIST是灰度图,所以原图的channel也得转换成1。
  3. 图片的背景:图片的背景得转换成MNIST相同的黑色,这样识别结果准确性更高。
  4. 数字的颜色:毋庸置疑,数字的颜色得变成MNIST相同的白色。
  5. 数字颜色中间深边缘前:观察MNIST的白色部分并不都是255全白,而是有渐变色的,这个渐变色模拟起来比较困难,算是难度最大的一点了。
    接下来直接上代码了:
import cv2
import numpy as np

def image_preprocessing():

	# 读取图片
	img = cv2.imread("picture/test8.jpeg")

	# =====================图像处理======================== #

	# 转换成灰度图像
	gray_img = cv2.cvtColor(img , cv2.COLOR_BGR2GRAY)

	# 进行高斯滤波
	gauss_img = cv2.GaussianBlur(gray_img, (5,5), 0, 0, cv2.BORDER_DEFAULT)

	# 边缘检测
	img_edge1 = cv2.Canny(gauss_img, 100, 200)

	# ==================================================== #
	# =====================图像分割======================== #

	# 获取原始图像的宽和高
	high = img.shape[0]
	width = img.shape[1]

	# 分别初始化高和宽的和
	add_width = np.zeros(high, dtype = int)
	add_high = np.zeros(width, dtype = int)

	# 计算每一行的灰度图的值的和
	for h in range(high):
		for w in range(width):
			add_width[h] = add_width[h] + img_edge1[h][w]

	# 计算每一列的值的和
	for w in range(width):
		for h in range(high):
			add_high[w] = add_high[w] + img_edge1[h][w]

	# 初始化上下边界为宽度总值最大的值的索引
	acount_high_up = np.argmax(add_width)
	acount_high_down = np.argmax(add_width)

	# 将上边界坐标值上移,直到没有遇到白色点停止,此为数字的上边界
	while add_width[acount_high_up] != 0:
		acount_high_up = acount_high_up + 1

	# 将下边界坐标值下移,直到没有遇到白色点停止,此为数字的下边界
	while add_width[acount_high_down] != 0:
		acount_high_down = acount_high_down - 1

	# 初始化左右边界为宽度总值最大的值的索引
	acount_width_left = np.argmax(add_high)
	acount_width_right = np.argmax(add_high)

	# 将左边界坐标值左移,直到没有遇到白色点停止,此为数字的左边界
	while add_high[acount_width_left] != 0:
		acount_width_left = acount_width_left - 1

	# 将右边界坐标值右移,直到没有遇到白色点停止,此为数字的右边界
	while add_high[acount_width_right] != 0:
		acount_width_right = acount_width_right + 1

	# 求出宽和高的间距
	width_spacing = acount_width_right - acount_width_left
	high_spacing = acount_high_up - acount_high_down

	# 求出宽和高的间距差
	poor = width_spacing - high_spacing

	# 将数字进行正方形分割,目的是方便之后进行图像压缩
	if poor > 0:
		tailor_image = img[acount_high_down - poor // 2 - 5:acount_high_up + poor - poor // 2 + 5, acount_width_left - 5:acount_width_right + 5]
  else:
		tailor_image = img[acount_high_down - 5:acount_high_up + 5, acount_width_left + poor // 2 - 5:acount_width_right - poor + poor // 2 + 5]

	# ==================================================== #
	# ======================小图处理======================= #

	# 将裁剪后的图片进行灰度化
	gray_img = cv2.cvtColor(tailor_image , cv2.COLOR_BGR2GRAY)

	# 高斯去噪
	gauss_img = cv2.GaussianBlur(gray_img, (5,5), 0, 0, cv2.BORDER_DEFAULT)

	# 将图像形状调整到28*28大小
	zoom_image = cv2.resize(gauss_img, (28, 28))

	# 获取图像的高和宽
	high = zoom_image.shape[0]
	wide = zoom_image.shape[1]

	# 将图像每个点的灰度值进行阈值比较
	for h in range(high):
		for w in range(wide):

			# 若灰度值大于100,则判断为背景并赋值0,否则将深灰度值变白处理
			if zoom_image[h][w] > 100:
				zoom_image[h][w] = 0
			else:
				zoom_image[h][w] = 255 - zoom_image[h][w]

	# ==================================================== #

	return zoom_image

在此,我在纸上写了个6,如下图所示:
手写数字识别(识别纸上手写的数字)
然后是对图像进行分割,首先要介绍下我分割图像的方法。下面是一张进行canny边缘检测后的6:
手写数字识别(识别纸上手写的数字)
在这里这个6有个特点,就是被白边给包围着了,因为白色的灰度值为255,黑色的灰度值为0,所以我就假设以高为很坐标,然后每个高对应着的宽的灰度值进行相加。所以会很明显发现就6这个字的整体的值比较聚集,当然有可能有零星的散点,但并不影响对6所在位置的判断。最后以高为例,得到的值的坐标图如下:
手写数字识别(识别纸上手写的数字)
因为最大值比较容易找到,所以就找到最大值然后向两边延伸,当发现值为零时就可以把边界给标定出来了。
最后进行分割分割注意的是后面对图像进行裁剪的时候是将宽和高较长的一边减去较短的一边然后除以2平分给较短的一边的两侧,为了防止边缘检测没有包裹着数字,于是在数字四周都加了五个像素点进行裁剪,最后裁剪出来的效果如下:
手写数字识别(识别纸上手写的数字)
这个图片就是上述代码中的tailor_image所显示出来的图片,因为显示图片的代码只作为测试使用,而且又很简单,这里就没有展示出来。
好了,接下来就是要对辛辛苦苦裁剪出来的小图进行图像进行处理了,首先还是最基本的灰度化和高斯滤波处理,然后就是对图像进行大小转换,因为MNIST数据形状就是 28 ∗ 28 28*28 2828所以也要将输入图片转换成 28 ∗ 28 28*28 2828的大小。大小转换完成后,就是要完成把灰度图转换成背景为0,然后数字变成白色的图片,因为这样和MNIST数据集里的数字图片特别的像。在这里我用了阈值控制的方法将背景变成黑色的。至于这100当然是将图片的灰度值打出来后观察得出来的。但是这种方法是比较危险的,因为这样的鲁棒性并不强,但后面如果要加强鲁棒性则同样可以用边缘检测把数字包裹住,然后数字之外的背景清零,这确实是一个很好的思路,但在这里就建议的用阈值控制的方法来实现背景黑化了。黑化背景后当然就是将数字白化了,之前有将数字部分都是255值,但发现识别的效果并不理想,所以这里我采用了用255-原先数字的值,这样如果原先的数字黑度深的部分就会变成白色程度深,就简单的实现了数字边缘浅,中间深的变换。最后处理得到的图像如下:
手写数字识别(识别纸上手写的数字)
虽说看起来没有第一张图那么完美,但大概还是能达到验证数据所需的要求了。至此,数据预处理已经完成了,接下来就是激动的预测了。

预测

预测代码如下:

import torch

# pretreatment.py为上面图片预处理的文件名,导入图片预处理文件
import pretreatment as PRE

# 加载网络模型
net = torch.load('weight/test.pkl')

# 得到返回的待预测图片值,就是pretreatment.py中的zoom_image
img = PRE.image_preprocessing()

# 将待预测图片转换形状
inputs = img.reshape(-1, 784)

# 输入数据转换成tensor张量类型,并转换成浮点类型
inputs = torch.from_numpy(inputs)
inputs = inputs.float()

# 丢入网络进行预测,得到预测数据
predict = net(inputs)

# 打印对应的最后的预测结果
print("The number in this picture is {}".format(torch.argmax(predict).detach().numpy()))

最后得到结果如图所示:
手写数字识别(识别纸上手写的数字)
这样,整个手写数字识别基本已经完成了。文章来源地址https://www.toymoban.com/news/detail-436087.html

到了这里,关于手写数字识别(识别纸上手写的数字)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • AI学习(4): PyTorch实战-手写数字识别

    在之前的文章中介绍了 PyTorch 的环境安装,和张量( tensor )的基本使用,为防止陷入枯燥的理论学习中,在这篇文章,我们将进行项目实战学习,项目主要内容: 基于 MNIST 数据集,实现一个手写数字识别的神经网络模型; @说明: 通过具体项目实战,我们可以初步了解:使用

    2024年02月21日
    浏览(45)
  • pytorch 入门 (三)案例一:mnist手写数字识别

    本文为🔗小白入门Pytorch内部限免文章 🍨 本文为🔗小白入门Pytorch中的学习记录博客 🍦 参考文章:【小白入门Pytorch】mnist手写数字识别 🍖 原作者:K同学啊 ​ 如果设备上支持GPU就使用GPU,否则使用CPU 使用dataset下载MNIST数据集,并划分好训练集与测试集 使用dataloader加载数

    2024年02月07日
    浏览(47)
  • PyTorch训练RNN, GRU, LSTM:手写数字识别

    数据集:MNIST 该数据集的内容是手写数字识别,其分为两部分,分别含有60000张训练图片和10000张测试图片 图片来源:https://tensornews.cn/mnist_intro/ 神经网络:RNN, GRU, LSTM 【1】https://www.youtube.com/watch?v=Gl2WXLIMvKAlist=PLhhyoLH6IjfxeoooqP9rhU3HJIAVAJ3Vzindex=5

    2024年02月15日
    浏览(41)
  • 用PyTorch实现MNIST手写数字识别(最新,非常详细)

    本文基于 PyTorch 框架,采用 CNN卷积神经网络 实现 MNIST 手写数字识别,仅在 CPU 上运行。 已分别实现使用Linear纯线性层、CNN卷积神经网络、Inception网络、和Residual残差网络四种结构对MNIST数据集进行手写数字识别,并对其识别准确率进行比较分析。(另外三种还未发布) 看完

    2024年02月06日
    浏览(34)
  • pytorch集智-5手写数字识别器-卷积神经网络

    简称:CNN,convolutional neural network 应用场景:图像识别与分类(CNN),看图说话(CNN+RNN)等 优越性:和多层感知机相比,cnn可以识别独特的模式,可以自动从数据中提取特征。一般机器学习需要特征工程,cnn可以自动识别,极大代替或取代了特征工程 和多层感知机原理不同

    2024年01月19日
    浏览(42)
  • PyTorch训练简单的全连接神经网络:手写数字识别

    数据集:MNIST 该数据集的内容是手写数字识别,其分为两部分,分别含有60000张训练图片和10000张测试图片 神经网络:全连接网络 【1】https://www.youtube.com/watch?v=Jy4wM2X21u0list=PLhhyoLH6IjfxeoooqP9rhU3HJIAVAJ3Vzindex=3

    2024年02月16日
    浏览(36)
  • [深度学习实战]基于PyTorch的深度学习实战(下)[Mnist手写数字图像识别]

    PyTorch——开源的Python机器学习库   首先感谢所有点开本文的朋友们!基于PyTorch的深度学习实战可能要告一段落了。本想着再写几篇关于 PyTorch神经网络深度学习 的文章来着,可无奈项目时间紧任务重,要求 短时间内出图并做好参数拟合 。所以只得转战 Matlab 编程,框架旧

    2024年02月16日
    浏览(50)
  • 人工智能概论报告-基于PyTorch的深度学习手写数字识别模型研究与实践

    本文是我人工智能概论的课程大作业实践应用报告,可供各位同学参考,内容写的及其水,部分也借助了gpt自动生成,排版等也基本做好,大家可以参照。如果有需要word版的可以私信我,或者在评论区留下邮箱,我会逐个发给。word版是我最后提交的,已经调整统一了全文格

    2024年02月05日
    浏览(71)
  • 在树莓派上实现numpy的conv2d卷积神经网络做图像分类,加载pytorch的模型参数,推理mnist手写数字识别,并使用多进程加速

    这几天又在玩树莓派,先是搞了个物联网,又在尝试在树莓派上搞一些简单的神经网络,这次搞得是卷积识别mnist手写数字识别 训练代码在电脑上,cpu就能训练,很快的: 然后需要自己在dataset里导出一些图片:我保存在了mnist_pi文件夹下,“_”后面的是标签,主要是在pc端导

    2024年02月07日
    浏览(33)
  • 【10个适合新手的人工智能项目 - 02】手写数字识别:使用Python和机器学习算法,编写一个手写数字识别程序,能够识别手写数字图像并将其转换为数字。

    为了编写一个手写数字识别程序,我们需要使用Python编程语言和一些机器学习算法。在这个项目中,我们将使用深度学习神经网络模型,它被广泛应用于图像识别任务。 以下是手写数字识别程序的基本步骤: 首先,我们需要一个数据集,用于训练和测试我们的模型。一个常

    2024年02月03日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包