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 FaceProcessing

app = 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"}), 405


if __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 requests

FACE_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 plt

def 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 = 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 = 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)

对于每个检测到的人脸,绘制矩形检测框以及左上角和右下角的点:

根据获得的响应信息在客户端绘制检测框

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,同时我们还使用了两种不同类型的响应结果提供不同的请求结果。

系列链接

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端的部署

你可能感兴趣的:(前端,html,python,opencv,前端)