Используйте ARCore в качестве входных данных для моделей машинного обучения.

Вы можете использовать изображение с камеры, которое ARCore захватывает в конвейере машинного обучения, для создания интеллектуального опыта дополненной реальности. Образец ARCore ML Kit демонстрирует, как использовать ML Kit и API Google Cloud Vision для идентификации реальных объектов. В образце используется модель машинного обучения для классификации объектов в поле зрения камеры и прикрепления метки к объекту в виртуальной сцене.

Образец набора ARCore ML написан на Kotlin. Он также доступен в виде примера приложения ml_kotlin в репозитории ARCore SDK GitHub.

Используйте образ процессора ARCore

По умолчанию ARCore захватывает как минимум два набора потоков изображений:

  • Поток изображений ЦП , используемый для распознавания функций и обработки изображений. По умолчанию изображение ЦП имеет разрешение VGA (640x480). При необходимости ARCore можно настроить на использование дополнительного потока изображений с более высоким разрешением.
  • Поток текстур графического процессора , который содержит текстуру высокого разрешения, обычно 1080p. Обычно это используется в качестве предварительного просмотра камеры, обращенной к пользователю. Это хранится в текстуре OpenGL, указанной Session.setCameraTextureName() .
  • Любые дополнительные потоки, указанные SharedCamera.setAppSurfaces() .

Рекомендации по размеру образа ЦП

Никаких дополнительных затрат не возникает, если используется поток ЦП по умолчанию размером VGA, поскольку ARCore использует этот поток для понимания мира. Запрос потока с другим разрешением может оказаться дорогостоящим, поскольку потребуется захватить дополнительный поток. Имейте в виду, что более высокое разрешение может быстро стать дорогостоящим для вашей модели: удвоение ширины и высоты изображения увеличивает количество пикселей в изображении в четыре раза.

Возможно, будет полезно уменьшить масштаб изображения, если ваша модель по-прежнему может хорошо работать с изображением с более низким разрешением.

Настройте дополнительный поток изображений ЦП высокого разрешения.

Производительность вашей модели машинного обучения может зависеть от разрешения изображения, используемого в качестве входных данных. Разрешение этих потоков можно настроить, изменив текущую CameraConfig с помощью Session.setCameraConfig() и выбрав допустимую конфигурацию из Session.getSupportedCameraConfigs() .

Джава

CameraConfigFilter cameraConfigFilter =
    new CameraConfigFilter(session)
        // World-facing cameras only.
        .setFacingDirection(CameraConfig.FacingDirection.BACK);
List<CameraConfig> supportedCameraConfigs =
    session.getSupportedCameraConfigs(cameraConfigFilter);

// Select an acceptable configuration from supportedCameraConfigs.
CameraConfig cameraConfig = selectCameraConfig(supportedCameraConfigs);
session.setCameraConfig(cameraConfig);

Котлин

val cameraConfigFilter =
  CameraConfigFilter(session)
    // World-facing cameras only.
    .setFacingDirection(CameraConfig.FacingDirection.BACK)
val supportedCameraConfigs = session.getSupportedCameraConfigs(cameraConfigFilter)

// Select an acceptable configuration from supportedCameraConfigs.
val cameraConfig = selectCameraConfig(supportedCameraConfigs)
session.setCameraConfig(cameraConfig)

Получить образ процессора

Получите изображение процессора с помощью Frame.acquireCameraImage() . От этих изображений следует избавиться, как только они больше не нужны.

Джава

Image cameraImage = null;
try {
  cameraImage = frame.acquireCameraImage();
  // Process `cameraImage` using your ML inference model.
} catch (NotYetAvailableException e) {
  // NotYetAvailableException is an exception that can be expected when the camera is not ready
  // yet. The image may become available on a next frame.
} catch (RuntimeException e) {
  // A different exception occurred, e.g. DeadlineExceededException, ResourceExhaustedException.
  // Handle this error appropriately.
  handleAcquireCameraImageFailure(e);
} finally {
  if (cameraImage != null) {
    cameraImage.close();
  }
}

Котлин

// NotYetAvailableException is an exception that can be expected when the camera is not ready yet.
// Map it to `null` instead, but continue to propagate other errors.
fun Frame.tryAcquireCameraImage() =
  try {
    acquireCameraImage()
  } catch (e: NotYetAvailableException) {
    null
  } catch (e: RuntimeException) {
    // A different exception occurred, e.g. DeadlineExceededException, ResourceExhaustedException.
    // Handle this error appropriately.
    handleAcquireCameraImageFailure(e)
  }

// The `use` block ensures the camera image is disposed of after use.
frame.tryAcquireCameraImage()?.use { image ->
  // Process `image` using your ML inference model.
}

Обработка образа процессора

Для обработки образа ЦП можно использовать различные библиотеки машинного обучения.

Отображение результатов в вашей AR-сцене

Модели распознавания изображений часто выводят обнаруженные объекты, указывая центральную точку или ограничивающий многоугольник, представляющий обнаруженный объект.

Используя центральную точку или центр ограничивающей рамки, полученной из модели, можно прикрепить привязку к обнаруженному объекту. Используйте Frame.hitTest() , чтобы оценить положение объекта в виртуальной сцене.

Преобразуйте координаты IMAGE_PIXELS в координаты VIEW :

Джава

// Suppose `mlResult` contains an (x, y) of a given point on the CPU image.
float[] cpuCoordinates = new float[] {mlResult.getX(), mlResult.getY()};
float[] viewCoordinates = new float[2];
frame.transformCoordinates2d(
    Coordinates2d.IMAGE_PIXELS, cpuCoordinates, Coordinates2d.VIEW, viewCoordinates);
// `viewCoordinates` now contains coordinates suitable for hit testing.

Котлин

// Suppose `mlResult` contains an (x, y) of a given point on the CPU image.
val cpuCoordinates = floatArrayOf(mlResult.x, mlResult.y)
val viewCoordinates = FloatArray(2)
frame.transformCoordinates2d(
  Coordinates2d.IMAGE_PIXELS,
  cpuCoordinates,
  Coordinates2d.VIEW,
  viewCoordinates
)
// `viewCoordinates` now contains coordinates suitable for hit testing.

Используйте эти координаты VIEW для проведения проверки попадания и создания привязки на основе результата:

Джава

List<HitResult> hits = frame.hitTest(viewCoordinates[0], viewCoordinates[1]);
HitResult depthPointResult = null;
for (HitResult hit : hits) {
  if (hit.getTrackable() instanceof DepthPoint) {
    depthPointResult = hit;
    break;
  }
}
if (depthPointResult != null) {
  Anchor anchor = depthPointResult.getTrackable().createAnchor(depthPointResult.getHitPose());
  // This anchor will be attached to the scene with stable tracking.
  // It can be used as a position for a virtual object, with a rotation prependicular to the
  // estimated surface normal.
}

Котлин

val hits = frame.hitTest(viewCoordinates[0], viewCoordinates[1])
val depthPointResult = hits.filter { it.trackable is DepthPoint }.firstOrNull()
if (depthPointResult != null) {
  val anchor = depthPointResult.trackable.createAnchor(depthPointResult.hitPose)
  // This anchor will be attached to the scene with stable tracking.
  // It can be used as a position for a virtual object, with a rotation prependicular to the
  // estimated surface normal.
}

Вопросы производительности

Следуйте следующим рекомендациям, чтобы сэкономить вычислительную мощность и потреблять меньше энергии:

  • Не запускайте модель машинного обучения в каждом входящем кадре. Вместо этого рассмотрите возможность запуска обнаружения объектов с низкой частотой кадров.
  • Рассмотрите онлайн-модель вывода ML, чтобы уменьшить сложность вычислений.

Следующие шаги