2015年8月15日土曜日

Google Play services 7.8 が出たので進化した、顔検出を試してみる

SYSTEM_KDです。

Google Play services 7.8 がリリースされ、新しいAPIが追加されたので、試してみたいと思います。

 

新しく追加されたAPIは、次の2つになります。

・Mobile Vision API

・Nearby Messages API

 

両方試してみたい所ですが、今回は「Mobile Vision API」の方を試してみたいと思います。

 

Mobile Vision APIを試す

そもそも、このAPIで何ができるかですが、画像や映像からリアルタイムに「顔」を認識できるようです。

画像から顔認識できるAPIは以前からありましたが、これはより多くの情報を認識できるようになっているようです。

具体的には、「目」「鼻」「口」「顔の向き」「目の開き」「笑顔」の情報を取得できるようです。
(おー!凄いですね!)

ということで、さっそくサンプルを取得して見てみたいと思います。
(サンプルはこちらにあるみたいです)

サンプルをそのまま動かして試しても良いのですが、ソースを見るのも兼ねて、別でプロジェクトを作成して、作っていきます。
(といっても、結局コピーするようなものですが^^;

 

では、さっそくサンプルからー。と言いたいとこですが、まずは準備を整えたいと思います。

Android SDK Manager で最新を取得。

SDK_Manager

Update 分をインストールします。

 

インストールできたら、次は「Google Play Services 7.8」が利用できるように、「build.gradle」へ1行追加

dependencies {
    ・・・
    compile 'com.google.android.gms:play-services:7.8.0'}

 

本題のコード部分に入ります。

とりあえず、簡単そうな画像から顔を認識するのを試したいと思います。

サンプルを見てみると、下記の流れになっているようです。

1.画像を読込。

2.顔検出

3.画像と検出結果を表示

静止画の顔検出を行うだけなら、かなり簡単そうです。

 

顔検出の準備

とりあえず、1の画像読込は割愛して、2の顔検出部分部分ですが、

    FaceDetector detector = new FaceDetector.Builder(getApplicationContext())
            .setTrackingEnabled(false)
            .setLandmarkType(FaceDetector.ALL_LANDMARKS)
            .build();

こんな感じらしいです。

 

静止画なので、trackingはOFFで、「目」や「鼻」「口」などを検出するために、「.setLandmarkType(FaceDetector.ALL_LANDMARKS)」と設定しているようです。

 

顔を検出する

検出の準備ができたら、実際の検出を行います。

    Frame frame = new Frame.Builder().setBitmap(bitmap).build();
    SparseArray<Face> faces = detector.detect(frame);

 

えーと、「1.」で読み込んだ画像から、Frameを作ってやって、それを先ほど準備した顔検出に渡してやると、検出した顔パーツの位置が返ってくるといったところかな。

 

サンプルでは、顔検出が利用可能化(端末のGoogle Play services対応したバージョンに更新されているか)をチェックする処理をはさんでいるようですので、ちょっと変更して追加。

    if (!detector.isOperational()) {
        Toast.makeText(getApplicationContext(), "残念ながらライブラリが更新されていないため、まだ未対応です", Toast.LENGTH_SHORT).show();

        finish();
    }

 

ちなみに、「Face」「FaceDetector」「Frame」は、Google Play servicesのものを利用する必要があります。

com.google.android.gms.vision.Frame;
com.google.android.gms.vision.face.Face;
com.google.android.gms.vision.face.FaceDetector;

 

結果表示

最後に検出結果の表示を行います。

サンプルでは、独自Viewを作って表示するようにしています。
(※R.id.faceViewは後述)

    FaceView overlay = (FaceView) findViewById(R.id.faceView);
    overlay.setContent(bitmap, faces);

    detector.release();

 

Viewの中身はこの様になっておりました。

public class FaceView extends View {
    private Bitmap mBitmap;
    private SparseArray<Face> mFaces;

    public FaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 表示を行う情報の設定
     */
    void setContent(Bitmap bitmap, SparseArray<Face> faces) {
        mBitmap = bitmap;
        mFaces = faces;
        invalidate();
    }

    /**
     * 描画部分
     * 画像の表示と検出結果の表示を行う
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if ((mBitmap != null) && (mFaces != null)) {
            double scale = drawBitmap(canvas);
            drawFaceAnnotations(canvas, scale);
        }
    }

    /**
     * ベースの画像表示(デバイスのサイズにスケーリングを行う)
     */
    private double drawBitmap(Canvas canvas) {
        double viewWidth = canvas.getWidth();
        double viewHeight = canvas.getHeight();
        double imageWidth = mBitmap.getWidth();
        double imageHeight = mBitmap.getHeight();
        double scale = Math.min(viewWidth / imageWidth, viewHeight / imageHeight);

        Rect destBounds = new Rect(0, 0, (int)(imageWidth * scale), (int)(imageHeight * scale));
        canvas.drawBitmap(mBitmap, null, destBounds, null);
        return scale;
    }

    /**
     * 検出結果の表示
     */
    private void drawFaceAnnotations(Canvas canvas, double scale) {
        Paint paint = new Paint();
        paint.setColor(Color.GREEN);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);

        for (int i = 0; i < mFaces.size(); ++i) {
            Face face = mFaces.valueAt(i);
            for (Landmark landmark : face.getLandmarks()) {
                int cx = (int) (landmark.getPosition().x * scale);
                int cy = (int) (landmark.getPosition().y * scale);
                canvas.drawCircle(cx, cy, 10, paint);
            }
        }
    }
}

読み込んだ画像と、顔検出した結果が重ねて表示されるようになっています。

 

レイアウトで「R.id.faceView」にあたるものを用意しておけば、完成です。

<com.xxxx.xxxx.FaceView
    android:id="@+id/faceView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

※FaceViewは、上記で用意した表示用のViewになります

 

実行結果

実行すると、検出した顔部分に緑の丸が描かれるようになっています。

実行する際の注意点ですが、エミュレータで実行される際は、通常の「System Image」ではなく、「Google APIs」のSystem Imageの方で実行する必要があります。

result1

 

フリー素材の「ぱくたそ」から、モデルさんの顔写真を持ってきて試してみましたが、正面を向いている写真はちゃんと検出されました。

 

result2

しかし、横向きや、斜めについては検出されませんでした。
(一度、正面で検出してトラッキングすれば、横とかが認識できるのかな??)

あと、複数人いる場合は、複数人検出されました。

result3

 

まとめ

静止画像から、顔を認識するだけなら、かなり簡単にできるようです。

動画をトラッキングしながら、行うサンプルも見てみたい所ですが、難しそうなので、別の機会にみてみたいと思います^^;

あと、バーコードも認識できるようになったようなので、そのうち試してみたいと思います。
(そういえば、標準のバーコード認識機能っていままでなかったような・・)

0 件のコメント:

コメントを投稿