Template Migration
Face templates are bound to the extraction model that produced them. The matcher, the face search service, base, the camera containers, edge-stream-processor and db-synchronization-follower check template compatibility at startup and stop when the stored templates do not match the configured model; docker compose logs of the stopped container shows the reason. Migration re-extracts every stored template with the new model from the saved face crops. You need it when you change Extraction__Algorithm, or when a release changes the model version (the release notes say so).
Not every template can be migrated. Re-extraction needs a fresh detection on the stored crop, which does not always succeed; the previous detection ran on the full frame, possibly with a different detector. Faces that fail are set to the ERROR state, the matchers skip them, and the affected watchlist members must be re-enrolled with a new image. The large majority of templates migrate cleanly.
Choose the algorithm
| Algorithm | FACE_MODEL_VERSION | Note |
|---|---|---|
fast | 52 | Fastest, lowest accuracy |
balanced | 53 | Default |
accurate | 54 | Higher accuracy, higher cost |
accurate_server | 55 | Highest accuracy, server-class hardware |
Set Extraction__Algorithm in .env (section 3.13) to the algorithm you migrate to, so that new enrollments produce the same template version. Decide the algorithm at installation time if you can; migrating a large database takes time. Take a backup before you start.
Run the migration
FACE_MODEL_VERSION=54 ./migrate-faces.sh
The script stops the engine services, starts temporary detector and extractor containers (sf_migration_face_detector_N, sf_migration_face_extractor_N, three of each by default), runs the migrate-faces command of the admin image with parallelism 4, stops the workers and finally prints a dry run of what could not be migrated. Save that output: it lists the watchlist members whose faces need re-enrollment. Without FACE_MODEL_VERSION the script migrates to 53.
Transient errors, typically RPC timeouts when the workers are overloaded, leave templates unmigrated without marking them. The script is safe to re-run; repeat it until the remaining failures are stable.
Finalize and restart
FACE_MODEL_VERSION=54 ./finalize-non-migrated-faces.sh
./start.sh
The finalize step marks the remaining faces as ERROR so the matchers can start; run it only after the re-runs stopped changing the result. The services are still stopped at this point, so start them again.
List members with faces in error state
Query the GraphQL API at http://localhost:8097/graphql to find the watchlist members whose faces were not migrated, and re-enroll them:
query {
faces(where: { faceState: { eq: ERROR } }) {
items {
tracklet {
watchlistMembers { id fullName displayName }
}
state
templateVersion
}
}
}
Performance options
- More workers.
FACE_DETECTORS_COUNTandFACE_EXTRACTORS_COUNTset the number of temporary containers (default 3 each):FACE_EXTRACTORS_COUNT=8 FACE_MODEL_VERSION=54 ./migrate-faces.sh. Match--parallelismin the script to the extractor count. Parallelism above the number of extractors causes timeouts and re-runs; extractors above the parallelism sit idle. - GPU extractors. On a host with a GPU, edit the extractor
docker runinmigrate-faces.shto add--runtime nvidia --env Gpu__GpuEnabled=true, and add--use-gpu-extractorsto themigrate-facescall. See GPU acceleration for the host prerequisites. - Edge streams. Devices running the Embedded Stream Processor send templates of their own model version.
FrameDataProcessing__ForceExtractionOfIncompatibleFaceTemplates=true(section 3.7 of.env) makesedge-stream-processorre-extract incompatible templates on the server until the devices are updated.