天天看點

視覺AI五天訓練營 Day05 搭建多場景人臉口罩檢測系統(附效果動圖)

搭建多場景人臉口罩檢測系統-基于阿裡雲視覺智能平台

最後一次的課了,就不辣麼水了。這次的試驗目的是将使用者上傳的圖檔(例如在公共場合的監控照片)進行識别,傳回目前圖檔中的人數、(人)活體的可信度、佩戴口罩的人數以及其占比、未佩戴口罩的人物坐标。可以設定适當的閥值,當達到一定閥值(占比)時進行警告或者通知等處理。

第一步,阿裡雲控制台擷取accesskey

視覺AI五天訓練營 Day05 搭建多場景人臉口罩檢測系統(附效果動圖)

不管你調用任何的能力,基本都需要擷取accesskey。

第二步,去阿裡雲視覺開放平台檢視接口 文檔

  • 這次試驗一共使用了阿裡雲視覺開放平台的兩個能力,分别是: 人臉檢測定位 :傳回圖檔中人臉數量,人臉坐标;
使用OpenCV切割人臉(無阿裡雲相關接口,是以通過本地實作)

:通過人臉坐标進行裁剪圖檔,以便下一步檢測口罩;

人臉口罩識别

:識别輸入圖檔中的人臉是否有戴口罩。

  • 預計效果:

    輸出圖檔中的人臉數以及可信度,輸出未戴口罩的人數以及可信度,未戴口罩的人數達到一定占比對使用者進行通知。

第三步,編寫代碼(進階CV工程師又上線了)

1.導入需要使用的子產品,以及阿裡雲視覺相關的子產品

import os
import json
import time
from urllib import request
import numpy as np
import cv2

from viapi.fileutils import FileUtils
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ClientException
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkfacebody.request.v20191230.DetectFaceRequest import DetectFaceRequest
from aliyunsdkfacebody.request.v20191230.DetectMaskRequest import DetectMaskRequest           

2.出于安全以及子產品化的考慮,将阿裡雲的AccessKey和AccessSecret寫入配置檔案,需要的時候通過函數調用,代碼如下。

#擷取accesskeyId和AccessSecret
def get_access():
    with open(r'accesskey.conf', 'r') as f:
        KeyId, Secret = f.read().split()  #split切割
        return KeyId, Secret           

3.由于将圖檔交由阿裡雲視覺平台處理需要使用阿裡雲OSS,在阿裡雲OSS的文檔中有關于各大程式設計語言的上傳下載下傳等操作的sdk,但是出于懶惰的考慮,我決定使用阿裡雲視覺平台提供的臨時OSS,預設region就是上海的,而上傳圖檔隻需要幾行代碼就搞定了,簡直完美;由于在調用的時候需要區分是上傳的是本地檔案還是圖檔URL,是以我多加了一個參數‘bool’,用于友善上傳本地或者網絡的圖檔,具體代碼如下。

# 上傳圖檔到臨時OSS
def uploadImage_2_oss(accessKeyId, accessSecret, uploadUrl, bool):
    file_utils = FileUtils(accessKeyId, accessSecret)
    oss_url = file_utils.get_oss_url(uploadUrl,"jpg",bool)
    return (oss_url)
    # oss_url = file_utils.get_oss_url("/home/xxx.mp4","mp4",True)
    # print(oss_url)           

4.準備就緒,編寫代碼調用阿裡雲視覺平台的

能力,對通過上傳到臨時OSS的圖檔進行處理,并格式化傳回的結果,因為我們不需要一些無用的傳回結果,隻需要傳回FaceProbabilityList以及Credibility和FaceCoordinate,對于人臉坐标,其實後面還有的用處,傳回json資料以及Face_Number,具體代碼如下。

#人臉檢測并傳回人臉數量、可信度、人臉坐标
def Face_Number_Check(accessKeyId, accessSecret, oss_url):       
    client = AcsClient(accessKeyId, accessSecret, 'cn-shanghai')
    request_Face_Number = DetectFaceRequest()
    request_Face_Number.set_accept_format('json')
    request_Face_Number.set_ImageURL(oss_url)
    response_Face_Number = client.do_action_with_exception(request_Face_Number)
    response_Face_Number = str(response_Face_Number, encoding='utf-8')
    res_Face_Data_List = json.loads(response_Face_Number)
    Face_Number = len(res_Face_Data_List['Data']['FaceProbabilityList'])
    Face_Credibility = res_Face_Data_List['Data']['FaceProbabilityList']
    #傳回人臉矩形框,分别是[left, top, width, height]
    face_Coordinate = list_split(res_Face_Data_List['Data']['FaceRectangles'],4) 
    C_C_json = {'Coordinate':[], 'Credibility':[]}
    # class1_value.append ('檢測到圖像中的人臉數共有 %d 個' %Face_Number)
    for i in range(Face_Number):
        C_C_json['Coordinate'].append (face_Coordinate[i])
        C_C_json['Credibility'].append ('%.2f%%' %(Face_Credibility[i]*100))
        # print (face_Coordinate[i])
    return C_C_json, Face_Number           

5.由于上方拿到了圖檔中所有的FaceCoordinate,我們在格式化之前對清單進行分割,以便後續定位切割友善使用,代碼如下。

#定義一個數組分割函數,對應一張人臉四個坐标
def list_split(items, n):   
    return [items[i:i+n] for i in range(0, len(items), n)]           

6.接下來就要通過本地的處理将圖檔中的人臉切割出來,我使用的是cv2進行處理的,是以上方格式化的Coordinate就有了用處,切割出來之後臨時存入本地即刻上傳臨時OSS,并在完成後删除切割的圖檔,将上傳好的人臉圖檔連結寫入list,最後傳回一個OSS_IMG_List,友善需要的時候使用。代碼如下。

#圖像人臉分割
def Face_Mask_Recognition(oss_url,ccjson):
    oss_url_list = []
    for i in range(len(ccjson['Coordinate'])):
        x0 = ccjson['Coordinate'][i][0]
        y0 = ccjson['Coordinate'][i][1]
        x1 = ccjson['Coordinate'][i][2] + x0
        y1 = ccjson['Coordinate'][i][3] + y0

        resp = request.urlopen(oss_url)
        image = np.asarray(bytearray(resp.read()), dtype="uint8")
        image = cv2.imdecode(image, cv2.IMREAD_COLOR)
        cropped = image[y0:y1, x0:x1]
        path = "./images/" + str(i) + ".jpg"
        print ('一共%d張人臉圖檔,正在分割第%d張圖檔...' %(len(ccjson['Coordinate']), i+1))
        cv2.imwrite(path, cropped)

        #上傳至region為上海的臨時OSS并傳回連結
        uploadPath = path
        oss_url_list.append (uploadImage_2_oss(accessKeyId, accessSecret, uploadPath, True))
        #上傳完畢删除檔案
        os.remove(path)
    print ('分割完畢,準備進行口罩佩戴識别...')
    print ('-'*80+'\n')
    return oss_url_list           

7.有了圖檔中的人臉圖檔的OSS連結之後,通過調用阿裡雲視覺平台的

能力,對切割好的人臉進行處理識别,對處理的結果進行簡單的處理之後,便可以格式化輸出了。代碼如下。

#人臉口罩識别
def Mask_Detection(accessKeyId, accessSecret, oss_url_list):
    client = AcsClient(accessKeyId, accessSecret, 'cn-shanghai')
    request_Mask = DetectMaskRequest()
    request_Mask.set_accept_format('json')

    res_Mask_List = []
    for i in range(len(oss_url_list)):
        print ('正在識别圖像中第%d個人臉口罩佩戴情況...' %(i+1))
        request_Mask.set_ImageURL(oss_url_list[i])
        response_Mask = client.do_action_with_exception(request_Mask)
        response_Mask = str(response_Mask, encoding='utf-8')
        res_Mask = json.loads(response_Mask)['Data']['Mask']
        res_Mask_List.append (res_Mask)
        time.sleep(0.5)
    print ('識别完畢,準備格式化輸出結果...')
    print ('-'*80+'\n')
    return res_Mask_List           

8.由于對于口罩識别的API傳回的資料并不是我們想要的,沒有戴口罩傳回1,戴了傳回2,通過簡單的轉換之後,就可以顯示成百分百了,不過隻有100%和0%,不過我不尴尬,尴尬的是阿裡雲,誰叫它隻傳回1或者2,即戴了或者沒戴,代碼如下。

#轉換口罩識别結果
def numlist2str(islist):
    for x in range(len(islist)):
        if islist[x] == 1:
            islist[x] = '0%'
        else:
            islist[x] = '100%'
    return islist           

9.口說無憑,你說圖檔有幾個人臉就幾個人臉嗎?你說戴了口罩就戴了口罩?為了更直覺的表達,還是把原圖讀取出來,并對人臉進行圈圈,就是畫個框框,(本來想要做成戴了口罩的用綠框框,沒戴的用紅框框,但是由于我比較懶,加上沒有時間,以及代碼寫的太亂了,就不搞了,有興趣的小夥伴可以搞一下,不難的)

#讀取檢測的圖檔,并通過cv2對人臉進行标記,最後顯示出來
def showimg():
    #顯示檢測的圖檔
    resp = request.urlopen(oss_url)
    image = np.asarray(bytearray(resp.read()), dtype="uint8")
    image = cv2.imdecode(image, cv2.IMREAD_COLOR)

    # 輸入參數分别為圖像、左上角坐标、右下角坐标、顔色數組、粗細
    for x in range(len(ccjson['Coordinate'])):
        x0 = ccjson['Coordinate'][x][0]
        y0 = ccjson['Coordinate'][x][1]
        x1 = ccjson['Coordinate'][x][2] + x0
        y1 = ccjson['Coordinate'][x][3] + y0
        cv2.rectangle(image, (x0,y0), (x1,y1), (0,0,255), 2)

    cv2.namedWindow("image" , cv2.WINDOW_NORMAL)
    cv2.imshow('image', image)
    cv2.waitKey(0)           

10.好了,到最後一步了,寫個入口,調用下把子函數,輸出下需要的資料就好了。代碼如下。

if '__main__' == __name__:

    uploadUrl = input('輸入需要檢測人臉的圖檔連結(路徑)後回車:\n')
    accessKeyId, accessSecret = get_access()
    oss_url = uploadImage_2_oss(accessKeyId, accessSecret, uploadUrl, False)

    #分割符
    print ('-'*80+'\n')

    #人臉數量及坐标的結果
    ccjson, Face_Number = Face_Number_Check(accessKeyId, accessSecret, oss_url)
    # print (len(ccjson['Coordinate']))
    
    #分割圖檔中的人臉并傳回分割好的圖檔連結
    oss_url_list = Face_Mask_Recognition(oss_url,ccjson)


    #識别人臉是否佩戴口罩結果
    result_Mask = Mask_Detection(accessKeyId, accessSecret, oss_url_list)
    result_Mask = numlist2str(result_Mask)

    for x in range(len(ccjson['Coordinate'])):
        print ('檢測到第%d張人臉坐标為%s\t人臉機率為%s\t佩戴口罩機率為%s' %(x+1, ccjson['Coordinate'][x], ccjson['Credibility'][x], result_Mask[x]))


    #标記圖檔中人臉
    showimg()
           

11.經過簡單的圖檔處理和視覺平台能力的整合,我們來試驗一下效果。

首先來一張本地圖檔

視覺AI五天訓練營 Day05 搭建多場景人臉口罩檢測系統(附效果動圖)

接着我們進行試驗,動圖如下。

視覺AI五天訓練營 Day05 搭建多場景人臉口罩檢測系統(附效果動圖)

再來一張網絡圖檔

視覺AI五天訓練營 Day05 搭建多場景人臉口罩檢測系統(附效果動圖)

再來一次看看效果。

視覺AI五天訓練營 Day05 搭建多場景人臉口罩檢測系統(附效果動圖)

第四步,檢視傳回結果

大概長這樣

視覺AI五天訓練營 Day05 搭建多場景人臉口罩檢測系統(附效果動圖)

結語

至此,就結束了,由于本人學藝不精,算是一個不入門級的選手,如果小夥伴們對此有更好的方法或者思路,歡迎一起讨論。最後的話,我,希望有機會能再參加阿裡雲的活動!

繼續閱讀