Opções de configuração

Configure cada caso de uso do CameraX para controlar diferentes aspectos das operações do caso de uso.

Por exemplo, com o caso de uso de captura de imagem, é possível configurar a proporção desejada e um modo de flash. O código a seguir mostra um exemplo:

Kotlin

val imageCapture = ImageCapture.Builder()
    .setFlashMode(...)
    .setTargetAspectRatio(...)
    .build()

Java

ImageCapture imageCapture =
    new ImageCapture.Builder()
        .setFlashMode(...)
        .setTargetAspectRatio(...)
        .build();

Além das opções de configuração, alguns casos de uso expõem APIs para mudar as configurações de forma dinâmica após a criação deles. Para informações sobre a configuração específica dos casos de uso individuais, consulte Implementar uma visualização, Análise de imagem e Captura de imagem.

CameraXConfig

Para simplificar, o CameraX tem configurações padrão, como gerenciadores e executores internos adequados para a maioria dos cenários de uso. No entanto, caso o aplicativo tenha requisitos especiais ou prefira personalizar essas configurações, a CameraXConfig é a interface ideal.

Com a CameraXConfig, o aplicativo pode fazer o seguinte:

Modelo de uso

O procedimento a seguir descreve como usar a CameraXConfig:

  1. Crie um objeto CameraXConfig com as configurações personalizadas.
  2. Implemente a interface CameraXConfig.Provider na sua classe Application e retorne o objeto CameraXConfig no método getCameraXConfig().
  3. Adicione a classe Application ao arquivo AndroidManifest.xml, conforme descrito nesta página.

Veja como o exemplo de código a seguir restringe a geração de registros do CameraX apenas a mensagens de erro:

Kotlin

class CameraApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
           .setMinimumLoggingLevel(Log.ERROR).build()
   }
}

Mantenha uma cópia local do objeto CameraXConfig se o aplicativo precisa saber qual é a configuração do CameraX após a definição.

Limitador de câmeras

Durante a primeira invocação de ProcessCameraProvider.getInstance(), o CameraX enumera e consulta as características das câmeras disponíveis no dispositivo. Como o CameraX precisa se comunicar com componentes de hardware, esse processo pode levar um tempo incomum para cada câmera, principalmente em dispositivos mais simples. Se o aplicativo usa apenas câmeras específicas no dispositivo, como a frontal padrão, é possível configurar o CameraX para ignorar as outras, o que pode reduzir a latência de inicialização das câmeras usadas pelo aplicativo.

Se a classe CameraSelector transmitida para o CameraXConfig.Builder.setAvailableCamerasLimiter() filtrar uma câmera, o CameraX vai se comportar como se ela não existisse. Por exemplo, o código a seguir limita o aplicativo a usar apenas a câmera traseira padrão do dispositivo:

Kotlin

class MainApplication : Application(), CameraXConfig.Provider {
   override fun getCameraXConfig(): CameraXConfig {
       return CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
              .setAvailableCamerasLimiter(CameraSelector.DEFAULT_BACK_CAMERA)
              .build()
   }
}

Linhas de execução

Muitas das APIs de plataforma em que o CameraX foi criado exigem o bloqueio da comunicação entre processos (IPC) com hardwares que às vezes podem levar centenas de milissegundos para responder. Por isso, o CameraX só chama essas APIs por linhas de execução em segundo plano, o que garante que a linha de execução principal não seja bloqueada e que a IU continue fluida. O CameraX gerencia internamente essas linhas de execução em segundo plano para que esse comportamento pareça transparente. No entanto, alguns aplicativos exigem um controle rigoroso das linhas de execução. A CameraXConfig permite que um aplicativo defina as linhas de execução em segundo plano que serão usadas por CameraXConfig.Builder.setCameraExecutor() e CameraXConfig.Builder.setSchedulerHandler().

Executor da câmera

O executor da câmera é usado para todas as chamadas de API internas da plataforma da câmera, assim como para os callbacks dessas APIs. O CameraX aloca e gerencia uma interface Executor interna para realizar essas tarefas. No entanto, se o aplicativo exigir um controle mais rigoroso das linhas de execução, use CameraXConfig.Builder.setCameraExecutor().

Gerenciador do programador

O gerenciador do programador é usado para programar tarefas internas em intervalos fixos, como abrir a câmera novamente quando ela não estiver disponível. Esse gerenciador não executa jobs e só os envia para o executor da câmera. Às vezes, ele também é usado em plataformas de API legadas que exigem uma classe Handler para callbacks. Nesses casos, os callbacks ainda são enviados diretamente apenas para o executor da câmera. O CameraX aloca e gerencia uma classe HandlerThread interna para realizar essas tarefas, mas ela pode ser substituída usando CameraXConfig.Builder.setSchedulerHandler().

Geração de registros

A geração de registros do CameraX permite que os aplicativos filtrem mensagens do Logcat, já que é recomendável evitar mensagens muito detalhadas no código de produção. O CameraX oferece suporte para quatro níveis de geração de registros, do mais detalhado ao mais simples:

  • Log.DEBUG (padrão)
  • Log.INFO
  • Log.WARN
  • Log.ERROR

Consulte a documentação sobre registros do Android para ver descrições detalhadas desses níveis de registro. Use CameraXConfig.Builder.setMinimumLoggingLevel(int) para definir o nível de geração de registros adequado para seu aplicativo.

Seleção automática

O CameraX fornece automaticamente uma funcionalidade específica do dispositivo em que seu app está sendo executado. Por exemplo, o CameraX determinará automaticamente a melhor resolução a ser usada se você não especificar nenhuma ou se a resolução especificada não for compatível. Tudo isso é processado pela biblioteca, eliminando a necessidade de criar um código específico para o dispositivo.

O objetivo do CameraX é inicializar uma sessão de câmera. Isso significa que o CameraX compromete a resolução e a proporção com base na capacidade do dispositivo. Isso pode acontecer pelos seguintes motivos:

  • O dispositivo não é compatível com a resolução escolhida.
  • O dispositivo tem problemas de compatibilidade, como dispositivos legados que precisam de determinadas resoluções para funcionar corretamente.
  • Em alguns dispositivos, determinados formatos estão disponíveis apenas em algumas proporções.
  • O dispositivo prefere um "mod16 mais próximo" para codificação JPEG ou de vídeo. Consulte SCALER_STREAM_CONFIGURATION_MAP para ver mais informações.

Embora o CameraX crie e gerencie a sessão, sempre verifique os tamanhos de imagem retornados na saída do caso de uso no seu código e faça as modificações necessárias.

Rotação

Por padrão, a rotação da câmera é definida para corresponder à rotação da tela padrão durante a criação do caso de uso. Nesse caso padrão, o CameraX produz saídas para permitir que o app corresponda facilmente ao que você espera da visualização. É possível mudar a rotação para um valor personalizado que seja compatível com dispositivos de várias telas transmitindo a orientação atual da tela ao configurar os objetos de caso de uso ou de forma dinâmica, depois de terem sido criados.

Seu app pode definir a rotação desejada usando as configurações. Em seguida, ele pode atualizar as configurações de rotação usando os métodos das APIs de caso de uso (como ImageAnalysis.setTargetRotation()), mesmo que o ciclo de vida esteja em execução. Você poderá usar isso quando o app estiver fixo no modo retrato para que nenhuma reconfiguração ocorra na rotação, mas o caso de uso de foto ou de análise precisará ser informado sobre a rotação atual do dispositivo. Por exemplo, os dados de rotação podem ser necessários para que os rostos fiquem na orientação correta para detecção facial ou as fotos sejam definidas como paisagem ou retrato.

Os dados das imagens capturadas podem ser armazenados sem informações de rotação. Os dados Exif contêm informações de rotação para que os aplicativos da galeria possam exibir a imagem na orientação correta depois de salvá-la.

Para exibir dados de visualização com a orientação correta, é possível usar a saída de metadados de Preview.PreviewOutput() para criar transformações.

A amostra a seguir traz um exemplo de como definir a rotação em um evento de orientação:

Kotlin

override fun onCreate() {
    val imageCapture = ImageCapture.Builder().build()

    val orientationEventListener = object : OrientationEventListener(this as Context) {
        override fun onOrientationChanged(orientation : Int) {
            // Monitors orientation values to determine the target rotation value
            val rotation : Int = when (orientation) {
                in 45..134 -> Surface.ROTATION_270
                in 135..224 -> Surface.ROTATION_180
                in 225..314 -> Surface.ROTATION_90
                else -> Surface.ROTATION_0
            }

            imageCapture.targetRotation = rotation
        }
    }
    orientationEventListener.enable()
}

Java

@Override
public void onCreate() {
    ImageCapture imageCapture = new ImageCapture.Builder().build();

    OrientationEventListener orientationEventListener = new OrientationEventListener((Context)this) {
       @Override
       public void onOrientationChanged(int orientation) {
           int rotation;

           // Monitors orientation values to determine the target rotation value
           if (orientation >= 45 && orientation < 135) {
               rotation = Surface.ROTATION_270;
           } else if (orientation >= 135 && orientation < 225) {
               rotation = Surface.ROTATION_180;
           } else if (orientation >= 225 && orientation < 315) {
               rotation = Surface.ROTATION_90;
           } else {
               rotation = Surface.ROTATION_0;
           }

           imageCapture.setTargetRotation(rotation);
       }
    };

    orientationEventListener.enable();
}

Com base na rotação definida, cada caso de uso fará a rotação dos dados da imagem diretamente ou fornecerá metadados de rotação aos consumidores dos dados de imagem não alterados.

  • Visualização: a saída de metadados é fornecida para que a rotação da resolução desejada seja conhecida usando Preview.getTargetRotation().
  • ImageAnalysis: a saída de metadados é fornecida para que as coordenadas do buffer de imagem sejam conhecidas em relação às coordenadas de exibição.
  • ImageCapture: os metadados, o buffer ou os metadados Exif da imagem serão alterados para observar a configuração de rotação. O valor alterado depende da implementação da HAL.

Retângulo de corte

Por padrão, o retângulo de corte é o retângulo de buffer completo. É possível personalizá-lo com ViewPort e UseCaseGroup. Ao agrupar casos de uso e definir a janela de visualização, o CameraX garante que os retângulos de corte de todos os casos de uso no grupo apontem para a mesma área no sensor da câmera.

O snippet de código a seguir mostra como usar essas duas classes:

Kotlin

val viewPort =  ViewPort.Builder(Rational(width, height), display.rotation).build()
val useCaseGroup = UseCaseGroup.Builder()
    .addUseCase(preview)
    .addUseCase(imageAnalysis)
    .addUseCase(imageCapture)
    .setViewPort(viewPort)
    .build()
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup)

Java

ViewPort viewPort = new ViewPort.Builder(
         new Rational(width, height),
         getDisplay().getRotation()).build();
UseCaseGroup useCaseGroup = new UseCaseGroup.Builder()
    .addUseCase(preview)
    .addUseCase(imageAnalysis)
    .addUseCase(imageCapture)
    .setViewPort(viewPort)
    .build();
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, useCaseGroup);

ViewPort define o retângulo de buffer visível para os usuários finais. Depois, o CameraX calcula o maior retângulo de corte possível com base nas propriedades da janela de visualização e dos casos de uso anexados. Geralmente, para atingir um efeito WYSIWYG, você precisa configurar a janela de visualização com base no caso de uso de visualização. Uma maneira simples de acessar a janela de visualização é usar PreviewView.

Os snippets de código a seguir mostram como acessar o objeto ViewPort:

Kotlin

val viewport = findViewById<PreviewView>(R.id.preview_view).viewPort

Java

ViewPort viewPort = ((PreviewView)findViewById(R.id.preview_view)).getViewPort();

No exemplo anterior, o que o app recebe de ImageAnalysis e ImageCapture corresponde ao que o usuário final vê na PreviewView, supondo que o tipo de escala de PreviewView esteja definido como o padrão, FILL_CENTER. Depois de aplicar o retângulo de corte e a rotação ao buffer de saída, a imagem de todos os casos de uso será a mesma, embora possivelmente com resoluções diferentes. Para mais informações sobre como aplicar as informações de transformação, consulte Saída de transformação.

Seleção de câmera

O CameraX seleciona automaticamente o melhor dispositivo de câmera para os requisitos e casos de uso do aplicativo. Caso você queira usar um dispositivo diferente do selecionado, há algumas opções:

O exemplo de código a seguir ilustra como criar um CameraSelector para influenciar a seleção de um dispositivo:

Kotlin

fun selectExternalOrBestCamera(provider: ProcessCameraProvider):CameraSelector? {
   val cam2Infos = provider.availableCameraInfos.map {
       Camera2CameraInfo.from(it)
   }.sortedByDescending {
       // HARDWARE_LEVEL is Int type, with the order of:
       // LEGACY < LIMITED < FULL < LEVEL_3 < EXTERNAL
       it.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
   }

   return when {
       cam2Infos.isNotEmpty() -> {
           CameraSelector.Builder()
               .addCameraFilter {
                   it.filter { camInfo ->
                       // cam2Infos[0] is either EXTERNAL or best built-in camera
                       val thisCamId = Camera2CameraInfo.from(camInfo).cameraId
                       thisCamId == cam2Infos[0].cameraId
                   }
               }.build()
       }
       else -> null
    }
}

// create a CameraSelector for the USB camera (or highest level internal camera)
val selector = selectExternalOrBestCamera(processCameraProvider)
processCameraProvider.bindToLifecycle(this, selector, preview, analysis)

Resolução da câmera

Você pode permitir que o CameraX defina uma resolução de imagem com base em uma combinação dos recursos do dispositivo, nível de hardware com suporte, caso de uso e proporção. Como alternativa, é possível definir uma resolução específica ou uma proporção específica em casos de uso compatíveis com essa configuração.

Resolução automática

O CameraX pode determinar automaticamente as melhores configurações de resolução com base nos casos de uso especificados em cameraProcessProvider.bindToLifecycle(). Sempre que possível, especifique todos os casos de uso necessários para serem executados simultaneamente em uma única sessão de uma única chamada bindToLifecycle(). O CameraX determinará resoluções com base no conjunto de casos de uso vinculados considerando o nível de hardware compatível e contabilizando a variação específica do dispositivo, em que um dispositivo pode exceder ou não cumprir as configurações de fluxo disponíveis. A intenção é permitir que o aplicativo seja executado em uma ampla variedade de dispositivos, minimizando os caminhos de código específicos do dispositivo.

A proporção padrão para os casos de uso de captura e análise de imagens é de 4:3.

Os casos de uso têm uma proporção configurável para permitir que o aplicativo especifique a proporção desejada com base no design da IU. A saída do CameraX será produzida para corresponder às proporções solicitadas tanto quanto possível. Se não houver uma correspondência exata com a resolução compatível, aquela que atender à maioria das condições será selecionada. Assim, o aplicativo determina como a câmera aparecerá no app, e o CameraX define as melhores configurações de resolução para diferentes dispositivos.

Por exemplo, um app pode:

  • especificar uma resolução desejada de 4:3 ou 16:9 para um caso de uso;
  • especificar uma resolução personalizada em que o CameraX tentará encontrar a correspondência mais próxima;
  • especificar uma proporção de corte para ImageCapture.

O CameraX escolherá automaticamente as resoluções de superfície internas do Camera2. A tabela a seguir mostra as resoluções:

Caso de uso Resolução de superfície interna Resolução de dados de saída
Visualização Proporção: a resolução que melhor se ajusta à configuração desejada. Resolução de superfície interna. Os metadados são fornecidos para permitir uma visualização para cortar, redimensionar e girar até chegar à proporção desejada.
Resolução padrão: a resolução de visualização mais alta ou a resolução preferencial por dispositivo mais alta correspondente à proporção acima.
Resolução máxima: o tamanho de visualização, referente ao melhor tamanho correspondente à resolução de tela do dispositivo ou a 1080 p (1920x1080), o que for menor.
Análise de imagem Proporção: a resolução que melhor se ajusta à configuração desejada. Resolução de superfície interna.
Resolução padrão: a configuração de resolução padrão é de 640 x 480. O ajuste da resolução e da proporção correspondente resultará em uma resolução mais compatível.
Resolução máxima: a resolução máxima de saída do dispositivo da câmera no formato YUV_420_888, que é extraída do método StreamConfigurationMap.getOutputSizes(). A resolução desejada é definida como 640 x 480 por padrão. Portanto, se você quiser uma resolução maior que 640 x 480, use os métodos setTargetResolution() e setTargetAspectRatio() para usar uma das resoluções compatíveis mais aproximada.
Captura de imagem Proporção: a proporção que melhor se adapta à configuração. Resolução de superfície interna.
Resolução padrão: a resolução mais alta disponível ou a resolução preferencial do dispositivo mais alta correspondente à proporção acima.
Resolução máxima: a resolução máxima de saída do dispositivo da câmera em formato JPEG. Use o método StreamConfigurationMap.getOutputSizes() para extrair esse valor.

Especificar uma resolução

Você pode definir resoluções específicas ao criar casos de uso com o método setTargetResolution(Size resolution), conforme mostrado na amostra de código a seguir:

Kotlin

val imageAnalysis = ImageAnalysis.Builder()
    .setTargetResolution(Size(1280, 720))
    .build()

Java

ImageAnalysis imageAnalysis =
  new ImageAnalysis.Builder()
    .setTargetResolution(new Size(1280, 720))
    .build();

Não é possível definir a proporção e a resolução desejadas no mesmo caso de uso. Fazer isso gerará uma IllegalArgumentException ao criar o objeto de configuração.

Expresse o Size da resolução no frame da coordenada depois de girar os tamanhos compatíveis pela rotação desejada. Por exemplo, um dispositivo com orientação natural de retrato na rotação natural pode especificar uma imagem de retrato de 480 x 640, enquanto o mesmo dispositivo, girado 90 graus e na orientação de paisagem, pode especificar 640 x 480.

A resolução desejada tenta estabelecer um limite mínimo para a resolução da imagem. A resolução real da imagem será a disponível mais próxima que não seja menor que a resolução desejada, conforme determinado pela implementação da câmera. No entanto, se não houver uma resolução igual ou maior que a resolução desejada, será escolhida a resolução mais próxima disponível da desejada, ainda que menor. As resoluções com a mesma proporção do Size fornecido têm prioridade mais alta que as resoluções de proporções diferentes.

O CameraX aplicará a melhor resolução adequada com base nas solicitações. Se a principal necessidade for atender à proporção, especifique apenas setTargetAspectRatio, e o CameraX determinará uma resolução específica adequada, com base no dispositivo. Use setTargetResolution(Size resolution) se a necessidade principal do app for especificar uma resolução para tornar o processamento de imagens mais eficiente, por exemplo, em uma imagem de tamanho pequeno ou médio com base na capacidade de processamento do dispositivo.

Se o app exigir uma resolução exata, consulte a tabela em createCaptureSession() para determinar quais resoluções máximas são compatíveis com cada nível de hardware. Para verificar as resoluções específicas compatíveis com o dispositivo atual, consulte StreamConfigurationMap.getOutputSizes(int).

Se o app está sendo executado no Android 10 ou mais recente, você pode usar isSessionConfigurationSupported() para verificar uma SessionConfiguration específica.

Controlar a saída da câmera

Além de permitir que você configure a saída da câmera conforme necessário para cada caso de uso individual, o CameraX também implementa as interfaces abaixo para oferecer suporte a operações de câmera comuns a todos os casos de uso vinculados:

  • CameraControl permite a configuração de recursos comuns de câmera.
  • CameraInfo permite consultar os estados desses recursos comuns de câmera.

Estes são os recursos de câmera compatíveis com o CameraControl:

  • Zoom
  • Lanterna
  • Foco e medição (toque para focar)
  • Compensação de exposição

Receber instâncias de CameraControl e CameraInfo

Recupere instâncias do CameraControl e da CameraInfo usando o objeto Camera retornado por ProcessCameraProvider.bindToLifecyle(). O código a seguir mostra um exemplo:

Kotlin

val camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// For performing operations that affect all outputs.
val cameraControl = camera.cameraControl
// For querying information and states.
val cameraInfo = camera.cameraInfo

Java

Camera camera = processCameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// For performing operations that affect all outputs.
CameraControl cameraControl = camera.getCameraControl()
// For querying information and states.
CameraInfo cameraInfo = camera.getCameraInfo()

Por exemplo, você pode enviar zoom e outras operações do CameraControl após chamar bindToLifecycle(). Depois que você interrompe ou destrói a atividade usada para vincular a instância da câmera, o CameraControl não executa mais operações e retorna um ListenableFuture com falha.

Zoom

O CameraControl oferece dois métodos para mudar o nível de zoom:

  • setZoomRatio() define o zoom pela proporção.

    A proporção precisa estar no intervalo de CameraInfo.getZoomState().getValue().getMinZoomRatio() e CameraInfo.getZoomState().getValue().getMaxZoomRatio(). Caso contrário, a função retorna um ListenableFuture com falha.

  • setLinearZoom() define o zoom atual com um valor de zoom linear de 0 a 1,0.

    A vantagem do zoom linear é que ele garante que o campo de visão (FOV, na sigla em inglês) seja dimensionado com as mudanças. Por isso, esse recurso é ideal para uso com a visualização Slider.

CameraInfo.getZoomState() retorna um LiveData do estado atual do zoom. O valor muda quando a câmera é inicializada ou se o nível de zoom é definido usando setZoomRatio() ou setLinearZoom(). A chamada de qualquer um dos métodos define os valores com base em ZoomState.getZoomRatio() e ZoomState.getLinearZoom(). Isso é útil se você quer exibir o texto da proporção de zoom ao lado de um controle deslizante. Basta observar os LiveData do ZoomState para atualizar os dois valores sem precisar fazer uma conversão.

O ListenableFuture retornado por ambas as APIs oferece a opção de notificar o aplicativo quando uma solicitação recorrente com o valor de zoom especificado for concluída. Além disso, se você define um novo valor de zoom enquanto a operação anterior ainda está em execução, o ListenableFuture da operação de zoom anterior falha imediatamente.

Lanterna

CameraControl.enableTorch(boolean) ativa ou desativa a lanterna.

CameraInfo.getTorchState() pode ser usado para consultar o estado atual da lanterna. Confira o valor retornado por CameraInfo.hasFlashUnit() para determinar se uma lanterna está disponível. Caso contrário, chamar CameraControl.enableTorch(boolean) faz com que o ListenableFuture retornado seja concluído imediatamente com um resultado com falha e define o estado da lanterna como TorchState.OFF.

Quando a lanterna está ativada, ela permanece assim durante a captura de fotos e vídeos, independentemente da configuração do flashMode. O flashMode na ImageCapture funciona apenas quando a lanterna está desativada.

Foco e medição

CameraControl.startFocusAndMetering() aciona a medição de foco e exposição automáticos definindo regiões de medição de AF/AE/AWB (foco, exposição e balanço de branco automáticos) com base na FocusMeteringAction determinada. Isso é usado com frequência para implementar o recurso "toque para focar" em muitos apps de câmera.

MeteringPoint

Para começar, crie um MeteringPoint usando MeteringPointFactory.createPoint(float x, float y, float size). Um MeteringPoint representa um único ponto na Surface da câmera. Ele é armazenado em uma forma normalizada para que possa ser facilmente convertido em coordenadas do sensor para especificar regiões de AF/AE/AWB.

O tamanho do MeteringPoint varia de 0 a 1, com tamanho padrão de 0,15f. Ao chamar MeteringPointFactory.createPoint(float x, float y, float size), o CameraX cria uma região retangular centralizada em (x, y) para o size fornecido.

O código a seguir demonstra como criar um MeteringPoint.

Kotlin

// Use PreviewView.getMeteringPointFactory if PreviewView is used for preview.
previewView.setOnTouchListener((view, motionEvent) ->  {
val meteringPoint = previewView.meteringPointFactory
    .createPoint(motionEvent.x, motionEvent.y)
…
}

// Use DisplayOrientedMeteringPointFactory if SurfaceView / TextureView is used for
// preview. Please note that if the preview is scaled or cropped in the View,
// it’s the application's responsibility to transform the coordinates properly
// so that the width and height of this factory represents the full Preview FOV.
// And the (x,y) passed to create MeteringPoint may need to be adjusted with
// the offsets.
val meteringPointFactory = DisplayOrientedMeteringPointFactory(
     surfaceView.display,
     camera.cameraInfo,
     surfaceView.width,
     surfaceView.height
)

// Use SurfaceOrientedMeteringPointFactory if the point is specified in
// ImageAnalysis ImageProxy.
val meteringPointFactory = SurfaceOrientedMeteringPointFactory(
     imageWidth,
     imageHeight,
     imageAnalysis)

startFocusAndMetering e FocusMeteringAction

Para invocar startFocusAndMetering(), os aplicativos precisam criar uma FocusMeteringAction, que consiste em um ou mais MeteringPoints com combinações opcionais de modo de medição de FLAG_AF, FLAG_AE e FLAG_AWB. O código a seguir demonstra esse uso.

Kotlin

val meteringPoint1 = meteringPointFactory.createPoint(x1, x1)
val meteringPoint2 = meteringPointFactory.createPoint(x2, y2)
val action = FocusMeteringAction.Builder(meteringPoint1) // default AF|AE|AWB
      // Optionally add meteringPoint2 for AF/AE.
      .addPoint(meteringPoint2, FLAG_AF | FLAG_AE)
      // The action will be canceled in 3 seconds (if not set, default is 5s).
      .setAutoCancelDuration(3, TimeUnit.SECONDS)
      .build()

val result = cameraControl.startFocusAndMetering(action)
// Adds listener to the ListenableFuture if you need to know the focusMetering result.
result.addListener({
   // result.get().isFocusSuccessful returns if the auto focus is successful or not.
}, ContextCompat.getMainExecutor(this)

Como mostrado no código acima, startFocusAndMetering() usa uma FocusMeteringAction composta por um MeteringPoint para as regiões de medição de AF/AE/AWB e outro pelo MeteringPoint apenas para AF e AE.

Internamente, o CameraX a converte em MeteringRectangles da Camera2 e define os CONTROL_AF_REGIONS / CONTROL_AE_REGIONS / CONTROL_AWB_REGIONS correspondentes à solicitação de captura.

Como nem todos os dispositivos são compatíveis com AF/AE/AWB e várias regiões, o CameraX executa a FocusMeteringAction da melhor forma possível. O CameraX vai usar o número máximo permitido de MeteringPoints, na ordem em que os pontos foram adicionados. Todos os MeteringPoints adicionados após a contagem máxima são ignorados. Por exemplo, se uma FocusMeteringAction for fornecida com três MeteringPoints em uma plataforma que aceita apenas dois, apenas os dois primeiros MeteringPoints vão ser usados. O MeteringPoint final é ignorado pelo CameraX.

Compensação de exposição

A compensação de exposição é útil quando os aplicativos precisam ajustar valores de exposição (EV) além do resultado de saída da exposição automática (AE, na sigla em inglês). Os valores de compensação de exposição são combinados da seguinte maneira para determinar a exposição necessária para as condições atuais da imagem:

Exposure = ExposureCompensationIndex * ExposureCompensationStep

O CameraX fornece a função Camera.CameraControl.setExposureCompensationIndex() para definir a compensação de exposição como um valor de índice.

Valores de índice positivos deixam a imagem mais clara, enquanto valores negativos escurecem a imagem. Os aplicativos podem consultar o intervalo compatível CameraInfo.ExposureState.exposureCompensationRange() descrito na próxima seção. Se o valor tiver suporte, o ListenableFuture retornado vai ser concluído quando o valor for ativado na solicitação de captura. Se o índice especificado estiver fora do intervalo com suporte, setExposureCompensationIndex() vai fazer com que o ListenableFuture retornado seja concluído imediatamente com um resultado de falha.

O CameraX mantém apenas a solicitação setExposureCompensationIndex() pendente mais recente. Chamar a função várias vezes antes que a solicitação anterior seja executada resulta no cancelamento dela.

O snippet a seguir define um índice de compensação de exposição e registra um callback para quando a solicitação de mudança de exposição foi executada.

Kotlin

camera.cameraControl.setExposureCompensationIndex(exposureCompensationIndex)
   .addListener({
      // Get the current exposure compensation index, it may be
      // different from the asked value in case this request was
      // canceled by a newer setting request.
      val currentExposureIndex = camera.cameraInfo.exposureState.exposureCompensationIndex
      …
   }, mainExecutor)
  • Camera.CameraInfo.getExposureState() recupera o ExposureState atual, incluindo:

    • A compatibilidade com o controle de compensação de exposição.
    • O índice de compensação de exposição atual.
    • O intervalo do índice de compensação de exposição.
    • A etapa usada no cálculo do valor de compensação de exposição.

Por exemplo, o código a seguir inicializa as configurações de uma exposição SeekBar com os valores dos ExposureState atuais.

Kotlin

val exposureState = camera.cameraInfo.exposureState
binding.seekBar.apply {
   isEnabled = exposureState.isExposureCompensationSupported
   max = exposureState.exposureCompensationRange.upper
   min = exposureState.exposureCompensationRange.lower
   progress = exposureState.exposureCompensationIndex
}

Outros recursos

Para saber mais sobre o CameraX, consulte os seguintes recursos.

Codelab

  • Introdução ao CameraX
  • Exemplo de código

  • Apps de exemplo do CameraX (link em inglês)
  • Comunidade de desenvolvedores

    Grupo de discussão do CameraX do Android