"거의 대칭인 얼굴 더하기 '낮은 error 유지' 패치는 머리를 무작위로 뒤로 돌리는 기계를 지어줘."
대칭 얼굴은 함정이야
사람 얼굴은 거의 거울 대칭이고, 그 순진한 사실이 카메라 solve 엔 지뢰야. 왼쪽과 오른쪽이 거의 맞아서, 머리가 한쪽을 보는 fit 과 그 거울상 — 반대쪽을 보는 — 이 landmark 에 거의 똑같은 error 로 reproject 돼. 두 답은 기하학적으로 반대(yaw 가 대략 180도 차이)인데 수치적으론 거의 동점이야. 데이터가 그걸 거의 구별 못 해.
error 로 chirality 를 고르지 마
솔깃한 패치가 저절로 써져: 정상과 뒤집힌 버전 둘 다 풀고, reprojection error 가 낮은 쪽을 유지해. robustness 처럼 보여. 동전 던지기야. 두 fit 이 수치 노이즈로 갈릴 때, '낮은 error 골라' 는 무작위 handedness 생성기야 — 대부분 사진은 맞게 떨어지고, 그러다 landmark 하나가 1픽셀 움직이면 머리가 홱 돌아 뒤를 봐. 바로 이 버그가 Loomis 에서 이미 일어났고, 그래서 규칙이 이렇게 무뚝뚝하게 쓰여: error 를 비교해서 mirror flip 을 절대 고르지 마.
선택이 아니라 setup 에서 고쳐
진짜 해법은 두 후보 사이의 더 똑똑한 선택이 아냐 — 옳은 후보만 애초에 생성되게 하는 거야. canonical 머리는 y-up, z 가 뷰어 쪽으로 정의돼; 적절한 x축 180도 회전을 통해 OpenCV 카메라 프레임에 도달해. 그 변환을 한 번 제대로 세우면, solvePnP 가 handedness 를 스스로 되찾아 — 고를 두 번째 후보도 없고 틀릴 flip 테스트도 없어. 이게 가족 전체가 지키는 같은 규율이야: 하류에서 증상을 패치하는 대신 모호함을 근원에서 없애라.
Euler 가 아니라 벡터에서 각도를 읽어
되찾은 회전을 읽을 수 있는 yaw/pitch/roll 로 바꾸는 데 두 번째, 사촌 함정이 있어. 뻔한 수 — 회전 행렬을 Euler 각도로 분해 — 는 wrap 되고 gimbal-lock 돼서, 깔끔한 readout 이 가장 필요한 거의 정면 얼굴에서 딱 쓰레기를 만들어. 그 버그도 일어났어. 해법은 분해를 통째로 건너뛰고 머리 자신의 forward, up 벡터(회전 행렬의 열)에서 각도를 읽는 거야, 정면 뷰에서도 안정적이고 모호하지 않게. 두 함정, 한 교훈: 얼굴이 대칭이거나 정면이면 순진한 수치가 거짓말해 — 그러니 그 거짓말이 표현조차 안 되게 기하를 지어.