AI手写输入法 - pytorch从入门到入道(二)
本章承接上一篇的手写数字识别,利用训练好的模型,结合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从入门到入道(二)的更多相关文章
- 识别手写数字增强版100% - pytorch从入门到入道(一)
手写数字识别,神经网络领域的“hello world”例子,通过pytorch一步步构建,通过训练与调整,达到“100%”准确率 1.快速开始 1.1 定义神经网络类,继承torch.nn.Modul ...
- 《深度学习框架PyTorch:入门与实践》的Loss函数构建代码运行问题
在学习陈云的教程<深度学习框架PyTorch:入门与实践>的损失函数构建时代码如下: 可我运行如下代码: output = net(input) target = Variable(t.a ...
- 通通WPF随笔(4)——通通手写输入法(基于Tablet pc实现)
原文:通通WPF随笔(4)--通通手写输入法(基于Tablet pc实现) 从我在博客园写第一篇博客到现在已经有1年半了,我的第一篇博客写的就是手写识别,当时,客户需求在应用中加入手写输入功能,由于第 ...
- 《深度学习框架PyTorch:入门与实践》读书笔记
https://github.com/chenyuntc/pytorch-book Chapter2 :PyTorch快速入门 + Chapter3: Tensor和Autograd + Chapte ...
- pytorch怎么入门学习
pytorch怎么入门学习 https://www.zhihu.com/question/55720139
- pytorch从入门到放弃(目录)
目录 前置基础 Pytorch从入门到放弃 推荐阅读 前置基础 Python从入门到放弃(目录) 人工智能(目录) Pytorch从入门到放弃 01_pytorch和tensorflow的区别 02_ ...
- 【笔记】PyTorch快速入门:基础部分合集
PyTorch快速入门 Tensors Tensors贯穿PyTorch始终 和多维数组很相似,一个特点是可以硬件加速 Tensors的初始化 有很多方式 直接给值 data = [[1,2],[3, ...
- [OpenCV入门教程之十二】OpenCV边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑
http://blog.csdn.net/poem_qianmo/article/details/25560901 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog ...
- Redis入门很简单之二【常见操作命令】
Redis入门很简单之二[常见操作命令] 博客分类: NoSQL/Redis/MongoDB redisnosql缓存 Redis提供了丰富的命令,允许我们连接客户端对其进行直接操作.这里简单介绍一 ...
随机推荐
- django-模板之extends(三)
/book/base.html <!DOCTYPE html> <html lang="en"> <head> <meta charset ...
- fenby C语言 P30
int a[5],*p=a; int a[5],*p=&a[0]; #include <stdio.h> int main(void){ int a[5],*p=a,i; for( ...
- GCC常用参数详解
转载:http://www.cnblogs.com/zhangsir6/articles/2956798.html 简介gcc and g++现在是gnu中最主要和最流行的c & c++编译器 ...
- const var let 三者的区别
1.const定义的变量不可以修改,而且必须初始化. ;//正确 const b;//错误,必须初始化 console.log('函数外const定义b:' + b);//有输出值 b = ; con ...
- Spring AOP 知识整理
通过一个多月的 Spring AOP 的学习,掌握了 Spring AOP 的基本概念.AOP 是面向切面的编程(Aspect-Oriented Programming),是基于 OOP(面向对象的编 ...
- 使用Bind提供域名解析服务(反向解析)
小知识: 一般来讲域名比IP地址更加的有含义.也更容易记住,所以通常用户更习惯输入域名来访问网络中的资源,但是计算机主机在互联网中只能通过IP识别对方主机,那么就需要DNS域名解析服务了. DNS域名 ...
- Web for pentester_writeup之File Upload篇
Web for pentester_writeup之File Upload篇 File Upload(文件上传) Example 1 直接上传一句话木马,使用蚁剑连接 成功连接,获取网站根目录 Exa ...
- (转载)学校搭建使用nginx同时编译rtmp-module进行直播的技术文档
原文地址:学校搭建使用 nginx 同时编译 rtmp-module 进行直播的技术文档 转载自我的大佬同学 MetalkgLZH.学校有几次需要全校观看网络直播的情况,但是学校的带宽不允许所有的班一 ...
- csps模拟测试50反思
又考崩了,T1一眼秒掉错误思路,然后迅速码完,并码完错误暴力,对拍拍上,以为AC.T2想到了二维莫队,发现是子任务就没去打,一直在想别的,T3最后想到60分打法,没有打完,也没时间暴力,挂掉.T2还有 ...
- 【转载】InstantRun 原理——深度剖析 AndroidStudio 2.0
一.前言 Android Studio 2.0开始支持 Instant Run 特性, 使得在开发过程中能快速将代码变化更新到设备上.之前,更新代码之后需要先编译一个完整的新Apk,卸载设备上已安装的 ...