Как стать автором
Обновить

Как с помощью HUAWEI ML Kit интегрировать в приложения стикеры с изображением лица

Время на прочтение 7 мин
Количество просмотров 971

Общая информация


Сейчас мы повсюду видим милые и смешные стикеры с изображением лица. Они используются не только в приложениях для камер, но и в социальных сетях и развлекательных приложениях. В этой статье я покажу вам, как создать 2D-стикеры с помощью инструмента HUAWEI ML Kit. Скоро мы также расскажем о процессе разработки 3D-стикеров, так что следите за обновлениями!

Сценарии


Приложения для съемки и редактирования фотографий, такие как селфи-камеры и социальные сети (TikTok, Weibo, WeChat и др.), часто предлагают набор стикеров для настройки изображений. С помощью этих стикеров пользователи могут создавать привлекательный и яркий контент и делиться им.

Подготовка


Добавьте репозиторий Maven Huawei в файл на уровне проекта build.gradle


Откройте файл build.gradle в корневом каталоге вашего проекта Android Studio.

image

Добавьте адрес репозитория Maven.

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

Добавьте зависимости SDK в файл на уровне приложения build.gradle


image

// 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));
}

На изображении ниже показано, как точки контура лица возвращаются с помощью API ML Kit.

image

Определение данных стикера в формате 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);
  }
}

Получилось! Ваш стикер с изображением лица готов.

Давайте проверим его в действии!

image

Для получения подробной информации перейдите на наш официальный сайт.
Вы также можете посмотреть пример кода.
Теги:
Хабы:
0
Комментарии 0
Комментарии Комментировать

Публикации

Истории

Работа

Data Scientist
66 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн