今年以來世界各地新冠肺炎肆虐,我在今年五月多用OpenCV搭配樹梅派做了一個簡單的口罩辨識。最近翻了一下以前寫的程式後發現還蠻有趣值得一寫的,不過這一篇不會寫有關樹梅派相關內容。
這篇算是一個OpenCV的簡單應用,其他OpenCV常用方法可以見我之前寫過的文章(雖然有點懶導致斷更了。不過因為觀看人數反響不錯,寒假有空我再把他補上去)
一、 實作流程與原理
原理非常簡單,計算戴口罩的部分(口鼻)與沒帶口罩的部分(眼額頭)之間顏色RGB的差距,當差距過大即辨識為戴口罩。
實作原理與流程如下面這幾個階段:
- 開啟鏡頭抓取影像
- 偵測人臉
- 將臉切成上下兩部分
- 計算各自上下兩部分的色彩直方圖
- 標準化畫計算上下兩部分直方圖差距
- 當差距過大即辨識為戴口罩
程式也非常簡單,就容我娓娓道來…。
二、程式
1. OpenCV開啟鏡頭
import cv2 as cv#抓取鏡頭 (如果你有的話)
cap = cv.VideoCapture(0)#設定影像尺寸大小
cap.set(cv.CAP_PROP_FRAME_WIDTH, 800)
cap.set(cv.CAP_PROP_FRAME_HEIGHT, 800)#如果cap能夠成功打開
while(cap.isOpened()):
ret,frame = cap.read()
# 顯示影像
cv.imshow("vedio",frame)
# 按下q離開
if(cv.waitKey(1) & 0xFF == ord('q')):
break#關閉視窗
cap.release()
cv.destroyAllWindows()
2. 偵測人臉
偵測人臉可以有兩種方式,一種是OpenCV的Haar Cascade與Dlib,Haar Cascade在網路上有較多的資料並且之後我也會寫到OpenCV方法整理的文章中,這次就來用dlib做吧 !
# 如果你是用anaconda環境安裝方式就是如下,不過我記得python環境要在3.6以下
conda install -c menpo dlib# 一般安裝就用
pip install dlib
安裝完dlib後,就能來抓取人臉了,程式非常簡單。
import dlibdetector = dlib.get_frontal_face_detector()img = cv.imread("./某個人臉圖片.jpg")
img = cv.cvtColor(img,cv.COLOR_BGR2RGB)for face in face_rects:
# 取得左上角的座標、右下角座標
x1 = face.left()
y1 = face.top()
x2 = face.right()
y2 = face.bottom()
# 畫出來
cv.rectangle(img, (x1, y1), (x2, y2), (0,255, 0), 4,cv.LINE_AA)
# 印出來
plt.imshow(img)
plt.show()
3. 切割人臉並計算直方圖
把抓到的人臉分成上 1/3 與下 2/3。
faceLong = y2-y1
upface = img[y1:y1+int(1/3*faceLong),x1:x2]
downface = img[y1+int(1/3*faceLong):y2,x1:x2]
計算各自之間的顏色統計圖,正規化後比較差距。計算顏色統計圖表會用到calHist
# calHist 參數
# cv2.calcHist(images, channels, mask(Size), histSize, ranges)
# image 輸入圖片
# channel RGB第幾個channel
# MASK 不要取樣的部分
# histSize 統計映射範圍,例如你設定[64],就能將255壓縮到64之間
# ranges 取樣範圍 [0,256] 取0~255color = ('r','g','b')for i, col in enumerate(color):
histr = cv.calcHist([upface],[i],None,[256],[0, 256])
plt.plot(histr, color = col)
plt.xlim([0, 256])
plt.title("upface")
plt.show()for i, col in enumerate(color):
histr = cv.calcHist([downface],[i],None,[256],[0, 256])
plt.plot(histr, color = col)
plt.xlim([0, 256])
plt.title("downface")
plt.show()
因為上半臉與下半臉的pixel數量不同,所以我們會先做正規化normalize。並計算兩者之間的相似度。(compareHist)
hist1 = cv.calcHist([upface], [0,1,2], None, [64, 64,64], [0, 256, 0, 256,0, 256])
# 平移縮放
cv.normalize(hist1, hist1, 0, 1.0, cv.NORM_MINMAX)
hist2 = cv.calcHist([downface], [0,1,2], None, [64,64,64], [0, 256, 0, 256,0, 256])
cv.normalize(hist2, hist2, 0, 1.0, cv.NORM_MINMAX)# 比較方法 HISTCMP_CORREL (相關姓) 0、HISTCMP_CHISQR (卡方) 1、HISTCMP_BHATTACHARYYA(巴式) 2
near = cv.compareHist(hist1,hist2,0)
print(near)# 相似度為: 0.15370643779881982
我們可以設定,當相似度過低代表他是戴口罩的。
4. 把上面所有東西整合。
設定上下臉相似度小於0.01就是沒有戴口罩,這個值可以透過自己的經驗去做調整。整體程式如下:
結果如下,本色演出:
三、 結論與參考資料
用這個方法優點是簡單、快速並且有著不低的準確率(我自己試的時候)。缺點是當戴著口罩時容易無法檢測到臉部、當光線過暗顏色差距不明顯容易辨識錯誤…等。總得來說是一個簡單的OpenCV應用練習。
參考資料如下
- https://blog.gtwang.org/programming/python-opencv-matplotlib-plot-histogram-tutorial/
- https://lufor129.medium.com/opencv%E5%9C%96%E7%89%87%E8%99%95%E7%90%86%E6%95%B4%E7%90%86-%E7%B7%A9%E6%85%A2%E6%9B%B4%E6%96%B0-b45e248d14bb
- https://chtseng.wordpress.com/2016/12/23/dlib-%E5%A5%BD%E7%94%A8%E7%9A%84%E7%9A%84machine-learning%E5%B7%A5%E5%85%B7-%E4%B8%80/