Você pode 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 Kit de ML do ARCore demonstra como usar o Kit de ML 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.
O exemplo do Kit de ML do ARCore é escrito em Kotlin. Ele também está disponível como o app de exemplo ml_kotlin no repositório GitHub do SDK do ARCore.
Usar a imagem da CPU do ARCore
Por padrão, o ARCore captura pelo menos dois conjuntos de streams de imagem:
- Um fluxo de imagens da CPU usado para reconhecimento de atributos 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 stream de imagem extra de resolução mais alta, se necessário.
- Um fluxo de textura de GPU, que contém textura de alta resolução, geralmente com resolução de 1080p. Normalmente, isso é usado como uma visualização da câmera voltada para o usuário.
Isso é armazenado na textura do OpenGL especificada por
Session.setCameraTextureName()
. - Qualquer transmissão adicional especificada por
SharedCamera.setAppSurfaces()
.
Considerações sobre o tamanho da imagem da CPU
Não haverá custo adicional se o stream de CPU padrão de tamanho VGA for usado, já que o ARCore usa esse stream para compreensão mundial. Solicitar um stream com uma resolução diferente pode ser caro, porque será necessário capturar outro stream. Lembre-se de que uma resolução mais alta pode custar rapidamente para seu modelo: dobrar a largura e a altura da imagem quadruplica a quantidade de pixels na imagem.
Pode ser vantajoso reduzir o tamanho da imagem, caso o modelo ainda tenha um bom desempenho em uma imagem de resolução mais baixa.
Configurar um fluxo extra de imagem da CPU de alta resolução
O desempenho do seu modelo de ML depende da resolução da imagem usada como entrada. A resolução desses streams pode ser ajustada mudando o CameraConfig
atual usando Session.setCameraConfig()
, selecionando uma configuração válida de 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)
Extrair a imagem da CPU
Extraia a imagem da CPU usando Frame.acquireCameraImage()
.
Descarte essas imagens assim que não forem mais 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
Para processar a imagem da CPU, várias bibliotecas de machine learning podem ser usadas.
- Kit de ML: oferece uma API Object Detection and Tracking no dispositivo.
Ele vem com um classificador geral integrado à API e também pode usar modelos de classificação personalizados para abranger um domínio mais restrito de objetos.
Use
InputImage.fromMediaImage
para converter a imagem da CPU em umaInputImage
. - Firebase Machine Learning: o Firebase fornece APIs Machine Learning que funcionam na nuvem ou no dispositivo. Consulte a documentação do Firebase sobre rótulos de imagens de maneira segura 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 geralmente emitem objetos detectados indicando um ponto central ou um polígono delimitador que representa o objeto detectado.
Usando o ponto ou centro da caixa delimitadora que é gerada pelo 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 coordenadas 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 estas coordenadas VIEW
para realizar um teste de hit e criar uma âncora com base no 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 a capacidade de processamento e consumir menos energia:
- Não execute seu modelo de ML em todos os frames recebidos. Em vez disso, considere executar a detecção de objetos em um frame rate baixo.
- Considere 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 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.