É possível usar o feed da câmera capturado pelo ARCore em um pipeline de machine learning para criar uma experiência inteligente de realidade aumentada. O exemplo do ARCore ML Kit demonstra como usar o ML Kit e a API Google Cloud Vision para identificar objetos reais. O exemplo usa um modelo de machine learning para classificar objetos na visualização da câmera e anexa um rótulo a eles na cena virtual.
A amostra do kit de ML do ARCore é em Kotlin. Ele também está disponível como exemplo de ml_kotlin. no SDK do ARCore repositório do GitHub.
Usar a imagem de CPU do ARCore
O ARCore captura pelo menos dois conjuntos de fluxos de imagem por padrão:
- Um stream de imagem da CPU usado para reconhecimento de recursos e processamento de imagens. Por padrão, a imagem da CPU tem uma resolução VGA (640 x 480). O ARCore pode ser configurado para usar um fluxo de imagem de resolução mais alta, se necessário.
- Um stream de textura da GPU, que contém uma textura de alta resolução, geralmente com resolução de 1080p. Isso normalmente é usado como uma visualização da câmera voltada para o usuário.
Ela é armazenada na textura OpenGL especificada por
Session.setCameraTextureName()
. - Todos os streams adicionais especificados por
SharedCamera.setAppSurfaces()
.
Considerações sobre o tamanho da imagem da CPU
Nenhum custo extra vai ser gerado se o stream de CPU padrão com tamanho de VGA for usado, porque o ARCore usa esse stream para compreensão do mundo. Solicitar um stream com uma resolução diferente pode ser caro, porque será necessário capturar um stream adicional. Lembre-se de que uma resolução mais alta pode rapidamente se tornar cara para seu modelo: dobrar a largura e a altura da imagem quadruplica a quantidade de pixels nela.
Pode ser vantajoso reduzir a imagem se o modelo ainda puder ter um bom desempenho em uma imagem de resolução mais baixa.
Configurar outro stream de imagem da CPU de alta resolução
O desempenho do seu modelo de ML pode depender da resolução da imagem usada como entrada. Para ajustar a resolução desses streams, mude o CameraConfig
atual usando Session.setCameraConfig()
e selecione uma configuração válida em 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)
Recuperar a imagem da CPU
Recupere a imagem da CPU usando Frame.acquireCameraImage()
.
Descarte-as assim que deixar de ser necessárias.
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. }
Processar a imagem da CPU
Várias bibliotecas de machine learning podem ser usadas para processar a imagem da CPU.
- Kit de ML: o kit fornece uma API de detecção e rastreamento de objetos no dispositivo.
Ele vem com um classificador abrangente integrado à API e também pode usar modelos de classificação personalizados para abranger um domínio de objetos mais restrito.
Use
InputImage.fromMediaImage
para converter a imagem da CPU em umInputImage
. - Firebase Machine Learning: o Firebase oferece APIs Machine Learning que funcionam na nuvem ou no dispositivo. Consulte a documentação do Firebase sobre rótulos seguros com o Cloud Vision usando o Firebase Auth e o Functions no Android.
Mostrar resultados na sua cena de RA
Os modelos de reconhecimento de imagem muitas vezes emitem os objetos detectados indicando um ponto central ou um polígono delimitador que representa o objeto detectado.
Usando o ponto central ou o centro da caixa delimitadora da saída do modelo, é possível anexar uma âncora ao objeto detectado. Use Frame.hitTest()
para estimar a pose de um objeto na cena virtual.
Converta coordenadas IMAGE_PIXELS
em 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.
Use essas coordenadas VIEW
para realizar um teste de hit e criar uma âncora a partir do resultado:
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. }
Considerações sobre desempenho
Siga as recomendações a seguir para economizar energia de processamento e consumir menos energia:
- Não execute seu modelo de ML em cada frame recebido. Em vez disso, considere executar a detecção de objetos com um frame rate baixo.
- Considere usar um modelo de inferência de ML on-line para reduzir a complexidade computacional.
Próximas etapas
- Saiba mais sobre as Práticas recomendadas para a engenharia de ML.
- Saiba mais sobre as práticas de IA responsável.
- Siga o curso Noções básicas de machine learning com o TensorFlow.