人工智能出现至今已60余年,近几年深度学习全面爆发推动其走向一个更为兴盛的阶段。尤其是2016年谷歌的AIphaGo横扫棋坛,让人工智能在普罗大众中掀起一波关注热潮。时至今日,人工智能依然是最热门的研究领域之一。众多巨头企业、初创企业等纷纷入局人工智能领域,尝试寻找全新突破口。而人工智能想要改变社会,不能仅停留于理论和概念层面,更重要的是要实现商业化、场景化落地。
目前,很多科技企业均提供基于AI的开放平台,以Web的形式对外提供AI服务,例如人脸识别、OCR识别、语音识别等。以人脸检测为例,用户通过特定的api接口上传需要检测的照片,然后由Web服务器对照片进行人脸检测,并将检测结果返回给用户。实际的AI算法往往需要复杂的配置、较高的服务器性能才能进行算法推演,采用Web部署这种方式使得开发人员只需要配置和维护服务器环境即可,不需要再关注用户PC的配置和性能。另外,AI算法的更新也只需要在服务器上修改即可,使用Web架构适合生产环境下AI产品的快速部署和更新。
考虑降低读者的学习难度,本篇只使用OpenCV提供的现成的人脸检测算法来进行检测,实际情况下可以根据服务器的CPU或GPU性能,采用更高级的人脸检测算法来提高检测精度,例如可以采用基于深度学习的MTCNN人脸检测算法等。本篇旨在为读者提供一个结合人工智能的Web开发方向,有兴趣转向人工智能方向的读者可以从这篇内容出发,学习基本的AI算法推理和部署技巧,而更复杂的人脸识别或者AI算法研发则需要读者参考其他人工智能书籍或论文。
01、人脸识别后台搭建
本篇采用OpenCV提供的人脸检测算法来搭建人脸检测后台。OpenCV是一个开放源代码的计算机视觉应用库,由英特尔企业下属研发中心俄罗斯团队发起该项目,开源免费,设计目标是实现实时高效的计算机视觉任务,是一个跨平台的计算机视觉库。从开发之日起就得到了迅猛发展,获得了众多企业和业界学者的鼎力支持与贡献。因为是BSD开源,因此可以免费应用在科研和商业应用领域。OpenCV中多数模块是基于C++实现,其中有少部分是基于C语言实现,算法经过高度优化,实现效率高,非常适合生产环境使用。当前OpenCV提供的SDK已经支持C++、Java、Python等语言的应用开发。
首先,下载并安装用于Python的OpenCV开发包:
pip install opencv_python
在国内在线安装上述Python包速度比较慢,安装往往不能成功。读者可以采用离线安装的方式,将上述安装包放置在某个路径下,然后采用下述形式命令安装:
pip install 路径\包名.whl
接下来开发后台视图处理函数。由于需要开发基于api的接口,因此大部分功能代码都会在views.py文件中编写。当后台收到用户的请求以后,由指定的视图函数对用户上传的图片进行读取,然后调用OpenCV人脸检测算法进行检测,最后将检测结果以JSON字符串形式返回给用户。
打开serviceApp应用下的views.py文件,在头部导入一些Python库:
import numpy as np # 矩阵运算 import urllib # url解析 import json # json字符串使用 import cv2 # opencv包 import os # 执行操作系统命令 from django.views.decorators.csrf import csrf_exempt # 跨站点验证 from django.http import JsonResponse # json字符串响应
为了能够进行人脸检测,需要使用特定的人脸检测器,一般情况下需要运用机器学习算法进行训练得到,而本篇使用的OpenCV库自带高效的人脸检测器,无需再训练直接拿来使用即可。在OpenCV的安装目录中找到haarcascade_frontalface_default.xml文件(路径:Python安装目录+\Lib\site-packages\cv2\data)。该xml文件本质上是一个配置文件,用于保存训练好的人脸特征检测器模型参数,使用时只需要导入该文件即可进行人脸检测。为了方便项目使用,将该xml文件放置在项目serviceApp应用目录下。
继续编辑views.py文件,添加facedetect函数如下:
face_detector_path = "serviceApp\\haarcascade_frontalface_default.xml" face_detector = cv2.CascadeClassifier(face_detector_path) # 生成人脸检测器 @csrf_exempt # 用于规避跨站点请求攻击 def facedetect(request): result = {} if request.method == "POST": # 规定客户端使用POST上传图片 if request.FILES.get("image", None) is not None: # 读取图像 img = read_image(stream=request.FILES["image"]) else: result.update({ "#faceNum": -1, }) return JsonResponse(result) if img.shape[2] == 3: img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 彩色图像转灰度 #进行人脸检测 values = face_detector.detectMultiScale(img, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30), flags=cv2.CASCADE_SCALE_IMAGE) # 将检测得到的人脸检测关键点坐标封装 values = [(int(a), int(b), int(a + c), int(b + d)) for (a, b, c, d) in values] result.update({ "#faceNum": len(values), "faces": values, }) return JsonResponse(result)
●人脸检测配置文件导入:首先在外部引入配置文件路径,将路径存入变量face_detector_path中;然后使用cv2.CascadeClassifier创建人脸检测器face_detector,人脸检测器的创建需要通过传入人脸检测配置文件face_detector_path来实现;
●人脸检测视图处理函数:首先在函数前导入@csrf_exempt装饰器,否则无法使用该api接口。result变量用于存放最终的返回结果。api接口规定客户端必须采用POST方式进行信息提交,视图处理函数从请求request的FILES中获取图像数据;
●读取图像方式:该开放平台将图像封装于request.FILES中,并且以键“image”来标示;然后采用自定义的read_image函数进行图像读取,该函数的定义和详细代码将在后面给出;读取的图像数据存放于临时变量img中;
●图像转化:由于OpenCV在检测人脸的过程中需要先将RGB彩色图像转化为灰度图像,因此使用OpenCV提供的cv2.cvtColor函数实现图像转化;
●人脸检测:使用人脸检测器并调用对应的detectMultiScale方法对图像执行人脸检测,检测结果values存放每个检测到的人脸框的坐标值;
●JSON封装和返回:由于每个人脸检测返回的结果以左上角横、纵坐标以及检测框长、宽返回,为了能够方便后期绘图,将检测结果转换为左上角和右下角坐标;最终的检测结果封装成JSON字符串并使用JsonResponse函数返回结果;
下面给出自定义的图像读取函数read_image:
def read_image(stream=None): if stream is not None: data_temp = stream.read() img = np.asarray(bytearray(data_temp), dtype="uint8") img = cv2.imdecode(img, cv2.IMREAD_COLOR) return img
read_image函数用于实现基于数据流的图像读取,默认上传图像为彩色图像,以二进制方式进行读取,然后通过cv2.imdecode函数解码为OpenCV的图像数据。
至此已在后端完成人脸检测视图处理函数,为了能够使用该处理函数执行人脸检测,需要为该函数定义对应的映射路由。打开serviceApp应用下的urls.py文件,在urlpatterns字段中添加路由:
path('facedetect/', views.facedetect, name='facedetect'), # 人脸检测api
保存所有修改后运行项目。至此,人脸识别后台服务已经成功开启,下一小节将阐述如何从本地执行Python脚本来调用该api接口。
02、本地脚本测试
本部分通过本地Python脚本调用,实现基于Web Api的人脸检测功能。为了能够在本地使用Python发送HTTP请求,需要下载requests库并进行安装:
pip install requests
这里为了项目集成方便将本地调用脚本放置在项目根目录下的test文件夹中,命名为faceDetectDemo.py,然后在文件同目录下放置一张测试图片face.jpg,用于进行人脸检测。编辑faceDetectDemo.py文件,添加代码如下:
import cv2, requests url = "http://localhost:8000/serviceApp/facedetect/" # 上传图像并检测 tracker = None imgPath = "face.jpg" #图像路径 files = { "image": ("filename2", open(imgPath, "rb"), "image/jpeg"), } req = requests.post(url, data=tracker, files=files).json() print("获取信息: {}".format(req)) # 将检测结果框显示在图像上 img = cv2.imread(imgPath) for (w, x, y, z) in req["faces"]: cv2.rectangle(img, (w, x), (y, z), (0, 255, 0), 2) cv2.imshow("face detection", img) cv2.waitKey(0)
●人脸检测api接口:开发阶段的后台服务器开启在http://localhost:8000,然后再根据url定义规则加上应用名serviceApp和对应的接口名facedetect即为最终的api接口;
●发送数据:首先根据本地图像路径使用open函数读取图片内容并封装在files变量中,然后使用requests.post函数发送请求;返回的请求数据转化为JSON字符串,通过print函数打印查看字符串信息;
●结果显示:为了显式的查看人脸检测效果,可以将获取到的人脸检测框输出在原图上。这里通过读取每一个人脸检测框然后使用OpenCV的rectangle函数在原图上绘制矩形框实现;
最终检测效果如图1所示。
在开发人脸识别开放平台时,有一点需要特别注意,由于每一个响应都需要主服务器进行人脸检测操作,而本身人脸检测算法相对其他类型的常规操作会更加耗时并且耗资源,当并发访问数过大时会导致网站奔溃,这也就是通常所说的访问量过载。访问量过载包含两个方面:一是超负荷访问,即后台主机性能有限,无法抗住过大的访问量;二是网站代码存在性能问题,将系统拖慢,导致网站服务奔溃。针对上述问题,通常有两种修复手段:
1)为了快速访问网站,让用户迅速正常访问,最有效的手段就是限制访问。比如限制访问的频率,这个调整应该是动态的。这样做可以确保服务的可用性,但也会牺牲部分用户的访问。正常情况下,服务器能支撑多大的访问量是需要技术人员在系统业务上线之前做好测试,提前做好数据支撑的;
2)在时间允许的情况下,如果后端服务具有扩容条件,则可以对奔溃期间的访问数据进行分析,然后根据分析结果进行扩容服务,再逐步开放访问限制;
本篇开发的人脸识别开放平台重点在于引导大家熟悉搭建人工智能Web接口的基本步骤,在实际的操作过程中需要结合服务器性能、用户群体、算法效率等综合考虑和设计Web架构,并且在上线前需要进行抗压测试,有感兴趣的小伙伴们可以自行学习相关知识。
03、前端说明页面
本部分将完善人脸识别开放平台的前端页面,主要为用户提供一个说明页面用于向用户展示如何使用开放平台接口。首先在serviceApp应用的templates文件夹中新建文件platForm.html,该文件头尾部分与docList.html文件基本一致,只需要修改对应的页面名称即可。
页面主体部分以说明文字为主,采用常规的HTML标签进行编写,主要对人脸识别开放平台的接口基本信息进行阐述,同时给出了基于Python的接口调用示例。可以看到,在演示代码部分,实现了适合Python格式的语法高亮功能,可以让用户方便的进行代码浏览和拷贝。为了实现该功能,这里通过集成CodeMirror插件来开发。CodeMirror是一款十分强大的代码编辑插件,提供了非常丰富的API,其核心基于JavaScript,可以实时在线代码高亮显示,值得指出的是该插件并不是某个富文本编辑器的附属产品,它本质上是一个在线代码编辑器的基础库。
本部分需要掌握在页面中高亮显示代码的方法。首先下载CodeMirror插件包,下载地址:https://codemirror.net/。插件包中包含各种代码的使用案例,由于本篇主要介绍基于Python语言的接口调用,因此介绍Python部分。用浏览器打开index.html文件,然后单击Python语言对应的示例即可跳转到Python示例页面,通过查看该页面源码即可进行开发。实际使用时只需要导入必要的js和css文件即可。
接下来进入具体的开发环节。首先需要从CodeMirror插件包中引入必要的js和css文件:
<link rel="stylesheet" href="{% static 'css/codemirror.css' %}"> <script src="{% static 'js/codemirror.js' %}"></script> <script src="{% static 'js/python.js' %}"></script> <style type="text/css"> .CodeMirror { border-top: 1px solid black; border-bottom: 1px solid black; } </style>
其中codemirror.css、codemirror.js和python.js文件可以从CodeMirror插件包中找到,将其拷贝到hengDaProject项目static文件夹下的css和js子文件夹中即可实现调用。
使用时通过HTML标签实现代码的显示和编辑:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22%3Cdiv%3E%3Ctextarea%20id%3D%5C%22code%5C%22%20name%3D%5C%22code%5C%22%3E%5Cn%20%20%20%20%E5%9C%A8%E6%AD%A4%E5%A4%84%E5%86%99%E5%85%A5Python%E4%BB%A3%E7%A0%81%20%5Cn%3C%2Ftextarea%3E%3C%2Fdiv%3E%22%2C%22heightLimit%22%3Atrue%2C%22margin%22%3Atrue%2C%22id%22%3A%22WnJvb%22%7D"></div><div> 最后,添加js代码:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22%3Cscript%3E%5Cn%20%20%20%20var%20editor%20%3D%20CodeMirror.fromTextArea(document.getElementById(%5C%22code%5C%22)%2C%20%7B%5Cn%20%20%20%20%20%20%20%20mode%3A%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20name%3A%20%5C%22python%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20version%3A%203%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20singleLineStringErrors%3A%20false%5Cn%20%20%20%20%20%20%20%20%7D%2C%5Cn%20%20%20%20%20%20%20%20lineNumbers%3A%20true%2C%5Cn%20%20%20%20%20%20%20%20indentUnit%3A%204%2C%5Cn%20%20%20%20%20%20%20%20tabMode%3A%20%5C%22shift%5C%22%2C%5Cn%20%20%20%20%20%20%20%20matchBrackets%3A%20true%5Cn%20%20%20%20%7D)%3B%5Cn%3C%2Fscript%3E%22%2C%22heightLimit%22%3Atrue%2C%22margin%22%3Atrue%2C%22id%22%3A%22WFnIa%22%7D"></div><div> 通过上述简单配置,可以使得页面显示的Python代码能够有效的进行高亮显示,方便用户浏览,效果如图2所示。</div><div><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F264424982fd345769b7da2a6b2fe98a2.png%22%2C%22originWidth%22%3A1468%2C%22originHeight%22%3A733%2C%22name%22%3A%22image.png%22%2C%22size%22%3A228269%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A734%2C%22height%22%3A367%7D"></span></div><div><br /></div>