인물, 배경 합성 촬영 (feat. TensorFlow.js/BodyPix)

하니·2025년 3월 14일

React 길잡이

목록 보기
17/21

"포토카드 영화리뷰 플랫폼" 프로젝트에서 담당하여 진행했던 TensorFlow.js/BodyPix를 이용한 인물, 배경 합성 촬영 개발을 지금이나마 간단하게 기록하고자 한다.

포토카드를 만들기 위해서는
1. 영화 포스터 이미지 실시간 바꾸기
2. 선택한 이미지를 배경으로 인물 촬영하기
기능이 필요했다.

프론트엔드에서 처리한 이유

고해상도 이미지 세그멘테이션(분할), 복잡한 효과, 비디오 세그멘테이션이 요구사항에 없고, 기본적인 배경 분리이기 때문에 백엔드가 아닌 프론트엔드에서 처리하기로 하였다.

백엔드에서 처리할 경우, 실시간 웹소켓 기반으로 프론트엔드에서 카메라로부터 프레임을 지속적으로 캡처하고 압축하여 웹소켓을 통해 서버로 전송해야 한다. 서버에서 처리 후 결과를 다시 웹소켓을 통해 클라이언트로 반환해준다.

❓ 프론트에서 처리하면 속도가 느려지지 않을까?

  • TensorFlow.js용 모델들은 웹 환경에 맞게 최적화되어 있다. BodyPix는 실시간 처리를 위해 경량화된 버전도 제공한다.
  • 최신 브라우저는 WebGL을 통해 GPU 가속을 지원한다.

Google Meet의 배경 처리 방법

Google Meet에서는 서버에서 처리하지 않고, MediaPipe로 구축된 웹 ML 솔루션을 사용해 사용자의 브라우저클라이언트에서 직접 모든 비디오 처리와 배경 효과를 적용하고 있다.

"현재 버전에서 모델 추론은 낮은 전력 소모와 가장 넓은 장치 커버리지를 위해 클라이언트의 CPU에서 실행됩니다." - Google Meet
  • WebAssembly 활용
  • WebGL2를 통한 렌더링

    WebGL Web Graphics Library
    웹 브라우저에서 플러그인 없이 하드웨어 가속 3D 및 2D 그래픽을 렌더링할 수 있게 해주는 Javascript API

프로세스

초기화 단계

1. 웹캠 입력

1-1. ML 모델BodyPix을 준비한다.

=> useModel 훅

1-2. 웹캠 스트림을 설정WebRTC API의 getUserMedia 메소드하고 비디오 요소와 연결한다.
=> useCamera 훅

1-3. 메인/오프스크린 캔버스를 초기화해준다.
=> useCanvas 훅

  • 메인 캔버스canvasRef : 실제 화면에 표시되는 캔버스로, <canvas> HTML 요소로 연결된다.
  • 오프스크린 캔버스offCanvasRef : 화면에 직접 표시되지 않는 캔버스로, 백그라운드에서 이미지 처리 작업을 수행하는 임시 작업 공간이다. -> 메인 UI에 영향을 주지 않고 이미지를 처리하므로 성능 최적화에 도움을 준다.

➡️ 모델, 비디오, 캔버스 준비 완료

비디오 프레임 프로세싱 Loop

=> useVideoProcessing 훅 (-renderVideo 함수에서 흐름대로 처리)
renderVideo 함수가 `requestAnimationFrame`을 통해 반복적으로 호출되면서 다음 과정이 계속 실행된다.

2. BodyPix 인물 감지 (세그먼테이션 작업)

세그먼테이션
이미지에서 사람의 윤곽을 찾아내는 단계로, ML 모델을 사용한 인식 작업이다.

  • 결과물 : 이진 마스크(검은색/투명)

모델을 사용하여 비디오의 인물 세그멘테이션(분할)을 처리하고, 마스킹 데이터(사람부분은 검은색, 배경은 투명색)를 생성한다.

3. 배경이 먼저 그려짐

메인 캔버스에 배경 이미지를 먼저 배치한다.

이때 배경 이미지사용자가 선택한 영화 포스터를 캔버스에 그릴 때 이미지의 비율을 유지하면서 캔버스 영역을 최대한 채우도록 배치한다.
이미지와 캔버스의 가로세로 비율을 비교하여,

  • 이미지가 캔버스보다 가로로 더 길면, width를 기준으로 맞추고 중앙에 배치한다.
  • 이미지가 캔버스보다 세로로 더 길면, height를 기준으로 맞추고 좌우 중앙에 배치한다.

4. 그 위에 인물 합성

마스킹 및 합성
캔버스 API를 사용한 그래픽 작업이다.

  • 결과물 : 실제 비디오에서 추출한 컬러 인물 이미지

➡️ *세그먼테이션은 "경계선"을 찾아내고, 마스킹 및 합성은 그 경계선을 이용해 실제 비디오에서 인물을 잘라낸다.

오프스크린 캔버스에 세그먼테이션으로 만든 마스크 이미지를 배치하여 겹치는 부분(인물 부분)만 표시되게 한다.
결과적으로 배경이 제거된 인물 이미지만 오프스크린 캔버스에 남게 된다.

➡️ 오프스크린 캔버스는 인물만 추출하는 중간 작업이 이루어지는 곳으로, 배경이 투명하게 처리된 인물 이미지만 그려진다.
➡️ 메인 캔버스에는 먼저 배경 이미지가 그려지고, 배경 이미지 위에 인물이 합성된 완성된 이미지가 표시된다.

개선사항

현재 코드는 사용자가 배경 이미지영화 포스터를 선택하여 바꿀 수 있기 때문에 매 프레임마다 배경을 다시 그리는 방식이다. (메인 캔버스에 배경 이미지를 새로 그림-배경 계산drawBackground 포함)

  • 사용자가 언제든 배경을 변경했을 때 즉시 반영 가능
  • 별도의 배경 변경 감지 로직이 필요 없음
  • 한 함수renderVideo로 전체 렌더링 프로세스 관리

-> 배경 변경이 자주 일어나지 않는다면, 배경이 변경될 때만 별도의 캔버스배경 캔버스에 그리고 사용자가 새 배경을 선택할 때만 이 배경용 캔버스에 배경 이미지를 그린다.
이후 매 프레임 렌더링 시 복잡한 계산 없이 이미 준비된 배경 캔버스를 메인 캔버스에 복사만 한다.

profile
Hi, I am HANI Developer(╹◡╹). .....1hani me?

0개의 댓글