机器学习项目实战:图片验证码识别

文章目录

  • 项目分析
  • 代码实现
    • 生成训练使用的验证码图片(150张)
      • 生成的图片效果展示
      • 生成图片代码
    • 图线清洗并切割
      • 清洗效果展示
      • 切割效果展示
      • 图片清洗切割代码实现
    • 数据处理,建模及测试
      • 建模效果展示
      • 测试效果展示
      • 数据处理,建模及测试代码实现

项目分析

基于逻辑回归和图像处理的项目
输入:一个验证码图像
输出:这个验证码图像中的数字(字母)

步骤

  1. 对图像进行处理
    (1)二值化:首先把图像从RGB3通道转化成Gray1通道,然后把灰度图(0~255)转化成二值图(0,1)

    (2)降噪:通过处理孤立点,对二值化的图进行降噪

    (3)图片切割:根据像素格,把图片中的所有(5个)数字,分别保存到对应的0~9文件夹下

    至此:数据处理就完成了

  2. 把数据带入逻辑回归进行建模
    (1)把切割好的数据,按照X(二位数组),Y(一维数组)的方式传入logisticRegression.fit()函数进行拟合,我们可以通过网格搜索(GridSearch)来进行调参

    (2)通过joblib包,把模型保存到本地

  3. 得到模型后,进行图像验证
    (1)根据步骤1,重复操作新的图像

    (2)对切割好的每个图像,独立的进行预测

    (3)把最后预测结果进行拼接

用到的技术:
分类:逻辑回归
模型选择:网格搜索, 查准率 查全率, 混淆矩阵,准确率(score)
图像处理的技术:RGB转灰度转二值,8位降噪,图像切割

代码实现

生成训练使用的验证码图片(150张)

生成的图片效果展示

机器学习项目实战:图片验证码识别_第1张图片

生成图片代码

# encoding=utf-8
"""
Date:2019-08-10 09:53
User:LiYu
Email:[email protected]

"""
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import random


def getRandomStr():
    return str(random.randint(0, 9))


def getRandomColor():
    R = random.randint(0, 255)
    G = random.randint(0, 255)
    B = random.randint(0, 255)
    if R == 255 and G == 255 and B == 255:
        R, G, B = 0, 0, 0
    return (R, G, B)


def generate_captcha():
    # 画布
    image = Image.new('RGB', (150, 50), (255, 255, 255))
    # 画笔
    draw = ImageDraw.Draw(image)
    # 字体
    font = ImageFont.truetype('LiberationSans-Bold.ttf', size=32)
    label = ''  # 保存随机到的五个数字字符串用于后续图片命名

    for i in range(5):  # 验证码为 五 个数字
        random_char = getRandomStr()  # 获取随机一个数字字符
        label += random_char  # 存起来
        # left, top = 10 + i * 30, 0  text = random_char  随机颜色getRandomColor() 指定字体
        draw.text((10 + i * 30, 0), random_char, getRandomColor(), font=font)

    # 画噪线噪点
    width = 150
    height = 30
    # 画线
    for i in range(3):  # 三条线
        x1 = random.randint(0, width)
        x2 = random.randint(0, width)
        y1 = random.randint(0, height)
        y2 = random.randint(0, height)
        # (起始X,Y, 终止X,Y)   颜色
        draw.line((x1, y1, x2, y2), fill=(0, 0, 0))

    # 画点
    for i in range(5):  # 五个彩色点,五个黑色弧线(相当于占四个像素的点)
        x = random.randint(0, width)
        y = random.randint(0, height)
        # 画点(x, y)  颜色
        draw.point((random.randint(0, width), random.randint(0, height)), fill=getRandomColor())
        # 画弧 (起始x, y, 终止x, y) 起始弧度0  终止弧度90  颜色
        draw.arc((x, y, x + 4, y + 4), 0, 90, fill=(0, 0, 0))

    # image.save(open(''.join(['captcha_images/', label, '.png']), 'wb'), 'png')
    image.save(open(''.join(['captcha_images_test/', 'unknow0.png']), 'wb'), 'png')
    print(label)


if __name__ == '__main__':
    for i in range(150):
        generate_captcha()

图线清洗并切割

清洗效果展示

机器学习项目实战:图片验证码识别_第2张图片

切割效果展示

机器学习项目实战:图片验证码识别_第3张图片
机器学习项目实战:图片验证码识别_第4张图片
机器学习项目实战:图片验证码识别_第5张图片

图片清洗切割代码实现

# encoding=utf-8
"""
Date:2019-08-10 10:50
User:LiYu
Email:[email protected]

"""
import os

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt


def binarization(path):
    """彩色验证码转为灰度图"""
    img = Image.open(path)
    # convert("L")变为灰度图
    img_gray = img.convert('L')
    img_gray = np.array(img_gray)
    # print(img_gray)
    w, h = img_gray.shape
    for x in range(w):
        for y in range(h):
            gray = img_gray[x, y]
            if gray <= 250:
                img_gray[x, y] = 0
            else:
                img_gray[x, y] = 1

    return img_gray


def noiseReduction(img_gray, label):
    """去除噪线噪点, 8位降噪"""
    w, h = img_gray.shape
    # 如果一个黑像素周围的黑像素少于四个,就把它变为白色
    for x in range(w):
        for y in range(h):
            count = 0
            if img_gray[x, y] == 1:
                continue

            try:
                if img_gray[x - 1, y - 1] == 0:
                    count += 1
            except:
                pass

            try:
                if img_gray[x - 1, y] == 0:
                    count += 1
            except:
                pass

            try:
                if img_gray[x - 1, y + 1] == 0:
                    count += 1
            except:
                pass

            try:
                if img_gray[x, y + 1] == 0:
                    count += 1
            except:
                pass

            try:
                if img_gray[x + 1, y + 1] == 0:
                    count += 1
            except:
                pass

            try:
                if img_gray[x + 1, y] == 0:
                    count += 1
            except:
                pass

            try:
                if img_gray[x + 1, y - 1] == 0:
                    count += 1
            except:
                pass

            try:
                if img_gray[x, y - 1] == 0:
                    count += 1
            except:
                pass

            if count < 4:
                img_gray[x, y] = 1

    plt.figure('')
    plt.imshow(img_gray, cmap='gray')
    plt.axis('off')
    # plt.show()
    # plt.savefig(''.join(['captcha_images_clean/', label, '.png']))
    plt.savefig(''.join(['captcha_images_test/', label]))  # 预测用
    # return img_gray


def getAllFileLabels(dir):
    """获取文件夹里的所有图片名,只保留数字部分"""
    imgFileList = os.listdir(dir)
    labels = []
    for imgFile in imgFileList:
        labels.append(imgFile.rstrip('.png'))
    return labels


def img2clean(labels):
    """清洗验证码图片,变成灰度图并降噪"""
    for label in labels:
        imgFile = ''.join(['captcha_images/', label, '.png'])
        img_gray = binarization(imgFile)
        noiseReduction(img_gray, label)


def cutImg(label):
    """切割图片并按数字分组保存"""
    img = Image.open(''.join(['captcha_images_clean/', label, '.png']))
    for i in range(5):
        pic = img.crop((100 * (1 + i), 170, 100 * (1 + i) + 100, 280))
        plt.imshow(pic)
        seq = getSaveSeq(label[i])
        pic.save(''.join(['cut_number/', str(label[i]), '/', str(seq), '.png']))


def getSaveSeq(num):
    """获取下一个数字图片的保存索引值(现有的图片最大索引加一)"""
    numList = os.listdir(''.join(['cut_number/', num, '/']))
    if len(numList) == 0 or numList is None:
        return 0
    else:
        max_file = 0
        for f in numList:
            if int(f.split('.')[0]) > max_file:
                max_file = int(f.split('.')[0])
        return int(max_file) + 1


def makeDir():
    if not os.path.exists('captcha_images_clean'):
        os.makedirs('captcha_images_clean')
    if not os.path.exists('cut_number'):
        os.makedirs('cut_number')
    for i in range(10):
        if not os.path.exists(''.join(['cut_number/', str(i)])):
            os.makedirs(''.join(['cut_number/', str(i)]))


if __name__ == '__main__':
    makeDir()
    labels = getAllFileLabels('captcha_images')
    img2clean(labels)
    labels = getAllFileLabels('captcha_images_clean')
    for label in labels:
        cutImg(label)

数据处理,建模及测试

建模效果展示

在这里插入图片描述

测试效果展示

原图片:
机器学习项目实战:图片验证码识别_第6张图片
测试结果:
机器学习项目实战:图片验证码识别_第7张图片
结果正确!

数据处理,建模及测试代码实现

# encoding=utf-8
"""
Date:2019-08-10 13:43
User:LiYu
Email:[email protected]

"""
import os
from PIL import Image
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.externals import joblib
from 处理切分验证码图片 import *


def img2data(num):
    X = []
    Y = []
    labels = getAllFileLabels(''.join(['cut_number/', str(num)]))
    for label in labels:
        img = Image.open(''.join(['cut_number/', str(num), '/', label, '.png']))
        # convert("L")变为灰度图
        img_gray = img.convert('L')
        img_gray = np.array(img_gray)
        w, h = img_gray.shape
        for x in range(w):
            for y in range(h):
                gray = img_gray[x, y]
                if gray <= 250:
                    img_gray[x, y] = 0
                else:
                    img_gray[x, y] = 1
        img_gray = np.reshape(img_gray, (1, -1))[0]
        # print("img_gray: ", img_gray)
        X.append(img_gray)
        Y.append(num)
    # print('X: ', X)
    # print('Y: ', Y)
    return X, Y


def getAllFileLabels(dir):
    """获取文件夹里的所有图片名,只保留数字部分"""
    imgFileList = os.listdir(dir)
    labels = []
    for imgFile in imgFileList:
        labels.append(imgFile.rstrip('.png'))
    return labels


def loadData():
    X = []
    Y = []
    for i in range(10):
        Xi, Yi = img2data(i)
        for x in Xi:
            X.append(x)
        for y in Yi:
            Y.append(y)
    X = np.array(X)
    Y = np.array(Y)
    print("X: ", X)
    print("Y: ", Y)
    return X, Y


def generatrModel(X, Y):
    # X_train, X_test, Y_train, Y_test = train_test_split(X, Y)
    logReg = LogisticRegression(multi_class='ovr', solver='sag')
    logReg.fit(X, Y)
    print(logReg.score(X, Y))
    joblib.dump(logReg, 'captcha_model/captcha_model.model')


def getModel():
    model = joblib.load('captcha_model/captcha_model.model')
    return model


def captcha_predict(path):
    img_gray = binarization('captcha_images_test/' + path)
    noiseReduction(img_gray, path)
    labels = ['0', '1', '2', '3', '4']
    img = Image.open('captcha_images_test/' + path)
    for i in range(5):
        pic = img.crop((100 * (1 + i), 170, 100 * (1 + i) + 100, 280))
        plt.imshow(pic)
        pic.save(''.join(['captcha_images_test/', labels[i], '.png']))

    result = ''
    model = getModel()
    for i in range(5):
        path = ''.join(['captcha_images_test/', labels[i], '.png'])
        img = Image.open(path)
        img_gray = img.convert('L')
        img_gray = np.array(img_gray)
        w, h = img_gray.shape
        for x in range(w):
            for y in range(h):
                gray = img_gray[x, y]
                if gray <= 250:
                    img_gray[x, y] = 0
                else:
                    img_gray[x, y] = 1
        img_gray = np.reshape(img_gray, (1, -1))
        X = img_gray[0]
        Y_pre = model.predict([X])
        result = ''.join([result, str(Y_pre[0])])
    return result


if __name__ == '__main__':
    # # 建模过程
    # X, Y = loadData()
    # generatrModel(X, Y)

    # 测试
    imgs = ['unknow2.png', 'unknow3.png', 'unknow4.png']
    for img in imgs:
        result = captcha_predict(img)
        print(result)

你可能感兴趣的:(机器学习练习)