本章承接上一篇的手写数字识别,利用训练好的模型,结合pyqt画板,实现简易手写输入法,为"hello world"例子增添乐趣。

pyqt是开发图形界面的框架,可以百度查找相关资料了解安装及基础方法,我搭建的环境是pycharm+pyqt5+qtdesigner,配置好之后的界面长这样:

在左边的项目中右键某个文件,也可以打开qt菜单

具体怎么画界面不展开了,直接看下代码:

 # coding: utf-8
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
sys.path.append(r'../ml/torch')
from digit_recog import Net
import torch
import os
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
net = Net().to(device)
# 加载参数
nn_state = torch.load(os.path.join('../ml/torch/model/', 'net.pth'))
# 参数加载到指定模型
net.load_state_dict(nn_state)
net.eval() def predict(img):
# 读取图片并重设尺寸
image = Image.open(img).resize((28, 28))
# 灰度图
gray_image = image.convert('L')
# plt.imshow(gray_image)
# plt.show()
# 图片数据处理
im_data = np.array(gray_image)
im_data = torch.from_numpy(im_data).float()
im_data = im_data.view(1, 1, 28, 28)
# 神经网络运算
outputs = net(im_data)
# 取最大预测值
_, pred = torch.max(outputs, 1)
return pred.item() class SimpleDrawingBoard(QWidget):
win = ''
wins = [] @classmethod
def showWin(cls):
# 聚焦到已有窗口
if not cls.win:
cls.win = cls()
cls.win.show()
else:
cls.win.activateWindow() def __init__(self, parent=None):
super(SimpleDrawingBoard, self).__init__(parent) self.setWindowTitle(u"手写数字识别")
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.size = (400, 350)
self.resize(*self.size)
self.setWindowFlag(Qt.FramelessWindowHint) # 隐藏边框
# self.setWindowOpacity(0.9) # 设置窗口透明度
# self.setAttribute(Qt.WA_TranslucentBackground) # 设置窗口背景透明 self.canvasSize = (280, 350)
self.sizeOffset = [a - b for a, b in zip(self.size, self.canvasSize)]
self.canvas = QPixmap(*self.canvasSize)
self.canvas.fill(Qt.black)
self.tempCanvas = QPixmap()
self.lastPoint = QPoint()
self.endPoint = QPoint()
self.isDrawing = False
self.penSize = 15 self.initUI() def initUI(self):
self.penSizeLabel = QLabel(u'画笔粗细')
self.penSizeSpinBox = QSpinBox()
self.penSizeSpinBox.setValue(self.penSize)
self.penSizeSpinBox.valueChanged.connect(self.penSizeSpinBox_valueChanged)
self.penSizeSpinBox.setFixedWidth(80) self.clearButton = QPushButton(u'清空')
self.clearButton.setFixedWidth(80)
self.clearButton.clicked.connect(self.clearPainter) self.closeButton = QPushButton(u'关闭')
self.closeButton.setFixedWidth(80)
self.closeButton.clicked.connect(self.close) self.inputLabel = QLabel(self)
self.inputLabel.setFixedSize(80, 200)
self.inputLabel.setAutoFillBackground(True)
self.inputLabel.setAlignment(Qt.AlignCenter)
self.inputLabel.setStyleSheet('''QLabel{background:#F76677;border-radius:5px;font-size:60px;font-weight:bolder;}''') mainLayout = QVBoxLayout(self) toolbarLayout = QGridLayout()
# toolbarLayout.setSpacing(20)
toolbarLayout.addWidget(self.penSizeLabel, 0, 0, 1, 1)
toolbarLayout.addWidget(self.penSizeSpinBox, 1, 0, 1, 1)
toolbarLayout.addWidget(self.clearButton, 2, 0, 1, 1)
toolbarLayout.addWidget(self.closeButton, 3, 0, 1, 1)
toolbarLayout.addWidget(self.inputLabel, 4, 0, 1, 1) toolbarLayout.setAlignment(Qt.AlignLeft) mainLayout.addLayout(toolbarLayout)
mainLayout.addStretch(1) def penSizeSpinBox_valueChanged(self):
# 设置画笔粗细
self.penSize = self.penSizeSpinBox.value() def paintEvent(self, event):
pp = QPainter(self.canvas)
pen = QPen(QColor(255, 255, 255), self.penSize)
pp.setPen(pen)
if self.lastPoint != self.endPoint:
pp.drawLine(self.lastPoint - QPoint(*self.sizeOffset), self.endPoint - QPoint(*self.sizeOffset))
painter = QPainter(self)
painter.drawPixmap(self.sizeOffset[0], self.sizeOffset[1], self.canvas)
self.lastPoint = self.endPoint def clearPainter(self):
print('clear...')
self.canvas.fill(Qt.black)
painter = QPainter(self)
painter.drawPixmap(self.sizeOffset[0], self.sizeOffset[1], self.canvas)
self.lastPoint = self.endPoint
self.update()
self.inputLabel.clear() def mousePressEvent(self, event):
# 按下左键
if event.button() == Qt.LeftButton:
self.lastPoint = event.pos()
self.endPoint = self.lastPoint
self.isDrawing = True def mouseMoveEvent(self, event):
if self.isDrawing:
self.update()
self.endPoint = event.pos() def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
self.isDrawing = False
self.endPoint = event.pos()
self.update()
self.canvas.toImage().save('input.png')
input = predict('input.png')
self.inputLabel.setText(str(input))
print('你输入的是{}'.format(input)) if __name__ == '__main__':
app = QApplication.instance()
if not app:
app = QApplication(sys.argv)
SimpleDrawingBoard.showWin()
app.exec_()

上面引入前一章训练好的模型,位于不同的文件夹内,需要加上这一行代码:

sys.path.append(r'../ml/torch')

看下运行效果:

上面写了两个数字,识别输出正确!

helloworld例子比较枯燥,通过动手参与与AI交互增强信心乐趣,信心是一步步建立起来的,而大的突破亦是如此,后面会持续围绕简单的例子,深入发掘AI的乐趣与应用场景。

AI手写输入法 - pytorch从入门到入道(二)的更多相关文章

  1. 识别手写数字增强版100% - pytorch从入门到入道(一)

    手写数字识别,神经网络领域的“hello world”例子,通过pytorch一步步构建,通过训练与调整,达到“100%”准确率 1.快速开始 1.1 定义神经网络类,继承torch.nn.Modul ...

  2. 《深度学习框架PyTorch:入门与实践》的Loss函数构建代码运行问题

    在学习陈云的教程<深度学习框架PyTorch:入门与实践>的损失函数构建时代码如下: 可我运行如下代码: output = net(input) target = Variable(t.a ...

  3. 通通WPF随笔(4)——通通手写输入法(基于Tablet pc实现)

    原文:通通WPF随笔(4)--通通手写输入法(基于Tablet pc实现) 从我在博客园写第一篇博客到现在已经有1年半了,我的第一篇博客写的就是手写识别,当时,客户需求在应用中加入手写输入功能,由于第 ...

  4. 《深度学习框架PyTorch:入门与实践》读书笔记

    https://github.com/chenyuntc/pytorch-book Chapter2 :PyTorch快速入门 + Chapter3: Tensor和Autograd + Chapte ...

  5. pytorch怎么入门学习

    pytorch怎么入门学习 https://www.zhihu.com/question/55720139

  6. pytorch从入门到放弃(目录)

    目录 前置基础 Pytorch从入门到放弃 推荐阅读 前置基础 Python从入门到放弃(目录) 人工智能(目录) Pytorch从入门到放弃 01_pytorch和tensorflow的区别 02_ ...

  7. 【笔记】PyTorch快速入门:基础部分合集

    PyTorch快速入门 Tensors Tensors贯穿PyTorch始终 和多维数组很相似,一个特点是可以硬件加速 Tensors的初始化 有很多方式 直接给值 data = [[1,2],[3, ...

  8. [OpenCV入门教程之十二】OpenCV边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑

    http://blog.csdn.net/poem_qianmo/article/details/25560901 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog ...

  9. Redis入门很简单之二【常见操作命令】

    Redis入门很简单之二[常见操作命令] 博客分类: NoSQL/Redis/MongoDB redisnosql缓存  Redis提供了丰富的命令,允许我们连接客户端对其进行直接操作.这里简单介绍一 ...

随机推荐

  1. laravel6.0控制器-资源控制器

    控制器:控制器用来处理业务的,不应该处理逻辑,如果是小项目可以把逻辑写到控制器里,大点的项目应该抽离出来业务处理层如下:services业务处理层:比如:获取值,验证值,异常捕获命名规则:控制器名:用 ...

  2. mysql 主从同步(转)

    教程开始:一.安装MySQL 说明:在两台MySQL服务器192.168.21.169和192.168.21.168上分别进行如下操作,安装MySQL 5.5.22  二.配置MySQL主服务器(19 ...

  3. Microsoft.Extensions.DependencyInjection 之三:展开测试

    目录 前文回顾 IServiceCallSite CallSiteFactory ServiceProviderEngine CompiledServiceProviderEngine Dynamic ...

  4. MySQL:数据库基本认识

    1.什么是数据库 通俗来讲,数据库就是用于存储数据的仓库.很多人可能会问,存储数据用文件不就行了吗?为什么还要弄数据库? 文件存储数据具有以下几个缺点: 文件的安全性问题 文件不利于查询和数据库管理 ...

  5. NLP预训练模型-百度ERNIE2.0的效果到底有多好【附用户点评】

    ERNIE是百度自研的持续学习语义理解框架,该框架支持增量引入词汇(lexical).语法 (syntactic) .语义(semantic)等3个层次的自定义预训练任务,能够全面捕捉训练语料中的词法 ...

  6. MIT线性代数:7.主变量,特解,求解AX=0

  7. kettle计划任务

    在kettle中固定抽取数据,需要用到kichen命令,编好批处理脚本:bat C: cd C:\soft\kettle\data-integration kitchen /file C:\soft\ ...

  8. Cocos引擎现身 IndiePrize 全球游戏开发者大会!Cocos的两大男神成为压轴嘉宾

    2019全球游戏开发者大会今天11月10日,在深圳南山海上世界文化艺术中心拉开帷幕.除了号称精品游戏"奥斯卡"的IndiePrize将在现场展开最终角逐,更有来自美国.俄罗斯.澳大 ...

  9. [考试反思]0919csp-s模拟测试47:苦难

    ISOLATION 也不粘上面的了,先管好自己. 附了个近期总分,可以看出什么. 反思一下考试心态: 开场看题目,T1傻逼题不用脑子,T2傻逼板子,T3... 这T3是啥啊?没看懂题目啊?再看一遍.啥 ...

  10. 差异---虐爆了yxs的 后缀数组裸题 板子题 单调栈的简单应用 字符串的基础理解考察题

    先玩柿子,发现可以拆开,前半部分可以瞬间求出,于是只求后半部分 然后抄板子就好了,完结撒花! 下边是个人口胡,因为已经被虐爆头脑不清醒了 定义:LCP(a,b)为排名为a,b两个后缀的最长公共前缀 证 ...