2015/09/16

OpenCV 人臉辨識, 就差沒認出馬爸.

因為想在四軸飛行器上整合影像辨識做些好玩的事, 所以萊恩大兵動念想自學 OpenCV. 胡亂摸索一陣之後, 覺得 OpenCV + Python 的學習資源較豐富, 就往這方向投入了.


上次是辨識多邊形 [記錄, OpenCV 學習路徑, (2) 辨識多邊形 (OpenCV, Python)], 這回來做人臉辨識.


(1) OpenCV + Python 做臉部辨識, 只需要 25 行程式碼



萊恩大兵搜索到幾篇文章, 有很清楚地講述應用 OpenCV + Python 來做人臉辨識.


Face Recognition With Python, in Under 25 Lines of Code (這篇蠻好讀的, 入門等級)

Face Detection in Python Using a Webcam (同上一篇的作者, 也是入門等級)
[OpenCV] 人臉偵測 (Face Detection) (難得的中文文章..)

從中學到幾個人臉辨識的概念:


Classifier


擷錄一段原文解釋: For something as complicated as a face, there isn’t one simple test that will tell you if it found a face or not. Instead, there are thousands of small patterns/features that must be matched. The algorithms break the task of identifying the face into thousands of smaller, bite-sized tasks, each of which is easy to solve. These tasks are also called classifiers.


萊恩大兵就不翻譯了. 大致知道 classifier 是做人臉辨識的基本比對任務 (task) 即可. 

一張臉, 會有超過 6000 個 classifiers. (這數字是在某篇文章看到的, 真假不知). 如果要辨識一張圖裡面有幾張人臉, 不就等於是要把圖裡面的全部區塊, 都分別跑完 6000 個 classifiers 比對. 這會需要很多很多的運算資源與時間. 


所以, 比較經濟有效率的作法, 是~


* Cascades 




Cascades (瀑布式) 是把人臉辨識的 6000 個 classifiers 分成數個階段. 比對圖區塊時, 都從第一個階段做辨識, 若沒通過就淘汱不用往下做, 有通過 (可能是臉) 才繼續做下階段的辨識, 直到通過全部的辨識, 才判斷為人臉. 這種類似瀑布一段一段往下做法的好處是~ 節省運算資源與時間, 不用每個圖區塊都得做足 6000 個辨識.


知道 classifier 和 cascade 後, 就可以來看 OpenCV + Python 怎麼做人臉辨識.


以 Face Detection in Python Using a Webcam 裡面的程式範例:


import cv2
import sys

cascPath = sys.argv[1]
faceCascade = cv2.CascadeClassifier(cascPath)

#videoPath = sys.argv[2]
#video_capture = cv2.VideoCapture(videoPath)
video_capture = cv2.VideoCapture(0)

while True:
    # Capture frame-by-frame
    ret, frame = video_capture.read()

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    faces = faceCascade.detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=5,
        minSize=(30, 30),
        flags=cv2.cv.CV_HAAR_SCALE_IMAGE
    )

    # Draw a rectangle around the faces
    for (x, y, w, h) in faces:
        cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)

    # Display the resulting frame
    cv2.imshow('Video', frame)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# When everything is done, release the capture
video_capture.release()
cv2.destroyAllWindows()


下指令: 
python fd_webcam.py haarcascade_frontalface_default.xml

執行結果: 即便遮住半張臉, 也還是有辨識出臉孔.


再以 OpenCV 裡面的範例 (facedetect.py, 這隻程式做兩段的辨識, 先辨識臉孔, 再辨識眼睛).

#!/usr/bin/env python

import numpy as np
import cv2
import cv2.cv as cv
from video import create_capture
from common import clock, draw_str

help_message = '''
USAGE: facedetect.py [--cascade <cascade_fn>] [--nested-cascade <cascade_fn>] [<video_source>]
'''

def detect(img, cascade):
    rects = cascade.detectMultiScale(img, scaleFactor=1.3, minNeighbors=4, minSize=(30, 30), flags = cv.CV_HAAR_SCALE_IMAGE)
    if len(rects) == 0:
        return []
    rects[:,2:]  = rects[:,:2]
    return rects

def draw_rects(img, rects, color):
    for x1, y1, x2, y2 in rects:
        cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)

if __name__ == '__main__':
    import sys, getopt
    print help_message

    args, video_src = getopt.getopt(sys.argv[1:], '', ['cascade=', 'nested-cascade='])
    try: video_src = video_src[0]
    except: video_src = 0
    args = dict(args)
    cascade_fn = args.get('--cascade', "../../data/haarcascades/haarcascade_frontalface_alt.xml")
 #   nested_fn  = args.get('--nested-cascade', "../../data/haarcascades/haarcascade_eye.xml")
    nested_fn  = args.get('--nested-cascade', "../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml")

    cascade = cv2.CascadeClassifier(cascade_fn)
    nested = cv2.CascadeClassifier(nested_fn)

    cam = create_capture(video_src, fallback='synth:bg=../cpp/lena.jpg:noise=0.05')

    while True:
        ret, img = cam.read()
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        gray = cv2.equalizeHist(gray)

        t = clock()
        rects = detect(gray, cascade)
        vis = img.copy()
        draw_rects(vis, rects, (0, 255, 0))
        for x1, y1, x2, y2 in rects:
            roi = gray[y1:y2, x1:x2]
            vis_roi = vis[y1:y2, x1:x2]
            subrects = detect(roi.copy(), nested)
            draw_rects(vis_roi, subrects, (255, 0, 0))
        dt = clock() - t

        draw_str(vis, (20, 20), 'time: %.1f ms' % (dt*1000))
        cv2.imshow('facedetect', vis)

        if 0xFF & cv2.waitKey(5) == 27:
            break
    cv2.destroyAllWindows()

執行結果: 臉孔和眼睛都有辨識出來.





(2) 調教人臉特癥值 (training haar cascades)

上面兩段程式碼裡面, 辨識的關鍵在於 haarcascade_xxx.xml. 

這個 haarcascade_xxx.xml 檔案就是人臉特癥值 (資料庫, pattern, 等等).



什麼是 Haar? 

稍微查了一下~ Haar-like features are digital image features used in object recognition. (取自 Wiki) 



Cascade classifier for Haar features 有很多種類; 人臉, 眼睛, 耳朵, 嘴... 甚至可以自己訓練一套專屬用途的.


常聽工程師說, 人臉辨識的特癥值, 會因人種或地區而有不同. (雖然這聽起來很像為辨識結果不準確的開脫之詞.) 這到底是在說什麼? 

本來是想研究這些人臉特癥值是如何產生的.. 不過, 那段的數學與理論太艱澀, 實在吞不下去. 退而求其次, 萊恩大兵找了幾篇講述如何調教 haar cascades 的文章.

TRAIN YOUR OWN OPENCV HAAR CLASSIFIER (這篇在教訓練一個能辨識香蕉的 haar cascade)

Tutorial: OpenCV haartraining (Rapid Object Detection With A Cascade of Boosted Classifiers Based on Haar-like Features) (網路上很多 training haar cascades 的分享文章, 似乎都來源自這篇)

本來 (again), 萊恩大兵想跟著調教一套 haar cascades 來用的. 考量時間.. 還是算了, 以後有需求或心血來潮再做.

直接拿香蕉的 haar cascades (banana_classifier.xml) 來套用. 利用上面 webcam 的程式碼來試試看.


下指令:
python fd_webcam.py banana_classifier.xml 

找了一段有香蕉的影片來嚐試. 是有抓到香蕉, 但誤判也多. 而且似乎, 它只認彎曲向上的香蕉.



喬了好久, 才終於有這漂亮的一張圖.



脖子被誤判為香蕉了, XD.




(3) 網路上的 haar cascades 資源


既然人臉辨識的關鍵是在 haar cascades. 猜想網路上應該會有不少的資源. 萊恩大兵搜尋了一下.


* 在 OpenCV 裡面, data 路徑下有一堆.
* 在 Haar Cascades 也有一堆.

萊恩大兵試了幾個, 有些看不出效果, 不知怎麼回事.

haarcascade_frontalface_default.xml -> 人臉
haarcascade_eye_tree_eyeglasses.xml -> 眼睛
haarcascade_frontalface_alt_tree.xml
haarcascade_frontalface_alt.xml
haarcascade_frontalface_alt2.xml
haarcascade_frontalface_default.xml
haarcascade_fullbody.xml
haarcascade_lefteye_2splits.xml
haarcascade_lowerbody.xml
haarcascade_mcs_eyepair_big.xml
haarcascade_mcs_lefteye.xml
haarcascade_mcs_mouth.xml
haarcascade_mcs_nose.xml
haarcascade_mcs_righteye.xml
haarcascade_mcs_upperbody.xml
haarcascade_profileface.xml
haarcascade_righteye_2splits.xml
haarcascade_upperbody.xml
haarcascade_mcs_eyepair_small.xml


(4) 附帶一提.. 人群追蹤

萊恩大兵也想做人群追蹤的應用. 本以為它和人臉辨識是類似的演算法. 查了一下, 似乎不是. 留個連結下來, 以後再看. 

* People Detection and Tracking
* Github: pedestrians-traffic-calc
* 108 大眼仔, 進化, (5) 迎著人來人往



最後有些問題, 萊恩大兵還沒搞懂. 
OpenCV 的這些範例, 要做照片或影片裡的人臉辨識還容易. 但要做年齡, 男女甚至是身份識別... 就得另外下工夫了. 有知道怎麼做的朋友, 請指點一下.


參考資料

OpenCV 介紹


[萊恩大兵的其它文章]


自製大四軸

自製大四軸, 實作分享@華山文創園區
自製大四軸, (1) 零組件篇, 遙控器 (Drone, Quadcopter, Futaba, Maker, Arduino, Animatronic Eye)
自製大四軸, (2) 零組件篇, 飛控板 (Drone, Quadcopter, MultiWii, Arduino, Futaba, Maker)
自製大四軸, (3) 零組件篇, 自行雷切木質機架 (Drone, Quadcopter, Maker, Laser Cut)
自製大四軸, (4) 零組件篇, 馬達與電變調整 (Drone, Quadcopter, Maker, Electric Speed Control, Motor)
自製大四軸, (5) 組裝篇, 四軸飛行器成形 (Drone, Quadcopter, MultiWii, Arduino, Maker, Electric Speed Control, Motor)
自製大四軸, (6) 調整篇, 飛行前兩三事 (Drone, Quadcopter, Maker, Futaba, Arduino, MultiWii)
自製大四軸, (7) 充電篇, iMax B6 充電器操作記要 (Charger, Battery)
自製大四軸, (8) 問題篇, 機架損壞維修 (Drone, Quadcopter, Laser Cut)
自製大四軸, (9) 改良篇, 方便拆卸的木質機架 (Drone, Quadcopter, Maker, Laser Cut)
自製大四軸, (10) 外飛篇, 新手的青蛙跳與遛狗 (Drone, Quadcopter, Maker, MultiWii)

自動報球速的棒球



CC2540 Bluetooth Low Energy
筆記, CC2540 Bluetooth Low Energy, (1) 開發環境 架設 (Bluetooth, CC2540)
筆記, CC2540 Bluetooth Low Energy, (2) 跑第一個範例程式 (Bluetooth, CC2540)
筆記, CC2540 Bluetooth Low Energy, (3) SimpleBLEPeripheral 簡單介紹 (Bluetooth, CC2540)
筆記, CC2540 Bluetooth Low Energy, (4) 在智慧手機上執行範例程式 (Bluetooth, CC2540)
筆記, CC2540 Bluetooth Low Energy, (5) 偵測與發送 iBeacon 訊號 (Bluetooth, CC2540, iBeacon)
實作, iBeacon 發訊器 x 防丟器 (Bluetooth, CC2540, iBeacon)
實作, iBeacon 尋寶遊戲 (Bluetooth, CC2540, iBeacon, iOS app)
實作, BLE + iOS app, 遙控燈泡君 (Bluetooth, CC2540, iOS app)
做實驗, 用 iBeacon 做自動控制的可行性 (Bluetooth, iBeacon, CC2540, Automation, URL Scheme, iOS app)

藍色小鋪一起來做

藍色小鋪一起來做, (1) 用 beacon 控制開關的枱燈
藍色小鋪一起來做, (2) 講解 BLE CC2540 UART 通訊範例程式 (Bluetooth, CC2540, UART)
藍色小鋪一起來做, (3) 藍牙枱燈專案實作 (上) (Bluetooth, CC2540)
藍色小鋪一起來做, (4) 藍牙枱燈專案實作 (下) (Bluetooth, CC2540)
藍色小鋪一起來做, (5) iBeacon scanner 專案示範與解說 (Bluetooth, CC2540, iBeacon)
藍色小鋪一起來做, (6) 完成, 用 iBeacon 控制開關的枱燈 (Bluetooth, CC2540, iBeacon)
藍色小鋪, 初嚐樹莓派 (Raspberry Pi)
藍色小鋪, iBeacon 應用, 自動記錄到訪時間 (iBeacon, Geohopper, Zapier)
藍色小鋪, 菲力普的 Docker 應用分享 (Docker)
藍色小鋪, PTT地震文團隊分享三連發, (1) Maker 的 IOT 遊樂場 (PTT, Hackathon, Python, Xively, Internet of Things)
藍色小鋪, PTT地震文團隊分享三連發, (2) mbed 新手分享 (mbed, MPU6050, Hackathon, Internet of Things)
藍色小鋪, 空中提升軌道車, 作品進化分享 (Pneumatic Tube System, Force of Friction, Mini 4 WD)
藍色小鋪, 數字管時鐘, 作品進化分享 (Nixie Tube, VFD)
藍色小鋪, 回憶之光, 3D 列印的經驗分享 (3D printing)
藍色小鋪, Humix 紙箱機器人, 機器人霸凌事件 (Humix, Internet of Thing)

OpenCV 學習路徑

記錄, OpenCV 學習路徑, (1) 環境安裝與第一個範例 (OpenCV, Python)
記錄, OpenCV 學習路徑, (2) 辨識多邊形 (OpenCV, Python)

小惡魔 無線溫度感測器

108 大眼仔
Plot Clock

體驗, 原住民互動故事書@宜蘭大同鄉泰雅生活館
體驗, 蛋生音互動裝置@兒童美術館 (Arduino, 3D Printing, HC-SR04, Interactive)



實作, 電容感應音樂樹

0 意見:

張貼留言