연구 블로그

리그가 없는 그림을 웹캠 포즈로 움직이기

컴퓨터 비전2026.06.09 · 6분 읽기
리그가 없는 그림을 웹캠 포즈로 움직이기

사용자가 화면에 캐릭터를 그린다. 머리, 몸통, 팔다리를 어디에 어떻게 그릴지는 아무도 정하지 않았다. 그 사람이 웹캠 앞에서 팔을 들면 방금 그린 그림의 팔이 같이 올라가고, 몸을 기울이면 캐릭터가 기운다.

이게 동작하려면 풀어야 하는 문제가 하나 있다. 우리는 그 그림이 어떻게 생겼는지 사전에 전혀 모른다. 게임 캐릭터처럼 아티스트가 미리 본(bone)을 심어둔 리그도, 정해진 관절 위치도 없다. 임의의 사용자가 임의로 그린 평면 그림 하나가 입력의 전부고, 거기에 실시간 웹캠 포즈를 입혀 매 프레임 변형해야 한다.

포즈 추정과 Linear Blend Skinning은 새로운 기법이 아니다. 우리는 "정해진 리그가 없다"는 제약 위에서 이 둘을 연결했다. 파이프라인 전체를 관통하는 원칙은 하나였다. 불확실성을 버리지 않고, 신뢰도로 들고 다닌다.

리그가 없는 그림을 어떻게 움직이나

전형적인 스켈레탈 애니메이션은 아티스트가 본을 배치하고, 정점에 가중치를 칠하고, 그다음 포즈를 입힌다. 우리에게는 앞 두 단계가 주어지지 않는다. 그래서 파이프라인이 매번 자동으로 채운다. 웹캠 포즈를 관절로 축약하고, 프레임 간 떨림을 줄이고, 그림에 맞는 골격과 가중치를 자동으로 만든다. 마지막으로 가려진 관절을 추론으로 메운 뒤 매 프레임 캐릭터를 변형해 그린다.

33개 랜드마크를 21개 관절로 줄였다

포즈 추정 모델은 얼굴 세부와 손끝, 발끝까지 33개 랜드마크를 준다. 평면 캐릭터 구동에는 과하다. 손가락 마디 하나하나를 매핑하면 노이즈만 늘고, 정작 필요한 건 "손이 어디 있나" 정도다.

그래서 21개 애니메이션 관절로 축약했다. 모델이 직접 주지 않는 관절(엉덩이 중심, 가슴, 목, 머리, 척추)은 주변 랜드마크의 평균으로 합성하고, 나머지는 1:1로 매핑했다. 합성 관절에는 기본 신뢰도를 부여했고, 이후 모든 단계가 그 값을 기준으로 동작했다.

신뢰도가 낮을수록 덜 반영했다

포즈 추정의 프레임별 출력은 떨린다. 그대로 캐릭터에 입히면 사지가 경련하듯 떤다. 특히 신뢰도가 낮은 랜드마크일수록 위치가 크게 튄다.

기본은 지수이동평균(EMA) 평활이고, 여기에 두 가지를 더했다. 이전 위치에서 한 프레임 만에 크게 점프한 측정은 이상치로 버리고 이전 값을 유지한다. 그리고 모든 관절을 같은 세기로 평활하지 않는다. 신뢰도가 낮은 관절일수록 새 측정을 덜 반영했고, 신뢰도가 일정 수준 아래로 떨어지면 측정 자체를 쓰지 않았다.

가중치는 거리로 자동 계산했다

리그가 없으니 스키닝 가중치도 자동으로 만들어야 한다. 메시는 캐릭터 영역을 덮는 수백 개 정점 수준의 단순한 그리드다. 각 정점은 가장 가까운 관절 4개를 골라 거리의 역제곱으로 가중치를 나눠 가진다. 가까운 관절일수록 강하게, 먼 관절일수록 약하게 따라간다는 단순한 직관이다.

골격 정의는 정규화 좌표로 유지하고 실제 렌더링은 픽셀 좌표로 했다. 자동 리깅은 사용자가 그린 그림의 앵커 위치로 기본 골격을 덮어써, 그림에 맞는 rest pose를 복원한다.

GPU 대신 CPU에서 스키닝했다

Linear Blend Skinning은 보통 GPU 버텍스 셰이더에서 돈다. 정점이 수만 개인 캐릭터를 매 프레임 변형하려면 병렬 처리가 필수이기 때문이다. 우리 메시는 수백 개 수준이라 사정이 달랐다. 매 프레임 JS 루프로 돌려도 부담이 작고, GPU로 보내면 얻는 연산 이득보다 잃는 게 컸다.

관절 회전과 이동 계산이 이미 JS 쪽에 있어서, LBS도 JS에 두면 데이터를 GPU 버퍼로 왕복시킬 필요 없이 같은 객체를 바로 쓴다. 정점이 이상하게 튀면 그 정점의 가중치와 변형 전후 좌표를 그 자리에서 찍어볼 수 있다는 디버깅 이점도 컸다. 우리에게 필요한 건 병렬이 아니라 통합이었다.

가려진 관절은 약한 추론으로 메웠다

웹캠 앞의 사람은 늘 정자세가 아니다. 손이 몸에 가려지고, 다리가 프레임 밖으로 나가고, 옆으로 돌아선다. 포즈 모델은 가려진 관절에 낮은 신뢰도를 매기거나 엉뚱한 위치를 주고, 그대로 쓰면 가려진 순간마다 캐릭터의 사지가 무너진다.

그래서 폐색 관절은 추론 체인으로 메웠다. 부모 관절이 유효하면 부모 위치 기준으로 추정하고, 다음은 자식 관절 기준, 다음은 몸통 이동량만 반영, 아무 단서가 없으면 기본 자세를 유지한다.

핵심은 추론값에 낮은 신뢰도를 일부러 매기고, 단서가 약해질수록 단계적으로 더 낮추는 데 있다. 추론값을 강하게 믿으면 가림이 풀린 뒤에도 스무딩이 추론값을 붙들어 실측이 늦게 반영된다. 약하게 두면 실측이 들어오는 순간 쉽게 덮어쓴다. 가림은 보통 일시적이므로, 추론은 짧은 구멍을 메우는 임시 다리여야지 새 진실이 되어선 안 된다.

회고

이 파이프라인의 모든 단계는 "이 값을 얼마나 믿어야 하나"를 숫자로 끌고 다닌다. 그 숫자가 스무딩의 세기와 추론 대체의 순서를 결정한다. 다른 스택에서 실시간 노이즈 입력을 다룰 때도 가져갈 수 있는 원칙이다.

다르게 한다면 스무딩과 신뢰도 임계값을 처음부터 측정 가능하게 깔았을 것이다. 지금 값들은 경험적으로 잘 동작하지만 튜닝 근거가 문서로 남아 있지 않다. 떨림 정도와 추론 빈도를 프레임 단위로 로깅했다면, 이 값들이 정말 최적인지 데이터로 답할 수 있었다.

#실시간포즈추정#자동리깅#LinearBlendSkinning#캐릭터애니메이션
리그가 없는 그림을 웹캠 포즈로 움직이기6분 읽기 · 0%