Enrolment image capture
The face you submit to biofrq.enroll becomes the reference
every future identify and verify call is compared against.
Every quality issue in that one image follows you for the lifetime
of the subject — bad reference, persistent misses.
This page is the standard operating procedure for capturing enrolment images. Probe images (the ones you submit at identify / verify time) follow the same rules, but the bar is highest at enrol.
The one rule that beats everything else
Inspect quality before you enrol. Call biofrq.detect with
returnQuality: true first, read the response, and only call
biofrq.enroll if the metrics pass the gates below. The detect call
costs a fraction of an enrol and saves you from poisoning your
gallery with a reference you can't trust.
# 1. Pre-flight: detect + quality
curl -X POST https://api.biofrq.com/v1/face/execute \
-H "X-Api-Key: bfq_live_…" \
-H "Content-Type: application/json" \
-d '{
"action": "biofrq.detect",
"params": {
"images": [{ "kind": "image", "bytes": "<base64>", "contentType": "image/jpeg" }],
"returnQuality": true
}
}'
# 2. Only if quality passes -> biofrq.enroll
The quality block lives at data.results[0].value.faces[0].quality.
Quality fields
| Field | Range | What it measures |
|---|---|---|
overallScore | 0.0 – 1.0 | Composite — use this as your headline gate. |
sharpnessScore | 0.0 – 1.0 | Edge contrast / focus. Low = motion blur or out-of-focus. |
brightnessScore | 0.0 – 1.0 | Exposure. Low = under- or over-exposed. |
faceBoxConfidence | 0.0 – 1.0 | How sure the detector is this is a face. |
facePixelWidth | px | Width of the face bounding box in source pixels. |
facePixelHeight | px | Height of the face bounding box. |
yawDegrees | ±90 | Head turn left / right. |
pitchDegrees | ±90 | Head tilt up / down. |
rollDegrees | ±90 | Head roll (clockwise / anti-clockwise). |
poseSupported | bool | false when the current model build doesn't estimate yaw/pitch/roll. |
Recommended gates
| Metric | Enrol minimum | Probe minimum | Rationale |
|---|---|---|---|
overallScore | ≥ 0.80 | ≥ 0.35 | Enrolment is the moment to be strict — a reference at 0.45 looks "good enough" but sits near the cluster centroid and acts as a "magnet" that wrongly attracts unrelated probes downstream. Aim for 0.80 or re-capture. Probes are scored against the gallery either way, so a lower probe bar is fine. |
facePixelWidth | ≥ 112 | ≥ 112 | The embedding model takes a 112-pixel-wide aligned crop. Sub-112 input is up-sampled (interpolated), losing identity signal. |
sharpnessScore | ≥ 0.40 | ≥ 0.30 | Motion blur destroys high-frequency face features. |
brightnessScore | ≥ 0.35 | ≥ 0.30 | Under-exposed faces collapse into noise; over-exposed lose skin tone variation. |
faceBoxConfidence | ≥ 0.85 | ≥ 0.80 | Low confidence means the detector isn't sure it's a face — embedding will be unstable. |
| pose (when supported) | ` | yaw | , |
These are recommendations, not hard limits the service imposes — you choose the policy that matches your operating environment.
Framing the face
The face needs to dominate the crop you send. A small face inside a wide frame wastes resolution; a face cropped tight to the chin loses the forehead and ears the embedding relies on.
Target: face fills 60–80% of the shorter dimension, centred, with margin on all sides.
Good: face fills the height of the crop, eyes near the mid-line, even ambient light.
What to avoid:
- Face too small in frame — sourced from a wide shot or full-body
capture.
facePixelWidthwill likely fall below 112 and the embedding will be unstable. Re-crop closer or capture at a shorter distance. - Face crop too tight — forehead, ears, or chin clipped. The detector aligns on those landmarks; missing any of them degrades the embedding quality.
- Off-centre — face crammed against one edge. Centre the face before cropping.
Pose tolerance
The embedding is sensitive to head pose. Frontal works best.
Acceptable for enrol when poseSupported is true:
|yaw| ≤ 20°, |pitch| ≤ 20°, |roll| ≤ 20°.
Left: frontal (yaw ≈ 0°) — primary reference. Centre: slight angle (yaw ≈ 15°) — fine as an additional reference. Right: profile (yaw ≈ 90°) — do not enrol.
If poseSupported: false is returned, the current model build did
not estimate angles for this face. Fall back to a visual check —
the subject should be looking close to the camera, not in profile.
Lighting
| Condition | Effect |
|---|---|
| Even, diffuse, front-of-face light | Best. |
| Single hard light from one side | Half the face goes dark — embedding becomes that lighting, not that person. |
| Backlit (window behind subject) | Face goes silhouette — brightnessScore plummets. |
| Heavy shadows under eyes / nose | Hides landmarks the detector aligns on. |
| Direct overhead light | Eye sockets shadow over; reduces sharpness on the most identifying region. |
Aim for a setup the operator can replicate every day. Inconsistent lighting across captures of the same person degrades the embedding average over time.
Capture distance and lens
| Setup | Outcome |
|---|---|
| Smartphone selfie, ~50 cm | Good. Plenty of resolution; minor lens distortion. |
| Webcam, ~60 cm | Usually fine if the webcam is ≥ 720p. |
| Surveillance camera, > 3 m, wide-angle | Often produces sub-112-pixel face crops. Re-crop closer or use a higher-resolution stream. |
| Phone camera at arm's length, ultra-wide lens | Wide-angle distortion stretches face features. Use the standard lens. |
Multiple references per identity
Identify scores the probe against every reference attached to a subject and reports the highest. The more reference embeddings a subject has, covering more poses / lighting / expressions, the more chances any single probe has to score above your match threshold.
Multiple references for one externalId. Identify returns the max score across all of them — so a probe only needs to match one reference well to surface.
Add a new reference by calling biofrq.enroll again with the
same externalId and mode: "addReference". The service attaches
the new reference to the existing subject; identify takes the
maximum across all of them.
{
"action": "biofrq.enroll",
"params": {
"externalId": "emp-4421",
"groupName": "employees",
"mode": "addReference",
"images": [ { "kind": "image", "bytes": "...base64..." } ]
}
}
Default mode is "create", which rejects a repeat
externalId with CONFLICT — that's the right behaviour for first
enrolment, not for adding references. forceEnroll: true is a
different flag: it only bypasses the duplicate-face dedup gate
(the check that prevents enrolling the same person twice under
different externalIds); it does not change the create-vs-add
behaviour. Use both flags together only when you're adding a
reference to an existing person whose new image happens to sit
very close to another subject's embedding.
When to add a reference:
- The subject's appearance changes meaningfully — new haircut, glasses, weight change, beard.
- You captured a higher-quality image than the original.
- Identify on a known-correct probe scores below your match threshold — the existing reference no longer represents the person well.
Common pitfalls
| Pitfall | Symptom | Fix |
|---|---|---|
| Re-enrolling the same low-quality crop | Reference stays poor; identify keeps under-scoring. | Capture a fresh frame at higher resolution; gate on quality. |
| Enrolling from CCTV crops without re-cropping | facePixelWidth < 100, identify fails on most probes. | Pull from a higher-resolution stream, or capture the enrolment image out-of-band (phone, kiosk). |
| Surveillance frame with two people | MULTIPLE_FACES_DETECTED. | Crop to a single face before submitting. |
| Sunglasses or face mask on enrolment | Embedding represents the obstruction, not the person. | Require an unobstructed reference. |
| Profile / 90° turn | Detector may pass but pose is unrecoverable for identify. | Re-capture frontal. |
| Mixing operator devices for the same person | Inconsistent colour and white balance shift the embedding. | Standardise the capture device per site. |
Checklist before enrolling
□ Subject is looking close to the camera (not profile)
□ Eyes, nose, mouth, chin, forehead all visible
□ No sunglasses, no face mask, no hand covering the face
□ Even lighting on both sides of the face
□ Face fills 60-80% of the shorter dimension of the frame
□ detect returned faceBoxConfidence >= 0.85
□ detect returned overallScore >= 0.80
□ detect returned facePixelWidth >= 112
□ No motion blur (sharpnessScore >= 0.40)
□ Exposure looks right (brightnessScore >= 0.35)
□ pose within ±20° (when poseSupported is true)
If every box is checked, submit biofrq.enroll. If any box fails,
re-capture; don't lower the bar.
What's next
- biofrq.detect — the pre-flight call.
- biofrq.enroll — the action itself.
- biofrq.identify — how the references you just enrolled are used.