python|OpenCV-Python实战(21)——OpenCV人脸检测项目在Web端的部署


OpenCV-Python实战(21)——OpenCV人脸检测项目在Web端的部署

    • 0. 前言
    • 1. OpenCV 人脸检测项目在 Web 端的部署
      • 1.1 解析请求并构建响应
      • 1.2 构建请求进行测试
    • 2. 根据获得的响应信息在客户端绘制检测框
    • 3. 在服务器端绘制检测框并返回
    • 小结
    • 系列链接

0. 前言 将 OpenCV 计算机视觉项目部署在 Web 端一个有趣的话题,部署在 Web 端的优势之一是不需要安装任何应用,只需要访问地址就可以访问应用。在本文中,我们使用 Python Web 框架创建并部署一个完整的 Web 人脸检测应用程序,在项目中我们将学习到如何处理来自浏览器的不同请求方式(例如 GETPOST 等),以及如何实战使用 OpenCVFlask 创建 Web 人脸检测 API
1. OpenCV 人脸检测项目在 Web 端的部署 本节中将使用 Python Web 框架创建并部署一个完整的 Web 人脸检测应用程序,此程序不仅可以处理本地图片(利用 request.files['image']),同时也可以用于处理来自网络中的图片(利用 request.args.get('url'))。
1.1 解析请求并构建响应
在此实战中,我们将看到如何使用 OpenCVFlask 创建一个 Web 人脸检测 API,我们将项目命名为 face_detection,项目目录结构如下所示:
face_detection |——server | ├─face_detection.py | └─face_processing.py └─client ├─request_test.py ├─request_and_draw_rectangle.py └─test_example.png

其中 face_detection.py 脚本负责解析请求并构建对客户端的响应:
# face_detection.py # 导入所需包 from flask import Flask, request, jsonify import urllib.request from face_processing import FaceProcessingapp = Flask(__name__) fc = FaceProcessing()@app.errorhandler(400) def bad_request(e): # 返回代码错误 return jsonify({"status": 'Not ok', "message": "This server could not understand your request"}), 400@app.errorhandler(404) def not_found(e): # 返回代码错误 return jsonify({"status": 'Not found', "message": "Route not found"}), 404@app.errorhandler(500) def internal_error(e): # 返回代码错误 return jsonify({"status": "Internal Error", "message": "Internal error occurred in server"}), 500@app.route('/detect', methods=['GET', 'POST', 'PUT']) def detect_human_faces(): if request.method == 'GET': if request.args.get('url'): with urllib.request.urlopen(request.args.get('url')) as url: return jsonify({"status": "Ok", "result": fc.face_detection(url.read())}), 200 else: return jsonify({"status": "Bad request", "message": "Parameter url is not present"}), 400 elif request.method == 'POST': if request.files.get('image'): return jsonify({"status": "Ok", "result": fc.face_detection(request.files['image'].read())}), 200 else: return jsonify({"status": "Bad request", "message": "Parameter image is not present"}), 400 else: return jsonify({"status": "Failure", "message": "PUT method not supported for API"}), 405if __name__ == '__main__': app.run(host='0.0.0.0')

如上所示,使用 jsonify() 函数来创建给定参数的 JSON 表示,以返回 application/json MIME 类型。 JSON 是信息交换的事实标准,此项目将返回 JSON 响应,在项目的最后我们将了解如何对其进行修改以返回图像,此 Web 人脸检测 API 支持 GETPOST 请求;此外,在 face_detection 脚本中,我们还通过使用 errorhandler() 装饰函数来注册错误处理程序,用来响应出错时向客户端返回设置的错误代码。
人脸检测程序与负责响应的程序进行了分离,人脸检测程序在 face_processing.py 脚本中执行,其中编写了 FaceProcessing() 类:
# face_processing.py import cv2 import numpy as np import os class FaceProcessing(object): def __init__(self): self.file = os.path.join(os.path.join(os.path.dirname(__file__), "data"), "haarcascade_frontalface_alt.xml") self.face_cascade = cv2.CascadeClassifier(self.file)def face_detection(self, image): # 将图像转换为 OpenCV 格式 image_array = np.asarray(bytearray(image), dtype=np.uint8) img_opencv = cv2.imdecode(image_array, -1) output = [] # 检测人脸并构建返回值 gray = cv2.cvtColor(img_opencv, cv2.COLOR_BGR2GRAY) faces = self.face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(25, 25)) for face in faces: # 返回检测框坐标 x, y, w, h = face.tolist() face = {"box": [x, y, x + w, y + h]} output.append(face) print(face) # 返回结果 return output

face_detection() 方法使用 OpenCVdetectMultiScale() 函数执行人脸检测,获得每个检测到的人脸的坐标 (x, y, w, h),并通过合适的格式对检测结果进行编码来构建返回检测框:
face = {"box": [x, y, x + w, y + h]}

最后,我们将编码完成的人脸检测框添加到 output 数组中,将所有检测到的人脸检测框都添加到 output 数组后,将其返回:
output.append(face)

1.2 构建请求进行测试
为了使用 Web 人脸检测 API,我们可以从浏览器执行 GET 请求;同时,此 API 还支持 POST 请求。接下来,我们构建测试脚本测试此 API ,此脚本可以执行 GETPOST 请求,以了解如何与人脸 API 进行交互,更具体的讲,测试脚本将对人脸 API 发送多个请求,以获得不同的响应,并查看错误处理的工作原理。
首先使用不正确的 URL 执行 GET 请求:
# request_test.py import requestsFACE_DETECTION_REST_API_URL = "http://localhost:5000/detect" FACE_DETECTION_REST_API_URL_WRONG = "http://localhost:5000/process" IMAGE_PATH = "test_example.png" URL_IMAGE = "https://imgs.mmkk.me/wmnv/img/20190625073459-5d11cea35c407.png" # 提交 GET 请求 r = requests.get(FACE_DETECTION_REST_API_URL_WRONG) # 查看响应 print("status code: {}".format(r.status_code)) print("headers: {}".format(r.headers)) print("content: {}".format(r.json()))

打印响应信息,可以看到:
status code: 404 headers: {'Content-Type': 'application/json', 'Content-Length': '51', 'Server': 'Werkzeug/1.0.1 Python/3.7.7', 'Date': 'Sat, 02 Oct 2021 01:45:19 GMT'} content: {'message': 'Route not found', 'status': 'Not found'}

状态码 404 表示客户端可以与服务器通信,但服务器找不到请求的内容。这是因为请求的 URL (http://localhost:5000/process) 不正确。
执行的第二个请求是正确的 GET 请求:
# 提交 GET 请求 payload = {'url': URL_IMAGE} r = requests.get(FACE_DETECTION_REST_API_URL, params=payload) # 查看响应 print("status code: {}".format(r.status_code)) print("headers: {}".format(r.headers)) print("content: {}".format(r.json()))

打印响应信息,可以看到:
status code: 200 headers: {'Content-Type': 'application/json', 'Content-Length': '52', 'Server': 'Werkzeug/1.0.1 Python/3.7.7', 'Date': 'Sat, 02 Oct 2021 01:54:31 GMT'} content: {'result': [{'box': [233, 77, 356, 252]}], 'status': 'Ok'}

状态码 200 表示请求已成功执行,还可以看到已检测到与人脸相对应的检测框坐标。
接下来执行缺少有效负载的 GET 请求:
# 提交 GET 请求 r = requests.get(FACE_DETECTION_REST_API_URL) # 查看响应 print("status code: {}".format(r.status_code)) print("headers: {}".format(r.headers)) print("content: {}".format(r.json()))

打印响应信息,可以看到:
status code: 400 headers: {'Content-Type': 'application/json', 'Content-Length': '66', 'Server': 'Werkzeug/1.0.1 Python/3.7.7', 'Date': 'Sat, 02 Oct 2021 01:58:00 GMT'} content: {'message': 'Parameter url is not present', 'status': 'Bad request'}

状态代码 400 表示错误请求,这是由于其缺少 url 参数。
接下来执行的第四个请求是具有正确负载的 POST 请求:
# 加载图像并构建有效负载 image = open(IMAGE_PATH, "rb").read() payload = {"image": image} # 提交 POST 请求 r = requests.post(FACE_DETECTION_REST_API_URL, files=payload) # 查看响应 print("status code: {}".format(r.status_code)) print("headers: {}".format(r.headers)) print("content: {}".format(r.json()))

打印响应信息,可以看到:
status code: 200 headers: {'Content-Type': 'application/json', 'Content-Length': '52', 'Server': 'Werkzeug/1.0.1 Python/3.7.7', 'Date': 'Sat, 02 Oct 2021 02:03:26 GMT'} content: {'result': [{'box': [193, 92, 355, 292]}], 'status': 'Ok'}

最后我们构造 PUT 请求:
# 提交 PUT 请求 r = requests.put(FACE_DETECTION_REST_API_URL, files=payload) # 查看响应 print("status code: {}".format(r.status_code)) print("headers: {}".format(r.headers)) print("content: {}".format(r.json()))

打印响应信息,可以看到:
status code: 405 headers: {'Content-Type': 'application/json', 'Content-Length': '66', 'Server': 'Werkzeug/1.0.1 Python/3.7.7', 'Date': 'Sat, 02 Oct 2021 02:05:54 GMT'} content: {'message': 'PUT method not supported for API', 'status': 'Failure'}

这是由于我们的 API 不支持 PUT 方法,仅支持 GET 和 POST 方法,因此返回状态码 405
2. 根据获得的响应信息在客户端绘制检测框 当请求成功执行时,将检测到的人脸作为 JSON 数据返回,接下来我们将编写程序了解如何解析响应并绘制检测到的人脸:
# request_and_draw_rectangle.py import cv2 import numpy as np import requests from matplotlib import pyplot as pltdef show_img_with_matplotlib(color_img, title, pos): img_RGB = color_img[:, :, ::-1] ax = plt.subplot(1, 1, pos) plt.imshow(img_RGB) plt.title(title, fontsize=10) plt.axis('off')FACE_DETECTION_REST_API_URL = "http://localhost:5000/detect" IMAGE_PATH = "test_example.png" # 加载图像构造有效负载 image = open(IMAGE_PATH, 'rb').read() payload = {'image': image} r = requests.post(FACE_DETECTION_REST_API_URL, files=payload) # 打印响应信息 print("status code: {}".format(r.status_code)) print("headers: {}".format(r.headers)) print("content: {}".format(r.json())) # 解析响应信息 json_data = https://www.it610.com/article/r.json() result = json_data['result']image_array = np.asarray(bytearray(image), dtype=np.uint8) img_opencv = cv2.imdecode(image_array, -1) # 绘制检测框 for face in result: left, top, right, bottom = face['box'] cv2.rectangle(img_opencv, (left, top), (right, bottom), (0, 255, 255), 2) cv2.circle(img_opencv, (left, top), 5, (0, 0, 255), -1) cv2.circle(img_opencv, (right, bottom), 5, (255, 0, 0), -1)# 可视化 fig = plt.figure(figsize=(8, 6)) plt.suptitle("Using face API", fontsize=14, fontweight='bold') show_img_with_matplotlib(img_opencv, "face detection", 1)plt.show()

在上示代码中,首先加载图像并构建有效负载,然后,执行 POST 请求,从响应中获取 JSON 数据并进行解析:
# 解析响应信息 json_data = https://www.it610.com/article/r.json() result = json_data['result']

接下来,就可以利用返回的信息绘制检测到的人脸:
# 绘制检测框 for face in result: left, top, right, bottom = face['box'] cv2.rectangle(img_opencv, (left, top), (right, bottom), (0, 255, 255), 2) cv2.circle(img_opencv, (left, top), 5, (0, 0, 255), -1) cv2.circle(img_opencv, (right, bottom), 5, (255, 0, 0), -1)

对于每个检测到的人脸,绘制矩形检测框以及左上角和右下角的点:
python|OpenCV-Python实战(21)——OpenCV人脸检测项目在Web端的部署
文章图片

3. 在服务器端绘制检测框并返回 我们也可以直接在服务器端在图像中绘制检测框,然后将结果图像返回(相关讲解可以在《OpenCV计算机视觉项目在Web端的部署》中查看),我们需要做的仅仅是修改 face_detection.py,这就是代码分离的优势之一:
# 这里仅修改 GET 请求,对于 POST 的修改也是类似的,可以自行探索 @app.route('/detect', methods=['GET', 'POST', 'PUT']) def detect_human_faces(): if request.method == 'GET': if request.args.get('url'): with urllib.request.urlopen(request.args.get('url')) as url: image = url.read() result = fc.face_detection(image) image_array = np.asarray(bytearray(image), dtype=np.uint8) img_opencv = cv2.imdecode(image_array, -1) for face in result: left, top, right, bottom = face['box']cv2.rectangle(img_opencv, (left, top), (right, bottom), (0, 255, 255), 2) cv2.circle(img_opencv, (left, top), 5, (0, 0, 255), -1) cv2.circle(img_opencv, (right, bottom), 5, (255, 0, 0), -1) retval, buffer = cv2.imencode('.jpg', img_opencv) response = make_response(buffer.tobytes()) response.headers['Content-Type'] = 'image' return response else: return jsonify({"status": "Bad request", "message": "Parameter url is not present"}), 400 elif request.method == 'POST': if request.files.get('image'): return jsonify({"status": "Ok", "result": fc.face_detection(request.files['image'].read())}), 200 else: return jsonify({"status": "Bad request", "message": "Parameter image is not present"}), 400 else: return jsonify({"status": "Failure", "message": "PUT method not supported for API"}), 405

修改之后,我们就可以通过 GET 请求来查看程序效果:
http://10.140.12.255:5000/detect?url=https://imgs.mmkk.me/wmnv/img/20190625073459-5d11cea35c407.png


小结 在本文中,我们使用 Python Web 框架创建并部署了一个完整的 Web 人脸检测应用程序,同时在项目中我们处理了来自浏览器的不同请求方式(例如 GETPOST 等),并通过实战使用 OpenCVFlask 创建 Web 人脸检测 API,同时我们还使用了两种不同类型的响应结果提供不同的请求结果。
系列链接 【python|OpenCV-Python实战(21)——OpenCV人脸检测项目在Web端的部署】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端的部署

    推荐阅读