OpenCV 3.0.0 による顔検出処理

opencv-logo

事の発端は別のところにあるのだが、画像処理ライブラリをいろいろと弄っているうちに OpenCV の顔検出機能を使ってみたので、その記録を以下に残したいと思う。

OpenCV は以前から気になるライブラリではあったのだが、個人的にはなかなか使う機会がなかった。今回は、2015 年 6 月 4 日に OpenCV 3.0.0 がリリースされたばかりだということもあり、試してみるには良いタイミングであろう。

掲載するプログラムは Java で書かれたものである。OpenCV が様々な言語に対応していることも災いして、Web 上にはいろいろな言語のサンプルコードを目にすることが多い。だが、自分が見たサンプルの大半は C か Python のコードだったので、Java ユーザの参考になれば非常に嬉しい。

OpenCV で行う静止画像の顔検出(OpenCV 3.0.0 で実行)

クラスパス

OpenCV のデータを入手したら、利用するのは

  1. OpenCV の JAR ファイル
  2. ネイティブライブラリ
  3. 顔検出用の設定ファイル

である。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: レナ画像

画像処理と言えばお馴染みの「レナ」でも無事に顔が検出できた。以下が元画像と解析結果である。

lena_std
レナ(入力画像)
lena_face
レナ(顔検出結果)
その2: フルベッキ群像写真

レナ画像は一人の画像だったので、次に集合写真(フルベッキ群像写真)で試してみた。フルベッキ群像写真とは、明治維新前後の集合写真である。オランダ人宣教師グイド・フルベッキを囲む 44 名の武士が写っている写真である。以下が、元画像と解析結果である。

フルベッキ集合写真(入力画像)
フルベッキ群像写真(入力画像)
フルベッキ群像写真(顔検出結果)
フルベッキ群像写真(顔検出結果)

上記の検出結果を見てみると、顔ではない部分についても誤検出してしまっている。が、かなりの精度で検出できていて、OpenCV の顔検出が利用価値の高い機能であることが体験できた。他の写真でも試してみたところ、少し隠れてしまっている顔はうまく検出できないことがあった。ある程度の false-positive、false-negative は致し方あるまい。

余談だが、顔ではない部分が四角く縁取られていると、そこに武士の霊が写っているのではないかと疑ってしまう。そういえばそんなソフトあったな……。

OpenCV 3.0.0 による顔検出処理” への2件のフィードバック

コメントを残す