エンジニアレポート

PythonとOpenCVを用いて、複数の画像の中から顔のみを正しく検出する

2020年07月20日 PythonOpenCVAnacondaSpyder

映像・メディア事業推進部の上田です。現在は主に野球のメディア向けサービスやシステム開発に携わっています。

当社では、データの中でも特にプレーデータと呼んでいる、試合中に起きる出来事をベースにしたデータの扱いを得意としています。例えば野球では投球・打球、サッカーではパス・シュート・クロスなど、発生した事象を1レコードとして記録したデータです。

また、昨今ではトラッキングやセンサリングなどプレー以外のデータを取得したり、機械学習を使ってデータを活用したりなど、様々な利用ができるようになっています。

今回はその中でも画像データについて、「PythonとOpenCVを使い、画像の中から顔を高い精度で検出する」をテーマにご紹介します。

  1. PythonとOpenCVで画像の中の顔を検出するための準備
  2. サンプルコード
  3. 顔を検出する
  4. 顔を検出する精度を上げる
  5. 学習済みモデルを使うメリット・デメリット

1. PythonとOpenCVで画像の中の顔を検出するための準備

以下のバージョンでやっていきます。
OS:Windows10 Pro 1909
Python :3.7.3
OpenCV :3.4.2
パッケージ管理ツール:Anaconda 2020.02
Spyder:4.1.3

Anacondaでの OpenCV ライブラリのインストール方法はこちら。(https://anaconda.org/conda-forge/opencv

OpenCVの顔検出をするための、学習済みのファイル(haarcascade_frontalface_alt.xml)はこちら(https://github.com/opencv/opencv/tree/master/data/haarcascades)からダウンロードできます。任意のフォルダに置いておきましょう。今回は正面から見た顔検出器を使います。

2. サンプルコード

いきなりですが、最終的なサンプルコードを記載します。コメントアウト(#)の個所に数字を振ってあります。後段の説明ではその数字を参照し、コード位置を確認してください。

3. 顔を検出する

複数の画像をまとめて検出にかけるとき扱いやすくするため、globメソッドを使い、変数pic_fillesに指定したフォルダ内の.jpgファイルのパスをリスト化しておきます。そして、ダウンロードした学習済みのデータが入っているxmlファイルを読み込みます(#1)。

肝心の顔検出にはdetectMultiScaleメソッドを使います(#2)。

検出すると、変数face_rectに検出した物体(長方形)の情報(X, Y, Width, Height)がnumpyのndarray配列で入ります。 X,Yは長方形の左上の座標点で、Width(幅)は座標(X,Y)から右方向の長さ、Height(高)は座標(X,Y)から下方向の長さです(#3)。

なお、常にWidth=Heightとなるので正方形で検出されます。また、1枚の画像から、複数の物体を検出します。上のサンプルコードでは、最初の検出の数値でforループを抜けるようにしてあります。

4. 顔を検出する精度を上げる

精度を上げるには、一般的に2つのアプローチがあります。

  • 検出しやすい画像に事前加工する
  • 検出しやすいパラメータに調整する

今回は、事前加工として画像をグレースケール(白黒の画像)に変換しています(#4)。

パラメータについては、OpenCVの公式ドキュメントを見ると複数あることがわかりますが、今回はそのうち以下3種類を使います。

  • scaleFactor – 画像サイズをどの程度縮小して検出していくかを指定するパラメータ。
  • minNeighbors – 検出した長方形が、どの程度隣接して検出していなければいけないかを指定するパラメータ。
  • minSize – 最小オブジェクトサイズのパラメータ。この値より小さいオブジェクトは無視される。

これらの最適な値を導くため、あらかじめ「1つの画像に1人の人間」という画像に対して、複数のパターンで検証しました。検出結果では長方形を赤色で塗り視認しやすくしています(#5)。

パターン1:大きい数値

min_sizes = [400]
scale_factors = [1.25]
min_neis = [3]

40枚中、何らかの検出ができたのは7枚だけ。ただ、7枚全てで顔を適切に検出、顔以外の検出(誤検出)は0枚でした。

パターン2:小さい数値

min_sizes = [15]
scale_factors = [1.05]
min_neis = [2]

40枚中、何らかの検出ができたのは39枚。うち32枚で顔を適切に検出、誤検出は7枚ありました。

右上の写真(sample_31.jpg)だと、腕のあたりを顔として検出してしまっています。

いずれのパラメータも数値が大きいほど、
・何らかの検出ができれば、正しく顔を検出しやすく、誤検出はしにくい
・ただし、検出無しも多くなる

そして数値を小さくすると、
・何らかの検出ができた場合でも、顔も顔以外も検出しやすい
・ただし、検出無しとなることは減る

つまり、設定値が大きいほど厳しめの検出、小さいと緩めの検出、と言えそうです。この簡単な実験から、大きな数値から小さな数値に (厳しめから緩やかに) 順に処理をすれば、良い結果になりそうです。

パターン3:組み合わせた数値

min_sizes = [400, 350, 300, 250, 200, 150, 100, 50, 25, 15]
scale_factors = [1.25, 1.2, 1.15, 1.1, 1.05]
min_neis = [3, 2]

実際のプログラムではこのようにパラメータを設定し、厳しい検出の組み合わせから順に緩やかになるように検出してみました(#6)。

結果は40枚中、何らかを検出できたのは39枚。うち38枚で顔を適切に検出、誤検出は1枚のみでした。

sample_28、29、33、38の4枚に対しては、パターン1では未検出、パターン2では誤検出していたところ、パターン3の組み合わせ処理では顔を正しく検出できるようになりました。

残念ながら今回のパラメータではキャプチャ右上のsample_31.jpgは腕の検出となってしまいました。これは頭部にタオルを巻いていることが影響していると思われます。そして実際に野球、サッカー、バスケットボール選手の写真を使った検出でも、野球のように帽子を被った写真では未検出や誤検出が多い傾向がありました。

未検出や誤検出を減らし顔のみを正しく検出することができれば、顔を検出した後の様々な処理が非常に楽になります。

5. 学習済みモデルを使うメリット・デメリット

最後に、OpenCVの顔検出器のように公開されている学習済みモデルを使う際の、メリットとデメリットについて触れておきます。

メリット
・学習用のデータ(画像)を大量に集める必要がない
・機械学習の手法を学ぶ必要がない

デメリット
・手に入れたい結果にならない可能性がある
(検出したい画像と学習済みデータにミスマッチがある場合や、適切なパラメータを見つけられない場合)

以上、「PythonとOpenCVを用いて、複数の画像の中から顔のみを正しく検出する」でした。(検証用の人物画像は、ぱくたそ(www.pakutaso.com)の画像を用いています)

エンジニア募集中

データスタジアムでは一緒に働いていただけるエンジニアを絶賛募集中です!

野球、サッカー、バスケをはじめとして色々なスポーツの仕事があります。

AWSやAzure、動画やAIなど技術的にもチャレンジできる職場です。

テクノロジーの力で、日本のスポーツを一緒に発展させていきましょう!

<募集中のポジション(2020年8月1日現在)>

開発エンジニア(速報サービス)

開発エンジニア(スポーツチーム向けサービス)

インフラエンジニア

最新記事

エンジニアトップ エンジニアインタビュー エンジニアレポート

  • 採用情報
  • おしらせ
  • 掲載事例

ページトップヘ