(二) 使用 Paddle Inference进行口罩检测

近期百度开源了口罩模型,包括端上和服务器上的,目前已经在北京地铁等实际上线。我们这里写一篇教程,教大家如何使用Paddle Inference 部署该口罩模型。

主要内容

  • 准备环境
  • 使用Paddle Python API进行预测
  • 构建人脸检测模型
  • 构建人脸口罩分类模型
  • 结果可视化
  • 总结

NOTE: 如果大家已经有Paddle1.7.0的python环境,可通过链接 下载整个程序,解压后即可运行python cam_video.py实时进行人脸口罩检测。(此程序可以在已安装Paddle1.7.0的Mac os, Linux,Windows 的Server服务器以及NV Jetson 硬件上运行)

一 :准备环境

1) 准备python环境

sudo apt-get install python3.6-dev liblapack-dev  gfortran libfreetype6-dev libpng-dev libjpeg-dev zlib1g-dev patchelf
sudo apt-get install python3-pip
sudo apt-get install python3-opencv
pip install virtualenv -i https://mirror.baidu.com/pypi/simple

2)准备Paddle环境

Mac,Windows,Linux可以通过https://www.paddlepaddle.org.cn/ 中的提示进行pip 安装(需要至少1.7的Paddle安装),NV Jetson上可以通过此链接下载已有的whl包然后pip安装。

# 可选:创建Python虚拟环境,避免对全局的环境造成影响。
virtualenv pd_env  --python=python3.6
# 进入Python虚拟环境
source pd_env/bin/activate

# 安装1.7 Paddle
# 这一步会同步安装numpy,scipy,cv2等依赖,会消耗较长的时间,请耐心等待。
# 如果为nv jetson 硬件,先wget https://paddle-inference-dist.cdn.bcebos.com/temp_data/paddlepaddle_gpu-0.0.0-cp36-cp36m-linux_aarch64.whl 获取whl包,
# 然后使用 pip install -U paddlepaddle_gpu-0.0.0-cp36-cp36m-linux_aarch64.whl 安装
python3 -m pip install paddlepaddle-gpu==1.7.1.post107 -i https://mirror.baidu.com/pypi/simple

安装后可以shell中运行python, 通过import paddle 测试是否安装成功。

3)准备usb摄像头
如果想使用摄像头实时获取视频流并进行口罩检测的话,准备一个usb摄像头,并插入到自己的机器上。

二:使用Paddle Inference Python API预测。

这个部分主要通过一段简单的程序介绍如何使用Paddle Inference Python API进行模型预测。
整个预测流程主要包含以下几个部分:
1) 创建AnalysisConfig并进行配置,包括设置模型的路径,是否开启gpu, 是否开始显存优化等。
2) 根据AnalysisConfig创建predictor。
3) 获取模型的所有输入tensor,并将数据设置到tensor中。
4) 模型运行。
5) 获取模型的所有输出tensor,并将tensor中的数值获取保存

代码如下:

# -*- coding: UTF-8 -*-

import numpy as np
from paddle.fluid.core import AnalysisConfig
from paddle.fluid.core import create_paddle_predictor

class Model:
   def __init__(self, model_file, params_file, use_mkldnn=True, use_gpu = False, device_id = 0):
     # 通过AnalysisConfig对配置预测引擎。
     config = AnalysisConfig(model_file, params_file)
     config.switch_use_feed_fetch_ops(False)
     config.switch_specify_input_names(True)
     config.enable_memory_optim() # 打开显存优化
     if use_gpu:
       print ("ENABLE_GPU")
       # 设置使用GPU
       config.enable_use_gpu(100, device_id)
     # 创建predictor
     self.predictor = create_paddle_predictor(config)

   def run(self, img_list):
     input_names = self.predictor.get_input_names() # 获取输入tensor的name
     for i, name in enumerate(input_names):  # 对每个输入进行设置
       input_tensor = self.predictor.get_input_tensor(input_names[i])
       input_tensor.reshape(img_list[i].shape)   
       input_tensor.copy_from_cpu(img_list[i].copy())

     self.predictor.zero_copy_run() # 预测

     results = []
     output_names = self.predictor.get_output_names()
     # 获取所有的输出并放入results中
     for i, name in enumerate(output_names):
       output_tensor = self.predictor.get_output_tensor(output_names[i])
       output_data = output_tensor.copy_to_cpu()
       results.append(output_data)
     return results

三:构建人脸检测模型

在整个口罩检测流任务中,首要做的一件事是对图像中的人脸进行检测。上一节中我们通过Paddle Inference Python API构建了模型预测模块,在这一节中,我们将用该模块来构建人脸检测模型。

  1. 下载人脸检测预测模型并解压:
$ wget https://paddle-inference-dist.cdn.bcebos.com/temp_data/nano_mask_detection/pyramidbox_lite.zip
  1. 创建人脸检测的Model对象,其中model 文件表示模型结构文件,params表示模型参数文件。
face_detector = Model('./pyramidbox_lite/model', './pyramidbox_lite/params')
  1. 人脸检测图像预处理
import cv2
import numpy as np
from PIL import Image
import math

def face_detect_preprocess(img, shrink=1.0):
   # BGR  
   img_shape = img.shape
   img = cv2.resize(img, (int(img_shape[1] * shrink), int(img_shape[0] * shrink)), interpolation=cv2.INTER_CUBIC)
   # HWC -> CHW
   img = np.swapaxes(img, 1, 2)
   img = np.swapaxes(img, 1, 0)
   # RBG to BGR
   mean = [104., 117., 123.]
   scale = 0.007843
   img = img.astype('float32')
   img -= np.array(mean)[:, np.newaxis, np.newaxis].astype('float32')
   img = img * scale
   img = img[np.newaxis, :]
   return img
  1. 读取图片并进行人脸检测模型预测。
    下载人脸图片, wget https://paddle-inference-dist.cdn.bcebos.com/temp_data/nano_mask_detection/test_mask_detection.jpg
    人脸图片
# 读取图片
ori_img = cv2.imread('test_mask_detection.jpg')
print (ori_img.shape) #  该图片为高1050(y轴), 长1682(x轴)的图片
# (1050, 1682, 3)
img_h, img_w, c = img
# 人脸检测图像预处理
img = face_detect_preprocess(ori_img, 0.3) # 0.3表示将读取的图片长,宽的大小缩小至原来的0.3倍。
# 运行人脸检测, result中存放检测的结果
result = face_detector.run([img])

print (result[0])
# [[1.         0.99998796 0.27059144 0.18095288 0.39210612 0.43089798]
# [1.         0.99998593 0.5600477  0.32936218 0.66750807 0.55742574]
# [1.         0.9999182  0.693954   0.28346226 0.7878771  0.47385922]
# [1.         0.01414413 0.89871734 0.6311271  0.96674687 0.9164002 ]]

可以看到,该模型的输出结果共有四行,每一行的第二个数字代表人脸的置信度,后四个数字表示人脸区域左上角以及右下角的位置信息。

我们拿输出结果的第一行来详细说明下每个数字的含义:
第一行结果:[1. 0.99998796 0.27059144 0.18095288 0.39210612 0.43089798] :
0.99998796表示该区域是人脸的置信度。
0.27059144表示该区域在原图中左上角的x轴信息,x轴的坐标可以通过 (0.27059144 * 1682) 得到。
0.18095288 表示该区域在原图中左上角的y轴信息,y轴的坐标可以通过 (0.18095288 * 1050) 得到。
右下角的信息也可以通过类似上述方式求得。

  1. 获取人脸的坐标信息
    通过上一节我们知道,模型输出的坐标信息是一个小数,我们通过以下代码来获取人脸在原图中坐标的真实值。
# 该函数的将检测模型的输出,原图的高度以及长度信息作为输入,输出所有人脸的左上角坐标以及人脸区域的高度,长度信息。
def get_faces(data, h, w):
  faces_loc = []
  for d in data:
    if d[1] >= 0.5:
      x_min = max(d[2] * w, 0)
      y_min = max(d[3] * h, 0)
      x_h = min((d[4] - d[2]) * w, w)
      y_w = min((d[5] - d[3]) * h, h)
      faces_loc.append([int(x_min), int(y_min), int(x_h), int(y_w)])
  return faces_loc

faces = get_faces(result[0], img_h, img_w)
print (faces) 
# [[455, 190, 204, 262], [942, 345, 180, 239], [1167, 297, 157, 199]] 

四:构建人脸口罩分类模型

  1. 下载口罩人脸检测预测模型并解压:
$ wget  https://paddle-inference-dist.cdn.bcebos.com/temp_data/nano_mask_detection/mask_detector.zip
  1. 根据第二节中的描述创建口罩分类的Model对象。
mask_classify = Model('./mask_detector/model', './mask_detector/params')
  1. 口罩分类模型图像预处理
    同样,进行口罩分类模型预测前,需要对人脸的图片进行预处理。
import cv2
import numpy as np
from PIL import Image
import math

# img 为原始图片,pts为单个人脸的坐标信息(x1, y1, x2, y1, x1, y2, x2, y2)。
def mask_classify_preprocess(img, pts):
   # BGR  
   img_face, _ = crop(img, pts)
   t_img_face = img_face.copy()
   img_face = img_face / 256.
   # HWC -> CHW
   img_face = np.swapaxes(img_face, 1, 2)
   img_face = np.swapaxes(img_face, 1, 0)
   # RBG to BGR
   mean = [0.5, 0.5, 0.5]
   img_face = img_face.astype('float32')
   img_face -= np.array(mean)[:, np.newaxis, np.newaxis].astype('float32')
   img_face = img_face.reshape(-1, 3, 128, 128) 
   return img_face, t_img_face

# crop函数会微调人脸的框
def crop(image, pts, shift=0, scale=1.5, rotate=0, res_width=128, res_height=128):
    res = (res_width, res_height)
    idx1 = 0
    idx2 = 1
    # angle
    alpha = 0
    if pts[idx2, 0] != -1 and pts[idx2, 1] != -1 and pts[idx1, 0] != -1 and pts[idx1, 1] != -1:
        alpha = math.atan2(pts[idx2, 1] - pts[idx1, 1], pts[idx2, 0] - pts[idx1, 0]) * 180 / math.pi
    pts[pts == -1] = np.inf
    coord_min = np.min(pts, 0)
    pts[pts == np.inf] = -1
    coord_max = np.max(pts, 0)
    # coordinates of center point
    c = np.array([coord_max[0] - (coord_max[0] - coord_min[0]) / 2, 
        coord_max[1] - (coord_max[1] - coord_min[1]) / 2])  # center
    max_wh = max((coord_max[0] - coord_min[0]) / 2, (coord_max[1] - coord_min[1]) / 2)
    # Shift the center point, rot add eyes angle
    c = c + shift * max_wh
    rotate = rotate + alpha
    M = cv2.getRotationMatrix2D((c[0], c[1]), rotate, res[0] / (2 * max_wh * scale))
    M[0, 2] = M[0, 2] - (c[0] - res[0] / 2.0)
    M[1, 2] = M[1, 2] - (c[1] - res[0] / 2.0)
    image_out = cv2.warpAffine(image, M, res)
    return image_out, M
  1. 人脸口罩分类

在人脸检测章节中,我们进行了人脸检测,通过模型输出结果了解到,模型检测到了三张人脸,我们并通过get_faces 函数获取到了人脸的真实坐标信息并保存在faces变量中。在这一部分中,我们将使用口罩分类模型, 对这些人脸是否戴有口罩进行分析判断。

faces_with_mask_conf = []
# 遍历所有的人脸
for loc in faces:
   pts = np.array([loc[0], loc[1], loc[2] + loc[0], loc[1], loc[0], loc[1] + loc[3], loc[2] + loc[0], loc[1] + loc[3]]).reshape(4, 2).astype(np.float32)

   # 人脸预处理,并将人脸大小resize到(128,128)
   face_img_t, temp_face = mask_classify_preprocess(ori_img, pts)
   mask_results = mask_classify.run([face_img_t])
   mask_conf = mask_results[0][0][1] # 获取戴口罩的置信度
   temp_loc = loc
   temp_loc.append(mask_conf) 
   faces_with_mask_conf.append(temp_loc)

print (faces_with_mask_conf)
# [[455, 190, 204, 262, 0.993429], [942, 345, 180, 239, 0.9189134], [1167, 297, 157, 199, 0.28184703]]

我们遍历每一张人脸的坐标,并从原图中将人脸截取出来,经过图像预处理后并通过口罩分类模型获取到该人脸是否带有口罩的置信度(置信度的值越大说明戴口罩的概率越大)

五: 结果可视化

到这一步,我们已经完成了对图片中的人脸进行检测,并对每个人脸进行是否戴有口罩的判断,接下来我们将检测的结果可视化看一下效果。

from PIL import Image
from PIL import ImageDraw, ImageFont

# 传入原始图片,人脸的坐标位置以及戴有口罩置信度信息, 该函数会将戴口罩人脸画上蓝框,不戴口罩的人脸画上红框。
def draw_boxes(img, boxes):
   h, w, _ = img.shape
   image = Image.fromarray(img)
   draw = ImageDraw.Draw(image)
   for box in boxes:
     x_min = box[0] 
     y_min = box[1] 
     x_max = box[0] + box[2]
     y_max = box[1] + box[3]
     (left, right, top, bottom) = (x_min, x_max, y_min, y_max)
     color = "red"
     if box[4] < 0.6:
        color = "blue"
     draw.line(
          [(left - 10, top - 10), (left - 10, bottom + 10), (right + 10, bottom + 10), (right + 10, top - 10),
           (left - 10, top - 10)],
          width=10,
          fill=color)
     conf_text = str(box[4])
   img = np.asarray(image)
   return img
drawed_img = draw_boxes(ori_img, faces_with_mask_conf)
cv2.imshow("mask_faces", drawed_img)
cv2.waitKey(0)

运行上述程序会在桌面上弹出带有画框的图片。

口罩检测结果展示

六: 总结

在这一章节中,我们介绍了如何使用Paddle Inference Python api进行预测模型预测,并且通过代码一步步的展示了如何在自己的机器上搭建自己的人脸口罩检测任务。 除此之外,我们还提供了整个代码服务,大家可以通过以下的链接下载:https://paddle-inference-dist.cdn.bcebos.com/temp_data/nano_mask_detection/nano_face_detection.tar

解压后运行python cam_video.py, 程序会从摄像头读取图像,然后实时进行人脸口罩检测。

讲到这里,大家有没有感觉部署AI应用也是非常简单的呢,那还等什么,赶紧在自己的机器上测试下吧!

你可能感兴趣的:((二) 使用 Paddle Inference进行口罩检测)