상품 사진 배경을 파이썬으로 한 번에 지우는 방법입니다. 포토샵 없이, 가상환경 설정도 없이, 파일 하나에 명령 한 줄로 수백 장을 처리합니다. 2026년 기준 가장 정확한 모델은 BiRefNet 계열이고, 상업용으로 쓸 때 라이선스를 확인해야 하는 모델도 섞여 있습니다.
요약 (Quick Answer) — 2026 누끼 자동화
가장 빠른 길 — 파일 하나만 만들면 끝
nukki.py 파일 상단에 의존성을 적어두면 uv가 알아서 환경을 만들고 실행합니다.
# /// script
# requires-python = ">=3.11,<3.14"
# dependencies = ["rembg[cpu]>=2.0.75", "pillow"]
# ///
from rembg import remove, new_session
from PIL import Image
session = new_session("birefnet-general")
Image.open("input.jpg").save # 아래 본문에 전체 코드
uv run nukki.py
가상환경도, pip install도, 파이썬 버전 걱정도 없습니다.
uv가 없으면 먼저
curl -LsSf https://astral.sh/uv/install.sh | sh # macOS·Linux
# Windows: powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
모델 어떤 걸 쓸까
| 상황 | 모델 | 라이선스 |
|---|---|---|
| 품질 최우선 | birefnet-general ⭐ |
MIT (상업 자유) |
| 속도·CPU 배치 | u2net |
MIT |
| 균형 | isnet-general-use |
MIT |
| 인물 사진 | birefnet-portrait |
MIT |
| 사람 전신 | u2net_human_seg |
MIT |
| 애니·일러스트 | isnet-anime |
MIT |
| SOTA (주의) | bria-rmbg |
⚠️ 상업용 유료 |
3줄 결론
- 품질이 중요하면
birefnet-general. 머리카락·털·투명 물체에서 차이가 크게 납니다. - CPU로 수백 장을 돌려야 하면
u2net. BiRefNet은 CPU에서 초 단위로 느려집니다. bria-rmbg는 성능이 좋지만 상업적 사용에 유료 라이선스가 필요합니다. 쇼핑몰에 쓸 거라면 확인하세요.
검증 환경: macOS Sequoia 15.x / Apple Silicon M2 Pro / uv 0.11.x / Python 3.12 / rembg 2.0.75
마지막 업데이트: 2026-07-27

배경 제거, 어떤 방법이 있나
상품 사진에서 배경을 지우는 방법은 크게 셋입니다.
| 방법 | 비용 | 품질 | 자동화 |
|---|---|---|---|
| 포토샵 수동 | 시간 | 최고 | 어려움 |
| 온라인 서비스 (remove.bg 등) | 장당 과금 | 좋음 | API 있음 |
| 파이썬 rembg ⭐ | 0원 | 모델에 따라 | 쉬움 |
이 글은 세 번째를 다룹니다. 로컬에서 돌기 때문에 장당 비용이 없고, 사진이 외부로 나가지 않습니다. 신제품 사진을 공개 전에 처리해야 한다면 이 점이 결정적입니다.
rembg란
rembg는 배경 제거 모델 16종을 하나의 API로 감싼 오픈소스 도구입니다. CLI, 파이썬, HTTP, 도커로 쓸 수 있습니다. MIT 라이선스이고 모델 체크포인트는 첫 실행 때 ~/.u2net/으로 자동 내려받습니다.
"pip install 하고 바로 쓰기"가 되는 가장 편한 선택입니다. 천장은 엣지 품질인데, 그 천장을 어느 모델로 얼마나 올릴지가 이 글의 핵심입니다.
어떤 모델을 써야 하나 — 모델 비교
rembg에 모델이 16종 들어 있습니다. 실제로 고민할 건 다섯 개면 충분합니다.
전체 비교
| 모델 | 계열 | 품질 | 속도 | 크기 | 라이선스 | 추천 상황 |
|---|---|---|---|---|---|---|
| birefnet-general | BiRefNet | ⭐⭐⭐⭐⭐ | 느림 | 큼 | MIT | 품질 최우선, GPU 있음 |
| birefnet-general-lite | BiRefNet | ⭐⭐⭐⭐ | 보통 | 중간 | MIT | 품질·속도 타협 |
| isnet-general-use | DIS | ⭐⭐⭐⭐ | 보통 | 중간 | MIT | 무난한 기본값 |
| u2net | U²-Net | ⭐⭐⭐ | 빠름 | 176MB | MIT | CPU 대량 배치 |
| silueta | U²-Net | ⭐⭐⭐ | 빠름 | 43MB | MIT | 디스크·메모리 제약 |
| bria-rmbg | BiRefNet 기반 | ⭐⭐⭐⭐⭐ | 느림 | 큼 | ⚠️ 상업용 유료 | 라이선스 확보 시 |
용도 특화 모델
| 모델 | 용도 |
|---|---|
u2net_human_seg |
사람 전신·인물 사진 |
birefnet-portrait |
얼굴·헤드샷, 프로필 사진 |
u2net_cloth_seg |
의류 파싱 (상의·하의·전신 3분류) |
isnet-anime |
애니·일러스트, 선이 뚜렷한 그림 |
birefnet-dis |
고정밀 이분 분할 |
birefnet-hrsod |
고해상도 돌출 객체 |
birefnet-cod |
위장 객체 — 배경과 색·질감이 비슷한 피사체 |
birefnet-massive |
대규모 데이터셋 학습판 |
sam |
Segment Anything, 프롬프트(점·박스)로 지정 |
u2netp |
u2net 경량판 |
BiRefNet이 왜 나은가
BiRefNet은 2024년 CAAI AIR에 발표된 Bilateral Reference Network입니다(Peng Zheng 외). 고해상도 참조 특징으로 미세한 엣지를 보존합니다. MIT 라이선스라 상업적 사용에 제약이 없습니다.
공개된 비교 결과를 보면 세 축에서 앞섭니다.
엣지 정확도 — 머리카락과 털이 회색 후광으로 뭉개지지 않습니다. u2net은 실루엣은 알아볼 수 있게 만들지만 미세한 가닥을 약 40% 잃습니다.
배경 거부 — 피사체 아래 그림자를 알파 채널에 섞지 않고 정확히 제외합니다.
해상도 — 2048×2048까지 네이티브 입력 크기로 처리하며 타일링 아티팩트가 없습니다. rembg의 기존 모델들은 내부적으로 다운샘플한 뒤 업샘플하는데, 엣지가 뭉개지는 원인이 여기입니다.
의류·전자제품·식품·화장품이 섞인 500장 배치 테스트에서 머리카락 정확도 격차가 6%로 나타났습니다. 500장당 약 30장이 수동 보정 대상이 된다는 뜻입니다. 물량이 커지면 절감 효과가 사라지는 지점입니다.
대가는 연산량
BiRefNet은 ViT 계열 인코더를 쓰는 무거운 모델입니다.
- CPU: 장당 초 단위. 수백 밀리초가 아닙니다.
- RTX A5000 (24GB): 1024×1024 기준 1초 미만
GPU 워커라면 쓸 만하고, 월 5달러짜리 VPS에서는 고통스럽습니다. 하드웨어를 보고 고르세요.
결정 트리
GPU가 있나?
├─ 있다 → birefnet-general
└─ 없다
├─ 사진이 50장 이하 → birefnet-general (느려도 감당 가능)
└─ 수백~수천 장
├─ 품질이 중요 → birefnet-general-lite
└─ 속도가 중요 → u2net 또는 isnet-general-use
피사체가 사람인가?
├─ 얼굴·헤드샷 → birefnet-portrait
└─ 전신 → u2net_human_seg
일러스트·애니인가? → isnet-anime
배경과 색·질감이 비슷한가? → birefnet-cod⚠️ 상업적 사용 전에 확인할 것
블로그에 쓰는 개인 사진이면 상관없습니다. 쇼핑몰 상품 이미지, 고객 납품, 서비스 기능으로 쓸 거라면 반드시 읽으세요.
안전한 모델 (MIT / 허용적 라이선스)
birefnet-* 계열, u2net 계열, isnet-* 계열, silueta — 상업적 사용에 제약이 없습니다.
확인이 필요한 모델
bria-rmbg — BRIA AI의 배경 제거 모델입니다. 성능은 최상급이지만 상업적 사용에 유료 라이선스가 필요합니다. 자체 호스팅할 때는 BRIA 상업용 라이선스 확인을 거친 뒤에 사용해야 합니다.
rembg가 이 모델을 포함하고 있다는 이유로 자유롭게 쓸 수 있다고 오해하기 쉽습니다. rembg 자체는 MIT이지만 개별 모델 가중치의 라이선스는 별개입니다.
허용적 라이선스로 자체 호스팅하면서 품질을 원한다면 BiRefNet general 또는 portrait 변형이 답입니다.
실무 권장
# 상업용 파이프라인이라면 모델을 명시적으로 화이트리스트하세요
SAFE_MODELS = {
"birefnet-general",
"birefnet-general-lite",
"birefnet-portrait",
"isnet-general-use",
"u2net",
"u2net_human_seg",
"silueta",
}
def make_session(model_name: str):
if model_name not in SAFE_MODELS:
raise ValueError(f"라이선스 확인 필요: {model_name}")
return new_session(model_name)
이렇게 두면 나중에 팀원이 무심코 bria-rmbg를 넣는 사고를 막습니다.
준비 — uv 하나만 깔면 됩니다
파이썬 환경 설정에서 막히는 경우가 대부분입니다. 2026년에는 uv로 이 단계를 건너뛸 수 있습니다.
uv 설치
# macOS · Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
파이썬이 깔려 있지 않아도 됩니다. uv가 자체 완결된 바이너리라 파이썬 인터프리터 없이 설치되고, 오히려 uv가 파이썬을 설치합니다.
확인:
uv --version
→ uv를 더 알고 싶으면 uv 완벽 가이드 (409)에 정리해뒀습니다.
왜 pip 대신 uv인가 — 실제로 막히는 지점
rembg는 Python 3.11 이상 3.14 미만만 지원합니다. 시스템 파이썬이 3.14 이상이면 pip install rembg가 이렇게 실패합니다.
ERROR: Could not find a version that satisfies the requirement rembg이 글에서 가장 많이 받는 질문이 이 오류입니다. pip으로 풀려면 pyenv를 깔고 3.12를 빌드하고 가상환경을 만들어야 합니다.
uv는 한 줄입니다.
uv python pin 3.12
없으면 알아서 내려받습니다. onnxruntime처럼 무거운 의존성 설치도 훨씬 빠릅니다.
방법 A. 파일 하나로 끝내기 (권장) ⭐
파이썬 프로젝트를 만들지 않고 스크립트 파일 하나만 씁니다. 비개발자에게 가장 편한 방식입니다.
nukki.py를 만들고 아래 내용을 그대로 붙여넣으세요.
# /// script
# requires-python = ">=3.11,<3.14"
# dependencies = [
# "rembg[cpu]>=2.0.75",
# "pillow>=10.0",
# ]
# ///
"""
사용법:
uv run nukki.py # inputs/ 폴더를 처리해 outputs/ 에 저장
uv run nukki.py in.jpg out.png # 한 장만
"""
import sys
from pathlib import Path
from PIL import Image
from rembg import new_session, remove
MODEL = "birefnet-general" # 느리면 "u2net" 또는 "isnet-general-use"
TO_WHITE = True # False면 투명 PNG
EXTS = {".jpg", ".jpeg", ".png", ".webp", ".bmp"}
def cut_one(src_path: Path, dst_path: Path, session) -> None:
src = Image.open(src_path).convert("RGBA")
cut = remove(src, session=session)
if TO_WHITE:
canvas = Image.new("RGBA", cut.size, (255, 255, 255, 255))
canvas.paste(cut, (0, 0), cut)
canvas.convert("RGB").save(dst_path, quality=95)
else:
cut.save(dst_path)
def main() -> None:
session = new_session(MODEL)
# 인수가 2개면 단일 파일 모드
if len(sys.argv) == 3:
cut_one(Path(sys.argv[1]), Path(sys.argv[2]), session)
print(f"완료: {sys.argv[2]}")
return
in_dir, out_dir = Path("inputs"), Path("outputs")
out_dir.mkdir(exist_ok=True)
files = sorted(p for p in in_dir.iterdir() if p.suffix.lower() in EXTS)
if not files:
print("inputs/ 폴더에 이미지를 넣어주세요")
return
ext = ".jpg" if TO_WHITE else ".png"
for i, src in enumerate(files, 1):
dst = out_dir / f"{src.stem}{ext}"
try:
cut_one(src, dst, session)
print(f"[{i}/{len(files)}] {src.name} → {dst.name}")
except Exception as e:
print(f"[{i}/{len(files)}] 실패: {src.name} — {e}")
if __name__ == "__main__":
main()
폴더 구조를 이렇게 만들고,
누끼작업/
├── nukki.py
├── inputs/ ← 원본 사진을 여기에
└── outputs/ ← 결과가 여기 생김명령 하나로 실행합니다.
uv run nukki.py
첫 실행은 조금 걸립니다. uv가 파이썬과 의존성을 준비하고, rembg가 모델 파일을 내려받습니다. 두 번째부터는 바로 시작합니다.
파일 상단 주석이 하는 일
# /// script
# requires-python = ">=3.11,<3.14"
# dependencies = ["rembg[cpu]>=2.0.75", "pillow>=10.0"]
# ///
PEP 723 표준입니다. 파이썬 버전 범위와 필요한 패키지를 파일 안에 적어두는 방식입니다. uv가 이 주석을 읽고 임시 환경을 만들어 실행합니다.
덕분에 이런 게 없어집니다.
python -m venv .venvsource .venv/bin/activatepip install -r requirements.txtrequirements.txt파일 자체
스크립트 하나만 남습니다. 다른 사람에게 줄 때도 이 파일만 보내면 됩니다.
방법 B. 프로젝트로 관리하기
정기적으로 돌리거나 팀과 공유한다면 프로젝트 모드가 낫습니다.
uv init nukki && cd nukki
uv python pin 3.12
uv add "rembg[cpu]>=2.0.75" "pillow>=10.0"
uv add가 세 가지를 한 번에 합니다. pyproject.toml에 항목을 쓰고, 의존성을 해결하고, uv.lock을 갱신합니다.
GPU가 있으면 extras를 바꿉니다.
uv add "rembg[gpu]>=2.0.75" "pillow>=10.0"
실행:
uv run python main.py
source .venv/bin/activate를 할 일이 없습니다. uv run이 실행 직전에 환경을 맞춰줍니다.
왜 프로젝트 모드인가
uv.lock이 전이 의존성까지 정확한 버전으로 못박습니다. 6개월 뒤 다시 돌렸을 때 같은 결과가 나옵니다. onnxruntime 버전이 바뀌어서 결과가 미묘하게 달라지는 일을 막습니다.
락파일은 git에 커밋하세요.
.venv/
inputs/
outputs/
__pycache__/
# uv.lock 은 커밋합니다
방법 C. 설치 없이 CLI만 쓰기
코드를 아예 안 쓰고 명령줄로 처리할 수도 있습니다.
# 한 장
uvx --from "rembg[cpu,cli]>=2.0.75" rembg i -m birefnet-general input.jpg output.png
# 폴더 전체
uvx --from "rembg[cpu,cli]>=2.0.75" rembg p -m birefnet-general inputs/ outputs/
uvx는 설치 없이 일회성 실행입니다. 파이썬 코드를 한 줄도 안 씁니다.
자주 쓴다면 전역 설치가 편합니다.
uv tool install "rembg[cpu,cli]>=2.0.75"
rembg i -m birefnet-general input.jpg output.png
이 방식은 흰 배경 합성이나 정사각형 규격 맞추기 같은 후처리를 못 합니다. 투명 PNG만 나옵니다.
🚨 버전 고정은 필수입니다
위 예시 모두에 >=2.0.75가 붙어 있습니다. 실수가 아닙니다.
rembg 2.0.75 미만 버전에 CVE-2026-40086 취약점이 있습니다. HTTP 서버의 custom-model-type 경로에 디렉터리 트래버설 결함입니다. rembg s로 서버를 띄우지 않는다면 직접 영향은 적지만, 명시해두는 편이 맞습니다.
PEP 723 헤더나 pyproject.toml 어느 쪽이든 반드시 최소 버전을 적으세요.
세 방법 비교
| 항목 | A. 단일 파일 | B. 프로젝트 | C. CLI만 |
|---|---|---|---|
| 준비 시간 | 1분 | 3분 | 30초 |
| 파일 개수 | 1개 | 3개+ | 0개 |
| 락파일 (재현성) | ❌ | ✅ | ❌ |
| 후처리 커스터마이징 | ✅ | ✅ | ❌ |
| 다른 사람에게 전달 | 파일 1개 | 폴더 전체 | 명령어만 |
| 추천 대상 | 비개발자, 일회성 | 팀·정기 작업 | 빠른 테스트 |
처음이면 A로 시작하세요. 반복 작업이 되면 B로 옮기면 됩니다.
모델 파일은 어디 저장되나
첫 실행 때 ~/.u2net/으로 자동 내려받습니다. uv 환경과 별개로 관리됩니다.
ls -lh ~/.u2net/
du -sh ~/.u2net/
모델 하나가 수십에서 수백 MB입니다. 여러 개 시험해보면 금세 커집니다.
경로를 옮기려면:
export U2NET_HOME=/Volumes/External/rembg-models
uv run nukki.py
uv 환경을 지워도 모델은 남습니다. 반대로 모델을 지워도 uv 환경은 그대로입니다. 둘은 독립적입니다.
기본 사용 — 배경 지우기
CLI로 한 장
rembg i -m birefnet-general input.jpg output.png
CLI로 폴더 전체
rembg p -m birefnet-general inputs/ outputs/
가장 빠른 방법입니다. 코드가 필요 없습니다.
파이썬으로 한 장
from rembg import remove, new_session
from PIL import Image
session = new_session("birefnet-general")
input_image = Image.open("input.jpg")
output_image = remove(input_image, session=session)
output_image.save("output.png")
결과는 배경이 투명한 PNG입니다. JPG로 저장하면 투명도가 사라지므로 반드시 PNG로 저장하세요.
세션 재사용이 중요합니다
new_session()은 모델을 메모리에 올립니다. 이미지마다 새로 만들면 매번 로딩을 다시 합니다.
# ❌ 느립니다
for path in paths:
output = remove(Image.open(path)) # 매번 모델 로딩
# ✅ 세션 하나를 재사용
session = new_session("birefnet-general")
for path in paths:
output = remove(Image.open(path), session=session)
수백 장을 처리할 때 이 차이가 몇 분에서 몇십 분까지 벌어집니다.
흰 배경으로 바꾸기
쇼핑몰 상품 이미지는 투명 배경보다 흰 배경을 요구하는 경우가 많습니다.
from rembg import remove, new_session
from PIL import Image
from pathlib import Path
def remove_bg_to_white(input_path, output_path, session, bg_color=(255, 255, 255)):
"""배경을 지우고 지정한 단색으로 채웁니다."""
src = Image.open(input_path).convert("RGBA")
cut = remove(src, session=session)
canvas = Image.new("RGBA", cut.size, (*bg_color, 255))
canvas.paste(cut, (0, 0), cut)
canvas.convert("RGB").save(output_path, quality=95)
print(f"{output_path} 저장 완료")
session = new_session("birefnet-general")
remove_bg_to_white("input.jpg", "output.jpg", session)
bg_color만 바꾸면 다른 색도 됩니다.
remove_bg_to_white("in.jpg", "out.jpg", session, bg_color=(245, 245, 245)) # 연회색
remove_bg_to_white("in.jpg", "out.jpg", session, bg_color=(0, 0, 0)) # 검정
여백을 추가하려면
쇼핑몰 규격이 "정사각형 + 상품이 중앙 80%"인 경우가 흔합니다.
def fit_to_square(input_path, output_path, session, size=1000, padding=0.1):
src = Image.open(input_path).convert("RGBA")
cut = remove(src, session=session)
# 실제 피사체 영역만 잘라냄
bbox = cut.getbbox()
cut = cut.crop(bbox)
# 여백을 뺀 크기로 리사이즈
inner = int(size * (1 - padding * 2))
cut.thumbnail((inner, inner), Image.LANCZOS)
canvas = Image.new("RGBA", (size, size), (255, 255, 255, 255))
offset = ((size - cut.width) // 2, (size - cut.height) // 2)
canvas.paste(cut, offset, cut)
canvas.convert("RGB").save(output_path, quality=95)
getbbox()로 투명 영역을 잘라내는 게 핵심입니다. 원본 여백이 제각각이어도 결과가 균일해집니다.
알파 매팅 — 머리카락·털·투명 물체
머리카락, 동물 털, 유리처럼 반투명한 물체는 기본 설정으로 잘 안 됩니다. 알파 매팅을 켜면 엣지가 부드러워집니다.
output = remove(
input_image,
session=session,
alpha_matting=True,
alpha_matting_foreground_threshold=240, # 전경 판정 임계값 (0~255)
alpha_matting_background_threshold=10, # 배경 판정 임계값 (0~255)
alpha_matting_erode_size=10, # 경계 침식 크기
)
파라미터 조정 감
| 증상 | 조정 |
|---|---|
| 배경이 조금 남는다 | background_threshold를 올림 (10 → 20) |
| 피사체 일부가 지워진다 | foreground_threshold를 내림 (240 → 220) |
| 경계가 지저분하다 | erode_size를 올림 (10 → 15) |
| 경계가 너무 파먹혔다 | erode_size를 내림 (10 → 5) |
항상 켤 필요는 없습니다
알파 매팅은 연산이 추가돼 느려집니다. 개선되는 경우와 그렇지 않은 경우가 갈립니다. 실제 사용 후기를 보면 알파 매트 조정으로 나아진 케이스와 나아지지 않은 케이스가 모두 보고됩니다.
머리카락·털이 없는 상품 사진이라면 끄는 편이 낫습니다. 대신 birefnet-general을 쓰면 알파 매팅 없이도 엣지 품질이 상당히 좋습니다.
마스크만 뽑기
후처리를 직접 하려면 알파 마스크만 받을 수 있습니다.
mask = remove(input_image, session=session, only_mask=True)
mask.save("mask.png") # 흑백 마스크
포토샵이나 다른 파이프라인에 넘길 때 유용합니다.
여러 장 한 번에 처리하기
from rembg import remove, new_session
from PIL import Image
from pathlib import Path
import time
EXTS = {".jpg", ".jpeg", ".png", ".webp", ".bmp"}
def batch_remove_bg(
input_dir: str,
output_dir: str,
model_name: str = "birefnet-general",
to_white: bool = True,
):
in_path = Path(input_dir)
out_path = Path(output_dir)
out_path.mkdir(parents=True, exist_ok=True)
files = sorted(p for p in in_path.iterdir() if p.suffix.lower() in EXTS)
if not files:
print(f"{input_dir}에 이미지가 없습니다")
return
session = new_session(model_name) # 세션 1회 생성
print(f"{len(files)}장 처리 시작 (모델: {model_name})")
started = time.time()
ok, failed = 0, []
for i, src_path in enumerate(files, 1):
try:
src = Image.open(src_path).convert("RGBA")
cut = remove(src, session=session)
if to_white:
canvas = Image.new("RGBA", cut.size, (255, 255, 255, 255))
canvas.paste(cut, (0, 0), cut)
result = canvas.convert("RGB")
dst = out_path / f"{src_path.stem}.jpg"
result.save(dst, quality=95)
else:
dst = out_path / f"{src_path.stem}.png"
cut.save(dst)
ok += 1
print(f"[{i}/{len(files)}] {src_path.name} → {dst.name}")
except Exception as e:
failed.append((src_path.name, str(e)))
print(f"[{i}/{len(files)}] 실패: {src_path.name} — {e}")
elapsed = time.time() - started
print(f"\n완료: {ok}장 성공, {len(failed)}장 실패")
print(f"소요 {elapsed:.1f}초 (장당 평균 {elapsed / len(files):.2f}초)")
if failed:
print("\n실패 목록:")
for name, err in failed:
print(f" - {name}: {err}")
if __name__ == "__main__":
batch_remove_bg("inputs", "outputs", model_name="birefnet-general")
원글 코드에서 바꾼 부분입니다.
glob3번 호출 →Path.iterdir()한 번으로 정리, webp·bmp 확장자 추가- 예외 처리 추가 — 한 장이 깨져도 나머지가 계속 돌아갑니다
- 진행률과 소요 시간 출력 — 수백 장 돌릴 때 필요합니다
gc.collect()제거 — 세션을 재사용하면 필요 없고, 오히려 매 호출마다 부르면 느려집니다
병렬 처리는 조심하세요
multiprocessing으로 돌리면 프로세스마다 모델을 새로 올려서 메모리가 터집니다. ONNX Runtime 스레드 수를 늘리는 편이 안전합니다.
export OMP_NUM_THREADS=8
내 환경에서 모델별 속도 재보기
공개된 수치는 하드웨어와 해상도에 따라 크게 달라집니다. rembg CPU 처리 시간이 장당 1초라는 보고도 있고 3~8초라는 보고도 있습니다. 직접 재는 게 정확합니다.
bench.py로 저장하고 uv run bench.py sample.jpg로 실행하세요.
# /// script
# requires-python = ">=3.11,<3.14"
# dependencies = ["rembg[cpu]>=2.0.75", "pillow>=10.0"]
# ///
"""사용법: uv run bench.py sample.jpg"""
import sys
import time
from PIL import Image
from rembg import new_session, remove
MODELS = [
"u2net",
"silueta",
"isnet-general-use",
"birefnet-general-lite",
"birefnet-general",
]
def benchmark(image_path: str, runs: int = 3) -> None:
img = Image.open(image_path).convert("RGBA")
print(f"이미지: {image_path} ({img.width}x{img.height})\n")
print(f"{'모델':<24} {'평균(초)':>10} {'최소':>8} {'최대':>8}")
print("-" * 54)
for name in MODELS:
session = new_session(name)
remove(img, session=session) # 워밍업 — 모델 로딩 시간 제외
times = []
for _ in range(runs):
t0 = time.perf_counter()
remove(img, session=session)
times.append(time.perf_counter() - t0)
print(
f"{name:<24} {sum(times) / len(times):>10.2f} "
f"{min(times):>8.2f} {max(times):>8.2f}"
)
if __name__ == "__main__":
path = sys.argv[1] if len(sys.argv) > 1 else "sample.jpg"
benchmark(path)
uv run bench.py sample.jpg
워밍업 호출이 중요합니다. 첫 호출에는 모델 로딩 시간이 섞여서 실제 추론 속도를 왜곡합니다.
결과를 보고 판단하세요. 장당 0.5초면 1,000장에 8분, 장당 5초면 1,000장에 83분입니다. 이 차이가 모델 선택을 결정합니다.
GPU 가속
NVIDIA (CUDA)
extras를 gpu로 바꿉니다.
단일 파일 방식
# /// script
# requires-python = ">=3.11,<3.14"
# dependencies = ["rembg[gpu]>=2.0.75", "pillow>=10.0"]
# ///
프로젝트 방식
uv add "rembg[gpu]>=2.0.75"
CUDA와 cuDNN이 설치돼 있어야 합니다. 확인:
uv run python -c "import onnxruntime as ort; print(ort.get_available_providers())"
# ['CUDAExecutionProvider', 'CPUExecutionProvider'] 가 나와야 GPU 사용
CPUExecutionProvider만 나오면 CUDA 설정이 잡히지 않은 상태입니다.
도커로 쓰는 편이 훨씬 간단합니다.
docker run --rm -it --gpus all \
-v /path/to/models:/root/.u2net \
-v $PWD:/data \
rembg-nvidia-cuda-cudnn-gpu i -m birefnet-general /data/in.png /data/out.png
Apple Silicon (macOS)
맥에는 CUDA가 없습니다. rembg는 CPU로 돌아갑니다. rembg[cpu]를 그대로 쓰세요.
통합 메모리 덕에 CPU 추론도 나쁘지 않지만 BiRefNet은 부담스럽습니다.
- 소량(50장 이하) →
birefnet-general - 대량 →
isnet-general-use또는u2net
컨테이너 안에서 맥 GPU에 접근하는 문제는 별도로 정리해뒀습니다.
→ macOS Docker에서 GPU 사용 가능한가? (410)
클라우드 GPU
물량이 크면 로컬보다 클라우드가 쌉니다. RunPod, Lambda Labs에서 시간당 0.3~2달러로 빌릴 수 있습니다. uv를 쓰면 서버에 접속해서 스크립트 파일 하나 올리고 uv run nukki.py로 끝입니다. 환경 설정이 없어서 일회성 GPU 대여에 특히 잘 맞습니다.
자주 발생하는 문제
Could not find a version that satisfies the requirement rembg
파이썬 버전 문제입니다. rembg는 3.11 이상 3.14 미만을 지원합니다. 시스템 파이썬이 3.14 이상이거나 3.10 이하면 이 오류가 납니다.
uv를 쓰면 자동으로 해결됩니다.
uv python pin 3.12
uv run nukki.py
PEP 723 헤더에 requires-python = ">=3.11,<3.14"가 있으면 uv가 맞는 버전을 알아서 찾습니다.
uv run이 매번 느립니다
첫 실행에만 환경을 만듭니다. 두 번째부터는 캐시를 씁니다. 계속 느리다면 PEP 723 헤더의 의존성이 매번 바뀌고 있는지 확인하세요.
프로젝트 모드(uv init + uv add)로 옮기면 uv.lock이 생겨서 더 안정적입니다.
첫 실행이 아주 느립니다
uv가 파이썬과 패키지를 준비하고, rembg가 모델 파일을 내려받습니다. ~/.u2net/에 저장되고 두 번째부터는 빨라집니다. BiRefNet 계열은 파일이 커서 첫 다운로드에 시간이 걸립니다.
결과 PNG의 배경이 검게 나옵니다
JPG로 저장했을 가능성이 큽니다. JPG는 투명도를 지원하지 않습니다. PNG로 저장하거나, 흰 배경을 합성한 뒤 JPG로 저장하세요.
피사체 일부가 지워집니다
모델을 바꿔보세요. 배경과 색이 비슷하면 birefnet-cod(위장 객체 특화)가 나을 수 있습니다. 알파 매팅의 foreground_threshold를 낮추는 것도 방법입니다.
머리카락이 뭉개집니다u2net 계열의 알려진 약점입니다. 미세한 가닥을 약 40% 잃습니다. birefnet-general 또는 birefnet-portrait로 바꾸세요.
엣지에 회색 후광이 남습니다
기존 모델들이 내부적으로 다운샘플한 뒤 업샘플하기 때문입니다. BiRefNet은 네이티브 해상도로 처리해서 이 문제가 없습니다.
메모리가 부족합니다 (OOM)
배치 루프에서 이미지를 하나씩 처리하고, multiprocessing으로 병렬화하지 마세요. 프로세스마다 모델이 중복 로딩됩니다. ONNX Runtime 스레드 수를 늘리는 편이 안전합니다.
OMP_NUM_THREADS=8 uv run nukki.py
수백 장 처리가 너무 느립니다new_session()을 루프 밖에서 한 번만 부르는지 확인하세요. 안에서 부르면 매번 모델을 다시 올립니다. 그다음 모델을 u2net이나 isnet-general-use로 내려보세요.
한글 파일명에서 오류가 납니다cv2.imread()는 한글 경로를 못 읽는 경우가 있습니다. rembg + PIL 조합을 쓰거나 이렇게 우회하세요.
import numpy as np, cv2
img = cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR)
Windows에서 uv run이 안 됩니다
설치 후 터미널을 새로 열어보세요. PATH 갱신이 반영되지 않은 경우입니다. uv --version으로 확인하세요.
Q. 파이썬을 처음 써도 따라할 수 있나요?
됩니다. uv를 설치하고 nukki.py 파일을 만들어 붙여넣은 뒤 uv run nukki.py를 입력하면 끝입니다. 가상환경, pip install, 파이썬 버전 같은 개념을 몰라도 됩니다. uv가 파일 상단 주석을 읽고 알아서 준비합니다.
Q. pip install rembg가 실패합니다
파이썬 버전 문제일 가능성이 큽니다. rembg는 3.11 이상 3.14 미만만 지원해서, 시스템 파이썬이 3.14 이상이면 설치가 실패합니다. uv python pin 3.12 한 줄로 해결됩니다. PEP 723 헤더에 버전 범위를 적어두면 uv가 자동으로 맞는 파이썬을 찾습니다.
Q. PEP 723 방식과 프로젝트 방식 중 뭐가 나은가요?
일회성이거나 남에게 파일을 전달할 거면 PEP 723 단일 파일이 편합니다. 팀과 공유하거나 정기 작업이면 uv init + uv add로 프로젝트를 만들어 uv.lock을 커밋하는 쪽이 낫습니다. 락파일이 있으면 6개월 뒤에도 같은 버전으로 돌아갑니다.
Q. uv 없이 pip으로도 되나요?
됩니다. pip install "rembg[cpu,cli]>=2.0.75" Pillow로 설치하면 코드는 동일합니다. 다만 파이썬 버전 관리와 가상환경을 직접 해야 합니다.
Q. 코딩 없이 명령줄만으로 할 수 있나요?
됩니다. uvx --from "rembg[cpu,cli]>=2.0.75" rembg p -m birefnet-general inputs/ outputs/ 로 폴더 전체를 처리합니다. 다만 흰 배경 합성이나 정사각형 규격 맞추기 같은 후처리는 안 됩니다. 투명 PNG만 나옵니다.
Q. rembg 버전을 왜 고정해야 하나요?
2.0.75 미만 버전에 CVE-2026-40086 취약점이 있습니다. HTTP 서버의 custom-model-type 경로에 디렉터리 트래버설 결함입니다. PEP 723 헤더든 pyproject.toml이든 >=2.0.75를 명시하세요.
이 글의 핵심 메트릭
- rembg 버전: 2.0.75 (백엔드 16종)
- 필수 최소 버전: 2.0.75 (CVE-2026-40086 대응)
- Python 지원: 3.11 이상 3.14 미만
- 모델 저장 경로:
~/.u2net/ - 품질 1순위:
birefnet-general(MIT) - 속도 1순위:
u2net,silueta(43MB) - 라이선스 주의:
bria-rmbg(상업용 유료) - BiRefNet 엣지 우위: u2net은 미세 가닥 약 40% 손실
- BiRefNet 최대 해상도: 2048×2048 네이티브
- BiRefNet GPU 속도: RTX A5000 24GB에서 1024×1024 1초 미만
- 검증 환경: macOS Sequoia 15.x / Apple Silicon M2 Pro / Python 3.12
함께 읽으면 좋은 글
📚 파이썬 자동화 & 로컬 AI (chan7ee)
- uv 완벽 가이드 (409) — 파이썬 환경을 30초에 세팅
- Ollama 완벽 가이드 (399) — 로컬에서 AI 모델 돌리기
- macOS Docker에서 GPU 사용 가능한가? (410) — Apple Silicon GPU 활용
- docker compose로 python 컨테이너 만들기 (378) — 배치 작업 컨테이너화
- False Positive와 False Negative 차이 (396) — 모델 품질 판단 기초
rembg 공식 저장소는 github.com/danielgatis/rembg에 있습니다.
'Programming > Python' 카테고리의 다른 글
| 2026년 파이썬 패키지 관리자는 무엇을 써야 하나요? — uv vs Poetry vs pip 비교와 uv 실전 워크플로 (1) | 2026.07.28 |
|---|---|
| decorator - 데커레이터 (0) | 2024.10.30 |
| python 으로 asdict, from_dict 직접 구현 (0) | 2023.12.18 |
| docker compose - python default 컨테이너 만들기 (0) | 2023.04.05 |
| try except를 깔끔하게 사용하기 - suppress (0) | 2023.01.31 |
댓글