站点图标 久久日记本

基于sklearn的简单数字图片识别

不久前趁着项目需求空档时间,捡起很久没有看的Python,翻了几天的Python视觉计算。对sklearn这个基础数学库有些兴趣,就略微看了看。正好联想到之前的验证码,就索性用C#生成了一些简单的标准数字图片,做一些实验。

1.图片准备

首先我们引入一下基本的图片操作,和文件操作的python库,有点像C#的using命名空间

import requests
import time
from PIL import Image, ImageFilter, ImageEnhance  # 操作图像
import os
import fnmatch
import sys

使用ASP.NET写了一个简单的页面,生成了一些数字图片,每张图片4个均匀分散的数字。

注:生成的数字宋体,不发散,不倾斜,无连体...

下载一下生成的图片保存到本地,

def down_pic(picName):
    url = "http://localhost:8891/code.aspx"
    res = requests.get(url, stream=True)
    with open('./pic/%s.jpg' % (picName), 'wb') as f:
        for chunk in res.iter_content(chunk_size=1024):
            if chunk:
                f.write(chunk)
                f.flush()
        f.close()

做一个简单的循环以保证保存到足够多的图片:

    # for i in range(300):
    #     picName = int(time.time()*100000)
    #     down_pic(picName)
2.简单图片切割

验证码识别中比较重要的是分割图片,在这里就暂时不再赘述了,这一点会在后面的博文中做重点讲述。

从一个路径读取图片:

def readPic(picUrl):
    return Image.open(picUrl)

这里的图片比较简单,我们切分一下:

def cutPic(img):
    # 72*30
    x = 6  # 10
    y = 5  # 7
    w = 8  # 9
    h = 12  # 17
    interval = 1  # 2
    newImgs = []

    for i in range(5):
        print(i)
        newImg = img.crop(
            (x+w*i+interval*i, y, x+w*(i+1)+interval*i, y+h)) #左上右下
        newImgs.append(newImg)

        # newImg.convert('RGB').save('10.jpg', 'JPEG')
        # print(newImg)
    return newImgs
3.简单的图片处理

增强图片对比度,转为灰度图

def transfer(img):
    img.filter(ImageFilter.MedianFilter())  # 滤波器
    enhancer = ImageEnhance.Contrast(img)
    img = enhancer.enhance(1)  # 对比度   增强
    return img.convert('L')  # 灰度
4.连续处理

那么,我们将上面的方法综合起来:

读取一批图片=>分割图片成单个数字图片=>存储到新的文件夹中

def beginRead(filename):
    img = readPic(filename).convert('RGB')
    newImg = transfer(img)
    pics = cutPic(newImg)

    for pic in pics:
        pic.save('./pics/%s.jpg' %
                 (int(time.time()*1000000)), 'jpeg')
5.简单识别

使用pytesseract对图片进行简单的识别,注意要安装一下openvc。

可以使用下面的方式。

# ocr
# 必须要安装这里的 https://github.com/UB-Mannheim/tesseract/wiki
# for windows https://github.com/tesseract-ocr/tesseract/wiki/4.0-with-LSTM#400-alpha-for-windows
# x from tesseract import image_to_string
import pytesseract

ocr图片:

def ocrImage(fileUrl):
    # print(fileUrl)
    img = readPic(fileUrl)
    # print('1')
    # content = pytesseract.image_to_string(img,lang='chi_sim')
    # content = pytesseract.image_to_string(img, lang='eng', boxes=False,
    #                                       config='--psm 10 --oem 3 -c tessedit_char_whitelist=0123456789')

    # content = pytesseract.image_to_string(
    #     img,  config="--psm 10 -c tessedit_char_whitelist=0123456789")

    try:
        # content = pytesseract.image_to_string(img, config="-psm 7")
        content = pytesseract.image_to_string(
            img,  config="--psm 10 -c tessedit_char_whitelist=0123456789")
        # print(content)
        # if content == 0 | content == 1 | content == 2 | content == 3 | content == 4 | content == 5 | content == 6 | content == 7 | content == 8 | content == 9:
        #     return content
        # else:
        #     return "none"
        pattern = re.compile(r'([0-9]+)', re.I)
        m = pattern.match(content)
        if m.groups() == 0:
            print('None')
            return 'none'
        else:
            print(content)
            return content

    except:
        print('Err')
        return 'none'
    # print(content)
    return content
6.分类图片

将识别的图片copy到新的文件夹进行分类:

def copyToCat(originfile, dir, filename):
    # print(originfile, dir, filename)
    if not os.path.exists(dir):
        os.makedirs(dir)
    shutil.copyfile(originfile, dir+'/'+filename)

我们将上述两个方法连续起来:

读取处理后的文件夹中图片列表=>对图片进行ocr识别=>将识别出来的图片归类=>未识别出来的同意放到none文件夹

def location(dir):
    for filename in os.listdir('./'+dir+'/'):
        if fnmatch.fnmatch(filename.lower(), '*.jpg'):
            fileUrl = './'+dir+'/'+filename
            if dir == 'pic':
                begin(fileUrl)
            else:
                result = ocrImage(fileUrl)
                copyToCat(fileUrl, './cat/%s' % result, filename)

7.人工分类

对已经分类的图片人工检查一下,错误的手动分类一下,对未识别出来的再手动进行一下分类。

可以保留一些未分类的图片以供后面作为测试集使用。

已经自动分类的和部分人工分类的作为训练集使用。

8.二值化

将一张图片二值化:

def imgTo01(img):
    pixdata = img.load()
    w, h = img.size
    str = ''
    for x in range(w):
        for y in range(h):
            if pixdata[x, y] < 127:
                str += '0'
            else:
                str += '1'
    return str

调用上面的方法对所有图片二值化,并将上一步中得到的训练集和测试进行二值化,并保存为csv文档。

这时候,需要引入pandas进行csv的读写操作。

import shutil
import re  # 正则
import pandas as pd

批量处理

def saveTocsv():
    columns_train = []
    columns_test = []
    for i in range(10):
        for filename in os.listdir('./cat/%s' % i):
            print(filename)
            # 灰度 二值化
            img = readPic('./cat/%s' %
                          i+'/'+filename).convert('L').convert('1')
            str = imgTo01(img)
            columns_train.append({
                'raw': str,
                'num': i
            })
    df_train = pd.DataFrame(columns_train, columns=['raw', 'num'])
    df_train.to_csv('train.csv')

    for filename in os.listdir('./cat/none'):
        print(filename)
        # 灰度 二值化
        img = readPic('./cat/none/'+filename).convert('L').convert('1')
        str_test = imgTo01(img)
        columns_test.append({
            'raw': str_test,
            'num': 'none',
            'filename': filename
        })
    df_test = pd.DataFrame(columns_test, columns=['raw', 'num'])
    df_test.to_csv('test.csv')

9.使用sklearn进行预测

这里就不再简述为什么要使用K近邻邻算法了,你可以自行Google文档查询想要的答案。

def skl():
    data = pd.read_csv('./train.csv') # 读取训练集
    x_train = data.drop(['num'], axis=1)  # 特征值 raw二值化数据
    y_train = data['num']  # 目标值 number值
    # x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25)

    # print(x_train.shape)
    # print(y_train.shape)

    data_test = pd.read_csv('./test.csv') #读取测试集
    x_test = data_test.drop(['num'], axis=1) #测试集 raw二值化数据

    knn = KNeighborsClassifier(n_neighbors=5) #K近邻算法
    knn.fit(x_train, y_train)
    # sgd = SGDRegressor()

    y_predict = knn.predict(x_test)

    # 重命名

    # img = readPic('./cat/none/'+filename).convert('L').convert('1')

    for index in range(len(y_predict)):
        filename = data_test[index]['filename']

        y_predict[index]

    print(x_test['raw'])
    # print(y_predict)

    # print('%:', knn.score(x_test, y_test))

我们可以看到,当k值越小,得到的预测值正确率越高。

上一张图看一下识别效果:

当然这里的数字很简单。

10.思考

上的二值化的结果比较小,如果我们遇到二值化的数据量比较大,甚至单个数据都比较大的情况呢?怎么预测?

标准化?当你使用标准化都爆出溢出呢,又该怎么办。

后面,我们将使用 sklearn 和 tensflow 对简单的手写字体进行预测,那里将会面临这个问题,敬请期待。

11.参考资料

源代码

sklearn中文翻译站

sklearn中文翻译github

退出移动版