OpenCV-Python实战(22)——使用Keras和Flask在Web端部署图像识别应用
0. 前言
在深度学习简介中,我们学习了如何使用 Keras 创建深度学习应用程序。在本文中,我们将看到如何使用 Keras 和 Flask 创建深度学习 API REST 。更具体地说,我们首先学习如何使用 Keras 中包含的预训练深度学习架构,然后介绍如何使用这些预训练深度学习架构创建深度学习 API 。
1. Keras 应用程序
Keras Applications 是 Keras 深度学习库的应用模块,提供了许多流行的深度学习模型架构(例如 VGG16 、ResNet50 、Xception 和 MobileNet 等)的预训练权重,可用于预测、特征提取和微调。 Keras 在实例化模型时会自动下载预训练的权重,所有这些深度学习架构与所有后端(TensorFlow 、Theano 和 CNTK ) 兼容。这些深度学习架构在 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) ;然后,使用 NumPy 的 expand_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, 描述, 预测置信度)构成:
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(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 。 首先是导入需要的包,如下:
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 应用程序和深度学习模型:
app = flask.Flask(__name__)
model = None
第三步是定义 load_model() 函数,用于创建深度学习架构并加载所需的权重:
def load_model():
global model
model = NASNetMobile(weights='imagenet')
接下来定义 preprocessing_image() 函数用于预处理:
def preprocessing_image(image, target):
if image.mode != "RGB":
image = image.convert("RGB")
image = image.resize(target)
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'):
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
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端的部署
|