Как легко определить капчу с помощью Serverless?

Serverless

vcg_VCG41N809917226_RF.jpg

Автор | Цзян Юй источник |Бессерверная официальная учетная запись

предисловие

Концепция serverless привлекла к себе большое внимание с тех пор, как была предложена.Особенно в последние годы бессерверность показала беспрецедентную живучесть.Инженеры в различных областях пытаются совместить бессерверную архитектуру со своей работой, чтобы получить «технологию», принесенную бессерверная архитектура. Дивиденд».

CAPTCHA — это аббревиатура от «Полностью автоматизированный общедоступный тест Тьюринга, позволяющий различать компьютеры и людей», который представляет собой общедоступную автоматическую программу, которая различает, является ли пользователь компьютером или человеком. Это может предотвратить злонамеренный взлом паролей, считывание билетов и заполнение форумов, а также эффективно предотвратить хакерские постоянные попытки войти в систему для определенного зарегистрированного пользователя с помощью определенного метода взлома программы методом грубой силы. На самом деле код подтверждения сейчас является распространенным методом для многих веб-сайтов, и мы реализовали эту функцию относительно простым способом. Вопрос CAPTCHA генерируется и оценивается компьютером, но на этот вопрос может ответить только человек, а не компьютер, поэтому пользователя, отвечающего на вопрос, можно считать человеком. Проще говоря, проверочный код — это код, используемый для проверки того, является ли он «кодом», к которому обращается человек или машина.

**Так какие искры столкнут распознавание проверочного кода и бессерверную архитектуру в области искусственного интеллекта? ** В этой статье будет реализована функция распознавания проверочного кода с помощью бессерверной архитектуры и алгоритма сверточной нейронной сети (CNN).

Говоря о коде подтверждения

Можно сказать, что разработка проверочного кода происходит очень быстро: от простого цифрового проверочного кода в начале до более позднего числа + буквенного проверочного кода, до более позднего номера + буквенного + китайского проверочного кода и графического кода подтверждения, простая проверка Кодовых материалов становится все больше. С точки зрения формы кода подтверждения, он также отличается: ввод, щелчок, перетаскивание, код подтверждения SMS, код подтверждения голоса...

Код подтверждения входа Bilibili включает несколько режимов, например, сдвинув ползунок для подтверждения:

1.png

Например, проверьте, щелкнув текст последовательно:

2.png

И Baidu Tieba, Zhihu, Google и другие связанные веб-сайты имеют разные коды подтверждения, такие как выбор текста, который пишется, выбор изображения, включающего указанный объект, и нажатие символов на изображении по порядку.

Идентификация проверочного кода может быть несовместимой в зависимости от типа проверочного кода.Конечно, самый простой проверочный код может быть самым оригинальным текстовым проверочным кодом:

3.png

Даже текстовые коды проверки имеют много отличий, например, простые цифровые коды проверки, простые цифровые + буквенные коды проверки, текстовые коды проверки, коды проверки, включая расчеты, и простые коды проверки, добавляющие некоторые помехи, чтобы стать сложными кодами проверки и т. д.

Идентификационные коды

1. Простое распознавание кода подтверждения

Распознавание капчи — древняя область исследований, проще говоря, это процесс преобразования текста на картинке в текст. В последние годы, с развитием больших данных, у большинства инженеров-краулеров предъявляются все более высокие требования к идентификации проверочных кодов при борьбе со стратегиями антикраулинга. В эпоху простых проверочных кодов идентификация проверочных кодов в основном предназначена для текстовых проверочных кодов.Посредством вырезания изображения каждая часть проверочного кода вырезается, а затем сравнивается сходство каждой режущей единицы для получения максимально возможных результатов. Наконец, для склеивания, например, проверочного кода:

4.png

Выполните такие операции, как бинаризация:

5.png

Отрежьте, когда закончите:

6.png

Относительно легко идентифицировать каждый символ после завершения резки и, наконец, до склеивания.

Однако с течением времени, когда этот простой проверочный код постепенно перестает решать задачу определения «человек это или машина», проверочный код претерпел небольшую модернизацию, то есть некоторые линии помех были удалены. добавлен в проверочный код, или проверочный код был серьезно искажен, добавляя сильные помехи цветового блока, например капчу с веб-сайта Dynadot:

7.png

Имеются не только перекрывающиеся искажения изображения, но также интерференционные линии и интерференция цветовых блоков. В настоящее время, если вы хотите распознать проверочный код, трудно получить хорошие результаты с помощью простого распознавания вырезания.В настоящее время вы можете получить хорошие результаты с помощью глубокого обучения.

2. Распознавание проверочного кода на основе CNN

Сверточная нейронная сеть (сокращенно CNN) — это нейронная сеть с прямой связью, в которой искусственные нейроны могут реагировать на окружающие единицы для крупномасштабной обработки изображений. Сверточные нейронные сети включают сверточные слои и объединяющие слои.

8.png

Как показано на рисунке, левое изображение представляет собой традиционную нейронную сеть, и ее основная структура: входной слой, скрытый слой и выходной слой. Изображение справа представляет собой свёрточную нейронную сеть, структура которой состоит из входного слоя, выходного слоя, свёрточного слоя, объединяющего слоя и полносвязного слоя. Сверточная нейронная сеть фактически является расширением нейронной сети, и на самом деле, структурно говоря, нет никакой разницы между простой СНС и простой СНС (конечно, введение специальной структуры, сложная СНС будет иметь больший размер чем NN. разница). По сравнению с традиционной нейронной сетью, CNN значительно сокращает количество сетевых параметров в фактическом эффекте, так что мы можем использовать меньше параметров для обучения лучшей модели, которая обычно дает вдвое больший результат с половиной усилий и может эффективно избежать переобучения. Точно так же из-за совместного использования параметров фильтра, даже если изображение подверглось определенной операции перевода, мы все равно можем идентифицировать признаки, что называется «инвариантностью перевода». Поэтому модель более надежная.

1) Генерация проверочного кода

Генерация проверочного кода — очень важный шаг, потому что проверочный код в этой части будет использоваться в качестве нашего обучающего набора и тестового набора, и то, какой тип проверочного кода может быть распознан нашей моделью, также относится к этой части.

# coding:utf-8
import random
import numpy as np
from PIL import Image
from captcha.image import ImageCaptcha
CAPTCHA_LIST = [eve for eve in "0123456789abcdefghijklmnopqrsruvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ"]
CAPTCHA_LEN = 4  # 验证码长度
CAPTCHA_HEIGHT = 60  # 验证码高度
CAPTCHA_WIDTH = 160  # 验证码宽度
randomCaptchaText = lambda char=CAPTCHA_LIST, size=CAPTCHA_LEN: "".join([random.choice(char) for _ in range(size)])
def genCaptchaTextImage(width=CAPTCHA_WIDTH, height=CAPTCHA_HEIGHT, save=None):
    image = ImageCaptcha(width=width, height=height)
    captchaText = randomCaptchaText()
    if save:
        image.write(captchaText, './img/%s.jpg' % captchaText)
    return captchaText, np.array(Image.open(image.generate(captchaText)))
print(genCaptchaTextImage(save=True))

С помощью приведенного выше кода можно сгенерировать простой код подтверждения на китайском и английском языках:image.gif

9.png

2) Модельное обучение

Код для обучения модели выглядит следующим образом (часть кода взята из сети).

util.py, в основном некоторые извлеченные общедоступные методы:

# -*- coding:utf-8 -*-
import numpy as np
from captcha_gen import genCaptchaTextImage
from captcha_gen import CAPTCHA_LIST, CAPTCHA_LEN, CAPTCHA_HEIGHT, CAPTCHA_WIDTH
# 图片转为黑白,3维转1维
convert2Gray = lambda img: np.mean(img, -1) if len(img.shape) > 2 else img
# 验证码向量转为文本
vec2Text = lambda vec, captcha_list=CAPTCHA_LIST: ''.join([captcha_list[int(v)] for v in vec])
def text2Vec(text, captchaLen=CAPTCHA_LEN, captchaList=CAPTCHA_LIST):
    """
    验证码文本转为向量
    """
    vector = np.zeros(captchaLen * len(captchaList))
    for i in range(len(text)):
        vector[captchaList.index(text[i]) + i * len(captchaList)] = 1
    return vector
def getNextBatch(batchCount=60, width=CAPTCHA_WIDTH, height=CAPTCHA_HEIGHT):
    """
    获取训练图片组
    """
    batchX = np.zeros([batchCount, width * height])
    batchY = np.zeros([batchCount, CAPTCHA_LEN * len(CAPTCHA_LIST)])
    for i in range(batchCount):
        text, image = genCaptchaTextImage()
        image = convert2Gray(image)
        # 将图片数组一维化 同时将文本也对应在两个二维组的同一行
        batchX[i, :] = image.flatten() / 255
        batchY[i, :] = text2Vec(text)
    return batchX, batchY
# print(getNextBatch(batch_count=1))

файл model_train.py, в основном для обучения модели. В этом файле определяется основная информация о модели, например, модель представляет собой трехслойную сверточную нейронную сеть, а исходный размер изображения равен 60.160, которое становится 60 после первой свертки160, которое становится 30 после первого объединения80; становится 30 после второй свертки80 , которое становится 15 после второго объединения40; становится 15 после третьей свертки40 , который становится 7 после третьего объединения20. После трех сверток и объединения исходные данные изображения становятся равными 7.20 плоскостных данных, а при обучении проекта тест данных выполняется каждые 100 раз для расчета точности:

# -*- coding:utf-8 -*-
import tensorflow.compat.v1 as tf
from datetime import datetime
from util import getNextBatch
from captcha_gen import CAPTCHA_HEIGHT, CAPTCHA_WIDTH, CAPTCHA_LEN, CAPTCHA_LIST
tf.compat.v1.disable_eager_execution()
variable = lambda shape, alpha=0.01: tf.Variable(alpha * tf.random_normal(shape))
conv2d = lambda x, w: tf.nn.conv2d(x, w, strides=[1, 1, 1, 1], padding='SAME')
maxPool2x2 = lambda x: tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
optimizeGraph = lambda y, y_conv: tf.train.AdamOptimizer(1e-3).minimize(
    tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=y_conv)))
hDrop = lambda image, weight, bias, keepProb: tf.nn.dropout(
    maxPool2x2(tf.nn.relu(conv2d(image, variable(weight, 0.01)) + variable(bias, 0.1))), keepProb)
def cnnGraph(x, keepProb, size, captchaList=CAPTCHA_LIST, captchaLen=CAPTCHA_LEN):
    """
    三层卷积神经网络
    """
    imageHeight, imageWidth = size
    xImage = tf.reshape(x, shape=[-1, imageHeight, imageWidth, 1])
    hDrop1 = hDrop(xImage, [3, 3, 1, 32], [32], keepProb)
    hDrop2 = hDrop(hDrop1, [3, 3, 32, 64], [64], keepProb)
    hDrop3 = hDrop(hDrop2, [3, 3, 64, 64], [64], keepProb)
    # 全连接层
    imageHeight = int(hDrop3.shape[1])
    imageWidth = int(hDrop3.shape[2])
    wFc = variable([imageHeight * imageWidth * 64, 1024], 0.01)  # 上一层有64个神经元 全连接层有1024个神经元
    bFc = variable([1024], 0.1)
    hDrop3Re = tf.reshape(hDrop3, [-1, imageHeight * imageWidth * 64])
    hFc = tf.nn.relu(tf.matmul(hDrop3Re, wFc) + bFc)
    hDropFc = tf.nn.dropout(hFc, keepProb)
    # 输出层
    wOut = variable([1024, len(captchaList) * captchaLen], 0.01)
    bOut = variable([len(captchaList) * captchaLen], 0.1)
    yConv = tf.matmul(hDropFc, wOut) + bOut
    return yConv
def accuracyGraph(y, yConv, width=len(CAPTCHA_LIST), height=CAPTCHA_LEN):
    """
    偏差计算图,正确值和预测值,计算准确度
    """
    maxPredictIdx = tf.argmax(tf.reshape(yConv, [-1, height, width]), 2)
    maxLabelIdx = tf.argmax(tf.reshape(y, [-1, height, width]), 2)
    correct = tf.equal(maxPredictIdx, maxLabelIdx)  # 判断是否相等
    return tf.reduce_mean(tf.cast(correct, tf.float32))
def train(height=CAPTCHA_HEIGHT, width=CAPTCHA_WIDTH, ySize=len(CAPTCHA_LIST) * CAPTCHA_LEN):
    """
    cnn训练
    """
    accRate = 0.95
    x = tf.placeholder(tf.float32, [None, height * width])
    y = tf.placeholder(tf.float32, [None, ySize])
    keepProb = tf.placeholder(tf.float32)
    yConv = cnnGraph(x, keepProb, (height, width))
    optimizer = optimizeGraph(y, yConv)
    accuracy = accuracyGraph(y, yConv)
    saver = tf.train.Saver()
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())  # 初始化
        step = 0  # 步数
        while True:
            batchX, batchY = getNextBatch(64)
            sess.run(optimizer, feed_dict={x: batchX, y: batchY, keepProb: 0.75})
            # 每训练一百次测试一次
            if step % 100 == 0:
                batchXTest, batchYTest = getNextBatch(100)
                acc = sess.run(accuracy, feed_dict={x: batchXTest, y: batchYTest, keepProb: 1.0})
                print(datetime.now().strftime('%c'), ' step:', step, ' accuracy:', acc)
                # 准确率满足要求,保存模型
                if acc > accRate:
                    modelPath = "./model/captcha.model"
                    saver.save(sess, modelPath, global_step=step)
                    accRate += 0.01
                    if accRate > 0.90:
                        break
            step = step + 1
train()

Когда эта часть будет завершена, мы можем обучить модель через локальную машину.Чтобы повысить скорость обучения, я установил часть кода accRate:

if accRate > 0.90:
    break

То есть, когда точность превысит 90%, система автоматически остановится и модель будет сохранена.

Далее вы можете тренировать:

10.png

Время обучения может быть относительно большим.После завершения обучения, вы можете нарисовать по результатам, чтобы увидеть кривую изменения показателя точности по мере увеличения Шага:

11.png

Горизонтальная ось представляет собой шаг обучения, а вертикальная ось представляет собой точность.

3. Распознавание проверочного кода на основе бессерверной архитектуры

Приведенная выше часть кода дополнительно интегрирована и закодирована в соответствии со спецификацией вычисления функции:

# -*- coding:utf-8 -*-
# 核心后端服务
import base64
import json
import uuid
import tensorflow as tf
import random
import numpy as np
from PIL import Image
from captcha.image import ImageCaptcha
# Response
class Response:
    def __init__(self, start_response, response, errorCode=None):
        self.start = start_response
        responseBody = {
            'Error': {"Code": errorCode, "Message": response},
        } if errorCode else {
            'Response': response
        }
        # 默认增加uuid,便于后期定位
        responseBody['ResponseId'] = str(uuid.uuid1())
        print("Response: ", json.dumps(responseBody))
        self.response = json.dumps(responseBody)
    def __iter__(self):
        status = '200'
        response_headers = [('Content-type', 'application/json; charset=UTF-8')]
        self.start(status, response_headers)
        yield self.response.encode("utf-8")
CAPTCHA_LIST = [eve for eve in "0123456789abcdefghijklmnopqrsruvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ"]
CAPTCHA_LEN = 4  # 验证码长度
CAPTCHA_HEIGHT = 60  # 验证码高度
CAPTCHA_WIDTH = 160  # 验证码宽度
# 随机字符串
randomStr = lambda num=5: "".join(random.sample('abcdefghijklmnopqrstuvwxyz', num))
randomCaptchaText = lambda char=CAPTCHA_LIST, size=CAPTCHA_LEN: "".join([random.choice(char) for _ in range(size)])
# 图片转为黑白,3维转1维
convert2Gray = lambda img: np.mean(img, -1) if len(img.shape) > 2 else img
# 验证码向量转为文本
vec2Text = lambda vec, captcha_list=CAPTCHA_LIST: ''.join([captcha_list[int(v)] for v in vec])
variable = lambda shape, alpha=0.01: tf.Variable(alpha * tf.random_normal(shape))
conv2d = lambda x, w: tf.nn.conv2d(x, w, strides=[1, 1, 1, 1], padding='SAME')
maxPool2x2 = lambda x: tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
optimizeGraph = lambda y, y_conv: tf.train.AdamOptimizer(1e-3).minimize(
    tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=y_conv)))
hDrop = lambda image, weight, bias, keepProb: tf.nn.dropout(
    maxPool2x2(tf.nn.relu(conv2d(image, variable(weight, 0.01)) + variable(bias, 0.1))), keepProb)
def genCaptchaTextImage(width=CAPTCHA_WIDTH, height=CAPTCHA_HEIGHT, save=None):
    image = ImageCaptcha(width=width, height=height)
    captchaText = randomCaptchaText()
    if save:
        image.write(captchaText, save)
    return captchaText, np.array(Image.open(image.generate(captchaText)))
def text2Vec(text, captcha_len=CAPTCHA_LEN, captcha_list=CAPTCHA_LIST):
    """
    验证码文本转为向量
    """
    vector = np.zeros(captcha_len * len(captcha_list))
    for i in range(len(text)):
        vector[captcha_list.index(text[i]) + i * len(captcha_list)] = 1
    return vector
def getNextBatch(batch_count=60, width=CAPTCHA_WIDTH, height=CAPTCHA_HEIGHT):
    """
    获取训练图片组
    """
    batch_x = np.zeros([batch_count, width * height])
    batch_y = np.zeros([batch_count, CAPTCHA_LEN * len(CAPTCHA_LIST)])
    for i in range(batch_count):
        text, image = genCaptchaTextImage()
        image = convert2Gray(image)
        # 将图片数组一维化 同时将文本也对应在两个二维组的同一行
        batch_x[i, :] = image.flatten() / 255
        batch_y[i, :] = text2Vec(text)
    return batch_x, batch_y
def cnnGraph(x, keepProb, size, captchaList=CAPTCHA_LIST, captchaLen=CAPTCHA_LEN):
    """
    三层卷积神经网络
    """
    imageHeight, imageWidth = size
    xImage = tf.reshape(x, shape=[-1, imageHeight, imageWidth, 1])
    hDrop1 = hDrop(xImage, [3, 3, 1, 32], [32], keepProb)
    hDrop2 = hDrop(hDrop1, [3, 3, 32, 64], [64], keepProb)
    hDrop3 = hDrop(hDrop2, [3, 3, 64, 64], [64], keepProb)
    # 全连接层
    imageHeight = int(hDrop3.shape[1])
    imageWidth = int(hDrop3.shape[2])
    wFc = variable([imageHeight * imageWidth * 64, 1024], 0.01)  # 上一层有64个神经元 全连接层有1024个神经元
    bFc = variable([1024], 0.1)
    hDrop3Re = tf.reshape(hDrop3, [-1, imageHeight * imageWidth * 64])
    hFc = tf.nn.relu(tf.matmul(hDrop3Re, wFc) + bFc)
    hDropFc = tf.nn.dropout(hFc, keepProb)
    # 输出层
    wOut = variable([1024, len(captchaList) * captchaLen], 0.01)
    bOut = variable([len(captchaList) * captchaLen], 0.1)
    yConv = tf.matmul(hDropFc, wOut) + bOut
    return yConv
def captcha2Text(image_list):
    """
    验证码图片转化为文本
    """
    with tf.Session() as sess:
        saver.restore(sess, tf.train.latest_checkpoint('model/'))
        predict = tf.argmax(tf.reshape(yConv, [-1, CAPTCHA_LEN, len(CAPTCHA_LIST)]), 2)
        vector_list = sess.run(predict, feed_dict={x: image_list, keepProb: 1})
        vector_list = vector_list.tolist()
        text_list = [vec2Text(vector) for vector in vector_list]
        return text_list
x = tf.placeholder(tf.float32, [None, CAPTCHA_HEIGHT * CAPTCHA_WIDTH])
keepProb = tf.placeholder(tf.float32)
yConv = cnnGraph(x, keepProb, (CAPTCHA_HEIGHT, CAPTCHA_WIDTH))
saver = tf.train.Saver()
def handler(environ, start_response):
    try:
        request_body_size = int(environ.get('CONTENT_LENGTH', 0))
    except (ValueError):
        request_body_size = 0
    requestBody = json.loads(environ['wsgi.input'].read(request_body_size).decode("utf-8"))
    imageName = randomStr(10)
    imagePath = "/tmp/" + imageName
    print("requestBody: ", requestBody)
    reqType = requestBody.get("type", None)
    if reqType == "get_captcha":
        genCaptchaTextImage(save=imagePath)
        with open(imagePath, 'rb') as f:
            data = base64.b64encode(f.read()).decode()
        return Response(start_response, {'image': data})
    if reqType == "get_text":
        # 图片获取
        print("Get pucture")
        imageData = base64.b64decode(requestBody["image"])
        with open(imagePath, 'wb') as f:
            f.write(imageData)
        # 开始预测
        img = Image.open(imageName)
        img = img.resize((160, 60), Image.ANTIALIAS)
        img = img.convert("RGB")
        img = np.asarray(img)
        image = convert2Gray(img)
        image = image.flatten() / 255
        return Response(start_response, {'result': captcha2Text([image])})

В этой функциональной части он в основном включает два интерфейса:

• Получить проверочный код: пользовательское тестовое использование, сгенерировать проверочный код • Получение результатов идентификации кода подтверждения: идентификация и использование пользователя, код подтверждения идентификации

Зависимости, необходимые для этой части кода, следующие:

tensorflow==1.13.1
numpy==1.19.4
scipy==1.5.4
pillow==8.0.1
captcha==0.3

Кроме того, для упрощения работы предоставляется тестовая страница, а фоновый сервис тестовой страницы использует фреймворк Python Web Bottle:

# -*- coding:utf-8 -*-
import os
import json
from bottle import route, run, static_file, request
import urllib.request
url = "http://" + os.environ.get("url")
@route('/')
def index():
    return static_file("index.html", root='html/')
@route('/get_captcha')
def getCaptcha():
    data = json.dumps({"type": "get_captcha"}).encode("utf-8")
    reqAttr = urllib.request.Request(data=data, url=url)
    return urllib.request.urlopen(reqAttr).read().decode("utf-8")
@route('/get_captcha_result', method='POST')
def getCaptcha():
    data = json.dumps({"type": "get_text", "image": json.loads(request.body.read().decode("utf-8"))["image"]}).encode(
        "utf-8")
    reqAttr = urllib.request.Request(data=data, url=url)
    return urllib.request.urlopen(reqAttr).read().decode("utf-8")
run(host='0.0.0.0', debug=False, port=9000)

Серверная служба, необходимые зависимости:

bottle==0.12.19

Код страницы интерфейса:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>验证码识别测试系统</title>
    <link href="https://www.bootcss.com/p/layoutit/css/bootstrap-combined.min.css" rel="stylesheet">
    <script>
        var image = undefined
        function getCaptcha() {
            const xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
            xmlhttp.open("GET", '/get_captcha', false);
            xmlhttp.onreadystatechange = function () {
                if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                    image = JSON.parse(xmlhttp.responseText).Response.image
                    document.getElementById("captcha").src = "data:image/png;base64," + image
                    document.getElementById("getResult").style.visibility = 'visible'
                }
            }
            xmlhttp.setRequestHeader("Content-type", "application/json");
            xmlhttp.send();
        }
        function getCaptchaResult() {
            const xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
            xmlhttp.open("POST", '/get_captcha_result', false);
            xmlhttp.onreadystatechange = function () {
                if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                    document.getElementById("result").innerText = "识别结果:" + JSON.parse(xmlhttp.responseText).Response.result
                }
            }
            xmlhttp.setRequestHeader("Content-type", "application/json");
            xmlhttp.send(JSON.stringify({"image": image}));
        }
    </script>
</head>
<body>
<div class="container-fluid" style="margin-top: 10px">
    <div class="row-fluid">
        <div class="span12">
            <center>
                <h3>
                    验证码识别测试系统
                </h3>
            </center>
        </div>
    </div>
    <div class="row-fluid">
        <div class="span2">
        </div>
        <div class="span8">
            <center>
                <img src="" id="captcha"/>
                <br><br>
                <p id="result"></p>
            </center>
            <fieldset>
                <legend>操作:</legend>
                <button class="btn" onclick="getCaptcha()">获取验证码</button>
                <button class="btn" onclick="getCaptchaResult()" id="getResult" style="visibility: hidden">识别验证码
                </button>
            </fieldset>
        </div>
        <div class="span2">
        </div>
    </div>
</div>
</body>
</html>

Когда код будет готов, начните писать файл развертывания:

Global:
  Service:
      Name: ServerlessBook
      Description: Serverless图书案例
      Log: Auto
      Nas: Auto
ServerlessBookCaptchaDemo:
  Component: fc
  Provider: alibaba
  Access: release
  Extends:
    deploy:
      - Hook: s install docker
        Path: ./
        Pre: true
  Properties:
    Region: cn-beijing
    Service: ${Global.Service}
    Function:
      Name: serverless_captcha
      Description: 验证码识别
      CodeUri:
        Src: ./src/backend
        Excludes:
          - src/backend/.fun
          - src/backend/model
      Handler: index.handler
      Environment:
        - Key: PYTHONUSERBASE
          Value: /mnt/auto/.fun/python
      MemorySize: 3072
      Runtime: python3
      Timeout: 60
      Triggers:
        - Name: ImageAI
          Type: HTTP
          Parameters:
            AuthType: ANONYMOUS
            Methods:
              - GET
              - POST
              - PUT
            Domains:
              - Domain: Auto
ServerlessBookCaptchaWebsiteDemo:
  Component: bottle
  Provider: alibaba
  Access: release
  Extends:
    deploy:
      - Hook: pip3 install -r requirements.txt -t ./
        Path: ./src/website
        Pre: true
  Properties:
    Region: cn-beijing
    CodeUri: ./src/website
    App: index.py
    Environment:
      - Key: url
        Value: ${ServerlessBookCaptchaDemo.Output.Triggers[0].Domains[0]}
    Detail:
      Service: ${Global.Service}
      Function:
        Name: serverless_captcha_website

Общая структура каталогов:

| - src # 项目目录
 |   | - backend # 项目后端,核心接口
 |       | - index.py # 后端核心代码
 |       | - requirements.txt # 后端核心代码依赖
 |   | - website # 项目前端,便于测试使用
 |       | - html # 项目前端页面
 |           | - index.html # 项目前端页面
 |       | - index.py # 项目前端的后台服务(bottle框架)
 |       | - requirements.txt # 项目前端的后台服务依赖

После завершения мы можем развернуть проект в каталоге проекта:

s deploy

После завершения развертывания откройте возвращенный адрес страницы:

12.png

Нажмите «Получить код подтверждения», чтобы сгенерировать код подтверждения онлайн:

13.png

На этом этапе нажмите на идентификационный код подтверждения, чтобы определить код подтверждения:

14.png

Поскольку целевой показатель точности, заполняемый при обучении модели, составляет 90%, можно считать, что общий показатель точности составляет около 90% после большого количества тестов одного и того же типа капчи.

Суммировать

Serverless быстро развивается, и я думаю, что это очень крутая вещь — сделать инструмент распознавания проверочного кода через Serverless. В будущем сборе данных и другой работе очень нужно иметь красивый инструмент распознавания проверочного кода. Конечно, существует много типов проверочных кодов, и определение различных типов проверочных кодов также является очень сложной задачей.