O Kit de ML fornece um SDK otimizado para a segmentação de selfies.
Os recursos do segmentador de selfie são vinculados estaticamente ao seu app no tempo de criação. Isso aumenta o tamanho do download do app em cerca de 4,5 MB e a latência da API pode variam de 25 ms a 65 ms, dependendo do tamanho da imagem de entrada, conforme medido em um 4)
Faça um teste
- Teste o app de exemplo para 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
:
dependencies {
implementation 'com.google.mlkit:segmentation-selfie:16.0.0-beta6'
}
1. Criar uma instância do segmentador
Opções do segmentador
Para fazer segmentação em uma imagem, primeiro crie uma instância de Segmenter
especificando as opções a seguir.
Modo de detector
O Segmenter
opera de dois modos. Certifique-se de escolher aquela que corresponde ao seu caso de uso.
STREAM_MODE (default)
Esse modo foi criado para transmitir frames de vídeo ou de câmera. Nesse modo, o segmentador aproveita os resultados de frames anteriores para retornar resultados de segmentação mais suaves.
SINGLE_IMAGE_MODE
Esse modo foi criado para imagens únicas não relacionadas. Nesse modo, o segmentador processa cada imagem de maneira independente, sem suavização dos frames.
Ativar máscara de tamanho bruto
Solicita ao segmentador para retornar a máscara de tamanho bruto que corresponda ao tamanho da saída do modelo.
O tamanho da máscara bruta (por exemplo, 256 x 256) geralmente é menor que o tamanho da imagem de entrada. Chame SegmentationMask#getWidth()
e SegmentationMask#getHeight()
para saber o tamanho da máscara ao ativar essa opção.
Sem especificar essa opção, o segmentador redimensionará a máscara bruta para corresponder ao tamanho da imagem de entrada. Considere usar essa opção se você quiser aplicar a lógica de redimensionamento personalizada ou se o redimensionamento não for necessário para seu caso de uso.
Especifique as opções do segmentador:
Kotlin
val options = SelfieSegmenterOptions.Builder() .setDetectorMode(SelfieSegmenterOptions.STREAM_MODE) .enableRawSizeMask() .build()
Java
SelfieSegmenterOptions options = new SelfieSegmenterOptions.Builder() .setDetectorMode(SelfieSegmenterOptions.STREAM_MODE) .enableRawSizeMask() .build();
Crie uma instância de Segmenter
. Transmita as opções especificadas:
Kotlin
val segmenter = Segmentation.getClient(options)
Java
Segmenter segmenter = Segmentation.getClient(options);
2. Preparar a imagem de entrada
Para realizar a segmentação em uma imagem, crie um objeto InputImage
.
de uma matriz de bytes Bitmap
, media.Image
, ByteBuffer
, ou um arquivo na
o dispositivo.
Você pode criar um InputImage
de diferentes origens, cada uma explicada abaixo.
Como usar um media.Image
Para criar um InputImage
de um objeto media.Image
, como quando você captura uma imagem de um
da câmera do dispositivo, transmita o objeto media.Image
e o
rotação para InputImage.fromMediaImage()
.
Se você usar o método
CameraX, os recursos OnImageCapturedListener
e
As classes ImageAnalysis.Analyzer
calculam o valor de rotação
para você.
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 informe o grau de rotação da imagem, pode calculá-lo usando o grau de rotação do dispositivo e a orientação da câmera no dispositivo:
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
grau de rotação para InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Usar um URI de arquivo
Para criar um InputImage
de um URI de arquivo, transmita o contexto do aplicativo e o URI do arquivo para
InputImage.fromFilePath()
. Isso é útil quando você
usar uma intent ACTION_GET_CONTENT
para solicitar que o usuário selecione
uma imagem do app Galeria.
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 InputImage
de uma ByteBuffer
ou ByteArray
, primeiro calcule a imagem
grau de rotação conforme descrito anteriormente para a entrada media.Image
.
Depois, crie o objeto InputImage
com o buffer ou a matriz, junto com o
altura, largura, formato de codificação de cores e grau de rotação:
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 InputImage
de 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.
3. Processar a imagem
Transmita o objeto InputImage
preparado para o método process
do Segmenter
.
Kotlin
Task<SegmentationMask> result = segmenter.process(image) .addOnSuccessListener { results -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
Task<SegmentationMask> result = segmenter.process(image) .addOnSuccessListener( new OnSuccessListener<SegmentationMask>() { @Override public void onSuccess(SegmentationMask mask) { // Task completed successfully // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
4. Acessar o resultado da segmentação
É possível ver o resultado da segmentação da seguinte maneira:
Kotlin
val mask = segmentationMask.getBuffer() val maskWidth = segmentationMask.getWidth() val maskHeight = segmentationMask.getHeight() for (val y = 0; y < maskHeight; y++) { for (val x = 0; x < maskWidth; x++) { // Gets the confidence of the (x,y) pixel in the mask being in the foreground. val foregroundConfidence = mask.getFloat() } }
Java
ByteBuffer mask = segmentationMask.getBuffer(); int maskWidth = segmentationMask.getWidth(); int maskHeight = segmentationMask.getHeight(); for (int y = 0; y < maskHeight; y++) { for (int x = 0; x < maskWidth; x++) { // Gets the confidence of the (x,y) pixel in the mask being in the foreground. float foregroundConfidence = mask.getFloat(); } }
Para um exemplo completo de como usar os resultados de segmentação, consulte o Amostra do guia de início rápido do Kit de ML.
Dicas para melhorar o desempenho
A qualidade dos resultados depende da qualidade da imagem de entrada:
- Para que o Kit de ML gere um resultado de segmentação preciso, a imagem precisa ter pelo menos 256 x 256 pixels.
- Uma imagem com foco inadequado também pode afetar a precisão. Se você não conseguir resultados aceitáveis, peça ao usuário para recapturar a imagem.
Se você quiser usar a segmentação em um aplicativo em tempo real, siga estas diretrizes para ter os melhores frame rates:
- Use
STREAM_MODE
. - Capture imagens em uma resolução mais baixa. No entanto, lembre-se também dos requisitos de dimensão de imagem dessa API.
- Considere ativar a opção de máscara de tamanho bruto e combinar toda a lógica de redimensionamento. Por exemplo, em vez de permitir que a API redimensione a máscara para corresponder ao tamanho da imagem de entrada e depois redimensione novamente para corresponder ao tamanho da visualização para exibição, solicite a máscara de tamanho bruto e combine essas duas etapas em uma.
- Se você usar o método
Camera
ou APIcamera2
, limitar chamadas ao detector. Se um novo vídeo fica disponível enquanto o detector está em execução, descarte esse frame. Consulte aVisionProcessorBase
no app de amostra do guia de início rápido para conferir um exemplo. - Se você usa a API
CameraX
, verificar se a estratégia de pressão de retorno está definida para o valor padrãoImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. Isso garante que apenas uma imagem será 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 enfileiradas entrega. Depois que a imagem que está sendo analisada é fechada, chamando ImageProxy.close(), a próxima imagem mais recente será entregue. - Se você usar a saída do detector para sobrepor elementos gráficos
a imagem de entrada, primeiro acesse o resultado do Kit de ML e, em seguida, renderize a imagem
e sobreposição em uma única etapa. Isso é renderizado na superfície da tela.
apenas uma vez para cada frame de entrada. Consulte a
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
ImageFormat.YUV_420_888
. Se você usar a API Camera mais antiga, capture imagens noImageFormat.NV21
.