HUAWEI ML Kitでフェースステッカーをアプリに組み込む方法

##概要
今では、かわいく、おもしろいフェースステッカーをどこでも見るようになりました。カメラアプリだけでなく、ソーシャルメディアやエンタテイメントのアプリでも使用されています。今回の投稿では、HUAWEI ML Kitを使用して2Dステッカーを作成する方法をご紹介します。近いうちに、3Dステッカーの開発プロセスをお知らせしますので、ご期待ください。
##シナリオ
ビューティカメラやソーシャルメディアアプリ（TikTok、Weibo、WeChatなど）など、写真を撮影して編集する場合に使用するアプリは、画像のカスタマイズに使用できる幅広いステッカーを用意しています。このようなステッカーを使って、ユーザーはより人目を引き、共有できるコンテンツを作成できます。
##準備
###Huawei Mavenリポジトリをプロジェクトレベルのbuild.gradleファイルに追加する
**Android Studio**プロジェクトのルートディレクトリにある**build.gradle**ファイルを開きます。
![2.1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696852/e44f892e-c4ce-d800-c84c-6d3a08f8fc26.png)
Mavenリポジトリアドレスを追加します。

```javascript
buildscript {
     {        
        maven {url 'http://developer.huawei.com/repo/'}
    }    
}
allprojects {
    repositories {       
        maven { url 'http://developer.huawei.com/repo/'}
    }
}
```
###SDK依存関係をアプリレベルのbuild.gradleファイルに追加する
![2.2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696852/65ec048f-2cc9-8020-1ca2-806ded572f99.png)

```javascript
// Face detection SDK.
implementation 'com.huawei.hms:ml-computer-vision-face:2.0.1.300'
// Face detection model.
implementation 'com.huawei.hms:ml-computer-vision-face-shape-point-model:2.0.1.300'
```
###AndroidManifest.xmlファイルでカメラ、ネットワークアクセス、およびストレージの権限を適用する

```javascript
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
```
##コードの開発
###顔分析機能を設定する
###顔の輪郭ポイントを取得し、FacePointEngineに渡す

```java

MLFaceAnalyzerSetting detectorOptions;
detectorOptions = new MLFaceAnalyzerSetting.Factory()
        .setFeatureType(MLFaceAnalyzerSetting.TYPE_UNSUPPORT_FEATURES)
        .setShapeType(MLFaceAnalyzerSetting.TYPE_SHAPES)
        .allowTracing(MLFaceAnalyzerSetting.MODE_TRACING_FAST)
        .create();
detector = MLAnalyzerFactory.getInstance().getFaceAnalyzer(detectorOptions);
```
カメラのコールバックを使用してカメラのフレームデータを取得し、顔分析機能を呼び出して顔の輪郭ポイントを取得し、**FacePointEngine**に渡します。これにより、ステッカーフィルタはポイントを後で利用できます。

```java
@Override
public void onPreviewFrame(final byte[] imgData, final Camera camera) {
    int width = mPreviewWidth;
    int height = mPreviewHeight;

    long startTime = System.currentTimeMillis();

    if (isFrontCamera()){
        mOrientation = 0;
    }else {
        mOrientation = 2;
    }
    MLFrame.Property property =
            new MLFrame.Property.Creator()
                    .setFormatType(ImageFormat.NV21)
                    .setWidth(width)
                    .setHeight(height)
                    .setQuadrant(mOrientation)
                    .create();

    ByteBuffer data = ByteBuffer.wrap(imgData);

    SparseArray<MLFace> faces = detector.analyseFrame(MLFrame.fromByteBuffer(data,property));

    if(faces.size()>0){
        MLFace mLFace = faces.get(0);
        EGLFace EGLFace = FacePointEngine.getInstance().getOneFace(0);
        EGLFace.pitch = mLFace.getRotationAngleX();
        EGLFace.yaw = mLFace.getRotationAngleY();
        EGLFace.roll = mLFace.getRotationAngleZ() - 90;
        if (isFrontCamera())
            EGLFace.roll = -EGLFace.roll;
        if (EGLFace.vertexPoints == null) {
            EGLFace.vertexPoints = new PointF[131];
        }
        int index = 0;
 
        for (MLFaceShape contour : mLFace.getFaceShapeList()) {
            if (contour == null) {
                continue;
            }
            List<MLPosition> points = contour.getPoints();

            for (int i = 0; i < points.size(); i++) {
                MLPosition point = points.get(i);
                float x = ( point.getY() / height) * 2 - 1;
                float y = ( point.getX() / width ) * 2 - 1;
                if (isFrontCamera())
                    x = -x;
                PointF Point = new PointF(x,y);
                EGLFace.vertexPoints[index] = Point;
                index++;
            }
        }
        
        FacePointEngine.getInstance().putOneFace(0, EGLFace);
      
        FacePointEngine.getInstance().setFaceSize(faces!= null ? faces.size() : 0);
    }else{
        FacePointEngine.getInstance().clearAll();
    }
    long endTime = System.currentTimeMillis();
    Log.d("TAG","Face detect time: " + String.valueOf(endTime - startTime));
}
```
ML Kit APIが返す顔の輪郭ポイントは下記の画像で確認できます。
![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696852/3598348d-8610-25d2-6512-00c62fb4f468.jpeg)

###ステッカーのJSONデータの定義
###猫のステッカーを作る
猫のステッカーの**JSON**ファイルを作成し、フェースインデックスを使用して眉毛（84）の間の中心点と鼻（85）の先端の点を見つけます。猫の耳と鼻をペーストし、**JSON**ファイルと画像を**assets**ディレクトリに置きます。
### ステッカーをレンダリングして質感を与える
**GLSurfaceView**を使用してステッカーをレンダリングして質感を与えます。これは、**TextureView**を使用するより簡単です。**onSurfaceChanged**でステッカーフィルタのインスタンスを作成し、ステッカーのパスを渡してカメラを開始します。
###onSurfaceChangedでステッカーフィルタを初期化する
###onDrawFrameで画面にステッカーを描画する
以上で終わりです。作成したフェースステッカーは使用可能です。
早速、使ってみましょう。
![20200820-201607(eSpace).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/696852/5b70a3bf-2b0c-0fd4-b2ad-87c33fcfe2df.gif)
詳しくは、当社の[公式ウェブサイト](https://developer.huawei.com/consumer/en/hms/huawei-mlkit)をご覧ください。
当社の[デモコード](https://github.com/HMS-Core/hms-ml-demo)もご覧ください。