将 OpenCV
计算机视觉项目部署在 Web
端一个有趣的话题,部署在 Web
端的优势之一是不需要安装任何应用,只需要访问地址就可以访问应用。在本文中,我们使用 Python Web
框架创建并部署一个完整的 Web
人脸检测应用程序,在项目中我们将学习到如何处理来自浏览器的不同请求方式(例如 GET
和 POST
等),以及如何实战使用 OpenCV
和 Flask
创建 Web
人脸检测 API
。
本节中将使用 Python Web
框架创建并部署一个完整的 Web
人脸检测应用程序,此程序不仅可以处理本地图片(利用 request.files['image']
),同时也可以用于处理来自网络中的图片(利用 request.args.get('url')
)。
在此实战中,我们将看到如何使用 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
脚本负责解析请求并构建对客户端的响应:
# 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()
类:
# 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()
方法使用 OpenCV
的 detectMultiScale()
函数执行人脸检测,获得每个检测到的人脸的坐标 (x, y, w, h)
,并通过合适的格式对检测结果进行编码来构建返回检测框:
face = {"box": [x, y, x + w, y + h]}
最后,我们将编码完成的人脸检测框添加到 output
数组中,将所有检测到的人脸检测框都添加到 output
数组后,将其返回:
output.append(face)
为了使用 Web
人脸检测 API,我们可以从浏览器执行 GET
请求;同时,此 API
还支持 POST
请求。接下来,我们构建测试脚本测试此 API
,此脚本可以执行 GET
和 POST
请求,以了解如何与人脸 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
。
当请求成功执行时,将检测到的人脸作为 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)
对于每个检测到的人脸,绘制矩形检测框以及左上角和右下角的点:
我们也可以直接在服务器端在图像中绘制检测框,然后将结果图像返回(相关讲解可以在《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
人脸检测应用程序,同时在项目中我们处理了来自浏览器的不同请求方式(例如 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端的部署