ピンボケや手ぶれ写真を機械的に検出する方法 – ポートレート編

Python

デジカメで撮影した大量の写真の中から削除しても差し支えない不要な写真を機械的に検出するためこのような実験をしています。
今回は前回の実装だけでは問題となる、意図的に美しいボケを含んでいるポートレートをピンボケや手ぶれ写真として誤検出しないような実装をしていきます。また、この実装で写真に顔が含まれているけども顔部分にピントがあっていない写真も検出できるようになります。

スポンサーリンク

前回は写真全体のピンボケや手ぶれ写真を検出

前回はエッジ検出によって、写真全体がピンボケや手ぶれしている写真を検出することができました。これをさらに拡張していきます。

前回の実装の問題点

前回の実装では写真全体からボケの検出をしてしまっているので、一眼レフカメラなどで撮影したポートレートなどで、背景や前面に美しいボケがあるような写真まで、ピンボケの写真だと誤検出してしまうことがあります。
たとえば、下記のような写真です。フリー写真素材ぱくたその写真を利用させていただいています。

Blurry: 93.34


前ボケと背景ボケを上手く利用して、顔にピントがあっている写真です。これは機械的に削除候補にするような写真ではありません。ですが前回の実装をそのまま利用すると、スコアとしては93.34100以下なのでピンボケ、手ぶれとして判定しています。
今回はこういった写真をピンボケ、手ぶれと判定しないように実装を拡張していきます。

問題解決のアプローチ

最近の一眼レフやミラーレスカメラは瞳AF機能の性能が向上したことにより、簡単に瞳にバッチリピントがあった状態で撮影できるので、今回、例として出したような写真が簡単に撮影できます。私もこういった写真をよく撮影します。また、スマホでも最近はポートレートモードで撮影すると、顔を認識して、背景を自動的にぼかし処理をするようなことをしています。
こういった写真をきちんと判断するためのアプローチは以下のようにします。

  1. 写真から顔を認識する
  2. 顔の領域を画像として切り取る
  3. 顔の領域画像がボケていないかを判断

このようにすることによって、写真に顔が含まれて、顔にピントがきっちりあっていればピンボケ、手ぶれ写真と判定しないようにします。

顔を検出をする

OpenCVでの顔認識は非常に簡単にはじめられるようになっています。既に学習済みの検出器がOpenCVと一緒に配布されています。自分で学習させることもできます。
顔認識を認識するためのものがいくつかあります。

  • haarcascade_frontalface_alt.xml
  • haarcascade_frontalface_alt_tree.xml
  • haarcascade_frontalface_alt2.xml
  • haarcascade_frontalface_default.xml
  • haarcascade_profileface.xml

正面(frontalface)と横顔(profileface)があります。学習データとアルゴリズムがそれぞれ異ります。詳細は下記の解説が適切です。それぞれのXMLの上部にどのようなものなのか説明が記述されています。

Haar-Like特徴量を計算していくhaarcascade_frontalface_default.xmlを利用することにします。

def face_recognition(gray):
    face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
    return face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(20, 20))

このようなメソッドにグレースケールの画像を与えれば、顔の位置がRect(x,y,w,h)として返されます。
与えるパラメータは

  • scaleFactor … 検出をする時に縮小していく値1.1だと10%ずつ縮小して検出
  • minNeighbors … 物体候補が最低でも含むべき近傍矩形の数
  • minSize … 物体が取り得る最小サイズ。これ以下は無視

詳細はリファレンスを参照してください。
minNeighborsは値を上げると誤検出しないための精度が増しますが、検出しない顔も増えます。このあたりのパラメータについての検証は下記の記事などが参考になりました。
http://blog.mudatobunka.org/entry/2016/10/03/014520
このようにOpenCVではグレースケールの画像を与えることで簡単に顔の検出と位置の特定ができるようになります。
特徴量を検出する工程をビジュアライズ化した動画は下記になります。

OpenCV Face Detection: Visualized from Adam Harvey on Vimeo.
またチュートリアルよりも若干詳しい解説は下記になります。
https://news.mynavi.jp/article/computer_vision-11/

顔の部分の画像を切り出す

次に顔の部分を切りだします。
上記の検出器で顔部分のRect(x,y,w,h)が取得できるので

def crop_faces(gray, faces):
    return [gray[y: y + h, x: x + w] for x,y,w,h in faces]

上記ようなメソッドでグレースケールの画像から顔を切り出すことができます。切り出された画像が下記です。

綺麗に顔の部分が抽出されています。

顔の画像がピンボケ/手ぶれしてないかを検証

これは前回実装した実装がそのまま利用できます。

def variance_of_laplacian(image):
    return cv2.Laplacian(image, cv2.CV_64F)

上記のメソッドに顔の画像を与えてください。返されたlaplacianのスコアを利用すれば、ピンボケ/手ぶれしてないかを判断できます。詳細は前回の記事が参考になります。

実装結果

全体の実装はGitHubで公開しています。処理をする前にリサイズしたり、スコアや処理過程を理解しやすくするのに画像をいくつか出力するなどの処理が記述してあります。こういった部分については、この記事では解説していません。
https://github.com/hirocaster/opencv_sandbox/blob/master/detect_blur_for_face.py

python detect_blur_for_face.py -i ./images

上記のように画像を含むディレクトリパスを与えてあげれば、処理されます。
以下が今回の写真を検証したスコアを含む画像です。

Blurry: 93.34 Face: 390.33


写真全体としては93.34なのでピンボケ、手ぶれ写真と判定されています。ですが、顔の領域では390.33となっており、バッチリピントがあっています。(判断基準として、スコア100以下で、ピンボケ、手ぶれ写真としています)
今回の実装から、この写真はポートレートとして人間の顔が認識されて、顔領域のピントがあっているので、この写真はピンボケ、手ぶれ写真として削除候補にはならないと判断することができるようになります。
一方で、両方のスコアが100以下の場合は、ピンボケ、手ぶれ写真の可能性が高く、削除候補となります。

実際に利用してみて

実際に私が撮影した写真を数百毎か検証しましてみました。顔の誤認識さえしなければ、かなりの精度でピンボケ、手ぶれ写真を検出できます。
明かに顔でないものを誤認識したときはスコアが低くでる傾向があります。これはボケている背景などを誤認識しているためです。
対策として、顔認識の精度を上げても良いのですが、そうすると顔と認識されない写真も出てきてしまいます。よって、精度は上げず、複数顔が認識された場合はスコアが一番高いものを判定材料にすることによって、実用的な精度になりました。

タイトルとURLをコピーしました