zoukankan      html  css  js  c++  java
  • Python 3 生成手写体数字数据集

    0.引言

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

      自己尝试写了一个生成手写体图片的 Python 程序,可以批量生成手写体数字数据集,在此分享下生成 30*30像素 的手写体数字 1-9 图片 的一种实现方法

      大概流程:新建空白图像 >>> 生成随机数 1-9 >>> 将数字写到空白图像上 >>> 旋转扭曲 处理 >>> 得到 “手写体数字”

      得到的手写体数字图像如 图1 所示,实现比较简单,有兴趣可以自己试;

      源码上传到了我的 GitHub,如果对您有帮助欢迎 star 支持下: https://github.com/coneypo/Generate_handwritten_number ;

      

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

      图 2 利用 generate_imgs.py 得到数字 3 图像

    1. 设计流程

    图 4 整体设计流程

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

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

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

      

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

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

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

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

      

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

    1.2 利用 PIL 在图像上写文字 text

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

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

     1 # 创建画笔
     2  draw = ImageDraw.Draw(img_50_blank)
     3 
     4 # 生成随机数1-9
     5 num = str(random.randint(1, 9))
     6 
     7 # 设置字体,这里选取字体大小25
     8 font = ImageFont.truetype('simsun.ttc', 20)
     9 
    10 # xy是左上角开始的位置坐标
    11 draw.text(xy=(18, 11), font=font, text=num, fill=(0, 0, 0))

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

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

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

    1.4 图像扭曲

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

     1 # 图形扭曲参数
     2 params = [1 - float(random.randint(1, 2)) / 100,
     3             0,
     4             0,
     5             0,
     6             1 - float(random.randint(1, 10)) / 100,
     7             float(random.randint(1, 2)) / 500,
     8             0.001,
     9             float(random.randint(1, 2)) / 500]
    10 
    11 # 创建扭曲
    12 img_50_transformed = img_50_rotated.transform((50, 50), Image.PERSPECTIVE, params)

    2. Source Code 介绍

    2.1 函数 mkdir_for_imgs()

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

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

    1 # 在目录下生成用来存放数字 1-9 的 9个文件夹,分别用 1-9 命名
    2 def mkdir_for_imgs():
    3     for i in range(49, 58):
    4         if os.path.isdir(path_img + "Num_" + chr(i)):
    5             pass
    6         else:
    7             print(path_img + "Num_" + chr(i))
    8             os.mkdir(path_img + "Num_" + chr(i))

    图 7 mkdir_for_imgs() 生成的用来存放指定图像的文件夹

    2.2 函数 del_imgs()

       删除子文件夹 Num_1-9 中的所有图片:

     1 # 删除路径下的图片
     2 def del_imgs():
     3     for i in range(1, 10):
     4         dir_nums = os.listdir(path_img+ "Num_"  + str(i))
     5         for tmp_img in dir_nums:
     6             if tmp_img in dir_nums:
     7                 # print("delete: ", tmp_img)
     8                 os.remove(path_img + "Num_" + str(i) + "/" + tmp_img)
     9     print("Delete finish", "
    ")

     2.3 完整的代码  generate_imgs.py

      mkdir_for_imgs() >>> del_imgs() >>> generate_1to9(n)

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

      Line 67 修改生成图像的大小,我这里取的是 30*30 像素;

       79     # 生成新的30*30空白图像
       80     im_30 = im_50_transformed.crop([10, 10, 40, 40])

      Line 105 给定生成手写体数字的次数:

      116 # generate n times
      117 generate_1to9(1000)

    generate_imgs.py:

      1 # Created on:   2018-01-09
      2 # Updated on:   2018-09-03
      3 # Author:       coneypo
      4 # Blog:      http://www.cnblogs.com/AdaminXie/
      5 # Github:       https://github.com/coneypo/Generate_handwritten_number
      6 # 生成手写体数字
      7 
      8 
      9 import random
     10 import os
     11 from PIL import Image, ImageDraw, ImageFont
     12 
     13 random.seed(3)
     14 path_img = "data_pngs/"
     15 
     16 
     17 # 在目录下生成用来存放数字 1-9 的 9个文件夹,分别用 1-9 命名
     18 def mkdir_for_imgs():
     19     for i in range(49, 58):
     20         if os.path.isdir(path_img + "Num_" + chr(i)):
     21             pass
     22         else:
     23             print(path_img + "Num_" + chr(i))
     24             os.mkdir(path_img + "Num_" + chr(i))
     25 
     26 
     27 # generate folders
     28 # mkdir_for_imgs()
     29 
     30 
     31 # 删除路径下的图片
     32 def del_imgs():
     33     for i in range(1, 10):
     34         dir_nums = os.listdir(path_img+ "Num_"  + str(i))
     35         for tmp_img in dir_nums:
     36             if tmp_img in dir_nums:
     37                 # print("delete: ", tmp_img)
     38                 os.remove(path_img + "Num_" + str(i) + "/" + tmp_img)
     39     print("Delete finish", "
    ")
     40 
     41 
     42 del_imgs()
     43 
     44 
     45 # 生成单张扭曲的数字图像
     46 def generate_single():
     47     # 先绘制一个50*50的空图像
     48     im_50_blank = Image.new('RGB', (50, 50), (255, 255, 255))
     49 
     50     # 创建画笔
     51     draw = ImageDraw.Draw(im_50_blank)
     52 
     53     # 生成随机数1-9
     54     num = str(random.randint(1, 9))
     55 
     56     # 设置字体,这里选取字体大小25
     57     font = ImageFont.truetype('simsun.ttc', 20)
     58 
     59     # xy是左上角开始的位置坐标
     60     draw.text(xy=(18, 11), font=font, text=num, fill=(0, 0, 0))
     61 
     62     # 随机旋转-10-10角度
     63     random_angle = random.randint(-10, 10)
     64     im_50_rotated = im_50_blank.rotate(random_angle)
     65 
     66     # 图形扭曲参数
     67     params = [1 - float(random.randint(1, 2)) / 100,
     68               0,
     69               0,
     70               0,
     71               1 - float(random.randint(1, 10)) / 100,
     72               float(random.randint(1, 2)) / 500,
     73               0.001,
     74               float(random.randint(1, 2)) / 500]
     75 
     76     # 创建扭曲
     77     im_50_transformed = im_50_rotated.transform((50, 50), Image.PERSPECTIVE, params)
     78 
     79     # 生成新的30*30空白图像
     80     im_30 = im_50_transformed.crop([10, 10, 40, 40])
     81 
     82     return im_30, num
     83 
     84 
     85 # 生成手写体数字1-9存入指定文件夹1-9
     86 def generate_1to9(n):
     87     # 用cnt_num[1]-cnt_num[9]来计数数字1-9生成的个数,方便之后进行命名
     88     cnt_num = []
     89     for i in range(10):
     90         cnt_num.append(0)
     91 
     92     for m in range(1, n + 1):
     93         # 调用生成图像文件函数
     94         im, generate_num = generate_single()
     95 
     96         # 取灰度
     97         im_gray = im.convert('1')
     98 
     99         # 计数生成的数字1-9的个数,用来命名图像文件
    100         for j in range(1, 10):
    101             if generate_num == str(j):
    102                 cnt_num[j] = cnt_num[j] + 1
    103 
    104                 # 路径如 "F:/code/***/P_generate_handwritten_number/data_pngs/1/1_231.png"
    105                 # 输出显示路径
    106                 print("Generate:", path_img + "Num_" + str(j) + "/" + str(j) + "_" + str(cnt_num[j]) + ".png")
    107                 # 将图像保存在指定文件夹中
    108                 im_gray.save(path_img + "Num_" + str(j) + "/" + str(j) + "_" + str(cnt_num[j]) + ".png")
    109 
    110     print("
    ")
    111     # 输出显示1-9的分布
    112     print("生成的1-9的分布:")
    113     for k in range(9):
    114         print("Num", k + 1, ":", cnt_num[k + 1], "in all")
    115 
    116 # generate n times
    117 generate_1to9(1000)

    图 8 利用 generate_imgs.py 得到数字 1 图像

    3.总结

      有兴趣可以自己生成手写体数字数据集,感谢你的支持;

     # 代码已上传到了我的GitHub,如果对您有帮助欢迎 Star下:https://github.com/coneypo/Generate_handwritten_number

    # 请尊重他人劳动成果,转载或者使用源码请注明出处:http://www.cnblogs.com/AdaminXie

    # 如有问题请留言或者联系邮箱 coneypo@foxmail.com

  • 相关阅读:
    单例模式
    pandas to_html
    restful
    numpy版本查看以及升降
    视频下载
    转载:caffe中的Reshape层
    caffe编译报错解决
    python 版Faster Rcnn
    转载:动态规划以及路径记录
    TensorFlow设置GPU占用量
  • 原文地址:https://www.cnblogs.com/AdaminXie/p/8379749.html
Copyright © 2011-2022 走看看