OpenCV-Python实战(22)——使用Keras和Flask在Web端部署图像识别应用

x33g5p2x  于2022-03-17 转载在 Python  
字(9.3k)|赞(0)|评价(0)|浏览(557)

0. 前言

在深度学习简介中,我们学习了如何使用 Keras 创建深度学习应用程序。在本文中,我们将看到如何使用 KerasFlask 创建深度学习 API REST。更具体地说,我们首先学习如何使用 Keras 中包含的预训练深度学习架构,然后介绍如何使用这些预训练深度学习架构创建深度学习 API

1. Keras 应用程序

Keras ApplicationsKeras 深度学习库的应用模块,提供了许多流行的深度学习模型架构(例如 VGG16ResNet50XceptionMobileNet 等)的预训练权重,可用于预测、特征提取和微调。
Keras 在实例化模型时会自动下载预训练的权重,所有这些深度学习架构与所有后端(TensorFlowTheanoCNTK) 兼容。这些深度学习架构在 ImageNet 数据集 上进行训练和验证,用于图像分类任务:

在上图中,可以看到 Keras Applications 模块中可用的各个模型的介绍,接下来,我们将使用这些预训练模型进行图像分类任务。除了图像分类任务外,这些预训练模型还可用于特征提取(例如,从任意中间层提取特征)和微调(例如,在新的任务中微调预训练模型)。
第一步是导入所需要的包:

from keras.preprocessing import image
from keras.applications import inception_v3, vgg16, vgg19, resnet50, mobilenet, xception, nasnet, densenet
from keras.applications.imagenet_utils import decode_predictions

第二步是实例化不同的模型架构:

# 加载可用模型
model_inception_v3 = inception_v3.InceptionV3(weights='imagenet')
model_vgg_16 = vgg16.VGG16(weights='imagenet')
model_vgg_19 = vgg19.VGG19(weights='imagenet')
model_resnet_50 = resnet50.ResNet50(weights='imagenet')
model_mobilenet = mobilenet.MobileNet(weights='imagenet')
model_xception = xception.Xception(weights='imagenet')
model_nasnet_mobile = nasnet.NASNetMobile(weights='imagenet')
model_densenet_121 = densenet.DenseNet121(weights='imagenet')

第三步是使用 preprocessing_image() 函数加载和预处理图像以进行分类:

def preprocessing_image(img_path, target_size, architecture):
    img = image.load_img(img_path, target_size=target_size)
    x = image.img_to_array(img)
    x = np.expand_dims(x, axis=0)
    x = architecture.preprocess_input(x)
    return x
x_inception_v3 = preprocessing_image(img_path, (299, 299), inception_v3)
x_vgg_16 = preprocessing_image(img_path, (224, 224), vgg16)
x_vgg_19 = preprocessing_image(img_path, (224, 224), vgg19)
x_resnet_50 = preprocessing_image(img_path, (224, 224), resnet50)
x_mobilenet = preprocessing_image(img_path, (224, 224), mobilenet)
x_xception = preprocessing_image(img_path, (299, 299), xception)
x_nasnet_mobile = preprocessing_image(img_path, (224, 224), nasnet)
x_densenet_121 = preprocessing_image(img_path, (224, 224), densenet)

preprocessing_image() 函数的第一步是使用 image.load_img() 函数加载图像,指定目标大小,由于 Keras 加载的是 PIL 格式 (width, height) 的图像,需要使用 image.img_to_array() 函数将其转换为 NumPy 格式 (height, width, channel);然后,使用 NumPyexpand_dims() 函数将输入图像转换为四维张量 (batchsize, height, width, channels);预处理图像的最后一步是对图像进行归一化,每种架构使用特定预处理方式,通过调用 preprocess_input() 函数实现;最后调用 preprocessing_image() 函数。
图像经过预处理后,就来到第四步使用 model.predict() 获得分类结果(每个类别的预测概率):

# 获取预测结果概率
preds_inception_v3 = model_inception_v3.predict(x_inception_v3)
preds_vgg_16 = model_vgg_16.predict(x_vgg_16)
preds_vgg_19 = model_vgg_19.predict(x_vgg_19)
preds_resnet_50 = model_resnet_50.predict(x_resnet_50)
preds_mobilenet = model_mobilenet.predict(x_mobilenet)
preds_xception = model_xception.predict(x_xception)
preds_nasnet_mobile = model_nasnet_mobile.predict(x_nasnet_mobile)
preds_densenet_121 = model_nasnet_mobile.predict(x_densenet_121)

预测值由元组列表(类别 ID, 描述, 预测置信度)构成:

# 打印结果 (class, description, probability):
print('Predicted InceptionV3:', decode_predictions(preds_inception_v3, top=5)[0])
print('Predicted VGG16:', decode_predictions(preds_vgg_16, top=5)[0])
print('Predicted VGG19:', decode_predictions(preds_vgg_19, top=5)[0])
print('Predicted ResNet50:', decode_predictions(preds_resnet_50, top=5)[0])
print('Predicted MobileNet:', decode_predictions(preds_mobilenet, top=5)[0])
print('Predicted Xception:', decode_predictions(preds_xception, top=5)[0])
print('Predicted NASNetMobile:', decode_predictions(preds_nasnet_mobile, top=5)[0])
print('Predicted DenseNet121:', decode_predictions(preds_densenet_121, top=5)[0])

模型输出是输入中的每个图像的预测结果元组(类别 ID 、描述和预测置信度),由于我们只有一张图像用作输入,因此得到的输出如下:

Predicted InceptionV3: [('n02510455', 'giant_panda', 0.97168857), ('n04254680', 'soccer_ball', 0.00053718797), ('n04266014', 'space_shuttle', 0.00035958426), ('n02509815', 'lesser_panda', 0.00035547058), ('n02500267', 'indri', 0.00032849688)]
Predicted VGG16: [('n02510455', 'giant_panda', 0.9851264), ('n02445715', 'skunk', 0.007836222), ('n02447366', 'badger', 0.006254676), ('n02441942', 'weasel', 0.0002136865), ('n02509815', 'lesser_panda', 0.00015041522)]
Predicted VGG19: [('n02510455', 'giant_panda', 0.9984491), ('n02445715', 'skunk', 0.0007714728), ('n02447366', 'badger', 0.00059817365), ('n02488702', 'colobus', 2.128689e-05), ('n02442845', 'mink', 1.9781652e-05)]
Predicted ResNet50: [('n02510455', 'giant_panda', 0.9832449), ('n02110341', 'dalmatian', 0.002478397), ('n02509815', 'lesser_panda', 0.0022290186), ('n02447366', 'badger', 0.0020123231), ('n02412080', 'ram', 0.00044684077)]
Predicted MobileNet: [('n02510455', 'giant_panda', 0.9991955), ('n02509815', 'lesser_panda', 0.0003145063), ('n02500267', 'indri', 0.0001445993), ('n02497673', 'Madagascar_cat', 0.0001292361), ('n02493509', 'titi', 5.8193353e-05)]
Predicted Xception: [('n02510455', 'giant_panda', 0.91967607), ('n02509815', 'lesser_panda', 0.0037806507), ('n04399382', 'teddy', 0.0013589169), ('n02134418', 'sloth_bear', 0.00050903426), ('n02132136', 'brown_bear', 0.0004746767)]
Predicted NASNetMobile: [('n02510455', 'giant_panda', 0.8976383), ('n02509815', 'lesser_panda', 0.0012320529), ('n04254680', 'soccer_ball', 0.0012049181), ('n02488702', 'colobus', 0.0007275882), ('n02971356', 'carton', 0.00048007787)]
Predicted DenseNet121: [('n02510455', 'giant_panda', 0.992669), ('n02509815', 'lesser_panda', 0.0039006013), ('n02445715', 'skunk', 0.0011870685), ('n02500267', 'indri', 0.0009038625), ('n02447366', 'badger', 0.00013635056)]

最后,使用 put_text() 函数显示每种架构预测的图像分类结果:

def put_text(img, model_name, decoded_preds, y_pos):
    # 调用了 cv2.putText() 函数来渲染图像分类结果字符串
    cv2.putText(img, "{}: {}, {:.2f}".format(model_name, decoded_preds[0][0][1], decoded_preds[0][0][2]),(20, y_pos), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 0, 255), 2)
    
put_text(numpy_image_res, "InceptionV3", decode_predictions(preds_inception_v3), 40)
put_text(numpy_image_res, "VGG16", decode_predictions(preds_vgg_16), 80)
put_text(numpy_image_res, "VGG19", decode_predictions(preds_vgg_19), 120)
put_text(numpy_image_res, "ResNet50", decode_predictions(preds_resnet_50), 160)
put_text(numpy_image_res, "MobileNet", decode_predictions(preds_mobilenet), 200)
put_text(numpy_image_res, "Xception", decode_predictions(preds_xception), 240)
put_text(numpy_image_res, "NASNetMobile", decode_predictions(preds_nasnet_mobile), 280)
put_text(numpy_image_res, "DenseNet121", decode_predictions(preds_densenet_121), 320)

程序输出结果如下所示:

2. 创建 Keras 应用程序的深度学习 REST API

在上一节中,我们学习了如何使用 Keras 提供的深度学习模型定义和预训练权重。接下来,我们进一步了解如何基于这些预训练架构创建深度学习 REST API
首先是导入需要的包,如下:

# keras_rest_api.py
from keras.applications import nasnet, NASNetMobile
from keras.preprocessing.image import img_to_array
from keras.applications import imagenet_utils
from PIL import Image
import numpy as np
import flask
import io

然后是初始化 Flask 应用程序和深度学习模型:

# 初始化 Flask 应用程序和深度学习模型
app = flask.Flask(__name__)
model = None

第三步是定义 load_model() 函数,用于创建深度学习架构并加载所需的权重:

def load_model():
    global model
    # 加载 NASNetMobile 权重
    model = NASNetMobile(weights='imagenet')

接下来定义 preprocessing_image() 函数用于预处理:

def preprocessing_image(image, target):
    if image.mode != "RGB":
        image = image.convert("RGB")
    # 缩放
    image = image.resize(target)
    # 将 PIL 类型转换为 Numpy 类型
    image = img_to_array(image)
    # 维度扩展
    image = np.expand_dims(image, axis=0)
    # 根据特定架构进行预处理
    image = nasnet.preprocess_input(image)
    return image

最后,我们使用 route() 装饰器将 predict() 函数绑定到 /predict URL。 predict() 函数处理请求并将预测返回给客户端,如下所示:

@app.route("/predict", methods=["POST"])
def predict():
    result = {'success': False}

    if flask.request.method == 'POST':
        if flask.request.files.get('image'):
            # 读取 PIL 格式的输入图像
            image = flask.request.files['image'].read()
            image = Image.open(io.BytesIO(image))
            # 输入图像预处理
            image = preprocessing_image(image, target=(224, 224))
            # 图像分类
            predictions = model.predict(image)
            results = imagenet_utils.decode_predictions(predictions)
            result['predictions'] = []
            # 将预测添加到结果中
            for (imagenet_id, label, prob) in results[0]:
                r = {'label': label, 'probability': float(prob)}
                result['predictions'].append(r)

            result['success'] = True
    # 将结果作为 JSON 响应返回
    return flask.jsonify(result)
if __name__ == '__main__':
    print("Loading Keras pre-trained model")
    load_model()
    print("Starting")
    app.run()

运行编写完成的服务器端程序:

$ python keras_rest_api.py

接下来,我们测试 Keras 深度学习 REST API 执行 POST 请求,打印结果,并创建一个图像来渲染获得的结果:

import requests

KERAS_REST_API_URL = 'http://localhost:5000/predict'
IMAGE_PATH = 'pandas.jpeg'
# 加载图像并构建有效负载
image = open(IMAGE_PATH, 'rb').read()
payload = {'image': image}
# 获取结果
r = requests.post(KERAS_REST_API_URL, files=payload).json()
# 获得原始图像以进行可视化
image_array = np.asarray(bytearray(image), dtype=np.uint8)
img_opencv = cv2.imdecode(image_array, -1)

y_pos = 40

if r['success']:
    for (i, result) in enumerate(r['predictions']):
        print("{}. {}: {:.4f}".format(i + 1, result["label"], result["probability"]))
        # 渲染结果图像
        cv2.putText(img_opencv, "{}. {}: {:.4f}".format(i + 1, result["label"], result["probability"]), (20, y_pos), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255, 0, 255), 2)
        y_pos += 40
else:
    print("Request failed")

如上所示,对 Keras 深度学习 REST API 执行 POST 请求,打印结果并在图像中呈现结果:

小结

在本文中,我们学习了使用 Keras 和 Flask 创建深度学习 REST API。更具体地说,我们首先学习了使用 Keras 中包含的预训练深度学习架构,然后使用这些预训练深度学习架构创建深度学习 REST API,用于高性能图像识别任务。

系列链接

OpenCV-Python实战(1)——OpenCV简介与图像处理基础
OpenCV-Python实战(2)——图像与视频文件的处理
OpenCV-Python实战(3)——OpenCV中绘制图形与文本
OpenCV-Python实战(4)——OpenCV常见图像处理技术
OpenCV-Python实战(5)——OpenCV图像运算
OpenCV-Python实战(6)——OpenCV中的色彩空间和色彩映射
OpenCV-Python实战(7)——直方图详解
OpenCV-Python实战(8)——直方图均衡化
OpenCV-Python实战(9)——OpenCV用于图像分割的阈值技术
OpenCV-Python实战(10)——OpenCV轮廓检测
OpenCV-Python实战(11)——OpenCV轮廓检测相关应用
OpenCV-Python实战(12)——一文详解AR增强现实
OpenCV-Python实战(13)——OpenCV与机器学习的碰撞
OpenCV-Python实战(14)——人脸检测详解
OpenCV-Python实战(15)——面部特征点检测详解
OpenCV-Python实战(16)——人脸追踪详解
OpenCV-Python实战(17)——人脸识别详解
OpenCV-Python实战(18)——深度学习简介与入门示例
OpenCV-Python实战(19)——OpenCV与深度学习的碰撞
OpenCV-Python实战(20)——OpenCV计算机视觉项目在Web端的部署
OpenCV-Python实战(21)——OpenCV人脸检测项目在Web端的部署

相关文章