搭建多場景人臉口罩檢測系統-基于阿裡雲視覺智能平台
最後一次的課了,就不辣麼水了。這次的試驗目的是将使用者上傳的圖檔(例如在公共場合的監控照片)進行識别,傳回目前圖檔中的人數、(人)活體的可信度、佩戴口罩的人數以及其占比、未佩戴口罩的人物坐标。可以設定适當的閥值,當達到一定閥值(占比)時進行警告或者通知等處理。
第一步,阿裡雲控制台擷取accesskey
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5CMwImMwYGM3gDNmZzYmNjYmljM0MWZmFDO0UTZ4MTM48CX5d2bs92Yl1iclB3bsVmdlR2LcNWaw9CXt92Yu4GZjlGbh5yYjV3Lc9CX6MHc0RHaiojIsJye.png)
不管你調用任何的能力,基本都需要擷取accesskey。
第二步,去阿裡雲視覺開放平台檢視接口 文檔
- 這次試驗一共使用了阿裡雲視覺開放平台的兩個能力,分别是: 人臉檢測定位 :傳回圖檔中人臉數量,人臉坐标;
:通過人臉坐标進行裁剪圖檔,以便下一步檢測口罩;
人臉口罩識别:識别輸入圖檔中的人臉是否有戴口罩。
-
預計效果:
輸出圖檔中的人臉數以及可信度,輸出未戴口罩的人數以及可信度,未戴口罩的人數達到一定占比對使用者進行通知。
第三步,編寫代碼(進階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.經過簡單的圖檔處理和視覺平台能力的整合,我們來試驗一下效果。
首先來一張本地圖檔
接着我們進行試驗,動圖如下。
再來一張網絡圖檔
再來一次看看效果。
第四步,檢視傳回結果
大概長這樣
結語
至此,就結束了,由于本人學藝不精,算是一個不入門級的選手,如果小夥伴們對此有更好的方法或者思路,歡迎一起讨論。最後的話,我,希望有機會能再參加阿裡雲的活動!