(二) 使用 Paddle Inference進(jìn)行口罩檢測(cè)

近期百度開源了口罩模型,包括端上和服務(wù)器上的蚕脏,目前已經(jīng)在北京地鐵等實(shí)際上線具钥。我們這里寫一篇教程,教大家如何使用Paddle Inference 部署該口罩模型驯用。

主要內(nèi)容

  • 準(zhǔn)備環(huán)境
  • 使用Paddle Python API進(jìn)行預(yù)測(cè)
  • 構(gòu)建人臉檢測(cè)模型
  • 構(gòu)建人臉口罩分類模型
  • 結(jié)果可視化
  • 總結(jié)

NOTE: 如果大家已經(jīng)有Paddle1.7.0的python環(huán)境脸秽,可通過鏈接 下載整個(gè)程序,解壓后即可運(yùn)行python cam_video.py實(shí)時(shí)進(jìn)行人臉口罩檢測(cè)蝴乔。(此程序可以在已安裝Paddle1.7.0的Mac os记餐, Linux,Windows 的Server服務(wù)器以及NV Jetson 硬件上運(yùn)行)

一 :準(zhǔn)備環(huán)境

1) 準(zhǔn)備python環(huán)境

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)準(zhǔn)備Paddle環(huán)境

Mac,Windows疑务,Linux可以通過https://www.paddlepaddle.org.cn/ 中的提示進(jìn)行pip 安裝(需要至少1.7的Paddle安裝)葬毫,NV Jetson上可以通過此鏈接下載已有的whl包然后pip安裝。

# 可選:創(chuàng)建Python虛擬環(huán)境雕沿,避免對(duì)全局的環(huán)境造成影響。
virtualenv pd_env  --python=python3.6
# 進(jìn)入Python虛擬環(huán)境
source pd_env/bin/activate

# 安裝1.7 Paddle
# 這一步會(huì)同步安裝numpy猴仑,scipy审轮,cv2等依賴,會(huì)消耗較長(zhǎng)的時(shí)間,請(qǐng)耐心等待疾渣。
# 如果為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中運(yùn)行python榴捡, 通過import paddle 測(cè)試是否安裝成功杈女。

3)準(zhǔn)備usb攝像頭
如果想使用攝像頭實(shí)時(shí)獲取視頻流并進(jìn)行口罩檢測(cè)的話,準(zhǔn)備一個(gè)usb攝像頭吊圾,并插入到自己的機(jī)器上达椰。

二:使用Paddle Inference Python API預(yù)測(cè)。

這個(gè)部分主要通過一段簡(jiǎn)單的程序介紹如何使用Paddle Inference Python API進(jìn)行模型預(yù)測(cè)项乒。
整個(gè)預(yù)測(cè)流程主要包含以下幾個(gè)部分:
1) 創(chuàng)建AnalysisConfig并進(jìn)行配置砰碴,包括設(shè)置模型的路徑,是否開啟gpu板丽, 是否開始顯存優(yōu)化等呈枉。
2) 根據(jù)AnalysisConfig創(chuàng)建predictor。
3) 獲取模型的所有輸入tensor埃碱,并將數(shù)據(jù)設(shè)置到tensor中猖辫。
4) 模型運(yùn)行。
5) 獲取模型的所有輸出tensor砚殿,并將tensor中的數(shù)值獲取保存

代碼如下:

# -*- 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對(duì)配置預(yù)測(cè)引擎啃憎。
     config = AnalysisConfig(model_file, params_file)
     config.switch_use_feed_fetch_ops(False)
     config.switch_specify_input_names(True)
     config.enable_memory_optim() # 打開顯存優(yōu)化
     if use_gpu:
       print ("ENABLE_GPU")
       # 設(shè)置使用GPU
       config.enable_use_gpu(100, device_id)
     # 創(chuàng)建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):  # 對(duì)每個(gè)輸入進(jìn)行設(shè)置
       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() # 預(yù)測(cè)

     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

三:構(gòu)建人臉檢測(cè)模型

在整個(gè)口罩檢測(cè)流任務(wù)中,首要做的一件事是對(duì)圖像中的人臉進(jìn)行檢測(cè)似炎。上一節(jié)中我們通過Paddle Inference Python API構(gòu)建了模型預(yù)測(cè)模塊辛萍,在這一節(jié)中,我們將用該模塊來構(gòu)建人臉檢測(cè)模型羡藐。

  1. 下載人臉檢測(cè)預(yù)測(cè)模型并解壓:
$ wget https://paddle-inference-dist.cdn.bcebos.com/temp_data/nano_mask_detection/pyramidbox_lite.zip
  1. 創(chuàng)建人臉檢測(cè)的Model對(duì)象贩毕,其中model 文件表示模型結(jié)構(gòu)文件,params表示模型參數(shù)文件仆嗦。
face_detector = Model('./pyramidbox_lite/model', './pyramidbox_lite/params')
  1. 人臉檢測(cè)圖像預(yù)處理
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. 讀取圖片并進(jìn)行人臉檢測(cè)模型預(yù)測(cè)辉阶。
    下載人臉圖片, 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軸)瘩扼, 長(zhǎng)1682(x軸)的圖片
# (1050, 1682, 3)
img_h, img_w, c = img
# 人臉檢測(cè)圖像預(yù)處理
img = face_detect_preprocess(ori_img, 0.3) # 0.3表示將讀取的圖片長(zhǎng)谆甜,寬的大小縮小至原來的0.3倍。
# 運(yùn)行人臉檢測(cè)集绰, result中存放檢測(cè)的結(jié)果
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 ]]

可以看到规辱,該模型的輸出結(jié)果共有四行,每一行的第二個(gè)數(shù)字代表人臉的置信度栽燕,后四個(gè)數(shù)字表示人臉區(qū)域左上角以及右下角的位置信息罕袋。

我們拿輸出結(jié)果的第一行來詳細(xì)說明下每個(gè)數(shù)字的含義:
第一行結(jié)果:[1. 0.99998796 0.27059144 0.18095288 0.39210612 0.43089798] :
0.99998796表示該區(qū)域是人臉的置信度改淑。
0.27059144表示該區(qū)域在原圖中左上角的x軸信息,x軸的坐標(biāo)可以通過 (0.27059144 * 1682) 得到炫贤。
0.18095288 表示該區(qū)域在原圖中左上角的y軸信息,y軸的坐標(biāo)可以通過 (0.18095288 * 1050) 得到付秕。
右下角的信息也可以通過類似上述方式求得兰珍。

  1. 獲取人臉的坐標(biāo)信息
    通過上一節(jié)我們知道,模型輸出的坐標(biāo)信息是一個(gè)小數(shù)询吴,我們通過以下代碼來獲取人臉在原圖中坐標(biāo)的真實(shí)值掠河。
# 該函數(shù)的將檢測(cè)模型的輸出,原圖的高度以及長(zhǎng)度信息作為輸入猛计,輸出所有人臉的左上角坐標(biāo)以及人臉區(qū)域的高度唠摹,長(zhǎng)度信息。
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]] 

四:構(gòu)建人臉口罩分類模型

  1. 下載口罩人臉檢測(cè)預(yù)測(cè)模型并解壓:
$ wget  https://paddle-inference-dist.cdn.bcebos.com/temp_data/nano_mask_detection/mask_detector.zip
  1. 根據(jù)第二節(jié)中的描述創(chuàng)建口罩分類的Model對(duì)象奉瘤。
mask_classify = Model('./mask_detector/model', './mask_detector/params')
  1. 口罩分類模型圖像預(yù)處理
    同樣勾拉,進(jìn)行口罩分類模型預(yù)測(cè)前,需要對(duì)人臉的圖片進(jìn)行預(yù)處理盗温。
import cv2
import numpy as np
from PIL import Image
import math

# img 為原始圖片藕赞,pts為單個(gè)人臉的坐標(biāo)信息(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函數(shù)會(huì)微調(diào)人臉的框
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. 人臉口罩分類

在人臉檢測(cè)章節(jié)中卖局,我們進(jìn)行了人臉檢測(cè)斧蜕,通過模型輸出結(jié)果了解到,模型檢測(cè)到了三張人臉砚偶,我們并通過get_faces 函數(shù)獲取到了人臉的真實(shí)坐標(biāo)信息并保存在faces變量中批销。在這一部分中,我們將使用口罩分類模型染坯, 對(duì)這些人臉是否戴有口罩進(jìn)行分析判斷均芽。

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)

   # 人臉預(yù)處理,并將人臉大小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]]

我們遍歷每一張人臉的坐標(biāo)骡技,并從原圖中將人臉截取出來,經(jīng)過圖像預(yù)處理后并通過口罩分類模型獲取到該人臉是否帶有口罩的置信度(置信度的值越大說明戴口罩的概率越大)

五: 結(jié)果可視化

到這一步羞反,我們已經(jīng)完成了對(duì)圖片中的人臉進(jìn)行檢測(cè)布朦,并對(duì)每個(gè)人臉進(jìn)行是否戴有口罩的判斷,接下來我們將檢測(cè)的結(jié)果可視化看一下效果昼窗。

from PIL import Image
from PIL import ImageDraw, ImageFont

# 傳入原始圖片是趴,人臉的坐標(biāo)位置以及戴有口罩置信度信息, 該函數(shù)會(huì)將戴口罩人臉畫上藍(lán)框澄惊,不戴口罩的人臉畫上紅框唆途。
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)

運(yùn)行上述程序會(huì)在桌面上彈出帶有畫框的圖片富雅。

口罩檢測(cè)結(jié)果展示

六: 總結(jié)

在這一章節(jié)中,我們介紹了如何使用Paddle Inference Python api進(jìn)行預(yù)測(cè)模型預(yù)測(cè)肛搬,并且通過代碼一步步的展示了如何在自己的機(jī)器上搭建自己的人臉口罩檢測(cè)任務(wù)没佑。 除此之外,我們還提供了整個(gè)代碼服務(wù)温赔,大家可以通過以下的鏈接下載:https://paddle-inference-dist.cdn.bcebos.com/temp_data/nano_mask_detection/nano_face_detection.tar

解壓后運(yùn)行python cam_video.py, 程序會(huì)從攝像頭讀取圖像蛤奢,然后實(shí)時(shí)進(jìn)行人臉口罩檢測(cè)。

講到這里陶贼,大家有沒有感覺部署AI應(yīng)用也是非常簡(jiǎn)單的呢啤贩,那還等什么,趕緊在自己的機(jī)器上測(cè)試下吧拜秧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末痹屹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子枉氮,更是在濱河造成了極大的恐慌志衍,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件聊替,死亡現(xiàn)場(chǎng)離奇詭異足画,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)佃牛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門淹辞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人俘侠,你說我怎么就攤上這事象缀。” “怎么了爷速?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵央星,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我惫东,道長(zhǎng)莉给,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任廉沮,我火速辦了婚禮颓遏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘滞时。我一直安慰自己叁幢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布坪稽。 她就那樣靜靜地躺著曼玩,像睡著了一般鳞骤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上黍判,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天豫尽,我揣著相機(jī)與錄音,去河邊找鬼顷帖。 笑死美旧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的窟她。 我是一名探鬼主播陈症,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蔼水,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼震糖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起趴腋,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤吊说,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后优炬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體颁井,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年蠢护,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雅宾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡葵硕,死狀恐怖眉抬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情懈凹,我是刑警寧澤蜀变,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站介评,受9級(jí)特大地震影響库北,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜们陆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一寒瓦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坪仇,春花似錦孵构、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜡镶。三九已至,卻和暖如春恤筛,著一層夾襖步出監(jiān)牢的瞬間官还,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工毒坛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留望伦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓煎殷,卻偏偏與公主長(zhǎng)得像屯伞,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子豪直,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354