Py-Featでお手軽に表情認識をやってみた

Py-Featでお手軽に表情認識をやってみた

Mediumのこちらの記事で、Py-Featというお手軽に表情表現(Facial Expression: FEX)を解析できるツールキットの存在を知ったのでさっそく試してみました。
今回は公式チュートリアルのうちの、画像からの表情認識をやってみました。

Py-Featとは

Py-Featは表情表現(Action Units、emotions、facial landmarks)を解析するための包括的なツールキットです。
具体的には以下の機能が提供されています。

  • 画像や動画から簡単に表情表現を検出できるモデル
  • 表情表現データの前処理や解析
  • 表情表現データの可視化

公式ドキュメント曰く、以下の理由からPy-Featが開発されたとのことです。

However, researchers seeking to use these algorithms or tools such as OpenFace, iMotions-Affectiva, or Noldus FaceReacer may find them difficult to install, use, or too expensive to purchase. It’s also difficult to use the latest model or know exactly how good the models are for proprietary tools. We developed Py-Feat to create a free, open-source, and easy to use tool for working with facial expressions data.

ちなみにPy-FeatはPython Facial Expression Analysis Toolboxの頭文字です。

環境

本記事で紹介しているコードはDockerコンテナ上での動作を想定しています。
なお、Dockerコンテナではなくても以下のライブラリをインストール済みであれば動作するはずです。
※依存ライブラリについてはGitHubのpyproject.tomlやpoetry.lockを確認してください。

  • python==”3.7″
  • sklearn==”0.0″
  • jupyterlab==”3.0.16″
  • pandas==”1.1.5″
  • numpy==”1.21.0″
  • matplotlib==”3.4.2″
  • py-feat = “0.3.7”

画像から表情表現(FEX)を検出してみる

まず、使用したいモデルを指定してdetectorクラスをロードします。
Py-Featで使用できるモデルは公式ドキュメントで確認できます。
※初回のモデルのダウンロードはかなり時間がかかります。
INPUT:

from feat import Detector
# rf: https://py-feat.org/content/intro.html#available-models
face_model = "retinaface"
landmark_model = "mobilenet"
au_model = "rf"
emotion_model = "resmasknet"
detector = Detector(
    face_model=face_model,
    landmark_model=landmark_model,
    au_model=au_model,
    emotion_model=emotion_model
)

ロード完了後、処理したい画像のファイルパスを指定します。
今回は写真ACの画像データを使用します。
INPUT:

from feat.tests.utils import get_test_data_path
import matplotlib.pyplot as plt
import os
from PIL import Image

# Py-Featでデフォルトで存在しているテストデータを使う場合
# test_data_dir = get_test_data_path()
# test_image = os.path.join(test_data_dir, "input.jpg")

# 写真ACからダウンロードした画像
test_image = './face.jpg'
# 画像の確認
f, ax = plt.subplots()
im = Image.open(test_image)
ax.imshow(im)

OUTPUT:

次に初期化したdetectorインスタンスを使って表情表現(FEX)を予測します。
INPUT:

image_prediction = detector.detect_image(test_image)
# Show results
image_prediction

OUTPUT:

frameFaceRectXFaceRectYFaceRectWidthFaceRectHeightFaceScorex_0x_1x_2x_3AU28AU43angerdisgustfearhappinesssadnesssurpriseneutralinput
00.0102.59143838.9592769.80327697.7516170.998283102.855199102.414743102.738646103.6937490.1337410.0687060.000050.000120.0000290.99890.0000520.0007550.000095./face.jpg

1 rows × 173 columns

detect_image()ではFeXクラスが返却されます。
FeXクラスはpandas.DataFrameを継承しているので、単体で呼び出すとDataFrameが表示されます。
FeXクラスの仕様に関しては公式ドキュメントを参照してください。

最後にplot_detections()メソッドで簡単に検出結果を可視化します。

image_prediction.plot_detections()


顔検出および表情認識結果と共に、活動単位(Action Units)や感情(Emotions)のスコアが可視化されます。
この画像の男性の場合は、happinessに全振りされているようです。

次に1人ではなく、複数人の表情表現データで試してみます。
こちらも写真ACの画像を使用しています。
INPUT:

# 表情表現データが複数ある画像
test_image = './many_faces.jpg'

f, ax = plt.subplots()
im = Image.open(test_image)
ax.imshow(im)

OUTPUT:

全体的に笑顔の写真ですが、右下の子供たちの表情が少し引きつっているような気がします。
先ほどと同様に表情表現(FEX)を予測し、検出結果を可視化します。
INPUT:

image_prediction = detector.detect_image(test_image)
image_prediction.plot_detections()

OUTPUT:

先ほどとは異なり、happinessのほかにもneutralやsadnessなどの他の感情のスコアも出ています。
実際に個々のスコアを確認してみます。
INPUT:

# Show results
image_prediction

OUTPUT:

frameFaceRectXFaceRectYFaceRectWidthFaceRectHeightFaceScorex_0x_1x_2x_3AU28AU43angerdisgustfearhappinesssadnesssurpriseneutralinput
00.0218.6963351238.053711401.300293533.2937010.999880218.186890213.404875213.563282218.8572120.1405180.1202190.0006280.0040390.0023890.9660330.0026900.0219140.002307./many_faces.jpg
10.02921.4887701129.358521433.478516535.1557620.9997982938.3693332938.8149662945.7229792957.7713260.3073990.4603870.0013920.0001510.0019240.9449970.0004140.0459550.005167./many_faces.jpg
20.01906.3509521720.287720339.735962456.1900630.9993761903.4878711903.0767841907.4119661914.8179420.0905220.0522760.1405820.0148110.1359710.0051560.1962630.2516280.255590./many_faces.jpg
30.01493.170166270.268097341.269043473.3484190.9992491495.5025741492.2137731493.7175471500.1128980.2520180.0489170.0085120.0000680.0492900.0590180.1347710.0646570.683684./many_faces.jpg
40.01097.1928711671.731567325.657104442.0555420.9986931086.4581471090.1512081097.9015931109.4438770.1157060.0666990.0467040.0017960.0852170.0070790.1054400.1250370.628726./many_faces.jpg
50.02823.6730961921.245117276.546631356.0493160.9985582816.2403392820.7182652828.1871202838.0308630.0883760.0927670.0044990.0042040.1053320.0334670.0560730.2833010.513123./many_faces.jpg
60.02195.525391439.311340328.869385447.1970210.9972572206.8505672200.4140912198.7241882202.6489960.1902880.1335620.0027070.0000440.0325940.0072980.1716110.1120450.673700./many_faces.jpg

FaceRectX(X座標)、FaceRectY(Y座標)より、2行目が下段右から2番目の子で、5行目が下段右から1番目の子ということが分かります。
写真を撮られることに慣れていないのか、2人ともsadnessやfearのスコアが他の人のよりも高くなっています。(顔が引きつっているのかもしれません)
一方、左右両端のおじいさん、おばさんはhappinessのスコアが0.9を超えており、笑顔を作るのが上手なことが分かります。

最後に

今まで顔認識などの画像データを扱ったタスクに取り組んだことがありませんでしたが、Py-Featを使えばかなり手軽に試せることが分かりました。
Py-Featは画像の他に動画でも使用できるので、今度試してみようと思います。

参考