简单的图片验证码识别

对于我来说, 图片验证码识别的主要难度在于如何将验证码切割为可识别的小块
因为对于单个英文或者数字来说, 不管是什么机器学习包都能做到较高的识别率

# 获取一个数组中连续的值的索引
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
from PIL import Image
import numpy as np


# 对数据进行处理, 只保留0, 1
def surround_line_in_num(img: np.array):
    col_index = []
    row_index = []
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            if img[i, j] == 0:
                col_index.append(i)
                row_index.append(j)
    return col_index, row_index
# surround_line_in_num(xxx)


# TODO: 增加处理, 当像素周围只有2x2时, 认为它是噪音, 这一步应该在统计出连续值之后做, 只对有疑问的点进行计算, 大面积连续的值不考虑该问题
def remove_noise(img):
    return img


# 获取一个数组中连续的值的索引
def search(lis, broke_num=3):
    # broke_num 需要拆分为多少个字符
    lis = list(set(lis))
    lis.sort()
    s_list = [lis[0]-1] + lis
    e_list = lis + [float('inf')]
    se = list(map(lambda x: x[1] - x[0], zip(s_list, e_list)))
    # 将坐标装入队列
    ss = []
    k = 0
    s = 0
    e = 0
    for i in range(len(se)):
        if se[i] == 1:
            if e < i:
                e = i
        else:
            if e != 0:
                # 直接转换至e_list坐标, 获取图片内部坐标
                ss.append((e_list[s], e_list[e]))
            k += 1
            s, e = i, 0

    if len(ss) == broke_num:
        return ss, ""
    elif len(ss) > broke_num:
        return ss[:broke_num], "有多余的长度列表 为: {}".format(ss)
    else:
        return ss, "解析失败!!! 没有获取到期望的分割点!!"


def save_splited_img(split_list, img):
    index = 0
    img_list = []
    for i in split_list:
        # crop 获取指定的部分
        temp = img.crop(i)
        temp.save(f"ii{time.time()}.png")
        img_list.append(temp)
    return img_list


def split(img, split_num=4):
    """
    :param img: 图像对象
    :type img: Image
    :param split_num: 图片中的字符个数
    :type split_num: int
    :return: split point list
    :rtype: list(tuple(left: int, up: int, right: int, down: int)), Error
    """
    # 将传入的img对象处理为灰度图像, 并且只保留0, 1
    limg = img.convert("L")
    limg_array = np.array(limg)
    # 大于200 是因为有的颜色太浅了, 比如: 浅黄色
    array_zero_one = np.where(limg_array > 200, 1, 0)
    # np.savetxt('num.txt', array_zero_one, fmt="%d")
    _, row_indx = surround_line_in_num(array_zero_one)
    split_list, error = search(row_indx, split_num)
    left_up_right_down_tuple_list = []
    if not error:
        col_split_num = 2
        # save_splited_img(split_list, img_array=limg_array)
        for i in split_list:
            temp_array = array_zero_one[:, i[0]:i[1]]
            col_index, _ = surround_line_in_num(temp_array)
            col_split_list, error = search(col_index, col_split_num)
            l, r = i
            u, d = col_split_list[0]
            left_up_right_down_tuple_list.append((l, u, r, d))
        return left_up_right_down_tuple_list, None
    else:
        print(error)
        return None, "计算失败!!"

def get_split_img_obj(img, split_num=1):
    img = img.convert("L")
    img_array = np.array(img)
    lurd_list, error = split(img, split_num)
    if not error:
        bimg_array = np.where(img_array > 200, 255, 0)
        # try:
        #     bimg = Image.fromarray(bimg_array)  # pillow == 4.3.0 需要指定模式才行, 但是指定的模式基本上都不能正常展示...
        #     # 最接近的模式是  "I"
        # except:
        bimg = img
        return save_splited_img(lurd_list, bimg), ""
    else:
        print(error)
        return "", error

if __name__ == "__main__":
    img = Image.open('result.jpg')
    # img = Image.open('show.png')
    get_split_img_obj(img, 4)
    """
    # 直接将图片置为灰度模式, 以便于 Image.fromarray 获取
    img = img.convert("L")
    img_array = np.array(img)
    lurd_list, error = split(img, 4)
    if not error:
        bimg_array = np.where(img_array > 200, 255, 0)
        try:
            bimg = Image.fromarray(bimg_array) # pillow == 4.3.0 需要指定模式才行, 但是指定的模式基本上都不能正常展示...
            # 最接近的模式是  "I"
        except:
            bimg = img
        save_splited_img(lurd_list, bimg)
    else:
        print(error)
    """

工作中常见的验证码只有4位6位的, 当拆分失败时直接就返回, 防止失败率过高
也可以在拆分时增加一个宽度判断, 来尝试分割黏连字符, 但是只限于较为规整的字符
否则会导致一个字符被拆成两个字符
原先的图片


result.jpg

拆分后的图片


ii1571129077.2711172.png
ii1571129077.2729177.png
ii1571129077.2744443.png
ii1571129077.2760947.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容