브라우저 사이드 배경 제거 도구 구축 및 Canvas vs WebAssembly 벤치마크 결과
요약
브라우저 환경에서 제품 사진의 배경을 제거하기 위한 두 가지 기술적 접근 방식인 Canvas API와 WebAssembly(WASM) 기반 ML 모델을 비교 분석합니다. 단순 픽셀 조작 방식과 정교한 세그멘테이션 모델의 성능 및 정확도 차이를 벤치마크 결과와 함께 제시합니다.
핵심 포인트
- Canvas API 방식은 속도가 매우 빠르지만 배경이 균일해야 함
- WebAssembly + ML 방식은 복잡한 경계선 처리에 탁월하나 모델 로딩 비용 발생
- Canvas 방식은 이미지당 약 15ms, ML 방식은 약 180-220ms 소요
- 데이터 보안을 위해 서버 업로드 대신 로컬 브라우저 실행 방식 제안
폴더에 300장의 제품 사진이 있었습니다. 대부분 흰색 배경이었죠. 고객의 Shopify 스토어를 위해 이 사진들을 투명 배경으로 바꿔야 했습니다. 가장 뻔한 방법은 온라인 배경 제거 서비스에 업로드하는 것이었습니다. 5분이면 끝날 일이었죠.
하지만 생각이 바뀌었습니다. 이 사진들은 아직 출시되지 않은 제품 사진들이었습니다. 무작위 서버에 업로드하는 것은 옳지 않다고 느껴졌습니다. 게다가 매달 새로운 제품이 들어올 때마다 이 작업을 반복해야 했습니다. 저는 로컬에서 실행되는 무언가가 필요했습니다.
이제 브라우저에서도 이 작업이 가능합니다. 문제는 얼마나 잘 수행하느냐였습니다.
두 가지 접근 방식, 하나의 브라우저 탭
픽셀을 서버로 전송하지 않고 브라우저에서 배경을 제거하는 두 가지 실질적인 방법이 있습니다:
Canvas API 픽셀 조작 (pixel bashing). 이미지를 <canvas>에 로드하고, getImageData()로 픽셀 데이터를 가져온 뒤, 수동으로 알파(alpha) 값을 설정합니다. 배경에서 기준 색상을 선택하고, 각 픽셀이 그 색상과 얼마나 떨어져 있는지 거리를 계산하여 임계값(threshold)을 적용합니다. 이 방식은 빠르고 의존성(dependencies)이 전혀 필요 없습니다. 하지만 균일한 배경에서만 작동합니다.
WebAssembly + ML 모델. 세그멘테이션(segmentation) 모델을 WASM으로 컴파일하여 로드한 뒤, 브라우저에서 추론(inference)을 실행합니다. ONNX Runtime Web을 사용하면 이것이 실용적으로 구현됩니다. 머리카락, 털, 복잡한 경계선 등을 처리할 수 있지만, 8MB 이상의 모델을 다운로드해야 하며 더 많은 CPU를 소모합니다.
저는 두 가지 방식을 모두 구축하여 500장의 테스트 이미지를 통해 실행해 보았습니다. 결과는 다음과 같습니다.
Canvas 접근 방식: 빠르고 단순함
코드는 간단합니다. 이미지를 로드하고, 모서리에서 픽셀 하나를 샘플링하여(그곳이 배경이라고 가정), 임계값 내에 있는 모든 픽셀의 알파 값을 0으로 설정합니다:
function removeBackgroundCanvas(image, threshold = 40) {
const canvas = document.createElement('canvas');
canvas.width = image.width;
...
이 방식은 스튜디오 조명을 받은 제품 사진에서 놀라울 정도로 잘 작동합니다. 제 노트북 기준으로 흰색 또는 밝은 회색 배경은 이미지당 약 15밀리초(milliseconds) 만에 제거됩니다. 의존성도 없고, 로딩 스피너도 필요 없으며, 모델 다운로드도 필요 없습니다.
문제점: 배경이 균일하지 않은 경우(그라데이션, 질감이 있는 벽, 벽 색상과 유사한 셔츠 등)에는 바로 성능이 무너집니다. 가장자리가 울퉁불퉁해지거나, 후광(halo) 현상이 나타나거나, 피사체의 일부가 사라지는 현상이 발생합니다.
WebAssembly 접근 방식: 정확하지만 무거움
실제 이미지의 경우, 무엇이 전경(foreground)이고 무엇이 아닌지를 이해하는 모델이 필요합니다. MediaPipe의 셀피 세그멘테이션 (selfie segmentation) 모델은 브라우저에서 실행되며 ONNX Runtime Web을 통해 로드할 수 있습니다:
import * as ort from 'onnxruntime-web';
async function removeBackgroundML(image) {
...
문제는 모델 파일 크기가 8.3 MB라는 점입니다. 빠른 연결 상태에서도 첫 로딩에 약 1.2초가 소요됩니다. 추론 (inference) 시간은 이미지당 대략 180-220밀리초 (milliseconds)가 걸립니다. 또한 onnxruntime-web을 가져와야 하므로 번들 크기가 약 2 MB 증가합니다.
하지만 결과물은 훨씬 더 뛰어납니다. 머리카락 한 올, 털, 투명한 물체 등 Canvas 접근 방식으로는 처리할 수 없는 부분들도 상당히 잘 처리됩니다.
벤치마크 (Benchmark)
2023년형 MacBook Pro (M2, 16 GB RAM)에서 두 가지 접근 방식을 모두 사용하여 500장의 이미지를 테스트했습니다:
| 지표 | Canvas | WebAssembly |
|---|---|---|
| 이미지당 시간 | 12-18 ms | 180-220 ms |
| ... | ... | ... |
Canvas 접근 방식은 기본적으로 비용이 거의 들지 않습니다. 이미지 디코딩 (decoding) 비용은 이미 지불하고 있는 셈이며, JIT가 작동하면 픽셀 루프 (pixel loop)가 네이티브 속도로 실행됩니다. 500장의 이미지에 대한 총 처리 시간은 8초 미만이었습니다.
WebAssembly 접근 방식은 동일한 배치에 대해 초기 모델 다운로드 시간을 제외하고 약 95초가 소요되었습니다. 하지만 Canvas가 500장 중 187장을 정확히 처리한 것에 비해, WebAssembly는 412장을 정확하게 처리했습니다.
최종 결정 사항
저는 두 방식을 결합했습니다. 도구는 먼저 Canvas 접근 방식을 시도합니다. 만약 가장자리 픽셀의 30% 이상이 부분적으로 투명해진다면(이는 배경이 균일하지 않다는 신호입니다), ML 모델로 전환(fallback)합니다. 이 하이브리드 (hybrid) 접근 방식은 일반적인 제품 사진 배치에서 이미지당 평균 약 40밀리초 (ms)를 기록했습니다.
단순한 이미지와 복잡한 이미지를 모두 처리할 수 있는 브라우저 기반 배경 제거 도구가 필요하다면, 이 도구는 완전히 사용자의 기기 내에서 실행됩니다. UI는 스페인어로 되어 있지만, 드래그 앤 드롭 방식은 번역이 필요 없습니다.
문서(docs)만 봐서는 명확히 알 수 없는 몇 가지 배운 점들이 있습니다:
-
getImageData()는 대부분의 브라우저에서 GPU-to-CPU 리드백 (readback)을 트리거합니다. 여러 이미지를 순차적으로 처리하는 경우, 픽셀을 다시 읽어오기 전에 Canvas 작업을 배치 (batch) 처리하세요. 그렇지 않으면 매번 동기화 (sync) 비용을 지불하게 됩니다. -
ONNX Runtime Web에는
wasm과webgl이라는 두 가지 백엔드 (backend)가 있습니다. WebGL 백엔드는 추론 (inference) 시 3~5배 더 빠르지만, WebGL을 사용할 수 있는 경우에만 작동합니다. 항상ort.env.wasm.numThreads를 확인하고 이를navigator.hardwareConcurrency로 설정하세요. 그렇지 않으면 코어 (core)를 유휴 상태로 두게 됩니다. -
제품 사진의 경우, 256x256 모델 입력으로도 충분합니다. 512x512로 높이면 가장자리 (edge)가 눈에 띄게 좋아지지만, 추론 시간은 대략 4배 더 걸립니다. 전문적인 리터칭 (retouching)을 하는 것이 아니라면 그만한 가치는 없습니다.
단순히 사진에서 배경을 빠르게 제거하고 싶다면, 먼저 Canvas 접근 방식을 시도해 보세요. 생각보다 간단합니다. 만약 그것으로 충분하지 않다면, 이제 브라우저에서 실제 ML 모델을 실행할 수 있습니다. 단지 잠시 기다리기만 하면 됩니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기