Quality Assessment — Guia de uso

Avaliação de qualidade da imagem facial no fluxo de enroll. O Fortface Core chama o troller (fortface-cortex), que analisa exposição, nitidez, uniformidade de iluminação e contraluz, e devolve se a foto está utilizável para enrollment.

Oclusão, pose, olhos fechados e múltiplas faces não fazem parte desta análise.


1. Como ativar

Basta pedir a análise na requisição. Não há configuração de galeria: qualquer galeria aceita, e o que decide é o request. Se a flag não vier — ou vier false —, a análise não é executada e o campo não aparece no payload de resposta.

{
"analysis": {
"qualityAssessment": true
}
}

No fluxo SDK a flag é enviada no handshake e vale para a sessão inteira (o body do enroll SDK não tem analysis); no backoffice, vai no body do próprio enroll.

Thresholds não são configuráveis: são globais, definidos na policy do troller.

Regra no código

EnrollFaceAnalysisOperator:

qualityAssessmentValue === true

onde qualityAssessmentValue é session.analysis?.qualityAssessment no SDK e analysis?.qualityAssessment no backoffice.


2. Onde se aplica

Apenas na operação enroll. Dois fluxos, ambos no EnrollController:

RotaMétodoDe onde vem o analysis
SdkEnrollRouteexecuteSdkda sessão (handshake)
BackofficeEnrollRouteexecuteBackofficedo body

Nenhuma outra operação executa quality. capture, liveness, evidence, identify e search v2 usam controllers próprios e nenhum deles referencia qualityAssessment.

A análise roda no Promise.all do EnrollFaceAnalysisOperator, junto de liveness, oclusão e embeddings — portanto não soma latência serial ao enroll.


3. Retorno do Fortface

O bloco fica dentro de photo, ao lado de liveness:

{
"photo": {
"liveness": { "value": true, "confidence": 100 },
"qualityAssessment": { "value": true, "confidence": 100 }
}
}
CampoTipoDescrição
valuebooleantrue = foto utilizável; false = reprovada
confidencenumber100 = o cortex respondeu (aprovado, reprovado ou sem conseguir avaliar a imagem); 0 = o cortex não pôde ser acessado (timeout, indisponibilidade)

Tabela de decisão

Resposta do trollervalueconfidence
passed: true / decision: acceptedtrue100
passed: false + reason: rejected_but_debatabletrue100
passed: false + reason: rejectedfalse100
status: 'error' (sem rosto, imagem inválida — o troller respondeu, só não conseguiu avaliar)false100
Falha de rede/timeout — o troller não pôde ser acessadofalse0
Feature desligada(bloco qualityAssessment ausente do payload)

A reprovação limítrofe (rejected_but_debatable) é aceita pelo Fortface. O troller marca a imagem como fora dos limites, mas por margem pequena — a regra de negócio do Fortface trata esse caso como utilizável para enrollment. Só a reprovação clara (rejected, que estourou um gate do bloco hard_reject) devolve value: false.

O algoritmo do cortex não foi alterado: a decisão continua vindo do troller e o payload cru é persistido no log de sessão (qualityAssessmentResponse) com scores, thresholds, failed_metrics, decision e estimated_mst para triagem posterior.

O fluxo de enroll nunca é bloqueado pela análise de qualidade: value: false é informativo, e a decisão de recapturar a foto é do integrador.


4. Referência do contrato

POST /v2/quality_assessment (troller)

  • Request: Content-Type: application/octet-stream, body = bytes crus da imagem. Não existe variante base64.
  • Response: sempre HTTP 200 — o endpoint nunca lança.
CampoTipoObservação
passedbooleanaprovação do troller; entra na regra do value
status"done" | "error"error = o troller respondeu mas não conseguiu avaliar a imagem → value: false, confidence: 100 (ele foi acessado)
reasonstring | nullnull quando aprovou; senão repete decision
failed_metricsstring[]métricas reprovadas, ou ["no_face"]
scoresobjectbadness em [0,1]maior = pior
thresholdsobjectlimites aplicados; {} quando não avaliou
estimated_mstintMonk Skin Tone estimado (calibração por tom de pele)
decision"accepted" | "rejected" | "rejected_but_debatable"
review_bucketstringespelha decision

Métricas

MétricaO que detectaThreshold
underexposurefoto escura demais0.75
overexposurefoto estourada de luz0.95
blurfalta de nitidez / desfoque0.85
uniformityiluminação irregular no rosto0.98
backlitrosto contra fundo muito claro0.45

Uma métrica falha quando seu score é maior ou igual ao threshold. passed é true somente quando todas ficam abaixo dos seus limites.

decision

ValorSignificadovalue no Fortface
acceptedpassou em todos os thresholdstrue
rejectedreprovou e estourou um gate do bloco hard_reject — imagem claramente ruimfalse
rejected_but_debatablereprovou, mas dentro dos gates — limítrofetrue

Os gates ficam no bloco hard_reject da policy e são avaliados apenas sobre features da imagem (luminância mediana, faixa dinâmica, caudas escura/clara, score de blur) — a mesma imagem sempre cai no mesmo bucket.


5. Cenários

Cenário 1 — Aprovada

Resposta do troller:

{
"passed": true,
"status": "done",
"reason": null,
"failed_metrics": [],
"scores": { "underexposure": 0.0, "overexposure": 0.0, "blur": 0.0, "uniformity": 0.1258, "backlit": 0.0 },
"estimated_mst": 6,
"decision": "accepted",
"review_bucket": "accepted"
}

Retorno do Fortface:

{ "photo": { "qualityAssessment": { "value": true, "confidence": 100 } } }

Cenário 2 — Reprovação limítrofe (aceita)

Resposta do troller:

{
"passed": false,
"status": "done",
"reason": "rejected_but_debatable",
"failed_metrics": ["blur"],
"scores": { "underexposure": 0.0, "overexposure": 0.0, "blur": 0.9, "uniformity": 0.3792, "backlit": 0.0 },
"estimated_mst": 4,
"decision": "rejected_but_debatable",
"review_bucket": "rejected_but_debatable"
}

Retorno do Fortface:

{ "photo": { "qualityAssessment": { "value": true, "confidence": 100 } } }

blur: 0.9 estourou o threshold (0.85), mas ficou abaixo do gate duro (1.0) — caso limítrofe, aceito pelo Fortface.


Cenário 3 — Reprovada

Resposta do troller:

{
"passed": false,
"status": "done",
"reason": "rejected",
"failed_metrics": ["blur"],
"scores": { "underexposure": 0.0, "overexposure": 0.0, "blur": 1.0, "uniformity": 0.4048, "backlit": 0.075 },
"estimated_mst": 9,
"decision": "rejected",
"review_bucket": "rejected"
}

Retorno do Fortface:

{ "photo": { "qualityAssessment": { "value": false, "confidence": 100 } } }

Cenário 4 — Troller respondeu, mas não conseguiu avaliar

Sem rosto detectado, imagem inválida ou body vazio, a resposta do troller é HTTP 200 com status: "error":

{
"passed": false,
"status": "error",
"reason": "rejected",
"failed_metrics": ["no_face"],
"thresholds": {},
"estimated_mst": 0,
"decision": "rejected",
"review_bucket": "rejected"
}

Retorno do Fortface:

{ "photo": { "qualityAssessment": { "value": false, "confidence": 100 } } }

confidence: 100 porque o troller foi acessado e devolveu uma resposta — só não conseguiu computar as métricas de qualidade para essa imagem. value: false porque reason não é rejected_but_debatable.

No fluxo de enroll, uma imagem sem rosto normalmente nem chega a gerar esse retorno: a geração do embedding (face-service/troller, um passo anterior e independente) já rejeita a requisição com 400 invalid.face_not_found antes da resposta ser montada. Esse cenário tende a aparecer só quando o troller falha em avaliar a imagem por um motivo que não impede a detecção de rosto no outro serviço.


Cenário 5 — Troller inacessível

Timeout, indisponibilidade ou qualquer falha de rede na chamada ao troller. Não há resposta para mapear — o adapter apenas não recebe nada do serviço.

Retorno do Fortface:

{ "photo": { "qualityAssessment": { "value": false, "confidence": 0 } } }

confidence: 0 é reservado exclusivamente para este caso: o troller não pôde ser acessado. Qualquer resposta dele, mesmo status: "error", já conta como acesso bem-sucedido e usa confidence: 100.