04. 블렌드셰이프와 얼굴 변환 행렬
목차
- 4.1 블렌드셰이프란 무엇인가
- 정의와 직관
- 이름의 유래 — morph target
- 왜 좌표(478점) 대신 블렌드셰이프인가
- 4.2 ARKit 52 표준 — 전체 목록과 "두 개의 52"
- 왜 ARKit 표준을 따랐나
- "52"는 같지만 집합이 다르다
- MediaPipe 기본 52 — 인덱스와 이름
- 4.3 어떻게 계산되나 — 별도의 두 번째 모델
- 블렌드셰이프는 "두 번째 모델"의 출력
- 입력과 출력의 형태
- 왜 478점 전부가 아니라 146점만 쓰나
- 4.4 (심화) Blendshape V2 모델 내부와 학습 데이터
- 모델 구조 (Blendshape V2 = GHUM real-time model)
- 데이터는 어떻게 만들었나 (왜 합성 데이터인가)
- 4.5 Facial Transformation Matrix (4×4) — 머리 자세
- 무엇인가
- 4×4 동차 변환 행렬의 구조
- 왜 필요한가 — 블렌드셰이프와의 역할 분담
- 4.6 응용 — 블렌드셰이프 계수를 리그에 매핑하기
- 대표 응용
- 리그 매핑 워크플로우 개요
- 4.7 자주 하는 오해 — 빠른 정리
- Sources
02장 (e)·(f)단계에서 옵션으로 52계수와 4×4 행렬이 나온다고 했다. 이 장은 그 둘을 깊게 푼다. 00장의 블렌드셰이프·변환 행렬 정의, 03장의 478점·홍채·146 서브셋 개념을 그대로 쓴다.
4.1 블렌드셰이프란 무엇인가
정의와 직관
블렌드셰이프(blendshape)는 얼굴 표정을 478개 좌표가 아니라 의미 있는 표정 단위 52개의 가중치 계수로 표현하는 방식이다. 계수는 0~1 사이의 값이고, 단위마다 하나씩 나온다.
0.0이면 그 표정 요소가 전혀 활성화되지 않은 상태다(중립).1.0이면 그 표정 요소가 최대로 활성화된 상태다.
한 프레임의 출력은 이렇게 생겼다.
| 블렌드셰이프 | 계수 | 해석 |
|---|---|---|
eyeBlinkLeft |
0.92 | 왼쪽 눈을 거의 다 감음 |
mouthSmileLeft |
0.65 | 왼쪽 입꼬리가 꽤 올라감(미소) |
mouthSmileRight |
0.61 | 오른쪽 입꼬리도 비슷하게 올라감 |
jawOpen |
0.10 | 입은 거의 다물고 있음 |
browInnerUp |
0.05 | 눈썹 안쪽은 거의 안 올라감 |
이 한 줄 한 줄이 곧 표정의 의미다. 좌표가 아니라 의도나 근육 동작 수준의 정보라는 점이 핵심이다.
이름의 유래 — morph target
블렌드셰이프는 MediaPipe가 새로 만든 개념이 아니다. 3D 캐릭터 애니메이션의 고전 기법인 morph target(모프 타깃, = blend shape)에서 왔다. 만드는 순서는 이렇다.
- 아티스트가 캐릭터의 중립 얼굴 메시
b₀를 먼저 만든다. - 그다음 "활짝 웃는 얼굴", "눈 완전히 감은 얼굴"처럼 하나의 표정만 최대로 적용된 메시
bᵢ들을 여러 개 만든다. 이 각각이 하나의 블렌드셰이프다. - 임의의 표정은 이 변형들을 가중합(blend)해서 만든다.
GHUM 논문의 식 (1)이 그 가중합이다.
읽으면 "중립 메시 + Σ(계수 wᵢ × i번째 표정의 변위)"다. 여기서 wᵢ가 바로 우리가 추정하려는 블렌드셰이프 계수다.
이름 그대로 여러 기본 shape를 blend, 곧 혼합해서 표정을 합성한다. MediaPipe가 푸는 것은 이 식의 역문제(inverse problem)다. 메시를 만드는 게 아니라, 이미지에서 거꾸로 wᵢ를 알아내는 일이다. 논문도 "We focus on the inverse problem"이라고 못 박는다.
비유. 블렌드셰이프는 오디오 믹싱 콘솔의 슬라이더들이다. 식 (1)은 각 슬라이더를
wᵢ만큼 올려 소리를 합성하는 정방향이다. Face Landmarker는 그 반대를 푼다. 완성된 소리를 듣고 각 슬라이더가 몇 %였는지 알아맞힌다.
블렌드셰이프 기저는 선형적이고 비교적 희소(sparse)하며 해석 가능하다. 그래서 사람 얼굴을 닮지 않은 캐릭터도 같은 52개 단위로 구동할 수 있다. 아티스트의 자유도가 크다.
왜 좌표(478점) 대신 블렌드셰이프인가
478개 좌표를 그대로 주면 기하학적 위치는 알 수 있다. 하지만 표정의 의미를 바로 알기는 어렵다. 아바타나 애니메이션에 필요한 정보는 "코끝이 (0.43, 0.51)에 있다"가 아니다. "입이 30% 벌어졌고 왼눈이 90% 감겼다"다.
위 그림에서 좌표 경로는 표정의 의미가 흐릿하고(점선), 계수 경로는 아바타 리그로 곧장 이어진다. 블렌드셰이프가 좌표보다 좋은 점은 네 가지다.
- 의미가 직접적이다(semantic). 계수 자체가 눈 감김 정도, 입 벌림 정도다. 아바타 리그(rig) 슬라이더에 바로 연결된다.
- 사람을 안 닮은 캐릭터에도 적용된다(retargeting). 너구리든 로봇이든 같은 52계수만 받으면 자기 식대로 표정을 만든다. 좌표를 직접 옮기면 캐릭터 얼굴 형태가 사람과 달라 깨진다.
- 차원이 작고 표준적이다. 478×3 ≈ 1434개 숫자 대신 52개면 된다. 게다가 ARKit 표준과 호환돼 기존 3D 자산을 재사용할 수 있다.
- 희소하고 안정적이다. 대부분의 계수는 0 근처라 노이즈에 강하고 전송·저장이 가볍다.
4.2 ARKit 52 표준 — 전체 목록과 "두 개의 52"
왜 ARKit 표준을 따랐나
MediaPipe 블렌드셰이프 모델(GHUM)은 Apple ARKit과 동일한 52개 집합을 채택했다. 논문 표현 그대로 "we use the same set of 52 blendshapes as those in Apple's ARKit, that are familiar to many 3D modeling studios and animators."
이유는 생태계 호환성이다. ARKit 52는 이미 업계 표준이다. 수많은 3D 캐릭터·아바타 자산이 이 52개에 맞춰 리그가 짜여 있다. 같은 이름과 같은 의미를 쓰면 MediaPipe 출력을 기존 ARKit용 아바타로 코드 거의 그대로 연결할 수 있다. 한 가지 덤도 있다. ARKit 52는 심리학의 FACS(Facial Action Coding System, 얼굴 동작 부호화 체계) Action Unit과 느슨하게 대응한다. 그래서 표정 분석에도 쓰기 좋다.
"52"는 같지만 집합이 다르다
이것이 이 장의 첫 번째 핵심 오해 정정이다. MediaPipe의 52와 ARKit의 52는 개수만 같고 집합이 다르다.
| 구분 | 개수 | _neutral 포함? |
tongueOut 포함? |
출처 |
|---|---|---|---|---|
| MediaPipe 기본 | 52 | 있음 (index 0) | 없음 | face_blendshapes_graph.cc kBlendshapeNames[52] |
| MediaPipe + 옵션 | 53 | 있음 | 있음 (53번째, EXTRA_FACE_BLENDSHAPES) |
소스 주석, GHUM 논문 Fig.3 "Extra blendshape (tongueOut)" |
| Apple ARKit | 52 | 없음 | 있음 | Apple BlendShapeLocation 문서 |
직관은 이렇다. MediaPipe는 _neutral을 한 칸 쓰고 tongueOut을 뺀 52다. ARKit은 _neutral을 빼고 tongueOut을 넣은 52다. 둘 다 51개 표정 계수는 공통이다.
- MediaPipe의
_neutral(index 0)은 소스 코드 주석에 "ignore it"이라 적혀 있다. 실제 표정 단위가 아니라 자리 표시(placeholder)에 가깝다. - MediaPipe에서 혀(
tongueOut)는 입력 스트림EXTRA_FACE_BLENDSHAPES를 줄 때만 53번째로 추가된다. GHUM 논문 Fig.3도tongueOut을 본 모델과 분리된 "Extra blendshape"로 그려 놓았다.
MediaPipe 기본 52 — 인덱스와 이름
아래는 MediaPipe GitHub face_blendshapes_graph.cc 안 kBlendshapeNames 배열을 그대로 옮긴 순서(인덱스 0~51)다. 이름 철자는 이 소스가 정답이다.
그룹 A — 중립 / placeholder (1개)
| idx | 이름 | 의미 |
|---|---|---|
| 0 | _neutral |
중립 표정 자리(placeholder). 소스 주석상 "ignore it" — 실제 사용 안 함 |
그룹 B — 눈썹 brow (5개)
| idx | 이름 | 의미 |
|---|---|---|
| 1 | browDownLeft |
왼쪽 눈썹을 아래로 찡그림 |
| 2 | browDownRight |
오른쪽 눈썹을 아래로 찡그림 |
| 3 | browInnerUp |
양 눈썹 안쪽을 위로 올림(놀람·걱정) |
| 4 | browOuterUpLeft |
왼쪽 눈썹 바깥쪽을 위로 올림 |
| 5 | browOuterUpRight |
오른쪽 눈썹 바깥쪽을 위로 올림 |
그룹 C — 뺨 cheek (3개)
| idx | 이름 | 의미 |
|---|---|---|
| 6 | cheekPuff |
양 볼을 부풀림(공기 머금기) |
| 7 | cheekSquintLeft |
왼쪽 볼을 위로 조여 올림(눈 밑 근육) |
| 8 | cheekSquintRight |
오른쪽 볼을 위로 조여 올림 |
그룹 D — 눈 eye (14개)
| idx | 이름 | 의미 |
|---|---|---|
| 9 | eyeBlinkLeft |
왼쪽 눈을 감음 |
| 10 | eyeBlinkRight |
오른쪽 눈을 감음 |
| 11 | eyeLookDownLeft |
왼쪽 눈동자를 아래로 |
| 12 | eyeLookDownRight |
오른쪽 눈동자를 아래로 |
| 13 | eyeLookInLeft |
왼쪽 눈동자를 안쪽(코 방향)으로 |
| 14 | eyeLookInRight |
오른쪽 눈동자를 안쪽으로 |
| 15 | eyeLookOutLeft |
왼쪽 눈동자를 바깥쪽으로 |
| 16 | eyeLookOutRight |
오른쪽 눈동자를 바깥쪽으로 |
| 17 | eyeLookUpLeft |
왼쪽 눈동자를 위로 |
| 18 | eyeLookUpRight |
오른쪽 눈동자를 위로 |
| 19 | eyeSquintLeft |
왼쪽 눈을 가늘게 찡그림 |
| 20 | eyeSquintRight |
오른쪽 눈을 가늘게 찡그림 |
| 21 | eyeWideLeft |
왼쪽 눈을 크게 뜸(놀람) |
| 22 | eyeWideRight |
오른쪽 눈을 크게 뜸 |
eyeLookIn/Out/Up/Down은 눈동자, 곧 시선의 방향을 나타낸다. 03장의 홍채(iris) 10점이 블렌드셰이프 입력 서브셋에 들어 있어서(4.3절) 이 시선 계수를 회귀할 수 있다.
그룹 E — 턱 jaw (4개)
| idx | 이름 | 의미 |
|---|---|---|
| 23 | jawForward |
턱을 앞으로 내밂 |
| 24 | jawLeft |
턱을 왼쪽으로 |
| 25 | jawOpen |
입을 벌림(턱을 아래로) |
| 26 | jawRight |
턱을 오른쪽으로 |
그룹 F — 입 mouth (23개)
| idx | 이름 | 의미 |
|---|---|---|
| 27 | mouthClose |
입을 다묾(jawOpen과 독립적으로 입술만 닫음) |
| 28 | mouthDimpleLeft |
왼쪽 입가에 보조개 |
| 29 | mouthDimpleRight |
오른쪽 입가에 보조개 |
| 30 | mouthFrownLeft |
왼쪽 입꼬리를 아래로(찡그림) |
| 31 | mouthFrownRight |
오른쪽 입꼬리를 아래로 |
| 32 | mouthFunnel |
입을 깔때기처럼 오므려 내밂("오~") |
| 33 | mouthLeft |
입 전체를 왼쪽으로 이동 |
| 34 | mouthLowerDownLeft |
왼쪽 아랫입술을 아래로 |
| 35 | mouthLowerDownRight |
오른쪽 아랫입술을 아래로 |
| 36 | mouthPressLeft |
왼쪽 입술을 눌러 다묾 |
| 37 | mouthPressRight |
오른쪽 입술을 눌러 다묾 |
| 38 | mouthPucker |
입을 모아 내밂(뽀뽀 모양, "우") |
| 39 | mouthRight |
입 전체를 오른쪽으로 이동 |
| 40 | mouthRollLower |
아랫입술을 안으로 말아 넣음 |
| 41 | mouthRollUpper |
윗입술을 안으로 말아 넣음 |
| 42 | mouthShrugLower |
아랫입술 아래를 위로 으쓱(턱 주름) |
| 43 | mouthShrugUpper |
윗입술 위를 위로 으쓱 |
| 44 | mouthSmileLeft |
왼쪽 입꼬리를 올림(미소) |
| 45 | mouthSmileRight |
오른쪽 입꼬리를 올림 |
| 46 | mouthStretchLeft |
왼쪽 입꼬리를 옆으로 당김 |
| 47 | mouthStretchRight |
오른쪽 입꼬리를 옆으로 당김 |
| 48 | mouthUpperUpLeft |
왼쪽 윗입술을 위로(이 드러냄) |
| 49 | mouthUpperUpRight |
오른쪽 윗입술을 위로 |
그룹 G — 코 nose (2개)
| idx | 이름 | 의미 |
|---|---|---|
| 50 | noseSneerLeft |
왼쪽 콧방울을 위로 찡그림(비웃음) |
| 51 | noseSneerRight |
오른쪽 콧방울을 위로 찡그림 |
그룹 H — 혀 tongue (옵션, 1개) — 기본 52에는 미포함
| idx | 이름 | 의미 | 조건 |
|---|---|---|---|
| 52 | tongueOut |
혀를 밖으로 내밂 | EXTRA_FACE_BLENDSHAPES 입력 제공 시에만 (53번째). GHUM 논문 Fig.3의 "Extra blendshape" |
합계 검산: 기본 = 1(_neutral) + 5(brow) + 3(cheek) + 14(eye) + 4(jaw) + 23(mouth) + 2(nose) = 52. 옵션 tongueOut을 포함하면 53이다.
좌우 기준 주의. 이름에 붙은
Left/Right는 얼굴 주인, 곧 피사체 기준의 좌·우다. 카메라 화면 기준이 아니다. 아바타 매핑에서 좌우가 뒤집히면 이 기준 차이를 의심하라(03장 left/right 규약과 같다).[1]
4.3 어떻게 계산되나 — 별도의 두 번째 모델
블렌드셰이프는 "두 번째 모델"의 출력
가장 흔한 오해는 "랜드마크 모델이 블렌드셰이프도 같이 뱉는다"는 것이다. 사실이 아니다. 블렌드셰이프까지 켜면 Face Landmarker는 순차로 동작하는 별개의 모델 두 개를 쓴다. 공식 문서가 이렇게 나눠 적는다.
- Face landmarks model: 478개 3D 얼굴 랜드마크를 추정한다.
- Blendshape prediction model: "receives output from the face mesh model, predicts 52 blendshape scores, which are coefficients representing facial different expressions."
즉 블렌드셰이프 모델은 이미지를 다시 보지 않는다. 첫 모델이 만든 랜드마크 좌표만 입력으로 받아 계수를 회귀한다.
이 그림에서 Blendshape V2 model은 Face landmarks model과 완전히 분리된 별도 박스다. output_face_blendshapes=False(기본값)이면 이 박스는 아예 실행되지 않는다.
입력과 출력의 형태
| 항목 | 값 | 출처 |
|---|---|---|
| 모델 이름 | Blendshape V2 (= GHUM "real-time blendshapes model") | MediaPipe 문서 / GHUM 논문 §4 |
| 입력 | 랜드마크 146개 × 2D 좌표 (텐서 1 × 146 × 2, float16) |
MediaPipe 문서; 논문 "146 × 2 tokens" |
| 입력 출처 | 478 랜드마크 중 146개 서브셋(입술·눈·눈썹·홍채·얼굴 윤곽) | 논문 §4; 소스 kLandmarksSubsetIdxs[146] |
| 출력 | 52 계수 (옵션 시 +1 tongueOut = 53) |
소스 kBlendshapeNames[52] |
| 계수 범위 | [0, 1] | MediaPipe / ARKit / 논문(0~1로 clip) |
| 켜는 옵션 | output_face_blendshapes=True (기본 False) |
MediaPipe 문서 |
합이 1이 아니다. 52계수는 합이 1이 아니다. 분류(classification)가 아니라 다중 회귀(multi-output regression)이기 때문이다. 여러 계수가 동시에 큰 값을 가질 수 있다. 웃으면서 눈을 감으면 미소 계수와 깜빡임 계수가 함께 크다. 출력 자료형 이름은
ClassificationList지만, 의미상은 분류가 아니라 회귀 점수다.
왜 478점 전부가 아니라 146점만 쓰나
블렌드셰이프 모델은 478점 전부를 받지 않는다. 146점 서브셋만 받는다. 표정에 가장 중요한 부위에 집중하기 위해서다. 논문 §4가 그 이유를 적는다. "We restrict ourselves to 146 landmarks on the lips, eyes, eyebrows, irises and face oval contours as they are most crucial for expression tracking."
소스 코드 kLandmarksSubsetIdxs의 실제 146개 인덱스에는 다음이 들어 있다.
- 입술 윤곽(예: 0, 13, 14, 17, 61, 78, 291, 308 …)
- 눈·눈꺼풀(예: 33, 133, 159, 145, 263, 386, 374 …)
- 눈썹(예: 46, 52, 53, 70, 276, 282, 283 …)
- 얼굴 외곽선(face oval, 예: 10, 152, 234, 454 …)
- 홍채(iris) 10점(468~477) — 그래서
eyeLookIn/Out/Up/Down시선 계수를 낼 수 있다(03장 홍채와 이어진다)
478점 전부를 넣지 않으니 모델이 더 작고 빠르다. "코 안쪽 메시"처럼 표정과 무관한 점에 휘둘리지도 않는다.
모델 내부 구조와 학습 방법은 학부 고학년용 심화다. 본류 가독성을 위해 다음 절 박스로 접어 둔다. 읽지 않아도 4.5절로 이어진다.
4.4 (심화) Blendshape V2 모델 내부와 학습 데이터
심화 박스. 이 절은 학부 고학년·대학원 수준이다. "블렌드셰이프 = 랜드마크를 입력받는 별도 회귀 모델"이라는 핵심만 필요하면 4.5절로 건너뛰어도 된다.
모델 구조 (Blendshape V2 = GHUM real-time model)
논문 §4 "Model" 단락 기준이다.
- 아키텍처: 경량 MLP-Mixer다. CNN도 Transformer도 아닌 all-MLP 비전 아키텍처다. 이미지가 아니라 랜드마크 토큰을 처리한다.
- 입력:
146 × 2토큰. 146개 점 × (x, y)다. - 내부 표현:
96 × 64latent로 변환한다. - 출력 헤드 2개.
- 52개 블렌드셰이프 계수.
- 6D facial rotation matrix. 머리 회전을 담으며, 두 개의 별도 2D convolution으로 예측한다. 6D 회전 표현은 신경망에서 안정적인 회전 표현으로 알려져 있다.
- 학습 손실: 두 부분이다. (a)는 예측 계수에 대한 L2 손실이고, (b)는 계수를 적용해 복원한 메시 랜드마크의 L2 손실이다. (a)는 정답 계수에 충실하게 만들고, (b)는 블렌드셰이프 기저의 비직교성을 고려한 지각적 정확도를 보강한다.
- 속도: Pixel 6에서 약 1.2 ms다(TF-Lite XNNPACK 백엔드). 참고로 랜드마크 모델은 같은 폰에서 약 8 ms다(TF-Lite OpenCL).
- 정확도: 실시간 블렌드셰이프 모델의 전체 MNE(Mean Normalized Error) 3.88%다(눈·입 컨투어 기준). 사람 주석자 간 교차검증 하한 2.33%에 근접한다.
지표 혼동 주의. 여기 나온 MNE 3.88%는 블렌드셰이프 모델의 정확도다. 06장에 나오는 IOD MAE 2.62%/3.24%는 메시(랜드마크) 모델의 정확도다. 둘은 다른 모델·다른 지표·다른 출처다. "정확도 = 한 숫자"로 섞어 읽으면 안 된다. 두 수치를 나란히 둔 비교는 06장 §6.7에서 지표·대상 모델·값·출처를 열로 분리해 정리했다.
데이터는 어떻게 만들었나 (왜 합성 데이터인가)
사람이 이미지마다 52계수를 손으로 다는 일은 거의 불가능하다. 어떤 표정의 mouthShrugUpper가 0.3인지 0.4인지 사람은 못 정한다. 그래서 GHUM은 오프라인 등록(offline registration) 파이프라인으로 정답을 만든다.
- 무표정 3D 스캔에 478 랜드마크를 정합한다. 이렇게 중립 메시를 등록한다(canonical template = 12,201 정점).
- 아티스트가 만든 52개 표준 블렌드셰이프를 각 사람에게 affine deformation transfer로 이식한다.
- 각 표정 스캔에 대해 L-BFGS로 블렌드셰이프 계수를 최적화한다(0~1로 제약). 이것이 정답 계수다.
- 이 정답으로 실시간 모델을 지도학습한다.
학습 데이터 규모는 이렇게 부풀린다. 6,000명을 스캔하고, 인물당 40개 표정을 받고, 샘플링·랜덤 변환·2D 투영으로 확장한다. 그 결과 (랜드마크, 정답 계수) 쌍이 약 2M(2백만) 개 나온다.
위 시퀀스는 오프라인에서 정답 계수를 만들어 실시간 모델을 학습시키는 과정이다. 추론할 때는 마지막 단계처럼 랜드마크만 보고 계수를 회귀한다.
정리. 블렌드셰이프 모델은 "좌표 → 표정 강도"를 학습한 작은 회귀기다. 이미지를 다시 보지 않고 랜드마크 기하만으로 표정을 추론한다. 그래서 빠르고 캘리브레이션이 필요 없다(calibration-free).
4.5 Facial Transformation Matrix (4×4) — 머리 자세
무엇인가
output_facial_transformation_matrixes=True로 켜면, Face Landmarker는 얼굴마다 4×4 변환 행렬을 하나 출력한다. 이 행렬은 머리의 3D 자세(pose)를 담는다. 회전(rotation) + 이동(translation) + 스케일(scale)이다. 공식 정의는 이렇다. "FaceLandmarker uses the matrix to transform the face landmarks from a canonical face model to the detected face, so users can apply effects on the detected landmarks." 풀면, 00장의 표준(canonical) 얼굴 모델을 지금 검출된 얼굴의 위치·방향·크기로 정렬시키는 변환이다.
4×4 동차 변환 행렬의 구조
컴퓨터 그래픽스의 표준 동차좌표(homogeneous) 변환 행렬 형태다.
| r11 r12 r13 | tx |
| r21 r22 r23 | ty |
| r31 r32 r33 | tz |
| 0 0 0 | 1 |
- 좌상단 3×3 (r..) = 회전(+스케일). 머리가 어느 방향을 보는지(yaw/pitch/roll)와 크기를 담는다.
- 오른쪽 열 (tx, ty, tz) = 이동. 카메라 기준 머리 위치다.
- 마지막 행 (0 0 0 1) = 동차좌표 관례다.
canonical 얼굴의 점 p에 이 행렬 M을 곱하면, M·p가 화면 속 실제 얼굴 위치로 간다. GHUM 모델은 내부적으로 4.4절의 6D 회전 표현으로 머리 회전을 예측하고, 이를 행렬 형태로 바꿔 제공한다. 별도의 큰 신경망이 따로 도는 것이 아니라 검출된 메시를 표준 메시에 맞추는 정렬 결과다.
왜 필요한가 — 블렌드셰이프와의 역할 분담
블렌드셰이프와 변환 행렬은 서로 다른 정보를 담는다. 둘은 보완 관계다.
| 구분 | 담는 정보 | 비유 |
|---|---|---|
| 52 blendshapes | 얼굴 내부 표정(눈·입·눈썹 움직임) | "어떤 표정을 짓는가" |
| 4×4 transformation matrix | 머리 전체의 자세(어디를 보는가, 어디 있는가) | "고개를 어디로 돌렸는가" |
아바타를 제대로 구동하려면 둘 다 필요하다. 블렌드셰이프만 쓰면 표정은 따라 하지만 아바타 머리가 정면에 고정돼 어색하다. 행렬을 같이 쓰면 사용자가 고개를 돌릴 때 아바타 머리도 같이 돌아간다.
위 그림처럼 한 얼굴에서 표정 계수와 자세 행렬이 갈라져 나와, 아바타에서 다시 합쳐진다.
4.6 응용 — 블렌드셰이프 계수를 리그에 매핑하기
대표 응용
- 실시간 3D 아바타 (Apple Memoji 류): 사용자의 52계수를 아바타의 52개 morph target 가중치로 1:1 연결한다. GHUM 논문의 동기 자체가 "facial motion capture applications like virtual avatars"다.
- VTubing: 웹캠 한 대로 2D·3D 버추얼 캐릭터를 구동한다. 마커·다중 카메라·캘리브레이션 없이 RGB 한 장이면 된다.
- AR 얼굴 필터: 변환 행렬로 머리 자세를 잡고, 블렌드셰이프로 표정을 반영해 마스크·이펙트를 얼굴에 정합한다.
- 표정 분석: 52계수가 FACS Action Unit과 느슨히 대응한다. 감정·표정 연구나 사용자 반응 측정에 쓴다.
리그 매핑 워크플로우 개요
위 시퀀스는 웹캠 프레임이 아바타 한 장으로 바뀌기까지의 데이터 흐름이다. 매핑에서 챙길 점은 네 가지다.
- 이름 일치(naming). 아바타 morph target 이름을 ARKit 52와 같게 만들면 매핑이 거의 자동이다. MediaPipe 기본 52는
_neutral을 빼고tongueOut을 안 쓴다는 점만 주의한다(4.2절). - 스무딩·필터링. 프레임마다 계수가 미세하게 떨릴 수 있다. One-Euro filter 등으로 시간축 평활화를 흔히 적용한다. MediaPipe 자체 기능이 아니라 응용 단의 관행이다.
- 표정 충돌 처리. 블렌드셰이프 기저는 비직교라 동시에 켜면 부자연스러운 조합이 생길 수 있다. 논문도
mouthClose계수가mouthOpen류보다 클 수 없다는 식의 제약을 prior로 둔다. 리그 측에서도 상충 슬라이더를 보정한다. - 머리 포즈 적용. 4×4 행렬에서 회전을 뽑아 아바타의 head 본에 적용한다. 표정(블렌드셰이프)과 자세(행렬)를 나눠 적용하는 게 깔끔하다.
4.7 자주 하는 오해 — 빠른 정리
| 오해 | 사실 | 근거 |
|---|---|---|
| "블렌드셰이프는 랜드마크 모델의 출력 채널 중 하나" | 별도의 두 번째 모델(Blendshape V2)이 랜드마크를 입력받아 회귀 | MediaPipe 문서, GHUM 논문 Fig.3 |
| "블렌드셰이프 모델이 원본 이미지를 본다" | 안 본다. 첫 모델이 만든 146개 랜드마크 좌표만 봄 | 논문 §4 |
| "478점 전부가 블렌드셰이프 입력" | 146점 서브셋만 입력(입·눈·눈썹·홍채·외곽) | 소스 kLandmarksSubsetIdxs, 논문 §4 |
| "MediaPipe 52 = ARKit 52, 완전히 동일" | 51개는 공통이나, MediaPipe는 _neutral 포함·tongueOut 제외, ARKit은 반대 |
소스 kBlendshapeNames, Apple 문서 |
| "blendshape 계수 합 = 1" | 합 제약 없음. 다중 회귀라 여러 개 동시에 큼 | 소스(ClassificationList지만 회귀 점수) |
| "기본으로 블렌드셰이프가 나온다" | 기본 False. output_face_blendshapes=True 필요 |
MediaPipe 문서 |
| "transformation matrix가 표정을 담는다" | 표정이 아니라 머리 자세(회전·이동·스케일)를 담음 | MediaPipe 문서 |
이 신규 출력 둘, 곧 52계수와 4×4 행렬은 레거시 Face Mesh에는 없던 것이다. 그 차이가 다음 장의 주제다.
→ 다음 장: 05. 기존 Face Mesh와의 차이
Sources
모든 수치·이름은 아래 출처 기준이다. 이름·인덱스의 ground truth는 MediaPipe 소스 코드다.
- MediaPipe Face Landmarker 공식 가이드 — Google AI Edge: https://ai.google.dev/edge/mediapipe/solutions/vision/face_landmarker
- "Blendshape prediction model: receives output from the face mesh model, predicts 52 blendshape scores";
output_face_blendshapes,output_facial_transformation_matrixes; Blendshape V2 입력1 × 146 × 2float16.
- "Blendshape prediction model: receives output from the face mesh model, predicts 52 blendshape scores";
- MediaPipe GitHub 소스 —
face_blendshapes_graph.cc(google-ai-edge/mediapipe, master): https://github.com/google-ai-edge/mediapipe/blob/master/mediapipe/tasks/cc/vision/face_landmarker/face_blendshapes_graph.cckBlendshapeNames(52개 이름·순서, index 0=_neutral"ignore it"),kLandmarksSubsetIdxs(146개 인덱스),EXTRA_FACE_BLENDSHAPES시 53개(tongueOut).
- Blendshapes GHUM: Real-time Monocular Facial Blendshape Prediction — Grishchenko et al. (Google), arXiv:2309.05782: https://arxiv.org/abs/2309.05782
- 52 블렌드셰이프 = ARKit 동일 집합(§1), 식 (1)(§3.1), MLP-Mixer
146×2토큰→96×64latent→52계수 + 6D 회전(§4), 약 2M 학습쌍·Pixel 6 1.2 ms·MNE 3.88%(§4, Table 1), Fig.3 "Extra blendshape (tongueOut)".
- 52 블렌드셰이프 = ARKit 동일 집합(§1), 식 (1)(§3.1), MLP-Mixer
- Apple ARKit —
ARFaceAnchor.BlendShapeLocation(52 표준,tongueOut포함·_neutral미포함, 0~1): https://developer.apple.com/documentation/arkit/arfaceanchor/blendshapelocation - (보조) ARKit↔FACS 대응 — Melinda Ozel, "ARKit to FACS Cheat Sheet": https://melindaozel.com/arkit-to-facs-cheat-sheet/
정밀 주석: GHUM 논문에 "Blendshape V2"라는 명칭은 등장하지 않는다. MediaPipe 공식 문서가 배포 모델을 "Blendshape V2"로 명명하고, 그 실체가 논문의 "real-time blendshapes model"이다(동일 입력
146×2·동일 52 출력으로 교차 확인). 둘을 동일 모델로 본 것은 합리적 교차식별이며, 명칭 자체의 1차 출처는 MediaPipe 문서다. MNE 3.88%(블렌드셰이프 모델)는 06장의 IOD MAE(메시 모델)와 별개 지표다(4.4절 명시).
일부 모델카드·문서는 한쪽 볼을 부풀리는
cheekPuff를 좌우로 나눠 표기하기도 하지만, MediaPipe 런타임이 내놓는 기본 52에서는cheekPuff한 개뿐이다. 표기 출처에 따라 이름이 갈리면 런타임 배열(kBlendshapeNames)을 기준으로 삼는다. ↩︎