Python3生成手写体数字方法


Posted in Python onJanuary 30, 2018

0.引言

平时上网干啥的基本上都会接触验证码,或者在机器学习学习过程中,大家或许会接触过手写体识别/验证码识别之类问题,会用到手写体的数据集;

自己尝试写了一个生成手写体图片的python程序,在此分享下生成单张 30*30像素的手写体数字1-9图像 的一种实现方法;

我是利用random生成随机数1-9,然后PIL写到图像上,然后经过旋转、扭曲处理,得到“手写体”,这里没有加干扰线和干扰点;

得到的手写体数字图像如图1所示;

实现比较简单,用了PIL库,不需要额外安装opencv啥的,有兴趣可以自己试试。

Python3生成手写体数字方法

图1 生成的手写体数字1-9

Python3生成手写体数字方法

图2 利用generate_pngs.py写入到文件夹3的数字3图像

如果你想生成手写体的字母/汉字也可以:

Python3生成手写体数字方法Python3生成手写体数字方法

图3 利用generate_single_png.py生成汉字的手写体

源码上传到了我的GitHub: https://github.com/coneypo/Generate_handwritten_number

1.设计流程

Python3生成手写体数字方法

图4 整体设计流程

Python3生成手写体数字方法

图5 生成的图像经过的处理

1.1 新建一个空白图像img_50,尺寸大小为50*50 

img_50_blank = Image.new('RGB', (50, 50), (255, 255, 255))

为什么我这里要先生成50*50的空白图像?

因为图像背景(50*50像素的画布)初始化的时候设置为白色(颜色数组(255, 255, 255)),而背景色之外的其实是黑色;

之后需要进行旋转处理,如果直接新建30*30像素的画布,旋转之后边上会出现黑边,如图6所示;

所以我新建了一个50*50,然后旋转之后从中间裁出来一个30*30的图像出来;

Python3生成手写体数字方法

图6 直接用30*30像素的画布写字旋转(会出现黑边)

1.2 利用PIL在图像上写文字

利用PIL的ImageDraw,创建画笔,然后利用draw.text在指定位置写字;

xy=(18,11)是从图像左上角开始的坐标,取值自己根据需求调整;

# 创建画笔
 draw = ImageDraw.Draw(img_50_blank)

# 生成随机数1-9
num = str(random.randint(1, 9))

# 设置字体,这里选取字体大小25
font = ImageFont.truetype('simsun.ttc', 20)

# xy是左上角开始的位置坐标
draw.text(xy=(18, 11), font=font, text=num, fill=(0, 0, 0))

1.3 将图像随机旋转一定角度

利用 rotate(angel) 进行旋转图像,angel取的是度数,这里让它随机旋转-10到+10度:

# 随机旋转-10-10角度
 random_angle = random.randint(-10, 10)
 img_50_rotated = img_50_blank.rotate(random_angle)

1.4 图像扭曲

这里是生成“手写体”数字的核心步骤,一个正常的图像经过扭曲之后就可以得到想要的验证码了:

# 图形扭曲参数
params = [1 - float(random.randint(1, 2)) / 100,
      0,
      0,
      0,
      1 - float(random.randint(1, 10)) / 100,
      float(random.randint(1, 2)) / 500,
      0.001,
      float(random.randint(1, 2)) / 500]

# 创建扭曲
img_50_transformed = img_50_rotated.transform((50, 50), Image.PERSPECTIVE, params)

2.py源码介绍

2.1 generate_folders_1to9.py

因为我们要将指定的图像分类放入指定文件夹,所以我们需要先在项目目录下面新建9个文件夹:

 (当然你也可以自己新建,新建9个文件夹工作量还不大,但是如果要生成的验证码包含英文字母那就比较多了,大写A-Z共24个+小写a-z共24个+数字1-9共9个=57个子文件夹)

# 2018-01-9
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# generate_folders_1to9.py
# 在目录下生成用来存放数字1-9的9个文件夹,分别用1-9命名


import os

path_folders = "F:/***/P_generate_handwritten_number/data_pngs/"

# 1-9
for i in range(49,58):
  if (os.path.isdir(path_folders + chr(i))):
    pass
  else:
    # print(i,": ",path_1+chr(i))
    # 生成目录
    os.mkdir(path_folders+chr(i))

Python3生成手写体数字方法

图7 自动生成的用来存放指定图像的文件夹

2.2 generate_pngs.py

根据给定随机次数samples, 生成samples个手写体数字1-9,然后存放到本地文件夹1-9生成数据集;

在49行可以修改生成图像的大小,我这里取的是30*30像素;

# 2018-01-9
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# generate_pngs.py
# 生成手写体数字


import random
from PIL import Image, ImageDraw, ImageFilter, ImageFont

random.seed(3)

# 生成单张扭曲的数字图像
def generate_single():

  # 先绘制一个50*50的空图像
  img_50_blank = Image.new('RGB', (50, 50), (255, 255, 255))

  # 创建画笔
  draw = ImageDraw.Draw(img_50_blank)

  # 生成随机数1-9
  num = str(random.randint(1, 9))

  # 设置字体,这里选取字体大小25
  font = ImageFont.truetype('simsun.ttc', 20)

  # xy是左上角开始的位置坐标
  draw.text(xy=(18, 11), font=font, text=num, fill=(0, 0, 0))

  # 随机旋转-10-10角度
  random_angle = random.randint(-10, 10)
  img_50_rotated = img_50_blank.rotate(random_angle)

  # 图形扭曲参数
  params = [1 - float(random.randint(1, 2)) / 100,
       0,
       0,
       0,
       1 - float(random.randint(1, 10)) / 100,
       float(random.randint(1, 2)) / 500,
       0.001,
       float(random.randint(1, 2)) / 500]

  # 创建扭曲
  img_50_transformed = img_50_rotated.transform((50, 50), Image.PERSPECTIVE, params)

  # 生成新的30*30空白图像,(在此处可以更改生成的图像大小)
  img_30 = img_50_transformed.crop([10, 10, 40, 40])

  return img_30, num

path_pic = "F:/***/P_generate_handwritten_number/data_pngs/"


# 生成手写体数字1-9存入指定文件夹1-9

# 用cnt_num[1]-cnt_num[9]来计数数字1-9生成的个数,方便之后进行命名
cnt_num = []
for i in range(10):
  cnt_num.append(0)

# 生成次数
samples = 200

for m in range(1, samples+1):

  # 调用生成图像文件函数
  img, generate_num = generate_single()

  # 取灰度
  imgray = img.convert('1')

  # 计数生成的数字1-9的个数,用来命名图像文件
  for j in range(1, 10):
    if(generate_num == str(j)):
      cnt_num[j] = cnt_num[j]+1

      # 路径如 "F:/code/***/P_generate_handwritten_number/data_pngs/1/1_231.png"
      # 输出显示路径
      print(path_pic + str(j) + "/" + str(j) + "_" + str(cnt_num[j]) + ".png")
      # 将图像保存在指定文件夹中
      imgray.save(path_pic + str(j) + "/" + str(j) + "_" + str(cnt_num[j]) + ".png")

# 输出显示1-9的分布
print("\n", "生成的1-9的分布:")
for k in range(9):
  print(k+1, ":", cnt_num[k+1], "张")

output

D:\***\anaconda\python.exe F:/***/P_generate_handwritten_number/generate_pngs.py
F:/***/P_generate_handwritten_number/data_pngs/4/4_1.png
F:/***/P_generate_handwritten_number/data_pngs/1/1_1.png
F:/***/P_generate_handwritten_number/data_pngs/8/8_1.png
F:/***/P_generate_handwritten_number/data_pngs/3/3_1.png
F:/***/P_generate_handwritten_number/data_pngs/1/1_2.png
...

生成的1-9的分布:
: 25 张
: 17 张
: 21 张
: 19 张
: 20 张
: 22 张
: 25 张
: 24 张
: 27 张

修改 generate_pngs.py中的samples, 你就可以生成指定大小的数据集;

2.3 generate_single_png.py

更改27行的char=" "(可以是数字/字母/汉字),生成单张手写体扭曲图像:

# 2018-01-9
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# generate_single_png.py
# 生成手写体数字/字母/汉字


import random
from PIL import Image, ImageDraw, ImageFilter, ImageFont

random.seed(3)

# 生成单张扭曲的数字图像
def generate_single():

  # 先绘制一个50*50的空图像
  img_50_blank = Image.new('RGB', (50, 50), (255, 255, 255))

  # 创建画笔
  draw = ImageDraw.Draw(img_50_blank)

  # 设置字体,这里选取字体大小25
  font = ImageFont.truetype('simsun.ttc', 20)

  # xy是左上角开始的位置坐标
  # text是你想要显示的内容,数字/字母/汉字
  char ="呵"
  draw.text(xy=(12, 11), font=font, text=char, fill=(0, 0, 0))

  # 随机旋转-10-10角度
  random_angle = random.randint(-10, 10)
  img_50_rotated = img_50_blank.rotate(random_angle)

  # 图形扭曲参数
  params = [1 - float(random.randint(1, 2)) / 100,
       0,
       0,
       0,
       1 - float(random.randint(1, 10)) / 100,
       float(random.randint(1, 2)) / 500,
       0.001,
       float(random.randint(1, 2)) / 500]

  # 创建扭曲
  img_50_transformed = img_50_rotated.transform((50, 50), Image.PERSPECTIVE, params)

  # 生成新的30*30空白图像
  img_30 = img_50_transformed.crop([10, 10, 40, 40])

  return img_30, char

path_pic = "F:/code/python/P_generate_handwritten_number/"


# 调用生成图像文件函数
img, generated_char = generate_single()
imgray = img.convert('1')

print(path_pic + "test.png")
# 将图像保存在指定文件夹中
imgray.save(path_pic + "test.png")

2.4 del_pngs.py

删除指定目录下子文件夹1-9中的所有图片:

# 2018-01-9
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# del_pngs.py
# 删除路径下生成的图像文件

import os

path_pic = "F:/***/P_generate_handwritten_number/data_pngs/"

#删除路径下的图片
def del_pic():
  for i in range(1, 10):
   #  print(path_png+chr(i))
    namedir = os.listdir(path_pic+str(i))

    for tmppng in namedir:
      if( tmppng in namedir):
       # print(tmppng)
        os.remove(path_pic+str(i)+"/"+tmppng)

del_pic()

3.总结

自己动手丰衣足食,有兴趣可以自己做手写体数字数据集,字母和汉字的数据集稍加修改也可以做;

# GitHub: https://github.com/coneypo/Generate_handwritten_number

Python 相关文章推荐
Python自动连接ssh的方法
Mar 07 Python
Python命令行参数解析模块getopt使用实例
Apr 13 Python
Python的pycurl包用法简介
Nov 13 Python
深入理解Python装饰器
Jul 27 Python
Ubuntu 16.04 LTS中源码安装Python 3.6.0的方法教程
Dec 27 Python
Python随机数用法实例详解【基于random模块】
Apr 18 Python
Python中input与raw_input 之间的比较
Aug 20 Python
解决pip install的时候报错timed out的问题
Jun 12 Python
Python爬虫爬取新浪微博内容示例【基于代理IP】
Aug 03 Python
flask session组件的使用示例
Dec 25 Python
python微信撤回监测代码
Apr 29 Python
matplotlib之属性组合包(cycler)的使用
Feb 24 Python
python字符串的方法与操作大全
Jan 30 #Python
Python实现带参数与不带参数的多重继承示例
Jan 30 #Python
Python实现的随机森林算法与简单总结
Jan 30 #Python
Python决策树和随机森林算法实例详解
Jan 30 #Python
在Python 2.7即将停止支持时,我们为你带来了一份python 3.x迁移指南
Jan 30 #Python
python使用Tkinter实现在线音乐播放器
Jan 30 #Python
Python字典及字典基本操作方法详解
Jan 30 #Python
You might like
PHP IN_ARRAY 函数使用注意事项
2010/07/24 PHP
PHP添加Xdebug扩展的方法
2014/02/12 PHP
yii框架builder、update、delete使用方法
2014/04/30 PHP
php返回字符串中所有单词的方法
2015/03/09 PHP
PHP利用超级全局变量$_GET来接收表单数据的实例
2016/11/05 PHP
php base64 编码与解码实例代码
2017/03/21 PHP
JavaScript 通过模式匹配实现重载
2010/08/12 Javascript
微信小程序 定义全局数据、函数复用、模版等详细介绍
2016/10/27 Javascript
基于node.js依赖express解析post请求四种数据格式
2017/02/13 Javascript
浅谈React前后端同构防止重复渲染
2018/01/05 Javascript
vue使用Element组件时v-for循环里的表单项验证方法
2018/06/28 Javascript
解决layui表格内文本超出隐藏的问题
2019/09/12 Javascript
vue之组件内监控$store中定义变量的变化详解
2019/11/08 Javascript
JS使用for in有序获取对象数据
2020/05/19 Javascript
关于uniApp editor微信滑动问题
2021/01/15 Javascript
javascript中layim之查找好友查找群组
2021/02/06 Javascript
[03:42]2018完美盛典-《加冕》
2018/12/16 DOTA
python 读写txt文件 json文件的实现方法
2016/10/22 Python
python实现按长宽比缩放图片
2018/06/07 Python
为什么str(float)在Python 3中比Python 2返回更多的数字
2018/10/16 Python
Python子类继承父类构造函数详解
2019/02/19 Python
python的time模块和datetime模块实例解析
2019/11/29 Python
详解Python实现进度条的4种方式
2020/01/15 Python
如何用Python提取10000份log中的产品信息
2021/01/14 Python
毕业生简单求职信
2013/11/19 职场文书
自荐书模板
2013/12/15 职场文书
告诉你怎样写创业计划书
2014/01/27 职场文书
食品销售计划书
2014/04/26 职场文书
户外宣传策划方案
2014/05/25 职场文书
经营理念口号
2014/06/21 职场文书
党的群众路线教育实践活动个人整改落实情况汇报
2014/10/28 职场文书
2014收银员工作总结范文
2014/12/16 职场文书
给领导的感谢信范文
2015/01/23 职场文书
员工自我工作评价
2015/03/06 职场文书
《你在为谁工作》心得体会(共8篇)
2016/01/20 职场文书
实现一个简单得数据响应系统
2021/11/11 Javascript