OpenCV-Python实战(21)——OpenCV人脸检测项目在Web端的部署
0. 前言
将 OpenCV 计算机视觉项目部署在 Web 端一个有趣的话题,部署在 Web 端的优势之一是不需要安装任何应用,只需要访问地址就可以访问应用。在本文中,我们使用 Python Web 框架创建并部署一个完整的 Web 人脸检测应用程序,在项目中我们将学习到如何处理来自浏览器的不同请求方式(例如 GET 和 POST 等),以及如何实战使用 OpenCV 和 Flask 创建 Web 人脸检测 API 。
1. OpenCV 人脸检测项目在 Web 端的部署
本节中将使用 Python Web 框架创建并部署一个完整的 Web 人脸检测应用程序,此程序不仅可以处理本地图片(利用 request.files['image'] ),同时也可以用于处理来自网络中的图片(利用 request.args.get('url') )。
1.1 解析请求并构建响应
在此实战中,我们将看到如何使用 OpenCV 和 Flask 创建一个 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 脚本负责解析请求并构建对客户端的响应:
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 支持 GET 和 POST 请求;此外,在 face_detection 脚本中,我们还通过使用 errorhandler() 装饰函数来注册错误处理程序,用来响应出错时向客户端返回设置的错误代码。 人脸检测程序与负责响应的程序进行了分离,人脸检测程序在 face_processing.py 脚本中执行,其中编写了 FaceProcessing() 类:
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):
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() 方法使用 OpenCV 的 detectMultiScale() 函数执行人脸检测,获得每个检测到的人脸的坐标 (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 ,此脚本可以执行 GET 和 POST 请求,以了解如何与人脸 API 进行交互,更具体的讲,测试脚本将对人脸 API 发送多个请求,以获得不同的响应,并查看错误处理的工作原理。 首先使用不正确的 URL 执行 GET 请求:
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"
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 请求:
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 请求:
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}
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 请求:
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 数据返回,接下来我们将编写程序了解如何解析响应并绘制检测到的人脸:
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 ,这就是代码分离的优势之一:
@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 人脸检测应用程序,同时在项目中我们处理了来自浏览器的不同请求方式(例如 GET 和 POST 等),并通过实战使用 OpenCV 和 Flask 创建 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端的部署
|