事の発端は別のところにあるのだが、画像処理ライブラリをいろいろと弄っているうちに OpenCV の顔検出機能を使ってみたので、その記録を以下に残したいと思う。
OpenCV は以前から気になるライブラリではあったのだが、個人的にはなかなか使う機会がなかった。今回は、2015 年 6 月 4 日に OpenCV 3.0.0 がリリースされたばかりだということもあり、試してみるには良いタイミングであろう。
掲載するプログラムは Java で書かれたものである。OpenCV が様々な言語に対応していることも災いして、Web 上にはいろいろな言語のサンプルコードを目にすることが多い。だが、自分が見たサンプルの大半は C か Python のコードだったので、Java ユーザの参考になれば非常に嬉しい。
OpenCV で行う静止画像の顔検出(OpenCV 3.0.0 で実行)
クラスパス
OpenCV のデータを入手したら、利用するのは
- OpenCV の JAR ファイル
- ネイティブライブラリ
- 顔検出用の設定ファイル
である。jAR ファイルはコンパイル時のクラスパスに通し、ネイティブライブラリと設定ファイルはソースコード中でロードする。
ソースコード
JPEG の静止画像を読み込んで、顔検出を行い、検出した顔部分に四角を描画する Java プログラムを掲載する。
package yam.sat.sample; import java.io.File; import org.opencv.core.Core; import org.opencv.core.Mat; import org.opencv.core.MatOfRect; import org.opencv.core.Point; import org.opencv.core.Rect; import org.opencv.core.Scalar; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; import org.opencv.objdetect.CascadeClassifier; public class OpenCVSample { public void processing() { System.out.println("Start."); // Read image. File inputFile = new File("./sample.jpg"); System.out.println( String.format("Read %s.", inputFile.getName())); Mat image = Imgcodecs.imread( inputFile.getAbsolutePath()); if (image == null) { throw new IllegalArgumentException("Illegal input file."); } // Detect faces in the image. File settingFile = new File("./haarcascade_frontalface_default.xml"); if (!settingFile.exists()) { throw new RuntimeException("No setting file."); } MatOfRect faces = new MatOfRect(); CascadeClassifier faceDetector = new CascadeClassifier( settingFile.getAbsolutePath()); faceDetector.detectMultiScale(image, faces); System.out.println( String.format("Detected %s faces.", faces.toArray().length)); // Draw a bounding box around each face. for (Rect rect : faces.toArray()) { Imgproc.rectangle( image, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), new Scalar(0, 255, 0)); } // Save the result image of detection. String filename = "./sample-face.jpg"; System.out.println(String.format("Write %s", filename)); Imgcodecs.imwrite(filename, image); System.out.println(String.format("done.")); } public static void main(String[] args) { System.loadLibrary(Core.NATIVE_LIBRARY_NAME); new OpenCVSample().processing(); } }
上記のプログラムは OpenCV 3.0.0 で動作確認済みである。3.0.0 では API の仕様が変わったため、それ以前のバージョンとは互換性がないので注意が必要だ。
以下はコードの解説である。コード全体は 5 つの部分から構成されているので、その単位で解説する。
- 21 ~ 29 行目: JPEG ファイル読み込む。OpenCV の Imgcodecs クラスの imread メソッドを使用して、画像データを表す Mat オブジェクトを生成する。
- 31 ~ 44行目: 顔検出のための設定ファイル haarcascade_frontalface_default.xml を読み込む。解析器 CascadeClassifier クラスを生成し、detectMultiScale メソッドで画像中の顔部分を MatOfRect オブジェクトに出力する。
- 46 ~ 53行目: 画像の顔部分に四角を描画する。
- 55 ~ 59行目: 四角を書き入れた画像データをファイルに出力する。ファイル出力には Imgcodecs クラスの imwrite メソッドを使用している。
- 62 ~ 65行目: main 関数。OpenCV を利用するに当たり、OpenCV の JAR ファイルにクラスパスを通す必要があるが、それに加えてネイティブライブラリもロードする必要があり、63 行目の System.loadLibrary メソッドでそれを実行している。
出力結果
その1: レナ画像
画像処理と言えばお馴染みの「レナ」でも無事に顔が検出できた。以下が元画像と解析結果である。
その2: フルベッキ群像写真
レナ画像は一人の画像だったので、次に集合写真(フルベッキ群像写真)で試してみた。フルベッキ群像写真とは、明治維新前後の集合写真である。オランダ人宣教師グイド・フルベッキを囲む 44 名の武士が写っている写真である。以下が、元画像と解析結果である。
上記の検出結果を見てみると、顔ではない部分についても誤検出してしまっている。が、かなりの精度で検出できていて、OpenCV の顔検出が利用価値の高い機能であることが体験できた。他の写真でも試してみたところ、少し隠れてしまっている顔はうまく検出できないことがあった。ある程度の false-positive、false-negative は致し方あるまい。
余談だが、顔ではない部分が四角く縁取られていると、そこに武士の霊が写っているのではないかと疑ってしまう。そういえばそんなソフトあったな……。
“OpenCV 3.0.0 による顔検出処理” への2件のフィードバック