É possível usar o Kit de ML para rotular objetos reconhecidos em uma imagem. O modelo padrão fornecido com o kit de ML oferece suporte a mais de 400 rótulos diferentes.
Engenharia de | Desagrupados | Agrupadas |
---|---|---|
Implementação | O download do modelo é feito dinamicamente pelo Google Play Services. | O modelo está estaticamente vinculado ao seu no tempo de compilação. |
Tamanho do app | Aumento de aproximadamente 200 KB no tamanho. | Aumento de aproximadamente 5,7 MB. |
Tempo de inicialização | Talvez seja necessário aguardar o download do modelo para usá-lo pela primeira vez. | O modelo fica disponível imediatamente |
Testar
- Teste o app de exemplo para conferir um exemplo de uso dessa API.
Antes de começar
No arquivo
build.gradle
no nível do projeto, inclua o repositório Maven do Google nas seçõesbuildscript
eallprojects
.Adicione as dependências das bibliotecas do Android do Kit de ML ao arquivo Gradle do módulo no nível do app, que geralmente é
app/build.gradle
. Escolha uma das seguintes dependências com base nas suas necessidades:Para agrupar o modelo e o app:
dependencies { // ... // Use this dependency to bundle the model with your app implementation 'com.google.mlkit:image-labeling:17.0.8' }
Para usar o modelo no Google Play Services:
dependencies { // ... // Use this dependency to use the dynamically downloaded model in Google Play Services implementation 'com.google.android.gms:play-services-mlkit-image-labeling:16.0.8' }
Se você optar por usar o modelo no Google Play Services, poderá configurar seu app para fazer o download automaticamente do modelo no dispositivo depois que o app for instalado da Play Store. Para fazer isso, adicione a seguinte declaração ao arquivo
AndroidManifest.xml
do seu app:<application ...> ... <meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="ica" > <!-- To use multiple models: android:value="ica,model2,model3" --> </application>
Você também pode verificar explicitamente a disponibilidade do modelo e solicitar o download pela API ModuleInstallClient do Google Play Services.
Se você não ativar os downloads do modelo no momento da instalação ou solicitar um download explícito, o modelo será transferido na primeira vez que você executar o rotulador. As solicitações feitas antes da conclusão do download não geram resultados.
Agora você já pode rotular imagens.
1. Preparar a imagem de entrada
Crie um objetoInputImage
usando sua imagem.
O rotulador de imagens é executado mais rapidamente quando você usa um Bitmap
ou, se você usa a
API Camera2, um YUV_420_888 media.Image
, que são recomendados quando
possível.
Você pode criar um objeto InputImage
de diferentes origens, cada uma explicada abaixo.
Como usar um media.Image
Para criar um objeto InputImage
usando um objeto media.Image
, como quando você captura uma imagem da
câmera de um dispositivo, transmita o objeto media.Image
e a rotação
da imagem para InputImage.fromMediaImage()
.
Se você usar a biblioteca
CameraX, as classes OnImageCapturedListener
e
ImageAnalysis.Analyzer
vão calcular o valor de rotação
automaticamente.
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null) { val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) // Pass image to an ML Kit Vision API // ... } } }
Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { @Override public void analyze(ImageProxy imageProxy) { Image mediaImage = imageProxy.getImage(); if (mediaImage != null) { InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees()); // Pass image to an ML Kit Vision API // ... } } }
Se você não usar uma biblioteca de câmera que ofereça o grau de rotação da imagem, será possível calculá-lo usando o grau de rotação do dispositivo e a orientação do sensor da câmera:
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 0) ORIENTATIONS.append(Surface.ROTATION_90, 90) ORIENTATIONS.append(Surface.ROTATION_180, 180) ORIENTATIONS.append(Surface.ROTATION_270, 270) } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Throws(CameraAccessException::class) private fun getRotationCompensation(cameraId: String, activity: Activity, isFrontFacing: Boolean): Int { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. val deviceRotation = activity.windowManager.defaultDisplay.rotation var rotationCompensation = ORIENTATIONS.get(deviceRotation) // Get the device's sensor orientation. val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360 } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360 } return rotationCompensation }
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 0); ORIENTATIONS.append(Surface.ROTATION_90, 90); ORIENTATIONS.append(Surface.ROTATION_180, 180); ORIENTATIONS.append(Surface.ROTATION_270, 270); } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private int getRotationCompensation(String cameraId, Activity activity, boolean isFrontFacing) throws CameraAccessException { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int rotationCompensation = ORIENTATIONS.get(deviceRotation); // Get the device's sensor orientation. CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360; } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360; } return rotationCompensation; }
Em seguida, transmita o objeto media.Image
e o
valor do grau de rotação para InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Como usar um URI de arquivo
Para criar um objeto InputImage
usando o URI de um arquivo, transmita o contexto do app e o URI do arquivo para
InputImage.fromFilePath()
. Isso é útil ao usar
uma intent ACTION_GET_CONTENT
para solicitar que o usuário selecione
uma imagem do app de galeria dele.
Kotlin
val image: InputImage try { image = InputImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
Java
InputImage image; try { image = InputImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Como usar ByteBuffer
ou ByteArray
Para criar um objeto InputImage
usando um ByteBuffer
ou ByteArray
, primeiro calcule o grau de rotação da imagem, conforme descrito anteriormente para a entrada media.Image
.
Em seguida, crie o objeto InputImage
com o buffer ou a matriz, com a altura,
a largura, o formato de codificação de cores e o grau de rotação da imagem:
Kotlin
val image = InputImage.fromByteBuffer( byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ) // Or: val image = InputImage.fromByteArray( byteArray, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 )
Java
InputImage image = InputImage.fromByteBuffer(byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ); // Or: InputImage image = InputImage.fromByteArray( byteArray, /* image width */480, /* image height */360, rotation, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 );
Como usar um Bitmap
Para criar um objeto InputImage
usando um objeto Bitmap
, faça a seguinte declaração:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
A imagem é representada por um objeto Bitmap
com os graus de rotação.
2. Configurar e executar o rotulador de imagens
Para rotular objetos em uma imagem, transmita o objetoInputImage
para o
método process
do ImageLabeler
.
Primeiro, receba uma instância de
ImageLabeler
.Se você quiser usar o rotulador de imagens no dispositivo, faça a seguinte declaração:
Kotlin
// To use default options: val labeler = ImageLabeling.getClient(ImageLabelerOptions.DEFAULT_OPTIONS) // Or, to set the minimum confidence required: // val options = ImageLabelerOptions.Builder() // .setConfidenceThreshold(0.7f) // .build() // val labeler = ImageLabeling.getClient(options)
Java
// To use default options: ImageLabeler labeler = ImageLabeling.getClient(ImageLabelerOptions.DEFAULT_OPTIONS); // Or, to set the minimum confidence required: // ImageLabelerOptions options = // new ImageLabelerOptions.Builder() // .setConfidenceThreshold(0.7f) // .build(); // ImageLabeler labeler = ImageLabeling.getClient(options);
- Depois, transmita a imagem para o método
process()
:
Kotlin
labeler.process(image) .addOnSuccessListener { labels -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
labeler.process(image) .addOnSuccessListener(new OnSuccessListener<List<ImageLabel>>() { @Override public void onSuccess(List<ImageLabel> labels) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
3. Conseguir informações sobre os objetos rotulados
Se a operação de rotulagem de imagem for bem-sucedida, uma lista de objetosImageLabel
será transmitida ao listener de êxito. Cada
objeto ImageLabel
representa algo que foi rotulado na imagem. O modelo base aceita mais de 400 rótulos diferentes.
É possível receber a descrição de texto de cada rótulo, o índice entre todos os rótulos compatíveis
com o modelo e a pontuação de confiança da correspondência. Exemplo:
Kotlin
for (label in labels) { val text = label.text val confidence = label.confidence val index = label.index }
Java
for (ImageLabel label : labels) { String text = label.getText(); float confidence = label.getConfidence(); int index = label.getIndex(); }
Dicas para melhorar o desempenho em tempo real
Caso você queira rotular imagens em um aplicativo em tempo real, siga estas diretrizes para ter as melhores taxas de frames:
- Se você usa a API
Camera
oucamera2
, limite as chamadas para o rotulador de imagens. Se um novo frame de vídeo ficar disponível enquanto o rotulador de imagens estiver em execução, elimine o frame. Consulte a classeVisionProcessorBase
no app de amostra do guia de início rápido para conferir um exemplo. - Se você usar a API
CameraX
, verifique se a estratégia de pressão de retorno está definida com o valor padrãoImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. Isso garante que somente uma imagem seja enviada para análise por vez. Se mais imagens forem produzidas quando o analisador estiver ocupado, elas serão descartadas automaticamente e não serão colocadas na fila. Depois que a imagem em análise for fechada chamando ImageProxy.close(), a próxima imagem mais recente será entregue. - Se você usar a saída do rotulador de imagens para sobrepor elementos gráficos na
imagem de entrada, primeiro acesse o resultado do Kit de ML e, em seguida, renderize a imagem
e a sobreposição em uma única etapa. Isso é renderizado na superfície de exibição
apenas uma vez para cada frame de entrada. Consulte as classes
CameraSourcePreview
eGraphicOverlay
no app de amostra do guia de início rápido para conferir um exemplo. - Se você usar a API Camera2, capture imagens no
formato
ImageFormat.YUV_420_888
. Se você usar a API Camera mais antiga, capture imagens no formatoImageFormat.NV21
.