O caso de uso de análise de
imagem fornece ao seu app uma imagem acessível por CPU em que é possível
realizar processamento de imagens, visão computacional ou inferências de machine learning. O
app implementa um método
analyze()
executado em cada frame.
Para saber como integrar o Kit de ML do Google ao seu app do CameraX, consulte o Analisador do Kit de ML.
Modos de operação
Quando o pipeline de análise do app não consegue atender aos requisitos de frame rate da CameraX, essa biblioteca pode ser configurada para descartar frames de uma das seguintes maneiras:
Sem bloqueio (padrão): nesse modo, o executor sempre armazena em cache a imagem mais recente em um buffer de imagem, semelhante a uma fila com profundidade de uma, enquanto o app analisa a imagem anterior. Se a CameraX receber uma nova imagem antes do término do processamento do app, a nova imagem será salva no mesmo buffer, substituindo a imagem anterior. Observe que
ImageAnalysis.Builder.setImageQueueDepth()
não tem efeito nesse cenário, e o conteúdo do buffer é sempre substituído. É possível ativar esse modo sem bloqueio chamando o métodosetBackpressureStrategy()
comSTRATEGY_KEEP_ONLY_LATEST
. Para mais informações sobre implicações do executor, consulte a documentação de referência paraSTRATEGY_KEEP_ONLY_LATEST
.Bloqueio: nesse modo, o executor interno pode adicionar várias imagens à fila interna de imagens e começar a descartar frames somente quando a fila estiver cheia. O bloqueio ocorre em todo o escopo do dispositivo de câmera. Se o dispositivo tiver vários casos de uso vinculados, eles serão bloqueados enquanto a CameraX estiver processando essas imagens. Por exemplo, quando a prévia e a análise de imagem estão vinculadas a um dispositivo de câmera, a prévia também é bloqueada durante o processamento das imagens pela CameraX. É possível ativar o modo de bloqueio transmitindo
STRATEGY_BLOCK_PRODUCER
para o métodosetBackpressureStrategy()
. Também é possível configurar a profundidade da fila de imagens usando ImageAnalysis.Builder.setImageQueueDepth().
Com um analisador de baixa latência e alto desempenho, em que o tempo total para analisar uma imagem é menor que a duração de um frame da CameraX (16 ms para 60 fps, por exemplo), qualquer um dos modos de operação oferece uma experiência suave de maneira geral. O modo de bloqueio ainda pode ser útil em algumas situações, como no caso de instabilidades muito breves no sistema.
Com um analisador de alta latência e alto desempenho, o modo de bloqueio com uma fila mais longa é necessário para compensar a latência. No entanto, o app ainda pode processar todos os frames.
Com uma alta latência e um analisador demorado (que não consegue processar todos os frames), um modo sem bloqueio pode ser a escolha mais adequada, já que os frames precisam ser descartados no caminho de análise, mas outros casos de uso vinculados simultaneamente ainda podem ver todos os frames.
Implementação
Para usar a análise de imagem no seu app, siga estas etapas:
- Crie um caso de uso de
ImageAnalysis
. - Crie uma interface
ImageAnalysis.Analyzer
. - Defina seu analisador como
ImageAnalysis
. - Vincule
o proprietário do ciclo de vida, o seletor de câmera e o caso de uso de
ImageAnalysis
ao ciclo de vida.
Imediatamente após a vinculação, a CameraX envia imagens para seu analisador registrado.
Depois de concluir a análise, chame
ImageAnalysis.clearAnalyzer()
ou desvincule o caso de uso de ImageAnalysis
para interromper a análise.
Criar um caso de uso de ImageAnalysis
A classe ImageAnalysis
conecta
seu analisador (um consumidor de imagem) à CameraX, que é uma produtora de imagens.
Os apps podem usar a classe
ImageAnalysis.Builder
para criar um objeto ImageAnalysis
. Com a ImageAnalysis.Builder
,
o app pode configurar o seguinte:
- Parâmetros de saída da imagem:
- Formato: a CameraX tem suporte aos formatos
YUV_420_888
eRGBA_8888
pelo métodosetOutputImageFormat(int)
. O formato padrão éYUV_420_888
. - Resolução e proporção: você pode definir um desses parâmetros, mas não os dois ao mesmo tempo.
- Rotação.
- Nome do destino: use esse parâmetro para fins de depuração.
- Formato: a CameraX tem suporte aos formatos
- Controles de fluxo de imagem:
Os apps podem definir a resolução ou a proporção, mas não
ambos. A resolução exata depende da proporção
ou do tamanho solicitado para o app e dos recursos de hardware e pode ser diferente da proporção
ou do tamanho solicitado. Para informações sobre o algoritmo de correspondência de resolução, consulte
a documentação de
setTargetResolution()
Um app pode configurar os pixels da imagem de saída para ficarem em espaços de
cor YUV (padrão) ou RGBA. Ao configurar um formato de saída RGBA, o CameraX converte internamente
imagens de YUV para RGBA e empacota os bits de imagem na classe
ByteBuffer
do primeiro plano de ImageProxy (os outros dois planos não são usados) com
esta sequência:
ImageProxy.getPlanes()[0].buffer[0]: alpha
ImageProxy.getPlanes()[0].buffer[1]: red
ImageProxy.getPlanes()[0].buffer[2]: green
ImageProxy.getPlanes()[0].buffer[3]: blue
...
Ao realizar uma análise de imagem complicada, em que o dispositivo não consegue acompanhar o frame rate, configure a CameraX para descartar frames com as estratégias descritas na seção Modos de operação desta página.
Criar seu analisador
Os aplicativos podem criar analisadores implementando
ImageAnalysis.Analyzer
interface e substituição de
analyze(ImageProxy image)
Em cada analisador, os apps recebem uma interface
ImageProxy
, que é um wrapper
para Media.Image.
O formato da imagem pode ser consultado com
ImageProxy.getFormat()
.
O formato é um dos seguintes valores fornecidos
pelo app com a classe ImageAnalysis.Builder
:
ImageFormat.RGBA_8888
se o app solicitouOUTPUT_IMAGE_FORMAT_RGBA_8888
.ImageFormat.YUV_420_888
se o app solicitouOUTPUT_IMAGE_FORMAT_YUV_420_888
.
Consulte a seção Criar um caso de uso de ImageAnalysis para ver configurações de espaço de cores e onde os bytes de pixel podem ser recuperados.
Em um analisador, o app precisa fazer o seguinte:
- Analisar um frame específico o mais rápido possível, de preferência dentro do limite de tempo de frame rate especificado (por exemplo, menos de 32 ms para 30 fps). Se o app não conseguir analisar um frame com rapidez suficiente, considere um dos mecanismos suportados para descarte de frames.
- Libere a interface
ImageProxy
para a CameraX chamandoImageProxy.close()
. Não chame a função de fechamento da Media.Image encapsulada (Media.Image.close()
).
Os apps podem usar a Media.Image
encapsulada diretamente na interface ImageProxy.
Só não chame Media.Image.close()
na imagem encapsulada, porque isso corromperia
o mecanismo de compartilhamento de imagens na CameraX. Em vez disso, use
ImageProxy.close()
para liberar a Media.Image
para a CameraX.
Configurar o analisador para a ImageAnalysis
Depois de criar um analisador, use
ImageAnalysis.setAnalyzer()
para registrá-lo e dar início à análise. Quando ela terminar, use
ImageAnalysis.clearAnalyzer()
para remover o analisador registrado.
Apenas um analisador ativo pode ser configurado para a análise de imagens. Chamar
ImageAnalysis.setAnalyzer()
substitui o analisador registrado, se ele já
existir. Os apps podem definir um novo analisador a qualquer momento, antes ou depois de vincular o
caso de uso.
Vincular a ImageAnalysis a um ciclo de vida
É altamente recomendável vincular a ImageAnalysis
a um ciclo de vida
do AndroidX com a
função
ProcessCameraProvider.bindToLifecycle()
. Observe que a função bindToLifecycle()
retorna o
dispositivo Camera
selecionado, que pode ser usado
para ajustar configurações avançadas, por exemplo, exposição. Consulte este guia para ver mais informações sobre como controlar a saída da câmera.
O exemplo a seguir combina todas as etapas anteriores, vinculando
os casos de uso de ImageAnalysis
e Preview
da CameraX a um proprietário de lifeCycle
.
Kotlin
val imageAnalysis = ImageAnalysis.Builder() // enable the following line if RGBA output is needed. // .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) .setTargetResolution(Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() imageAnalysis.setAnalyzer(executor, ImageAnalysis.Analyzer { imageProxy -> val rotationDegrees = imageProxy.imageInfo.rotationDegrees // insert your code here. ... // after done, release the ImageProxy object imageProxy.close() }) cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, imageAnalysis, preview)
Java
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder() // enable the following line if RGBA output is needed. //.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) .setTargetResolution(new Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build(); imageAnalysis.setAnalyzer(executor, new ImageAnalysis.Analyzer() { @Override public void analyze(@NonNull ImageProxy imageProxy) { int rotationDegrees = imageProxy.getImageInfo().getRotationDegrees(); // insert your code here. ... // after done, release the ImageProxy object imageProxy.close(); } }); cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, imageAnalysis, preview);
Outros recursos
Para saber mais sobre o CameraX, consulte os seguintes recursos.
Codelab
Exemplo de código