HUAWEIMLキットを使用してフェイスステッカーをアプリに統合する方法

一般情報



今日、私たちはいたるところにかわいくて面白い顔のステッカーを見ます。カメラアプリだけでなく、ソーシャルメディアやエンターテインメントアプリでも使用されています。この記事では、HUAWEIMLキットツールを使用して2Dステッカーを作成する方法を紹介します。3Dステッカーの開発プロセスについても間もなく取り上げますので、ご期待ください。



スクリプト



セルフカメラやソーシャルメディア(TikTok、Weibo、WeChatなど)などの写真キャプチャおよび編集アプリは、多くの場合、画像をカスタマイズするための一連のステッカーを提供します。これらのステッカーを使用すると、ユーザーは魅力的で活気のあるコンテンツを作成して共有できます。



トレーニング



MavenHuaweiリポジトリをプロジェクトレベルのファイルbuild.gradleに追加します



開き、build.gradleファイルあなたのルートディレクトリにAndroidのスタジオプロジェクトを



画像



Mavenリポジトリアドレスを追加します。



buildscript {
    {        
      maven {url 'http://developer.huawei.com/repo/'}
  }    
}
allprojects {
  repositories {       
      maven { url 'http://developer.huawei.com/repo/'}
  }
}


SDKの依存関係をアプリレベルのファイルbuild.gradleに追加します



画像



// 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ファイルでカメラ、ネットワーク、メモリの権限をリクエストする



<!--Camera permission--> 
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA" />
<!--Write permission--> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--Read permission--> 
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />


コード開発



フェイスアナライザーを設定する



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に渡します



カメラコールバックを使用してカメラデータを取得してから、フェイスアナライザーを呼び出して顔の輪郭ポイントを取得し、それらのポイントをFacePointEngineに渡します。ステッカーフィルターは後でそれらを使用できるようになります。



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

  long startTime = System.currentTimeMillis();
  // Set the shooting directions of the front and rear cameras to be the same.
  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);
  // Call the face analyzer API.
  SparseArray<MLFace> faces = detector.analyseFrame(MLFrame.fromByteBuffer(data,property));
  // Determine whether face information is obtained.
  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;
      // Obtain the coordinates of a user's face contour points and convert them to the floating point numbers in normalized coordinate system of OpenGL.
      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++;
          }
      }
      // Insert a face object.
      FacePointEngine.getInstance().putOneFace(0, EGLFace);
      // Set the number of faces.
      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 KitAPIを使用して顔の輪郭点がどのように返されるかを示しています。



画像



JSON形式でのステッカーデータの定義



public class FaceStickerJson {

  public int[] centerIndexList;   // Center coordinate index list. If the list contains multiple indexes, these indexes are used to calculate the central point.
  public float offsetX;           // X-axis offset relative to the center coordinate of the sticker, in pixels.
  public float offsetY;           // Y-axis offset relative to the center coordinate of the sticker, in pixels.
  public float baseScale;         // Base scale factor of the sticker.
  public int startIndex;         // Face start index, which is used for computing the face width.
  public int endIndex;           // Face end index, which is used for computing the face width.
  public int width;               // Sticker width.
  public int height;             // Sticker height.
  public int frames;             // Number of sticker frames.
  public int action;             // Action. 0 indicates default display. This is used for processing the sticker action.
  public String stickerName;     // Sticker name, which is used for marking the folder or PNG file path.
  public int duration;           // Sticker frame displays interval.
  public boolean stickerLooping; // Indicates whether to perform rendering in loops for the sticker.
  public int maxCount;           // Maximum number of rendering times.
...
}


猫の写真でステッカーを作る



猫のステッカー用のJSON ファイル作成し、顔のインデックスを使用して眉毛(84)と鼻の先端(85)の間の中心点を定義します。猫の耳と鼻を貼り付けてから、JSONファイルと画像をassetsフォルダーに配置します



{
  "stickerList": [{
      "type": "sticker",
      "centerIndexList": [84],
      "offsetX": 0.0,
      "offsetY": 0.0,
      "baseScale": 1.3024,
      "startIndex": 11,
      "endIndex": 28,
      "width": 495,
      "height": 120,
      "frames": 2,
      "action": 0,
      "stickerName": "nose",
      "duration": 100,
      "stickerLooping": 1,
      "maxcount": 5
  }, {
      "type": "sticker",
      "centerIndexList": [83],
      "offsetX": 0.0,
      "offsetY": -1.1834,
      "baseScale": 1.3453,
      "startIndex": 11,
      "endIndex": 28,
      "width": 454,
      "height": 150,
      "frames": 2,
      "action": 0,
      "stickerName": "ear",
      "duration": 100,
      "stickerLooping": 1,
      "maxcount": 5
  }]
}


ステッカーをテクスチャにレンダリングする



GLSurfaceView クラスを使用してステッカーをテクスチャにレンダリングします。これTextureViewよりも単純ですonSurfaceChangedステッカーフィルターをインスタンス化し、ステッカーパスを渡して、カメラを起動します。



@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {

  GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  mTextures = new int[1];
  mTextures[0] = OpenGLUtils.createOESTexture();
  mSurfaceTexture = new SurfaceTexture(mTextures[0]);
  mSurfaceTexture.setOnFrameAvailableListener(this);

  // Pass the samplerExternalOES into the texture.
  cameraFilter = new CameraFilter(this.context);

  // Set the face sticker path in the assets directory.
  String folderPath ="cat";
  stickerFilter = new FaceStickerFilter(this.context,folderPath);

  // Create a screen filter object.
  screenFilter = new BaseFilter(this.context);

  facePointsFilter = new FacePointsFilter(this.context);
  mEGLCamera.openCamera();
}


ステッカーフィルターをonSurfaceChangedに初期化します



@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
  Log.d(TAG, "onSurfaceChanged. width: " + width + ", height: " + height);
  int previewWidth = mEGLCamera.getPreviewWidth();
  int previewHeight = mEGLCamera.getPreviewHeight();
  if (width > height) {
      setAspectRatio(previewWidth, previewHeight);
  } else {
      setAspectRatio(previewHeight, previewWidth);
  }
  // Set the image size, create a FrameBuffer, and set the display size.
  cameraFilter.onInputSizeChanged(previewWidth, previewHeight);
  cameraFilter.initFrameBuffer(previewWidth, previewHeight);
  cameraFilter.onDisplaySizeChanged(width, height);

  stickerFilter.onInputSizeChanged(previewHeight, previewWidth);
  stickerFilter.initFrameBuffer(previewHeight, previewWidth);
  stickerFilter.onDisplaySizeChanged(width, height);

  screenFilter.onInputSizeChanged(previewWidth, previewHeight);
  screenFilter.initFrameBuffer(previewWidth, previewHeight);
  screenFilter.onDisplaySizeChanged(width, height);

  facePointsFilter.onInputSizeChanged(previewHeight, previewWidth);
  facePointsFilter.onDisplaySizeChanged(width, height);
  mEGLCamera.startPreview(mSurfaceTexture);
}


onDrawFrameを使用して画面にステッカーを描画します



@Override
public void onDrawFrame(GL10 gl) {
  int textureId;
  // Clear the screen and depth buffer.
  GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);
  // Update a texture image.
  mSurfaceTexture.updateTexImage();
  // Obtain the SurfaceTexture transform matrix. 
  mSurfaceTexture.getTransformMatrix(mMatrix);
  // Set the camera display transform matrix.
  cameraFilter.setTextureTransformMatrix(mMatrix);

  // Draw the camera texture.
  textureId = cameraFilter.drawFrameBuffer(mTextures[0],mVertexBuffer,mTextureBuffer);
  // Draw the sticker texture.
  textureId = stickerFilter.drawFrameBuffer(textureId,mVertexBuffer,mTextureBuffer);
  // Draw on the screen.
  screenFilter.drawFrame(textureId , mDisplayVertexBuffer, mDisplayTextureBuffer);
  if(drawFacePoints){
      facePointsFilter.drawFrame(textureId, mDisplayVertexBuffer, mDisplayTextureBuffer);
  }
}


起こりました!フェイスステッカーの準備ができました。



実際に見てみましょう!



画像



詳しくは公式サイトをご覧くださいサンプルコード

もご覧ください



All Articles