port: migrate from mp.solutions to mp.tasks.vision (FaceLandmarker)#106
Merged
ahmetabdullahgultekin merged 1 commit intoMay 28, 2026
Merged
Conversation
The mediapipe.solutions namespace was removed in mediapipe 0.10.35 (currently deployed in production). Every /verify call was logging "Landmark detection failed: module 'mediapipe' has no attribute 'solutions'" and all temporal/face signals (EAR, MAR, yaw, pitch, roll, blink, smile) collapsed to null in the liveness_calibration event. Ports four server-side consumers from the legacy face_mesh API to the new mp.tasks.vision.FaceLandmarker API: app/infrastructure/ml/landmarks/mediapipe_landmarks.py app/infrastructure/ml/quality/quality_assessor.py app/infrastructure/ml/proctoring/mediapipe_gaze_tracker.py (VIDEO mode) app/infrastructure/ml/liveness/active_liveness_detector.py tests/demo_local.py (test harness) Result-shape adaptations: Old: result.multi_face_landmarks[0].landmark[i].x New: result.face_landmarks[0][i].x The Tasks API requires a .task model asset. Centralised the path-resolution + SHA256 integrity check in a new shared loader so the four call sites + the verification route's _evaluate_ear_liveness_safe (PR #102) follow one contract: app/infrastructure/ml/landmarks/face_landmarker_loader.py - FACE_LANDMARKER_MODEL_PATH override (env-var first, repo-root fallback) - FACE_LANDMARKER_MODEL_SHA256 verification (warn-and-disable on mismatch) - VIDEO running-mode with monotonic timestamp_ms (gaze tracker) - IMAGE running-mode (the other three) Model delivery: Dockerfile bakes face_landmarker.task (float16/latest, ~3.7 MB) into /app/models/ via curl with build-time SHA256 verification. PIN: 64184e229b263107bc2b804c6625db1341ff2bb731874b0bcc2fe6544e0bc9ff Documented in .env.example. Configured first-class in app/core/config.py (FACE_LANDMARKER_MODEL_PATH, FACE_LANDMARKER_MODEL_SHA256 already declared per PR #102; this PR adds the path default + the live SHA reference). Tests: + 22 new unit tests (loader path/SHA/import fail-soft, ported sites asserting new result shape adaption, gaze tracker VIDEO-mode timestamp monotonicity) + 1 new integration test that AST-walks app/ for any executable reference to mp.solutions / mediapipe.solutions and fails the build if any survive (regression guard). Verified with `grep -rn "mp.solutions" app/ tests/` on branch HEAD: zero executable references; all hits are docstrings/comments documenting the migration. Depends on PR #102 for the FACE_LANDMARKER_MODEL_SHA256 settings field + the _evaluate_ear_liveness_safe loader pattern this PR generalises. No conflict in the shared verification.py path (that route is unchanged by this PR). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The
mediapipe.solutionsnamespace was removed in mediapipe 0.10.35(currently deployed in production). Every
/verifycall is logging:…and all temporal/face signals (EAR, MAR, yaw, pitch, roll, blink, smile)
collapse to
nullin theliveness_calibrationevent. This PR closes thatgap by porting the four server-side consumers from the legacy
face_meshAPI to the new
mp.tasks.vision.FaceLandmarkerAPI.Inventory of ported call sites
app/infrastructure/ml/landmarks/mediapipe_landmarks.pymp.solutions.face_mesh.FaceMesh(...).process(image)FaceLandmarker.detect(mp_image)app/infrastructure/ml/quality/quality_assessor.pywith mp.solutions.face_mesh.FaceMesh(...) as fm: fm.process(rgb)FaceLandmarker.detect(mp_image)(per-call lifecycle)app/infrastructure/ml/proctoring/mediapipe_gaze_tracker.pymp.solutions.face_mesh.FaceMesh(static_image_mode=False, ...).process(rgb)FaceLandmarker.detect_for_video(mp_image, ts_ms)(VIDEO mode + monotonic timestamps)app/infrastructure/ml/liveness/active_liveness_detector.pymp.solutions.face_mesh.FaceMesh(...).process(rgb)FaceLandmarker.detect(mp_image)tests/demo_local.py5 executable call sites fixed. Result-shape adaptation throughout:
result.multi_face_landmarks[0].landmark[i].xresult.face_landmarks[0][i].xNew shared loader
To avoid duplicating model-path + SHA + env-fallback logic across all
sites, this PR centralises everything in
app/infrastructure/ml/landmarks/face_landmarker_loader.pywith:FACE_LANDMARKER_MODEL_PATHenv override → repo-root default → cwd fallbackFACE_LANDMARKER_MODEL_SHA256integrity check (warn-and-disable on mismatch)RunningMode.IMAGEvsRunningMode.VIDEOswitchingto_mp_image(rgb)helper so call sites don't need toimport mediapipejust for the
ImageFormatenumPR #102's
_evaluate_ear_liveness_safeinapp/api/routes/verification.pyalready follows the same pattern — no shared-file conflicts.
Model delivery
The Dockerfile now bakes
face_landmarker.task(float16/latest, ~3.7 MB)into
/app/models/via a singleRUN curl … | sha256sum -cso the imageis reproducible:
This change is intentionally standalone (single-stage RUN) so it does
not conflict with Team B's PR #104 model-fetcher multi-stage build. If
that PR merges first, the
face_landmarker.taskcurl in this PR willneed to be moved into the model-fetcher stage at merge time — a trivial
rebase. Otherwise this PR ships the asset on its own.
.env.exampledocuments bothFACE_LANDMARKER_MODEL_PATHand theexpected SHA.
app/core/config.pymakes both first-class Pydanticsettings (the SHA field was added by PR #102; this PR adds the path
field).
Tests
test_face_landmarker_loader.py— path/SHA/import fail-soft (11 tests)test_mediapipe_landmarks_tasks_api.py— new result shape adaption + lazy init caching (5)test_active_liveness_detector_tasks_api.py— empty-face handling + mp.Image plumbing (3)test_mediapipe_gaze_tracker_tasks_api.py— VIDEO mode invariants (3)app/for any executablereference to
mp.solutions/mediapipe.solutionsand fails the buildif any survive — regression guard.
mp.solutionshits are docstrings/comments documenting the migration.
(no regressions).
Coordination with Team B PR #102 (Bio-Python)
app/api/routes/verification.py_evaluate_ear_liveness_safeapp/core/config.pyFACE_LANDMARKER_MODEL_PATHfieldFACE_LANDMARKER_MODEL_SHA256app/application/services/active_liveness_manager.pyThis PR depends on PR #102 in the sense that the SHA settings field comes
from there. If #102 lands first, this PR rebases cleanly. If this PR lands
first, #102 will need to rebase the SHA field on top of the path field.
Operator notes
.env.prodSHA pin: the live asset SHA is already documented in.env.example. On rebuild + redeploy, setFACE_LANDMARKER_MODEL_SHA256in
.env.prodto the same value to activate integrity verification./app/models/— the asset is image-baked.face_landmarker.task: OKfromsha256sum -c.Test plan
face_landmarker.taskends up at/app/models/face_landmarker.taskwith SHA pin OK./verifywith a valid still frame, confirmlog line
Landmark detection failed: module 'mediapipe' has no attribute 'solutions'is GONE.liveness_calibrationevent payload now contains non-nullear_left,ear_right,mar,yaw,pitch,roll.pytest tests/unit/infrastructure/test_face_landmarker_loader.py tests/unit/infrastructure/test_mediapipe_*_tasks_api.py tests/integration/test_no_mp_solutions_runtime.py— all 23 pass.Memory-rule compliance:
feedback_readonly_rootfs_cache_dirs: model is image-baked + chmod 0644;no volume needed.
feedback_git_push: used baregit push.feedback_verify_completion_claims: rangrep -rn "mp.solutions" app/ tests/on branch HEAD; zero executable hits.