【玩转腾讯云】万物皆可Serverless之使用SCF快速部署验证码识别接口

2020-04-22 18:13:09 浏览数 (1)

万物皆可Serverless系列文章

  1. 万物皆可Serverless之免费搭建自己的不限速大容量云盘(5TB)
  2. 万物皆可Serverless之使用云函数Timer触发器实现每天自动定时打卡
  3. 万物皆可Serverless之使用SCF COS快速开发全栈应用
  4. 万物皆可Serverless之使用SCF COS免费运营微信公众号
  5. 万物皆可Serverless之使用SCF快速部署验证码识别接口
  6. 万物皆可Serverless之Kaggle SCF端到端验证码识别从训练到部署
  7. 万物皆可Serverless之借助微信公众号简单管理用户激活码
  8. 万物皆可Serverless之使用SCF COS给未来写封信
  9. 万物皆可Serverless之在Flutter中快速接入腾讯云开发
  10. 万物皆可Serverless之在Flutter中写一个Dart原生腾讯云对象存储插件
  11. 万物皆可Serverless之我的Serverless之路

一、本文介绍

验证码识别是搞爬虫实现自动化脚本避不开的一个问题,

通常验证码识别程序要么部署在本地,要么部署在服务器端,

如果部署在服务器端就需要自己去搭建配置网络环境并编写调用接口,这是一个极其繁琐耗时的过程。

但是现在我们通过无服务器云函数(SCF),就可以快速将本地的验证码识别程序发布上线,极大地提高了开发效率。

废话少说,上图 ↓

一种比较简单的验证码一种比较简单的验证码
识别扭曲变形的验证码识别扭曲变形的验证码

可以看到,识别效果还是蛮好的,甚至超过了肉眼识别率。

二、操作步骤

传统的验证码识别流程是

  1. 图像预处理(灰化,去噪,切割,二值化,去干扰线等)
  2. 验证码字符特征提取(SVM,CNN等)
  3. 验证码识别

下面我就带大家一起来创建、编写并发布上线一个验证识别云函数

第一步:新建python云函数

参见我之前的系列文章《万物皆可Serverless之使用SCF COS快速开发全栈应用》

第二步:编写验证识别云函数

一个简单的验证码一个简单的验证码

Life is short, show me the code.

这里我就以一个最简单的验证码识别程序为例,直接上代码

代码语言:python代码运行次数:0复制
import io
import os
import time
from PIL import Image as image
import json

#字符特征
chars = {
    '1': [1, 1, 1, 0, 1, ...],
    '2': [1, 0, 0, 1, 0, ...],
    '3': [0, 1, 0, 0, 1, ...],
    # 其他字符特征...
}


# 灰度处理
def covergrey(img):
    return img.convert('L')

# 去除验证码边框
def clearedge(img):
    for y in range(img.size[1]):
        img.putpixel((0, y), 255)
        img.putpixel((1, y), 255)
        img.putpixel((2, y), 255)
        img.putpixel((img.size[0]-1, y), 255)
        img.putpixel((img.size[0]-2, y), 255)
        img.putpixel((img.size[0]-3, y), 255)
    for x in range(img.size[0]):
        img.putpixel((x, 0), 255)
        img.putpixel((x, 1), 255)
        img.putpixel((x, 2), 255)
        img.putpixel((x, img.size[1]-1), 255)
        img.putpixel((x, img.size[1]-2), 255)
        img.putpixel((x, img.size[1]-3), 255)
    return img

# 去除干扰线并转换为黑白照片
def clearline(img):
    for y in range(img.size[1]):
        for x in range(img.size[0]):
            if int(img.getpixel((x, y))) >= 110:
                img.putpixel((x, y), 0xff)
            else:
                img.putpixel((x, y), 0x0)
    return img

# 去噪/pnum-去噪效率
def del_noise(im, pnum=3):
    w, h = im.size
    white = 255
    black = 0
    for i in range(0, w):
        im.putpixel((i, 0), white)
        im.putpixel((i, h - 1), white)
    for i in range(0, h):
        im.putpixel((0, i), white)
        im.putpixel((w - 1, i), white)
    for i in range(1, w - 1):
        for j in range(1, h - 1):
            val = im.getpixel((i, j))
            if val == black:
                cnt = 0
                for ii in range(-1, 2):
                    for jj in range(-1, 2):
                        if im.getpixel((i   ii, j   jj)) == black:
                            cnt  = 1
                if cnt < pnum:
                    im.putpixel((i, j), white)
            else:
                cnt = 0
                for ii in range(-1, 2):
                    for jj in range(-1, 2):
                        if im.getpixel((i   ii, j   jj)) == black:
                            cnt  = 1
                if cnt >= 7:
                    im.putpixel((i, j), black)
    return im

# 图片数据二值化
def two_value(code_data):
    table = []
    for i in code_data:
        if i < 140:  # 二值化分界线140
            table.append(0)
        else:
            table.append(1)
    return table

# 图片预处理
def pre_img(img):
    img = covergrey(img)  # 去色
    img = clearedge(img)  # 去边
    img = clearline(img)  # 去线
    img = del_noise(img)  # 去噪
    return img

# 处理图片数据
def data_img(img):
    code_data = []  # 验证码数据列表
    for i in range(4):  # 切割验证码
        x = 5   i * 18  # 可用PS确定图片切割位置
        code_data.append(img.crop((x, 9, x   18, 33)).getdata())
        code_data[i] = two_value(code_data[i])  # 二值化数据
    return code_data

# 验证码识别
def identify(data):
    code = ['']*4  # 验证码字符列表
    diff_min = [432]*4  # 初始化最小距离--不符合的数据点个数(共120数据点)
    for char in chars:  # 遍历验证码字符(每个字符比较一次4个验证码)
        diff = [0]*4  # 各验证码差距值(每个字符判断前重置此距离)
        for i in range(4):  # 计算四个验证码
            for j in range(432):  # 逐个像素比较验证码特征
                if data[i][j] != chars[char][j]:
                    diff[i]  = 1  # 距离 1
        for i in range(4):
            if diff[i] < diff_min[i]:  # 比已有距离还要小(更加符合)
                diff_min[i] = diff[i]  # 刷新最小距离
                code[i] = char  # 刷新最佳验证码
    return ''.join(code)  # 输出结果


def predict(imgs):
    code = ''
    img = imgs.read()
    img = image.open(io.BytesIO(img))
    img = pre_img(img)  # 预处理图片
    data = data_img(img)  # 获取图片数据
    code = identify(data)  # 识别验证码
    return code


def apiReply(reply, code=200):
    return {
        "isBase64Encoded": False,
        "statusCode": code,
        "headers": {'Content-Type': 'application/json', "Access-Control-Allow-Origin": "*"},
        "body": json.dumps(reply, ensure_ascii=False)
    }


def main_handler(event, context):
    main_start = time.time()
    flag = True if 'image' in event['queryString'] else False
    code = predict(event['queryString']['image']) if 'image' in event['queryString'] else '无效的请求'
    return apiReply({
        'ok': flag,
        'code': code,
        'spendTime': str(time.time()-main_start)
    })

老规矩,先捋一下整个云函数的流程。

代码语言:javascript复制
def main_handler(event, context):
    main_start = time.time()
    flag = True if 'image' in event['queryString'] else False
    code = predict(event['queryString']['image']) if 'image' in event['queryString'] else '无效的请求'
    return apiReply({
        'ok': flag,
        'code': code,
        'spendTime': str(time.time()-main_start)
    })

首先,我们通过event事件拿到api请求的验证码image数据,

然后判断一下image参数是否存在,若不存在就返回请求无效的提示

代码语言:python代码运行次数:0复制
def predict(imgs):
    code = ''
    img = imgs.read()
    img = image.open(io.BytesIO(img))
    img = pre_img(img)  # 预处理图片
    data = data_img(img)  # 获取图片数据
    code = identify(data)  # 识别验证码
    return code

如果image请求参数存在就调用 predict 函数解析识别验证码,流程如下

  1. 读取验证码图像
  2. 验证码图像预处理
  3. 识别处理后的验证码
代码语言:javascript复制
# 图片预处理
def pre_img(img):
    img = covergrey(img)  # 去色
    img = clearedge(img)  # 去边
    img = clearline(img)  # 去线
    img = del_noise(img)  # 去噪
    return img

我们来看一下图像预处理过程

  1. 将验证码去色,转为灰度图
  2. 去除验证码黑色边框
  3. 去除验证码干扰线
  4. 去除验证码噪点
代码语言:javascript复制
#字符特征
chars = {
    '1': [1, 1, 1, 0, 1, ...],
    '2': [1, 0, 0, 1, 0, ...],
    '3': [0, 1, 0, 0, 1, ...],
    # 其他字符特征...
}

# 验证码识别
def identify(data):
    code = ['']*4  # 验证码字符列表
    diff_min = [432]*4  # 初始化最小距离--不符合的数据点个数(共120数据点)
    for char in chars:  # 遍历验证码字符(每个字符比较一次4个验证码)
        diff = [0]*4  # 各验证码差距值(每个字符判断前重置此距离)
        for i in range(4):  # 计算四个验证码
            for j in range(432):  # 逐个像素比较验证码特征
                if data[i][j] != chars[char][j]:
                    diff[i]  = 1  # 距离 1
        for i in range(4):
            if diff[i] < diff_min[i]:  # 比已有距离还要小(更加符合)
                diff_min[i] = diff[i]  # 刷新最小距离
                code[i] = char  # 刷新最佳验证码
    return ''.join(code)  # 输出结果

PS:文章中的字符特征 chars 并不完整,你可能需要自行提取所有特征

最后来看一下验证码的识别过程

这里我们直接简单粗暴地取处理后图像数据的所有像素点作为字符的特征(所谓大道至简,哈哈哈),

然后将每个待识别字符处理后图像的数据与所有字符的特征逐个比较,取最相似的那个字符作为识别结果。

正确的识别结果正确的识别结果

嗯,没什么问题的话,你就可以得到正确的识别结果了。

第三步:上线发布云函数、添加Api网关触发器、启用响应集成

参见我之前的系列文章《万物皆可Serverless之使用SCF COS快速开发全栈应用》

三、文章最后

当然,以上只是以一个简单的验证码识别为例,

对于一些比较复杂的验证码,你也可以使用Tensorflow,Pytorch等深度学习计算框架搭建、训练模型,

然后将训练好的模型借助无服务器云函数快速上线发布使用。

在下一篇文章《万物皆可Serverless之Kaggle SCF端到端验证码识别从训练到部署》中,

我会继续带大家从头训练并部署了一个通用验证码识别模型,纯干货,直接上手那种的哈~

如果你已经有本地的验证码识别算法,

不妨尝试将它上线发布成一个云函数方便随时调用。

以上,Have fun.

0 人点赞