ARCore를 머신러닝 모델의 입력으로 사용

ARCore가 머신러닝 파이프라인에서 캡처한 카메라 피드를 사용하여 지능형 증강 현실 환경을 만들 수 있습니다. ARCore ML Kit 샘플에서는 ML KitGoogle Cloud Vision API를 사용하여 실제 객체를 식별하는 방법을 보여줍니다. 이 샘플은 머신러닝 모델을 사용하여 카메라 뷰에서 객체를 분류하고 가상 장면의 객체에 라벨을 연결합니다.

ARCore ML Kit 샘플은 Kotlin으로 작성되었습니다. ARCore SDK GitHub 저장소에서 ml_kotlin 샘플 앱으로도 제공됩니다.

ARCore의 CPU 이미지 사용

ARCore는 기본적으로 다음과 같이 최소 두 개의 이미지 스트림 세트를 캡처합니다.

  • 특성 인식 및 이미지 처리에 사용되는 CPU 이미지 스트림. 기본적으로 CPU 이미지의 해상도는 VGA (640x480)입니다. 필요한 경우 추가 고해상도 이미지 스트림을 사용하도록 ARCore를 구성할 수 있습니다.
  • 일반적으로 해상도 1080p의 고해상도 텍스처가 포함된 GPU 텍스처 스트림 이는 보통 사용자 대상 카메라 미리보기로 사용됩니다. 이는 Session.setCameraTextureName()에서 지정한 OpenGL 텍스처에 저장됩니다.
  • SharedCamera.setAppSurfaces()에서 지정된 모든 추가 스트림.

CPU 이미지 크기 고려사항

ARCore는 전 세계 이해를 위해 이 스트림을 사용하므로 기본 VGA 크기의 CPU 스트림을 사용하는 경우 추가 비용이 발생하지 않습니다. 다른 해상도의 스트림을 요청하면 추가 스트림을 캡처해야 하므로 비용이 많이 들 수 있습니다. 해상도가 높으면 모델 비용이 빠르게 증가할 수 있습니다. 이미지의 너비와 높이를 두 배로 늘리면 이미지의 픽셀 수가 네 배가 됩니다.

모델이 저해상도 이미지에서 잘 작동할 수 있다면 이미지를 축소하는 것이 유용할 수 있습니다.

추가 고해상도 CPU 이미지 스트림 구성

ML 모델의 성능은 입력으로 사용되는 이미지의 해상도에 따라 달라질 수 있습니다. Session.setCameraConfig()를 사용해 현재 CameraConfig를 변경하고 Session.getSupportedCameraConfigs()에서 유효한 구성을 선택하면 스트림의 해상도를 조정할 수 있습니다.

Java

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

Kotlin

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)

CPU 이미지 검색

Frame.acquireCameraImage()를 사용하여 CPU 이미지를 가져옵니다. 이러한 이미지는 더 이상 필요하지 않으면 즉시 폐기해야 합니다.

Java

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

Kotlin

// 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.
}

CPU 이미지 처리

CPU 이미지를 처리하기 위해 다양한 머신러닝 라이브러리를 사용할 수 있습니다.

AR 장면에 결과 표시

이미지 인식 모델은 감지된 객체를 나타내는 중심점 또는 경계 다각형을 표시하여 감지된 객체를 출력하는 경우가 많습니다.

모델에서 출력되는 경계 상자의 중심점 또는 중심을 사용하면 감지된 객체에 앵커를 연결할 수 있습니다. Frame.hitTest()를 사용하여 가상 장면에서 객체의 포즈를 추정합니다.

IMAGE_PIXELS 좌표를 VIEW 좌표로 변환합니다.

Java

// 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.

Kotlin

// 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 좌표를 사용하여 조회 테스트를 실행하고 결과에서 앵커를 만듭니다.

Java

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.
}

Kotlin

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 모델을 실행하지 마세요. 대신 낮은 프레임 속도로 객체 감지를 실행하는 것이 좋습니다.
  • 계산 복잡성을 줄이기 위해 온라인 ML 추론 모델을 고려합니다.

다음 단계