はじめに
前回の記事では、肌色部分の抽出を行いました。今度はもう少し一歩進んで、顔など特徴をもった領域の認識をさせてみたいと思います。
なをOpenCVを初めて使う方は、事前に以下の記事をご参考にして頂きAndroid向けのOpenCVのセットアップをお願いします
OpenCV CascadeClassifier
特徴を持った領域の認識には、CascadeClassifierというクラスを利用します。このクラスは、カスケードファイルという物体の特徴を記述したXMLファイルが必要になります。
CascadeClassifier(java.lang.String filename)
カスケードファイルは、SDK内の以下のディレクトリにあります。
OpenCV-android-sdk/sdk/etc/libpcascades
今回は顔認識を致しますので、lbpcascade_frontalface.xmlというファイルを顔認識させるアプリケーションのres/rawにコピーしておいて下さい。
CascadeClassifierクラスを使用する前の準備
残念ながらCascadeClassifierクラスはリソースを読み込めず、ファイルを作成してファイルパスを指定する必要があります。よって、XMLの内容を事前にファイルに書き出す必要があります。今回は、setupCascadeFile()という以下のメソッドを作成して、特定ディレクトリ以下にカスケードファイルがない場合は作成するという実装をします。
private File setupCascadeFile() {
File cascadeDir = mContext.getDir("cascade", Context.MODE_PRIVATE);
File cascadeFile = null;
InputStream is = null;
FileOutputStream os = null;
try {
cascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml");
if (!cascadeFile.exists()) {
is = mContext.getResources().openRawResource(R.raw.lbpcascade_frontalface);
os = new FileOutputStream(cascadeFile);
byte[] buffer = new byte[4096];
int readLen = 0;
while ((readLen = is.read(buffer)) != -1) {
os.write(buffer, 0, readLen);
}
}
} catch (IOException e) {
return null;
} finally {
if (is != null) {
try {
is.close();
} catch (Exception e) {
// do nothing
}
}
if (os != null) {
try {
os.close();
} catch (Exception e) {
// do nothing
}
}
}
return cascadeFile;
}
CascadeClassifierインスタンスの作成
上記のカスケードファイルが作成されれば、CascadeClassifierクラスのインスタンス化は容易です。ここでは、OpenCVライブラリの読み込み後に呼び出される、onManagerConntected()メソッド内でCascadeClassifierのインスタンス化を行います。
private CascadeClassifier setupFaceDetector() {
File cascadeFile = setupCascadeFile();
if (cascadeFile == null) {
return null;
}
CascadeClassifier detector = new CascadeClassifier(cascadeFile.getAbsolutePath());
if (detector.empty()) {
return null;
}
return detector;
}
@Override
public void onManagerConnected(int status) {
super.onManagerConnected(status);
switch (status) {
case LoaderCallbackInterface.SUCCESS:
mCameraView.enableView();
mFaceDetector = setupFaceDetector();
break;
default:
break;
}
}
顔領域の認識
上記のインスタンス化されたCascadeClassifierを利用して領域を認識します。検出には、以下のdetectMultiScaleというメソッドを用います。入力画像にはグレースケールが必要です。
public void detectMultiScale(Mat image,
MatOfRect objects,
double scaleFactor,
int minNeighbors,
int flags,
Size minSize,
Size maxSize)
上記のメソッドを用いて獲得された顔領域に枠で囲む例が以下になります。ここでは、最低の領域サイズを画面の縦サイズの1/5と仮にしています。
@Override
public void onCameraViewStarted(int width, int height) {
if (mMinFaceSize == null) {
mMinFaceSize = new Size(height / 5, height / 5);
}
}
@Override
public void onCameraViewStopped() {
}
@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
Mat rgba = inputFrame.rgba();
if (mFaceDetector != null) {
MatOfRect faces = new MatOfRect();
mFaceDetector.detectMultiScale(inputFrame.gray(), faces, 1.1, 2, 2, mMinFaceSize,
new Size());
Rect[] facesArray = faces.toArray();
for (int i = 0; i < facesArray.length; i++) {
Imgproc.rectangle(rgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
}
}
return rgba;
}
}

顔以外の領域の認識
上記のカスケードファイルを変更すれば他の領域を検出することも可能。例えば、以下のサイトからカスケードファイル(aGest.xml)をダウンロードして使用すると、握りこぶしを認識するようになる。