PIL 识别简单验证码

只使用了 PIL (Python Image Lib), 直接放代码, 具体细节查看知乎, Python识别简单验证码

# coding=utf-8
# /usr/bin/Python

from __future__ import division, print_function
import os
from PIL import Image
# import re

H = 12  # 切片高
W = 8   # 切片宽
ST = (7, 7) # 左边开始位置, 顶部开始位置
PIECE = 5   # 切片
PAD = 1     # 间隔

END_Y = ST[1] + H   # 底部
PAD_X = W + PAD     # 偏移

DEBUG_LEAVE = False

'''
    LEAVE False: not detail
    LEAVE 3: 
    LEAVE 5: all detail
'''

EACH_RECT = (
                (ST[0] + PAD_X * 0, ST[1], ST[0] + PAD_X * 0 + W, END_Y),     # 左边距   上边距     右边距     下边距
                (ST[0] + PAD_X * 1, ST[1], ST[0] + PAD_X * 1 + W, END_Y),
                (ST[0] + PAD_X * 2, ST[1], ST[0] + PAD_X * 2 + W, END_Y),
                (ST[0] + PAD_X * 3, ST[1], ST[0] + PAD_X * 3 + W, END_Y),
                (ST[0] + PAD_X * 4, ST[1], ST[0] + PAD_X * 4 + W, END_Y)
            )

def get_cut(file_name):

    img = Image.open(file_name)
    img = img.convert('L')
    cut_img = []
    # 每个数字的范围
    rct = EACH_RECT
    # 转换图片
    bin_img = get_bin(img)
    # bin_img.crop(rct[0]).show()
    # pr(bin_img.crop(rct[0]))
    # 将图片分割为CAP部分
    for part in range(PIECE):
        debug('\n', rct[part])
        if DEBUG_LEAVE >= 5:
            bin_img.crop(rct[part]).save(part.__str__() + '.gif', 'gif')
        cut_img.append(bin_img.crop(rct[part]))
    return cut_img


# 去除图片噪点并转为黑白
def get_bin(img):

    bin_img = Image.new('L', img.size, 255)     # 新图片 模式 L灰度 大小  色彩深度
    cvt_img = img.convert("L")      # 转换模式为灰度图
    for x in range(img.size[1]):
        for y in range(img.size[0]):
            pix = cvt_img.getpixel((y, x))
            if pix < 110:   # 去噪阈值过滤灰度大于 110 的像素点
                bin_img.putpixel((y, x), 0)     # 填充新图片
            else:
                bin_img.putpixel((y, x), 255)
    return bin_img


def pr(img):

    if img.mode != 'L':
        img = img.convert('L')

    size = img.size
    for x in range(size[1]):
        for y in range(size[0]):
            px = img.getpixel((y, x))
            if px == 255:
                print ('-',)
            else:
                print ('@',)
        print()
    print ('━' * 50)


# 去干扰
def denoise(img):

    cvt_im = Image.new('L', img.size, 255)
    img = img.convert('L')
    s = img.size
    for x in range(s[1]):
        for y in range(s[0]):
            if x == 0 or x == s[1]-1 or y == 0 or y == s[0]-1:
                cvt_im.putpixel((y, x), 255)
            else:
                if img.getpixel((y, x)) == 255:
                    cvt_im.putpixel((y, x), 255)
                else:
                    cvt_im.putpixel((y, x), 0)
    return cvt_im


# 转换为矢量
def get_vector(img):

    img = img.convert('L')
    d1 = []
    for i in range(H):
        for j in range(W):
            d1.append(0)
            index = i * W + j
            if img.getpixel((j, i)) == 255:
                d1[index] = 1
            else:
                d1[index] = 0
    return d1


# 获取列表最大数
def getmax(list):
    li = list
    max = -1
    index = 0
    for i in range(len(li)):
        if max < li[i]:
            max = li[i]
            index = i
    return index


def getmini(list):
    li = list
    mini = 0
    index = 0
    for i in range(len(li)):
        if mini > li[i]:
            mini = li[i]
            index = i
    return index


def prbin(i):

    for o in range(H):
        for j in range(W):
            ind = o * W + j
            if i[ind] == 1:
                print ('. ', end='')
            else:
                print('# ', end='')
        print()
    print()


def get_dict(path, deep):

    dict_ = {}
    for i in range(10):
        count = 0
        temp = []
        imDir = (''.join([path, '\\', str(i)]))
        debug ('\n', imDir)
        for im in os.listdir(imDir):
            img = ''.join([path, '\\', str(i), '\\', im])
            img = Image.open(img)
            temp.append(get_vector(denoise(img)))
            count += 1
            if count >= deep:
                break
        dict_[i] = temp
        # print 'Finished: ', i
    return dict_


def createDict(labeledPath, savePath, dictDeepness):

    fil = open(savePath, 'w')
    d = get_dict(labeledPath, dictDeepness).values()

    fil.write('val = \\\n')
    fil.write(d.__str__())

    fil.close()


# 识别每个图片块的内容
def get_result(src_img, labeled):

    guess = [-1, []]
    src_img = src_img.convert('L')
    # 将图片转换为二值列表
    srcimgbin = get_vector(src_img)
    # 用于储存每个对比数字的相似率
    simalist = []
    for i in range(H):
        simalist.append(0)
    # 与每个数字对比
    for cp_numb in range(W):

        comp = labeled[cp_numb]
        cp_count = len(labeled[cp_numb])

        for each in comp:
            xy = 0
            yx = 0
            # 纵向扫描对比 并统计相同
            for x in range(W):
                for y in range(H):
                    # 如果像素点一样
                    if each[x * y + y] == srcimgbin[x * y + y] == 1:
                        xy += 1
                    else:
                        if each[x * y + y] != srcimgbin[x * y + y]:
                            xy -= 1
            # 横向扫描对比
            for y in range(H):
                for x in range(W):
                    if each[y * x + x] == srcimgbin[y * x + x] == 1:
                        yx += 1
                    else:
                        if each[y * x + x] != srcimgbin[y * x + x]:
                            yx -= 1
            # 得出单张图片相似率
            sima = (xy + yx) / 280
            # 统计该数相似率
            simalist[cp_numb] += sima
        # 求于个数平均值
        simalist[cp_numb] /= cp_count
        debug('\n', simalist[cp_numb])
        # 得到相似度最高的数字
    guess[0] = getmax(simalist)  # max(simalist)
    guess[1] = simalist

    return guess


def scan(img, dict_val):

    import random

    global DEBUG_LEAVE

    try:
        dictionary = dict_val
    except:
        print ('dict Exception')
        return
    # 得到只有黑白值的图片分割块
    cut_img = get_cut(img)
    result = ''
    # 识别每个图片块的内容
    for im in cut_img:
        if DEBUG_LEAVE >= 3:
            prbin(get_vector(im))
        # 将识别的结果连接
        res = str(get_result(denoise(im), dictionary)[0])
        # im.save(res + '\\' + str(random.randint(0, 2000)) + '.gif')
        # get_bin(denoise(img))
        result = result + str(res)
    return result


def detect(imFile, dict):

    import time
    for x in os.listdir(imFile):
        image = ''.join([imFile, '\\', x])
        t = time.time()
        capRes = scan(image, dict)
        t = time.time() - t
        print (t, capRes)
        newName = ''.join([imFile, capRes, '.gif'])
        try:
            os.rename(image, newName)
        except WindowsError, Exception :
            os.rename(image, newName.join('0'))

def make_piece(samplePath = 'img\\', pieceSavePath = 'classify\\'):

    import random
    if not os.path.isdir(samplePath):
        os.makedirs(samplePath)

    for labeledPath in range(10):
        labeledPath = ''.join([pieceSavePath, str(labeledPath)])
        if not os.path.isdir(labeledPath):
            os.makedirs(labeledPath)

    for sample in os.listdir(samplePath):

        sample = ''.join([samplePath, sample])
        if os.path.isfile(sample):
            for piece_ in get_cut(sample):
                pieceName = ''.join([pieceSavePath, random.randint(0, 9999).__str__(), '.gif'])
                piece_.save(pieceName, 'gif')
                piece_.close()

def debug(end = '\n', *args):

    if not DEBUG_LEAVE:
        return

    for x in args:

        print(x, end=end)

def cutPiece(Height_, Width_, ST_, Piece_, Pad_, samplePath_ = '\img', pieceSavePath_ = 'classify\\'):

    global  H
    global W
    global ST
    global PIECE
    global PAD

    H = Height_
    W = Width_
    ST = ST_
    PIECE = Piece_
    PAD = Pad_

    global  END_Y
    global PAD_X
    global EACH_RECT

    END_Y = ST[1] + H
    PAD_X = W + PAD

    EACH_RECT = (
        (ST[0] + PAD_X * 0, ST[1], ST[0] + PAD_X * 0 + W, END_Y),  # 左边距   上边距     右边距     下边距
        (ST[0] + PAD_X * 1, ST[1], ST[0] + PAD_X * 1 + W, END_Y),
        (ST[0] + PAD_X * 2, ST[1], ST[0] + PAD_X * 2 + W, END_Y),
        (ST[0] + PAD_X * 3, ST[1], ST[0] + PAD_X * 3 + W, END_Y),
        (ST[0] + PAD_X * 4, ST[1], ST[0] + PAD_X * 4 + W, END_Y)
    )

    make_piece(samplePath_, pieceSavePath_)


if __name__ == '__main__':

    import login_x as cap_dict

    # print (read_dict('login.dict'))
    # dictPath = 'labeled.dict'
    #
    # print scan('7.gif', dictPath)
    # count = 0
    # for im in os.listdir('img\\'):
    #     count += 1
    #     print scan('img\\' + im, dictPath)
    #
    # createDict('classify', 'login_x.py', 10)
    # detect('img\\', cap_dict.val)
    # make_piece()
    print('', '\nResult: ', scan('chk.gif', cap_dict.val), '\n')





个人博客链接 https://www.denua.cn/blog/article/9/

转载于:https://my.oschina.net/Denua/blog/1583743

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值