OpenCVで顔検出
前々からやってみたかったOpenCVで顔検出にトライ。
これ自体はググればあちこちにサンプルソースが見つかるので、まずはそれをほぼそのままコピって来て実行。静止画から探索する例とウェブカメラの動画から探索する例が有りますが、自分は相当昔に購入したウェブカメラで試しました。
顔だけだと本当にサンプルソースそのままなので、目の検出も同時にやって表示させています。
# -*- Coding: utf-8 -*- import cv2 cap = cv2.VideoCapture(0) cascade1 = cv2.CascadeClassifier('/home/foo/anaconda3/envs/py35/share/OpenCV/haarcascades/haarcascade_eye_tree_eyeglasses.xml') cascade2 = cv2.CascadeClassifier('/home/foo/anaconda3/envs/py35/share/OpenCV/haarcascades/haarcascade_frontalface_alt2.xml') while(True): ret,frame = cap.read() facerect = cascade1.detectMultiScale(frame) for rect in facerect: cv2.rectangle(frame,tuple(rect[0:2]),tuple(rect[0:2] + rect[2:4]),(255,255,255),thickness = 2) facerect = cascade2.detectMultiScale(frame) for rect in facerect: cv2.rectangle(frame,tuple(rect[0:2]),tuple(rect[0:2] + rect[2:4]),(255,255,255),thickness = 2) cv2.imshow('frame',frame) if cv2.waitKey(1) == 27: break; cap.release() cv2.destroyAllWindows()
こんな感じのソースです。anaconda3を使ってOpenCVはローカルにインストール。
(ちなみにpython3.5環境です)
特にどうということも無く動くのですが、動画の表示がものすごくカクつく。
カメラの画像は640x480、CPUは2GHzのシングルコアCeleronですが、そもそも全く並列処理とか取り入れていないプログラムなので、クァッドコアとかでも大差無いのかも。
1フレーム毎に顔検出と目検出を両方走らせていて、これが重たいのだと思います。
そこでもう少し動きが軽くなるように手を入れて見ました。あまりこーいうサンプルは載っていなかったので。
# -*- Coding: utf-8 -*- import cv2 scaleFactor1 = 2 scaleFactor2 = 4 firstFlag = True eyeDetect = True faceDetect = True cap = cv2.VideoCapture(0) cascade1 = cv2.CascadeClassifier('/home/foo/anaconda3/envs/py35/share/OpenCV/haarcascades/haarcascade_eye_tree_eyeglasses.xml') cascade2 = cv2.CascadeClassifier('/home/foo/anaconda3/envs/py35/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml') while(True): ret,frame = cap.read() if ret == True: if firstFlag: check_width1 = frame.shape[1] // scaleFactor1 check_height1 = frame.shape[0] // scaleFactor1 check_width2 = frame.shape[1] // scaleFactor2 check_height2 = frame.shape[0] // scaleFactor2 firstFlag = False if eyeDetect == True: frame2 = cv2.resize(frame,(check_width1,check_height1)) facerect = cascade1.detectMultiScale(frame2) for rect in facerect: rect_t1 = tuple(rect[0:2]) rect_t1_scaled = tuple([x * scaleFactor1 for x in rect_t1]) cv2.rectangle(frame,rect_t1_scaled,rect_t2_scaled,(0,0,255),thickness = 2) if faceDetect == True: frame3 = cv2.resize(frame,(check_width2,check_height2)) facerect = cascade2.detectMultiScale(frame3) for rect in facerect: rect_t1 = tuple(rect[0:2]) rect_t1_scaled = tuple([x * scaleFactor2 for x in rect_t1]) rect_t2 = tuple(rect[0:2] + rect[2:4]) rect_t2_scaled = tuple([x * scaleFactor2 for x in rect_t2]) cv2.rectangle(frame,rect_t1_scaled,rect_t2_scaled,(255,255,255),thickness = 2) cv2.imshow('frame',frame) if cv2.waitKey(1) == 27: break; cap.release() cv2.destroyAllWindows()
ソースにコメント入れていませんが、やっていることは以下の内容です。
- カスケード検出器にかける前にキャプチャ画像を一定比率で縮小
- 縮小した画像に検出器をかける
- 検出された顔や目の領域の座標を元のスケールに変換
- 変換された座標を元にキャプチャ画像に顔や目の領域を示す矩形を描画
640x480の元画像に対して、顔検出と目検出でそれぞれ別の縮小率を適用出来る様にして実験した感じでは、顔検出は1/4でもOK,目は1/4だと厳しく、1/2だとOKで、全体的にはかなり滑らかに動画表示しつつ、顔と目を同時検出できる様になりました。
フルHDや4K画像なんかに真面目に検出器をかけると、ものすごい処理量になるので割と良く使われる手法じゃないかと思います。
ちなみにOpenCVでの画像はnumpyのndarrayで表現されているみたいですが、ndarrayの扱いに慣れていないので検出した顔や目の領域の座標変換の部分があまりイケていないコードになっている気がします
この辺はpythonについては結構初心者なので、ご容赦を。