Pular para o conteúdo principal

Capítulo 15: Foco

A exposição controla o brilho. O foco controla o que é nítido. Uma foto perfeitamente exposta com foco suave é uma foto fracassada. Neste capítulo, você aprenderá como funcionam os sistemas de foco de smartphones, como conduzir o Foco Automático (AF) de forma confiável via Camera2 e como implementar um controle deslizante de foco manual suave usando LENS_FOCUS_DISTANCE.

O aplicativo Android Camera Parameters demonstra tudo isso em seu painel de Foco — você pode assistir à transição da máquina de estados de AF ao vivo e arrastar o controle deslizante de foco manual para ver a lente se mover do infinito até a distância mínima de foco.


Foco Automático (AF) em Smartphones Modernos​

Antes de mergulhar nas especificidades da API, vamos entender os três mecanismos físicos de foco que os smartphones utilizam.

1. AF por Detecção de Contraste (CDAF) — Varredura Passiva​

A técnica de software: analise o quadro da imagem, procure o contraste máximo nas bordas (bordas nítidas = frequência espacial mais alta) e mova a lente até encontrar o pico de contraste.

  • Vantagem: Funciona em qualquer hardware de câmera (não precisa de pixels especiais)
  • Desvantagem: Lento. A lente deve caçar (hunt) para frente e para trás em toda a faixa de foco. Rótulos de texto de cena como "AF SCANNING" no mapa de estados da Camera2 correspondem a isso.

2. AF por Detecção de Fase (PDAF) — Varredura Ativa​

Fotodiodos especiais no sensor são divididos em duas metades. A diferença de fase entre as metades esquerda/direita mede diretamente quão longe e em qual direção a lente deve se mover — sem necessidade de "caça". Os telefones topo de linha de hoje usam o Dual-Pixel PDAF, onde cada pixel faz a detecção de fase.

  • Vantagem: Extremamente rápido (trava em < 100ms em boa luz); funciona de forma confiável em vídeo
  • Desvantagem: Dificuldade em pouca luz (não há fótons suficientes para computar a fase de forma confiável) e possui limites de distância mínima de foco.

3. Laser AF / ToF AF (Ativo) — Telêmetro​

Um módulo de hardware dedicado dispara um pulso de laser infravermelho, cronometra a reflexão e informa diretamente a distância do assunto ao ISP. Muito comum em telefones de gama média a premium.

  • Vantagem: Travamento ultrarrápido em qualquer alvo, mesmo na escuridão total (se o alvo refletir IR)
  • Desvantagem: Alcance efetivo limitado (máximo de ~50cm–5m), falha em objetos de vidro ou transparentes ao IR.

Os telefones reais combinam todos os três: PDAF para travamento aproximado rápido, CDAF para ajuste fino e Laser AF para cenas com pouca luz ou close-ups. O Camera2 expõe esse pipeline unificado como uma única máquina de estados abstrata.


Modos de AF: CONTROL_AF_MODE​

O Camera2 define estes modos de AF em CameraMetadata:

Modo (CONTROL_AF_MODE_*)ComportamentoCaso de Uso
OFFSem AF. Você define LENS_FOCUS_DISTANCE manualmente.Foco manual, empilhamento de foco, astrofotografia (trava no infinito)
AUTOAF de disparo único. Não faz nada até você enviar CONTROL_AF_TRIGGER = START, então varre uma vez e trava.Fotografia estática clássica de "apontar e disparar"
MACROIgual ao AUTO, mas tendencioso para detecção de assuntos próximos.Close-ups, digitalização de documentos, "modo comida"
CONTINUOUS_PICTURERe-foca constantemente, mas pausa a re-focagem quando você dispara uma captura estática para evitar mudança de foco durante a foto.Padrão para fotografia estática
CONTINUOUS_VIDEORe-foca constantemente — nunca pausa. Pode caçar visivelmente, mas mantém o vídeo em foco.Gravação de vídeo, videochamadas
EDOFProfundidade de Campo Estendida: foco profundo simulado por software/firmware. Sem movimento físico da lente.Dispositivos econômicos sem atuadores de movimento de lente

Duas notas críticas:

  1. Dispositivos EDOF (telefones baratos, câmeras de selfie) têm um plano focal fixo. Você nunca receberá FOCUSED_LOCKED deles — o máximo que você consegue é PASSIVE_SCAN → PASSIVE_FOCUSED → INACTIVE. O aplicativo Android Camera Parameters mostra explicitamente "Foco Fixo" para essas câmeras.

  2. Modos CONTINUOUS_* retornam para INACTIVE após inatividade, em vez de permanecerem travados. Não espere FOCUSED_LOCKED no modo contínuo — isso é apenas para AUTO/MACRO + gatilho explícito.


A Máquina de Estados de AF​

O Camera2 relata o status do AF via CaptureResult.CONTROL_AF_STATE. Entender esses estados é crucial para sequências de captura estática confiáveis.

Tabela de referência de estados:

CONTROL_AF_STATESignificadoPróxima Ação
INACTIVE (0)AF desligado, ocioso ou modo contínuo sem varredura no momentoSe no modo AUTO: envie TRIGGER_START
PASSIVE_SCAN (1)Modo contínuo está varrendo passivamenteAguarde; não dispare a captura estática ainda
PASSIVE_FOCUSED (2)Contínuo encontrou foco, mas NÃO travado (pode flutuar)Seguro disparar estática em CONTINUOUS_PICTURE (ele travará)
ACTIVE_SCAN (3)Gatilho explícito iniciou uma varreduraApenas aguarde...
NOT_FOCUSED_LOCKED (4)Falha ao encontrar foco, mas a lente está travada de qualquer maneiraAviso ao usuário; opcionalmente tente novamente ou capture assim mesmo
FOCUSED_LOCKED (5)SUCESSO. Foco encontrado e travado por hardware.Prossiga imediatamente para o gatilho de pré-captura AE
PASSIVE_UNFOCUSED (6)Contínuo não conseguiu travar, ainda varrendoMelhore a iluminação ou use um alvo diferente

Regra inegociável para fotografia estática: Nunca envie uma captura estática (especialmente com flash!) até ver FOCUSED_LOCKED. Pule esta etapa e você entregará um aplicativo que produz intermitentemente fotos suaves.


Distância de Foco: Dioptrias, Não Metros​

Aqui está a segunda "pegadinha" que confunde os desenvolvedores da Camera2 (após a surpresa do obturador em nanossegundos):

LENS_FOCUS_DISTANCE usa dioptrias (D), não metros. As dioptrias são o recíproco matemático da distância de foco:

Distância de Foco (metros) = 1.0 / Dioptrias
Dioptrias = 1.0 / Distância de Foco (metros)
Dioptrias (LENS_FOCUS_DISTANCE)Distância de Foco Física
0.0Infinito (∞) — estrelas, montanhas distantes
0.110 metros
0.254 metros
0.52 metros
1.01 metro
2.00.5 metro (50 cm)
5.00.2 metro (20 cm)
10.00.1 metro (10 cm)
20.00.05 metro (5 cm)

Por que dioptrias? Porque o atuador da lente se move linearmente com a potência óptica, não com a distância física. Uma varredura de foco de 0.0D → 20.0D corresponde a um movimento uniforme da lente, enquanto uma varredura de "metros" de 10m → 5cm seria altamente não linear.

Consultar a Distância Mínima de Foco​

Cada lente tem uma distância de foco mais próxima (você não pode focar fisicamente um objeto pressionado contra o vidro). Consulte-a:

// Valor máximo de dioptria útil para esta lente
val maxDiopters = characteristics.get(
CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE
) ?: 0.0f // 0.0f = lente EDOF de foco fixo (sem controle de foco!)

if (maxDiopters == 0.0f) {
Log.w("Focus", "Esta é uma lente de foco fixo. AF manual desativado.")
} else {
// A faixa de dioptria válida é [0.0f .. maxDiopters]
Log.d("Focus", "Faixa de foco: 0.0D (inf) → $maxDiopters D (${1/maxDiopters}m perto)")
}

Valores típicos:

  • Câmera traseira de telefone econômico: ~10D (foco mínimo de 10 cm)
  • Câmera wide topo de linha: ~15–25D (4–7 cm mínimo)
  • Câmera macro: ~30–50D (2–3 cm mínimo)
  • Câmera selfie frontal: Frequentemente 0.0D (foco fixo, EDOF)

Distância Hiperfocal (Conceito)​

Fotógrafos de paisagem adoram isso: ajuste o foco para a distância hiperfocal e tudo, desde a metade dessa distância até o infinito, ficará "aceitavelmente nítido". Em um telefone com abertura f/1.8 e uma lente wide padrão, a hiperfocal é de aproximadamente 0,5–1,0 metro.

Regra geral para smartphones: Ajustar LENS_FOCUS_DISTANCE = 2.0D (distância de foco de 50 cm) aproxima a hiperfocal na maioria das lentes wide de telefone. Bom para fotografia de paisagem e de rua onde você não quer esperar pelo AF.

// Pré-configuração de hiperfocal "tudo nítido"
const val HYPERFOCAL_DIOPTERS_APPROX = 2.0f

fun setHyperfocal(builder: CaptureRequest.Builder, characteristics: CameraCharacteristics) {
val maxD = characteristics.get(
CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE
) ?: 0.0f
val diopters = min(HYPERFOCAL_DIOPTERS_APPROX, maxD.takeIf { it > 0 } ?: 0.0f)
builder.set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_OFF)
builder.set(CaptureRequest.LENS_FOCUS_DISTANCE, diopters)
}

Quer calcular a hiperfocal precisa para sua lente exata? Você também precisará de LENS_INFO_AVAILABLE_FOCAL_LENGTHS (distância focal em mm) e o tamanho físico do pixel do sensor. Para 95% dos casos de uso de smartphones, 2.0D é próximo o suficiente.


Exemplo Completo 1: Gatilho de AF de Disparo Único e Captura​

Este é o fluxo básico de fotografia estática para o modo AUTO / MACRO. É também a sequência exata que a orquestração 3A no Capítulo 17 reutilizará.

Objetivo: Usuário toca em "Capturar" → conduz o AF até o foco travado → uma vez travado, envia a captura estática.

class AutoFocusCaptureHelper(
private val characteristics: CameraCharacteristics,
private val captureSession: CameraCaptureSession,
private val previewSurface: Surface,
private val jpegReaderSurface: Surface
) {
private var afTriggered = false
private var capturePlanned = false

fun triggerAutoFocusAndCapture(onCaptureComplete: () -> Unit) {
// ---- PASSO 1: Construir solicitação repetida com gatilho de AF explícito ----
val triggerRequest = captureSession.device.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW
).apply {
addTarget(previewSurface)

// Use o modo AUTO para garantir o estado LOCKED ao final
set(CaptureRequest.CONTROL_AF_MODE,
CameraMetadata.CONTROL_AF_MODE_AUTO)

// Dispare o gatilho de AF de disparo único AGORA
set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_START)
}

afTriggered = true
capturePlanned = true

// ---- PASSO 2: Inscrever nosso callback de rastreamento de estado ----
captureSession.setRepeatingRequest(
triggerRequest.build(),
object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(
session: CameraCaptureSession,
request: CaptureRequest,
result: TotalCaptureResult
) {
val afState = result.get(CaptureResult.CONTROL_AF_STATE)
?: return
Log.d("AF", "Estado AF: $afState")

when (afState) {
// --- CAMINHO DE SUCESSO ---
CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED -> {
if (capturePlanned) {
capturePlanned = false
submitStillCapture()
onCaptureComplete()
}
}
// --- CAMINHO DE FALHA: não pôde travar, mas tentaremos de qualquer maneira ---
CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED -> {
Log.w("AF", "AF não pôde travar — capturando de qualquer maneira (borrado?)")
if (capturePlanned) {
capturePlanned = false
submitStillCapture()
onCaptureComplete()
}
}
// --- AINDA VARRENDO: ignore ---
CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN,
CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN -> {
// Ainda trabalhando, não faça nada ainda
}
}
}
},
null // Handler no thread atual
)
}

private fun submitStillCapture() {
val stillRequest = captureSession.device.createCaptureRequest(
CameraDevice.TEMPLATE_STILL_CAPTURE
).apply {
addTarget(previewSurface)
addTarget(jpegReaderSurface)

// Mantenha o AF travado para esta foto — NÃO solte o gatilho ainda
set(CaptureRequest.CONTROL_AF_MODE,
CameraMetadata.CONTROL_AF_MODE_AUTO)
// Deixe AF_TRIGGER como está (START permanece até cancelarmos explicitamente)

set(CaptureRequest.JPEG_QUALITY, 95)
}

captureSession.capture(
stillRequest.build(),
object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(
session: CameraCaptureSession,
request: CaptureRequest,
result: TotalCaptureResult
) {
// Foto capturada — agora solte a trava de AF, volte ao contínuo
resetFocusToContinuous()
}
},
null
)
}

private fun resetFocusToContinuous() {
val resumePreview = captureSession.device.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW
).apply {
addTarget(previewSurface)
set(CaptureRequest.CONTROL_AF_MODE,
CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_CANCEL)
}
afTriggered = false
captureSession.setRepeatingRequest(resumePreview.build(), null, null)
}
}

Detalhe crítico: Você cancela o gatilho depois que a captura estática é concluída — não antes. Cancele muito cedo e a lente destrava durante a foto, produzindo uma foto suave.

Proteção contra timeout (não mostrada): Aplicativos reais adicionam um timeout de 2 a 3 segundos na varredura de AF. Se o ACTIVE_SCAN rodar por 3 segundos e nunca atingir FOCUSED_LOCKED, cancele e exiba uma dica de usuário "Toque para focar em uma área de alto contraste".


Exemplo Completo 2: Controle Deslizante SeekBar de Foco Manual​

Este é o recurso de Foco Manual voltado para o usuário que você viu em aplicativos de câmera profissionais. Um SeekBar mapeia a faixa de foco física de 0.0D → maxD suavemente.

Layout (res/layout/fragment_manual_focus.xml)​

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">

<TextView
android:id="@+id/tvFocusLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Foco: ∞ (infinito)"
android:textSize="14sp"/>

<SeekBar
android:id="@+id/seekFocus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="1000"/>
</LinearLayout>

Kotlin: Ligação de Fragment / Activity​

class ManualFocusController(
private val seekBar: SeekBar,
private val labelView: TextView,
private val characteristics: CameraCharacteristics,
private val captureSessionProvider: () -> CameraCaptureSession?,
private val previewSurface: Surface
) {
// O controle deslizante usa 1000 passos inteiros para precisão sub-dioptria
private val sliderSteps = 1000
private val maxDiopters = characteristics.get(
CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE
) ?: 0.0f

private var currentDiopters = 0.0f

init {
if (maxDiopters == 0.0f) {
seekBar.isEnabled = false
labelView.text = "Foco Fixo (Sem AF manual)"
} else {
bindSeekBar()
applyFocus(0.0f) // Começa no infinito
}
}

private fun bindSeekBar() {
// Converter int do slider [0..1000] ↔ dioptrias [0.0 .. maxDiopters]
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
private var lastUpdate = 0L

override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (!fromUser) return

// Limitar a ~30fps (33ms) — evita sobrecarregar o HAL com solicitações
val now = SystemClock.elapsedRealtime()
if (now - lastUpdate < 33L) return
lastUpdate = now

val diopters = progress.toFloat() / sliderSteps.toFloat() * maxDiopters
applyFocus(diopters)
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
// Alternar para o modo AF manual total imediatamente quando o usuário começa a arrastar
switchToManualMode()
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
// Aplicar valor exato final para remover erro de limitação (throttle)
val diopters = seekBar.progress.toFloat() / sliderSteps.toFloat() * maxDiopters
applyFocus(diopters, force = true)
}
})
}

private fun switchToManualMode() {
// CONTROL_AF_MODE_OFF desativa a condução automática do motor de AF
val session = captureSessionProvider() ?: return
val request = session.device.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW
).apply {
addTarget(previewSurface)
set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_OFF)
set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_USE_SCENE_MODE)
}
session.setRepeatingRequest(request.build(), null, null)
}

fun applyFocus(diopters: Float, force: Boolean = false) {
currentDiopters = diopters.coerceIn(0.0f, maxDiopters)

// Atualizar rótulo: mostrar "∞" para < 0.1D, "X.Y m" caso contrário
labelView.text = when {
currentDiopters < 0.1f -> "Foco: ∞ (infinito)"
else -> {
val meters = 1.0f / currentDiopters
String.format("Foco: %.1f D (%.2f m)", currentDiopters, meters)
}
}

// Construir e enviar uma solicitação repetida com a nova distância de foco
val session = captureSessionProvider() ?: return
val request = session.device.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW
).apply {
addTarget(previewSurface)
set(CaptureRequest.CONTROL_AF_MODE, CameraMetadata.CONTROL_AF_MODE_OFF)
set(CaptureRequest.LENS_FOCUS_DISTANCE, currentDiopters)
}

// Use setRepeatingRequest para que cada quadro de pré-visualização respeite o novo foco
session.setRepeatingRequest(request.build(), null, null)
}

// --- Auxiliares de predefinição ---
fun setInfinity() { seekBar.progress = 0; applyFocus(0.0f, true) }
fun setHyperfocalApprox() {
val d = min(2.0f, maxDiopters)
seekBar.progress = (d / maxDiopters * sliderSteps).toInt()
switchToManualMode()
applyFocus(d, true)
}
fun setNearest() { seekBar.progress = sliderSteps; applyFocus(maxDiopters, true) }
}

Detalhes principais da implementação:

  1. Limitação (Throttle). Os SeekBars disparam onProgressChanged até 200Hz. Enviar um setRepeatingRequest em cada evento inunda o HAL de trabalho, causando atraso. Um limite de 33ms restringe as atualizações a ~30fps — bastante suave para a velocidade física do motor da lente.

  2. Mudar para CONTROL_AF_MODE_OFF cedo. Se você estiver em CONTINUOUS_PICTURE e definir LENS_FOCUS_DISTANCE sem desativar o AF, o algoritmo de AF irá lutar contra você — voltando o foco para o que ele acha correto um quadro depois. A mudança deve acontecer primeiro, em onStartTrackingTouch.

  3. Atualizar via setRepeatingRequest, não em uma única capture(). O foco manual precisa permanecer em cada quadro de pré-visualização até que o usuário mova o controle deslizante novamente.

  4. Aplicar à força ao soltar. O limitador ignora posições intermediárias; quando o usuário levanta o dedo, aplique o valor final exato do controle deslizante.


Regiões de Foco (Tocar para Focar)​

Aplicativos de câmera modernos permitem tocar no visor para escolher um alvo de foco. O Camera2 implementa isso via CONTROL_AF_REGIONS — uma lista de retângulos (no espaço de coordenadas do active-array) com pesos.

// Converter um toque no Visor (x,y) em uma região de coordenadas do Sensor
fun createTapFocusRegion(viewfinderWidth: Int, viewfinderHeight: Int,
tapX: Float, tapY: Float,
characteristics: CameraCharacteristics): MeteringRectangle {
val activeArray = characteristics.get(
CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE
)!!
// Normalizar o toque [0..1] em cada eixo
val nx = tapX / viewfinderWidth.toFloat()
val ny = tapY / viewfinderHeight.toFloat()
// Mapear para o array ativo do sensor, criar uma região de 200×200 centrada no toque
val cx = (nx * activeArray.width()).toInt()
val cy = (ny * activeArray.height()).toInt()
val rSize = 200
return MeteringRectangle(
max(0, cx - rSize/2), max(0, cy - rSize/2),
rSize, rSize,
MeteringRectangle.METERING_WEIGHT_MAX
)
}

// Anexar região a um construtor de solicitação
fun applyTapFocus(builder: CaptureRequest.Builder, region: MeteringRectangle) {
builder.set(CaptureRequest.CONTROL_AF_REGIONS, arrayOf(region))
builder.set(CaptureRequest.CONTROL_AE_REGIONS, arrayOf(region)) // Acoplar ponto de AE também!
builder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_CANCEL) // Cancelar qualquer trava anterior
builder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_START) // Disparar varredura na nova região
}

Dica profissional: Sempre acople CONTROL_AE_REGIONS para corresponder a CONTROL_AF_REGIONS. O usuário tocou em um rosto porque quer que esse rosto esteja tanto em foco quanto exposto corretamente — não focado no rosto, mas medido com base no céu brilhante atrás dele.


Solução de Problemas de Foco​

SintomaCausa RaizCorreção
O estado de AF nunca passa de ACTIVE_SCANCena de baixo contraste (parede branca, céu azul puro) ou falha de hardwareTimeout após ~3s; avisar o usuário; reverter para predefinição hiperfocal
O controle deslizante de foco manual não faz nadaEsqueceu de definir CONTROL_AF_MODE = OFF → o AF está lutando contra vocêChame switchToManualMode() no onStartTrackingTouch
A captura estática sai borrada apesar de FOCUSED_LOCKEDGatilho de AF cancelado antes da conclusão da captura estáticaCancele apenas em onCaptureCompleted da solicitação estática
A câmera frontal ignora comandos de focoLente EDOF de foco fixo (MINIMUM_FOCUS_DISTANCE == 0)Degradação graciosa: desativar a UI de foco para essa câmera
O AF de vídeo "caça" muitoUsando CONTINUOUS_PICTURE em vez de CONTINUOUS_VIDEO para gravação de vídeoMude o modo para CONTINUOUS_VIDEO quando o MediaRecorder iniciar

Resumo​

O foco no Camera2 é uma máquina de estados que você deve conduzir explicitamente, não uma configuração de "definir e esquecer":

  • Hardware de AF: Os smartphones combinam AF por Detecção de Contraste, AF por Detecção de Fase (Dual-Pixel) e Laser AF para travamentos rápidos e confiáveis.
  • Modos: AUTO (disparo único, trava), CONTINUOUS_PICTURE (refoca, pausa para fotos), CONTINUOUS_VIDEO (sempre refocando), MACRO, OFF (manual). Lentes EDOF não têm movimento de foco.
  • Estados: Aguarde por FOCUSED_LOCKED (não apenas PASSIVE_FOCUSED) antes de capturas estáticas de alto valor.
  • Dioptrias: LENS_FOCUS_DISTANCE usa a distância recíproca (0.0D = ∞, 10D = 10 cm). A faixa é [0.0 .. LENS_INFO_MINIMUM_FOCUS_DISTANCE].
  • Captura AF de disparo único: TRIGGER = START → aguarde FOCUSED_LOCKED → envie a captura estática → depois CANCEL.
  • Controle deslizante de foco manual: SeekBar com 1000 passos, limitado a 30fps; mude o modo para AF_MODE_OFF primeiro para que o algoritmo automático não lute contra sua configuração manual.
  • Tocar para Focar usa CONTROL_AF_REGIONS em coordenadas do active-array do sensor. Acople com AE_REGIONS para resultados profissionais.

O Que Vem a Seguir​

Brilho ✓ Nitidez ✓. Agora vamos ajustar a cor. No Capítulo 16: Balanço de Branco e Cor, cobrimos:

  • Balanço de Branco Automático (AWB) e as 7 predefinições (Incandescente → Sombra)
  • Correção de cor manual com COLOR_CORRECTION_GAINS (4 canais R/G/B/G) e COLOR_CORRECTION_TRANSFORM (matriz RGB 3×3)
  • Conceito de temperatura de cor (vela 2000K → sombra 10000K) e como ela mapeia para o balanço de branco
  • Código funcional para uma predefinição de "aparência de pôr do sol" em tons quentes e modo AWB manual total desligado

A cor é a última perna da trilogia de controles manuais.