iT邦幫忙

2021 iThome 鐵人賽

DAY 9
1
AI & Data

[Computer Vision] 電腦視覺下的人臉系列 第 9

[Day9] Face Detection - 使用OpenCV & Dlib:Haar cascades

了解一套工具最好的方法是:動手完成一個現有的範例
了解一門技術最好的方法是:用那套技術完整做出一個自己的應用

Get your hands dirty!

介紹完目前主流的API與相關平台後,還是要回頭來看看要如何自行寫程式完成一樣的功能。

本文開始

前面提到過,使用OpenCV & Dlib來做人臉偵測,大概可以分為四種方式:

  1. OpenCV Haar cascades <-- 今天說這個
  2. OpenCV deep neural networks (DNNs)
  3. Dlib HOG + Linear SVM
  4. Dlib max-margin object detector (MMOD)

接下來就依序介紹各個方式吧。

:如同在第一天提到的,這個系列使用的主要語言是Python,對Python不了解或有疑問的可以參考其他邦友發表的優質文章

環境準備

這邊推薦兩種Python開發環境使用:

  1. Google Colab:只要有瀏覽器+網路就可以使用、預先就安裝好常用套件、需要有Google帳號
  2. PyCharm:整合環境方便管理大型與複雜的專案、提供幾種常見的虛擬環境方便切換使用(Virtualenv、Conda、Pipenv)

由於接下來的實作大部分都會圍繞在自行開發上,為了能夠更方便的整合專案,後續的開發環境都會使用PyCharm來作範例說明與實作,關於PyCharm的安裝與使用請參考邦幫忙其他相關文章

OpenCV Haar cascades

習慣上我喜歡稱呼這個方法叫哈爾特徵檢測

它以辨識速度極快、硬體需求低,常常會被應用在各種行動裝置或是樹莓派(Raspberry Pi)等硬體規格相對低階的裝置上。

這個方法主要是透過滑動檢測事先定義好的小矩形,由左上至右下,找尋人臉上明顯的特徵:如兩眼之間的鼻子區域通常比眼睛更亮,然後慢慢把非人臉區域排除。從而檢測出人臉真正區域

這個技術相關的原理在網路上都可以搜尋的到,這邊就讓我們直接開始實作吧!

  1. 打開PyCharm IDE,新增一個專案

  2. (可不用) 點擊上方工具欄的「File」->「Settings」->「Project: your_project_name」->「Python Interpreter」,新增一個Python的虛擬運作環境
    haar_ide_1

  3. 點選上方的"+"符號安裝套件
    haar_ide_2

  4. 依序在搜尋框輸入下面的套件名稱,確認版本相同後,按下方的「Install Package」安裝套件

    • opencv-contrib-python (版本:4.1.2.30)
    • cmake (版本:3.21.2)
    • dlib (版本:19.22.1)
    • imutils (版本:0.5.4)
      haar_ide_3
      haar_ide_5
      haar_ide_4
      imutils
  5. 安裝需要一點時間...等待安裝都成功後,關閉視窗。在主視窗開啟下方的「Terminal」,輸入pip freeze
    haar_ide_6

    > pip freeze
    

    確認你安裝的套件與版本是否與下面相同

    cmake==3.21.2
    dlib==19.22.1
    imutils==0.5.4
    numpy==1.21.2
    opencv-contrib-python==4.5.3.56
    
  6. 接下來在新增的專案目錄下新增目錄:face_detection,專門放人臉偵測相關檔案用
    haar_ide_7

  7. 再來新增一個Python檔案:opencv_haar_cascade.py,撰寫今天主程式
    haar_ide_8

  8. 在新增的Python檔案內輸入以下內容 (相關程式碼說明在註解內):

    # 匯入必要套件
    import argparse
    import ntpath
    import os
    import time
    
    import cv2
    import imutils
    from imutils.video import WebcamVideoStream
    
    detectors = {
        "eye": os.path.sep.join([ntpath.dirname(cv2.__file__), 'data', 'haarcascade_eye.xml']),
        "face": os.path.sep.join([ntpath.dirname(cv2.__file__), 'data', 'haarcascade_frontalface_default.xml'])
    }
    
    
    # 定義人臉偵測函數方便重複使用
    def detect(gray, part="face"):
        # 初始化Haar cascades函數
        detector = cv2.CascadeClassifier(detectors[part])
        # 根據選擇的模型偵測
        rects = detector.detectMultiScale(gray, scaleFactor=1.05, minNeighbors=5, minSize=(15, 15),
                                          flags=cv2.CASCADE_SCALE_IMAGE)
        return rects
    
    
    def main():
        # 初始化arguments
        ap = argparse.ArgumentParser()
        ap.add_argument("-p", "--part", type=str, choices=["eye", "face"], default="face", help="detect which part of face")
        args = vars(ap.parse_args())
    
        # 啟動WebCam
        vs = WebcamVideoStream().start()
        time.sleep(2.0)
        start = time.time()
        fps = vs.stream.get(cv2.CAP_PROP_FPS)
        print("Frames per second using cv2.CAP_PROP_FPS : {0}".format(fps))
    
        while True:
            # 取得當前的frame,變更比例為寬300,並且轉成灰階圖片
            frame = vs.read()
            img = frame.copy()
            img = imutils.resize(img, width=300)
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
            # 呼叫偵測函數,取得結果
            rects = detect(gray, args["part"])
    
            # 繪出偵測結果 (記得將偵測的座標轉回原本的frame大小)
            ratio = frame.shape[1] / img.shape[1]
            for rect in rects:
                rect = rect * ratio
                (x, y, w, h) = rect.astype("int")
                cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
    
            # 標示FPS
            end = time.time()
            text = f"FPS: {str(int(1 / (end - start)))}"
            cv2.putText(frame, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
            start = end
    
            # 顯示影像
            cv2.imshow("Frame", frame)
    
            # 判斷是否案下"q";跳離迴圈
            key = cv2.waitKey(1) & 0xff
            if key == ord('q'):
                break
    
        #  清除畫面與結束WebCam
        cv2.destroyAllWindows()
        vs.stop()
    
    
    if __name__ == '__main__':
        main()
    
    
  9. 運行程式:

    • 在Terminal中輸入python face_detection/opencv_haar_cascade.py
    • 直接點程式碼第61行前的"啟動"icon
      haar_ide_10
  10. 執行後,會開啟WebCam,並且顯示類似下面的節果:
    haar_ide_9

你可以試著將啟動指令改成python face_detection/opencv_haar_cascade.py -p eye跑看看會有什麼結果。

結論

  1. 戴口罩基本上無法成功辨識,這是由於前面提到哈爾特徵檢測的作法是滑動視窗的限制,你可以試著比對戴上口罩前與戴上口罩後玩看看

  2. 一般WebCam的FPS為30 (你可以透過print(vs.stream.get(cv2.CAP_PROP_FPS))檢查看看),透過哈爾特徵檢測處理寬為300px的影像還是可以有約28 ~ 32 fps;後續可以跟其他方法比較看看,這個速度算是非常快了 (在實際使用下幾乎感受不到延遲)

  3. 哈爾特徵檢測的false-positive (失敗偵測)結果會受程式碼39行的detectMultiScale()參數影響:

    • scaleFactor:指定原始圖片在進行檢測時,要縮放的比例為多少;1.05表示每次以95%的比例進行縮放;值的範圍為[1.0, MAX_VALUE],越小檢測出的結果越多 (也需花費更多時間),通常會使用1.05 ~ 1.1之間的數值
    • minNeighbors:指定在檢測結果中需要多少個相鄰的檢測框才認定檢測成功;5表示需要5個相鄰檢測框;值必須為正整數,越小檢測的結果越多 (但失敗偵測的可能性也變大),通常會使用3 ~ 10之間的數值
    • minSize:指定最小可以接受(寬, 高)的檢測框,小於這個大小的結果會被丟棄;(30, 30)表示使用寬高皆至少為30px的檢測結果;值必須為(正整數, 正整數)的格式,通常會從(30, 30)開始使用,根據結果再調整
    • flags:目前版本中無作用,可以不用寫或是給cv2.CASCADE_SCALE_IMAGE

    以上的參數的調整會非常容易改變最終的結果,這邊建議根據你實際執行的情況來做調整,順序建議為:
    minSize -> minNeighbors -> scaleFactor

  4. OpenCV內建的檢測模型還有其他很多可以使用,如偵測眼睛微笑上半身全身等等,可以參考程式碼20-21行的路徑目錄;有興趣可以到該目錄下看看

參考程式碼在這
就這樣,想到再補充。

明天見!


上一篇
[Day8] 關於人臉偵測(Face Detection)的二三事
下一篇
[Day10] Face Detection - 使用OpenCV & Dlib:OpenCV DNNs
系列文
[Computer Vision] 電腦視覺下的人臉30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

1
u09127104
iT邦新手 5 級 ‧ 2022-06-06 19:35:32

MFVideoFormat_RGB24(unsupported media type)
Traceback (most recent call last):

不好意思跑完出現這個要如何解決

u09127104 iT邦新手 5 級 ‧ 2022-06-06 19:36:04 檢舉

File "C:/Users/ADMIN/PycharmProjects/pythonProject1/opencv_haar_cascade.py", line 43, in main
img = frame.copy()
AttributeError: 'NoneType' object has no attribute 'copy'

還有這個

這個問題我沒遇到過...不過你用關鍵字去找可能會找到這篇類似的狀況與解法。

希望有幫助到您。

u09127104 iT邦新手 5 級 ‧ 2022-06-07 10:12:21 檢舉

好像是相機沒辦法開我是用usb相機

0
as43647
iT邦新手 5 級 ‧ 2022-09-05 02:39:56

請問dlib無法下載是python版本問題嗎?

可能原因有很多,方便放上你的錯誤訊息嗎?

我要留言

立即登入留言