【openCV】顔、笑顔、目の検出を試してみる[Python]

  • URLをコピーしました!
目次

openCV

前回、Pythonのプログラムやライブラリの場所を探す方法を紹介しました。

今回はopenCVに戻って、顔、笑顔、目の検出を試してみます。

それでは始めていきましょう。

学習データの場所

まずはopenCVがそれぞれのパーツを検出するための学習データが必要です。

そしてopenCV自体が学習データを持っていて「/path/site-package/cv2/data」に入っています。

ただ「path」の部分は人によって異なるので、前回学んだライブラリの場所を探すコマンドを使用します。

pip show opencv-python

実行結果
Name: opencv-python
Version: 4.8.0.74
Summary: Wrapper package for OpenCV python bindings.
Home-page: https://github.com/opencv/opencv-python
Author: 
Author-email: 
License: Apache 2.0
Location: /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages
Requires: numpy, numpy, numpy, numpy, numpy
Required-by: japanmap

ということで私の環境では「/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages」がライブラリの場所だということが分かりました。

そして「/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/cv2/data」に学習データが入っています。

  • haarcascade_eye.xml(目)
  • haarcascade_eye_tree_eyeglasses.xml(メガネ)
  • haarcascade_frontalcatface.xml(猫の顔)
  • haarcascade_frontalcatface_extended.xml(猫の顔)
  • haarcascade_frontalface_alt.xml(人の顔)
  • haarcascade_frontalface_alt2.xml(人の顔)
  • haarcascade_frontalface_alt_tree.xml(人の顔)
  • haarcascade_frontalface_default.xml(人の顔)
  • haarcascade_fullbody.xml(全身)
  • haarcascade_lefteye_2splits.xml(左目)
  • haarcascade_license_plate_rus_16stages.xml(ロシアのナンバープレート)
  • haarcascade_lowerbody.xml(下半身)
  • haarcascade_profileface.xml(証明写真の顔)
  • haarcascade_righteye_2splits.xml(右目)
  • haarcascade_russian_plate_number.xml(ロシアのナンバープレートの数字)
  • haarcascade_smile.xml(笑顔)
  • haarcascade_upperbody.xml(上半身)

今回はしたの学習データを使って、それぞれの部位の検出を試してみます。

  • haarcascade_frontalface_default.xml(人の顔)
  • haarcascade_smile.xml(笑顔)
  • haarcascade_eye.xml(目)

今回使用する画像はこちら。

画像から特定の部位を検出するにはまずは画像をグレースケールにする必要があります。

また検出した枠を示すためにはカラー画像の方が良いので、カラー画像も用意しておきます。

ということで画像の読み込みに関してはこんな感じです。

import cv2

img = cv2.imread("facedetection.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

人の顔検出

まずは人の顔を検出してみます。

人の顔を検出するには「haarcascade_frontalface_default.xml」を読み込みます。

その際、「cv2.CascadeClassifier(学習データセット)」を用います。

そして「detectMultiScale(画像, scaleFactor=縮小量, minNeighbors=信頼性のパラメータ, minSize=(最小のX, 最小のY))」を使い、検出します。

scaleFactorは画像のスケールを変化させて検出するための縮小量で、大きいほど誤検知が多く、小さいほど未検出が多くなります。

minNeighborsは目的の場所は何度も検出されることになるが、何度も検出された場所がより信頼性が高いといえます。

そのための閾値で、値が大きくなると検出の信頼性は上がるが、見逃し率も増えます。

minSizeは検出する場所の最小サイズで、これより小さいサイズのものは検出されなくなります。

ということでとりあえず試してみます。

import cv2

img = cv2.imread("facedetection.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")

facelist = cascade.detectMultiScale(img_gray, scaleFactor=1.1, minNeighbors=3, minSize=(50, 50))

print(facelist)

実行結果
[[1369  639  901  901]
 [ 437  814  272  272]
 [ 840  823  242  242]
(中略)
 [3214 2765   85   85]
 [2120 2846   77   77]
 [1143 3391   58   58]]

得られたリストが検出された場所で、[X, Y, 幅, 高さ]になっています。

そのため、元画像に検出された場所を枠で示すにはこのようにします。

import cv2

img = cv2.imread("facedetection.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")

facelist = cascade.detectMultiScale(img_gray, scaleFactor=1.1, minNeighbors=3, minSize=(50, 50))

for face in facelist:
    x = face[0]; y = face[1]
    w = face[2]; h = face[3]
    cv2.rectangle(img, pt1=(x, y), pt2=(x+w, y+h), color=(0, 255, 0), thickness=10, lineType=cv2.LINE_4, shift=0)
    
cv2.imwrite("face10-1.jpg", img)

実行結果

顔は検出できていますが、他の場所も顔として認識されてしまっています。

まず顔としては小さすぎる場所が認識されているので、minSizeを大きくして認識される場所を減らしてみます。

import cv2

img = cv2.imread("facedetection.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")

facelist = cascade.detectMultiScale(img_gray, scaleFactor=1.1, minNeighbors=3, minSize=(100, 100))

for face in facelist:
    x = face[0]; y = face[1]
    w = face[2]; h = face[3]
    cv2.rectangle(img, pt1=(x, y), pt2=(x+w, y+h), color=(0, 255, 0), thickness=10, lineType=cv2.LINE_4, shift=0)
    
cv2.imwrite("face10-2.jpg", img)

小さいところは消えましたが、まだまだ誤検出しているところがたくさんあります。

信頼性の値であるminNeightborsを上げてみます。

import cv2

img = cv2.imread("facedetection.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")

facelist = cascade.detectMultiScale(img_gray, scaleFactor=1.1, minNeighbors=15, minSize=(100, 100))

for face in facelist:
    x = face[0]; y = face[1]
    w = face[2]; h = face[3]
    cv2.rectangle(img, pt1=(x, y), pt2=(x+w, y+h), color=(0, 255, 0), thickness=10, lineType=cv2.LINE_4, shift=0)
    
cv2.imwrite("face10-3.jpg", img)

これで顔だけ認識されるようになりました。

ただこれではscaleFactorをいじった時の挙動が分からないので、一度minNeightborsを3 に戻して、scaleFactorをいじってみます。

scaleFactorが1だとエラーが出るので注意です。

import cv2

img = cv2.imread("facedetection.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")

facelist = cascade.detectMultiScale(img_gray, scaleFactor=1, minNeighbors=15, minSize=(100, 100))

for face in facelist:
    x = face[0]; y = face[1]
    w = face[2]; h = face[3]
    cv2.rectangle(img, pt1=(x, y), pt2=(x+w, y+h), color=(0, 255, 0), thickness=10, lineType=cv2.LINE_4, shift=0)
    
cv2.imwrite("face10-4.jpg", img)

実行結果
---------------------------------------------------------------------------
error                                     Traceback (most recent call last)
Cell In[5], line 8
      4 img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
      6 cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
----> 8 facelist = cascade.detectMultiScale(img_gray, scaleFactor=1, minNeighbors=15,
 minSize=(100, 100))
     10 for face in facelist:
     11     x = face[0]; y = face[1]

error: OpenCV(4.8.0) /Users/runner/work/opencv-python/opencv-python/opencv/modules/
objdetect/src/cascadedetect.cpp:1389: error: (-215:Assertion failed) scaleFactor > 
1 && _image.depth() == CV_8U in function 'detectMultiScale'

scaleFactorを3まで上げてみます。

import cv2

img = cv2.imread("facedetection.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")

facelist = cascade.detectMultiScale(img_gray, scaleFactor=3, minNeighbors=3, minSize=(100, 100))

for face in facelist:
    x = face[0]; y = face[1]
    w = face[2]; h = face[3]
    cv2.rectangle(img, pt1=(x, y), pt2=(x+w, y+h), color=(0, 255, 0), thickness=10, lineType=cv2.LINE_4, shift=0)
    
cv2.imwrite("face10-5.jpg", img)

実行結果

これで顔だけ認識されるようになりました。

笑顔の検出

次に笑顔の検出を試してみます。

単純に読み込む学習データが変わるだけなので、「cv2.CascadeClassifier(学習データセット)」の学習データセットを笑顔の学習データ「haarcascade_smile.xml」に変えるだけです。

import cv2

img = cv2.imread("facedetection.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

cascade = cv2.CascadeClassifier("haarcascade_smile.xml")

facelist = cascade.detectMultiScale(img_gray, scaleFactor=2, minNeighbors=15, minSize=(50, 50))

for face in facelist:
    x = face[0]; y = face[1]
    w = face[2]; h = face[3]
    cv2.rectangle(img, pt1=(x, y), pt2=(x+w, y+h), color=(0, 255, 0), thickness=10, lineType=cv2.LINE_4, shift=0)
    
cv2.imwrite("face10-6.jpg", img)

実行結果

笑顔と言っても歯が見えているかどうかを検出しているように見えます。

またかなり認識が難しく、これ以上うまく設定できませんでした。

目の検出

目の検出の際には「haarcascade_eye.xml」を用います。

import cv2

img = cv2.imread("facedetection.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

cascade = cv2.CascadeClassifier("haarcascade_eye.xml")

eyelist = cascade.detectMultiScale(img_gray, scaleFactor=1.1, minNeighbors=10, minSize=(10, 10))

for eye in eyelist:
    x = eye[0]; y = eye[1]
    w = eye[2]; h = eye[3]
    cv2.rectangle(img, pt1=(x, y), pt2=(x+w, y+h), color=(0, 255, 0), thickness=10, lineType=cv2.LINE_4, shift=0)
    
cv2.imwrite("face10-7.jpg", img)

実行結果

目の検出は結構良い感じで検出できているのですが、顔が少し横になっていたり、メガネをしていたりすると検出できないことがありました。

こんな感じで結構簡単に試せるので、ぜひいろいろな部位の検出を試してみてください。

次回からはそろそろ専門分野であるバイオのライブラリBiopythonをいじっていこうと思います。

ではでは今回はこんな感じで。

よかったらシェアしてね!
  • URLをコピーしました!

コメント

コメントする

目次