diff --git a/DEPLOY.md b/DEPLOY.md index c063639..fd45580 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -1,5 +1,7 @@ # Deploying BgFilter to a Linux server +> 中文版:[DEPLOY_ZH.md](DEPLOY_ZH.md) + This guide covers everything needed to run the BgFilter HTTP service (`POST /remove-background`, `GET /healthz`) on a Linux server. It assumes a CPU deployment (the default); GPU notes are called out where relevant. @@ -99,7 +101,38 @@ the `hub/` folder over. ### Option B — plain folders inside the project Drop each model into `models//` and the loader uses it directly (no -HF cache, no network). Absent folders fall back to the repo id, so this is opt-in: +HF cache, no network). Absent folders fall back to the repo id, so this is opt-in. + +**The folder name must match the repo basename exactly** — that is how the loader +finds it (`ZhengPeng7/BiRefNet` → `models/BiRefNet`). Required files per model: + +```text +/ +└── models/ + ├── vitmatte-base-composition-1k/ # ViTMatte matting model + │ ├── config.json + │ ├── preprocessor_config.json + │ └── pytorch_model.bin # ~369 MB + ├── BiRefNet/ # default segmenter (trust_remote_code) + │ ├── config.json + │ ├── BiRefNet_config.py # custom code — required by trust_remote_code + │ ├── birefnet.py # custom code — required by trust_remote_code + │ └── model.safetensors # ~424 MB + └── anime-seg/ # optional segmenter (seg_model=anime-seg) + └── isnetis.onnx # ~168 MB +``` + +Notes: +- **ViTMatte**: needs `config.json`, `preprocessor_config.json`, and the weights + (`pytorch_model.bin`; `model.safetensors` also works if present). +- **BiRefNet**: the two `.py` files are mandatory — `trust_remote_code` executes them + to build the model. Keep `config.json` + `model.safetensors` alongside them. (The + repo's `handler.py` / `requirements.txt` / `README.md` are not needed.) +- **anime-seg**: only `isnetis.onnx` is read; nothing else is required. +- `anime-seg` is optional — omit its folder if you never use `seg_model=anime-seg`. + +The easiest way to populate the folders is to let `hf download` fetch them (it also +pulls a couple of unused files like `README.md`, which is harmless): ```bash hf download hustvl/vitmatte-base-composition-1k --local-dir models/vitmatte-base-composition-1k diff --git a/DEPLOY_ZH.md b/DEPLOY_ZH.md new file mode 100644 index 0000000..1330544 --- /dev/null +++ b/DEPLOY_ZH.md @@ -0,0 +1,261 @@ +# 将 BgFilter 部署到 Linux 服务器 + +> English version: [DEPLOY.md](DEPLOY.md) + +本指南涵盖在 Linux 服务器上运行 BgFilter HTTP 服务(`POST /remove-background`、 +`GET /healthz`)所需的全部步骤。默认按 **CPU** 部署(推荐默认);涉及 GPU 的地方会单独标注。 + +服务入口是 `app.py`(`uvicorn app:app`)。API 契约和算法细节见 [README.md](README.md)。 + +--- + +## 1. 前置条件 + +- Linux x86-64(在 Ubuntu 上验证过;其它主流发行版同理)。 +- 推荐 **Python 3.11–3.13**。更新的版本(如 3.14)也可能可用,但部分 ML 包可能还没发布对应 + wheel——尽量选一个 `torch` / `onnxruntime` / `opencv` / `pymatting` 都有预编译 wheel 的版本。 +- `git`,以及 venv 模块(Debian/Ubuntu 上是 `python3-venv`)。 +- 磁盘空间:Python 环境约 3 GB + 模型权重约 1 GB。 +- **一次性**联网即可(用于装依赖和下模型);服务跑起来后可完全离线。 + +```bash +sudo apt update +sudo apt install -y python3-venv python3-pip git +``` + +## 2. 获取代码 + +```bash +sudo mkdir -p /opt/genarrative-image-host/bgfilter-service +sudo chown "$USER" /opt/genarrative-image-host/bgfilter-service +cd /opt/genarrative-image-host/bgfilter-service +git clone . # 或把仓库拷到这里;使用 server-edition 分支 +``` + +## 3. Python 环境 + +```bash +python3 -m venv .venv +. .venv/bin/activate +python -m pip install -U pip +``` + +## 4. 安装依赖(CPU) + +**先从 CPU wheel 源装 PyTorch**——否则 `pip` 会拉默认的 CUDA 构建,附带约 2–3 GB 的 +`nvidia-*` 包,CPU 机器完全用不上: + +```bash +pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu +``` + +无 GUI 的服务器请用 **`opencv-python-headless`** 代替 `opencv-python`(GUI 构建依赖 +`libGL.so.1` 等服务器通常没有的库): + +```bash +sed -i 's/^opencv-python$/opencv-python-headless/' requirements.txt +``` + +再装其余依赖。此时 `torch`/`torchvision` 已满足,不会再拉 CUDA: + +```bash +pip install -r requirements.txt +``` + +验证整套依赖可导入、且 torch 是 CPU 构建(`cuda= None`): + +```bash +python -c "import torch,cv2,fastapi,transformers,pymatting,onnxruntime,kornia,timm; \ +print('torch', torch.__version__, 'cuda=', torch.version.cuda)" +``` + +> 在墙内,给第二条 `pip install` 加上 `-i https://pypi.tuna.tsinghua.edu.cn/simple` 提速。 + +## 5. 准备模型权重 + +共用到三个模型:`hustvl/vitmatte-base-composition-1k`(抠图)、 +`ZhengPeng7/BiRefNet`(默认分割)、`skytnt/anime-seg`(可选分割)。 +下面两种放法**二选一**。 + +### 方案 A —— HuggingFace 缓存 + 离线模式(推荐) + +先下载到一个缓存目录,之后离线运行: + +```bash +export HF_HOME=/opt/genarrative-image-host/bgfilter-service/model-cache/hf +hf download hustvl/vitmatte-base-composition-1k +hf download ZhengPeng7/BiRefNet +hf download skytnt/anime-seg +``` + +运行时把 `HF_HOME` 指向同一路径,并设 `HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1`(见 §6)。 +也可以在另一台机器下好,把 `hub/` 目录整个拷过来。 + +### 方案 B —— 权重作为普通文件夹放进项目 + +把每个模型放进 `models//`,加载器会直接用它(不走 HF 缓存、不联网)。 +没放对应文件夹时会回退到 repo id,所以这是可选功能。 + +**文件夹名必须和 repo 末段名完全一致**——加载器靠这个找到它 +(`ZhengPeng7/BiRefNet` → `models/BiRefNet`)。各模型需要的文件: + +```text +<项目根目录>/ +└── models/ + ├── vitmatte-base-composition-1k/ # ViTMatte 抠图模型 + │ ├── config.json + │ ├── preprocessor_config.json + │ └── pytorch_model.bin # 约 369 MB + ├── BiRefNet/ # 默认分割模型(trust_remote_code) + │ ├── config.json + │ ├── BiRefNet_config.py # 自定义代码——trust_remote_code 必需 + │ ├── birefnet.py # 自定义代码——trust_remote_code 必需 + │ └── model.safetensors # 约 424 MB + └── anime-seg/ # 可选分割模型(seg_model=anime-seg) + └── isnetis.onnx # 约 168 MB +``` + +说明: +- **ViTMatte**:需要 `config.json`、`preprocessor_config.json` 和权重 + (`pytorch_model.bin`;若有 `model.safetensors` 也可)。 +- **BiRefNet**:两个 `.py` 文件是必需的——`trust_remote_code` 会执行它们来构建模型。 + 和 `config.json` + `model.safetensors` 放在一起。(repo 里的 `handler.py` / + `requirements.txt` / `README.md` 用不到。) +- **anime-seg**:只读 `isnetis.onnx`,其它都不需要。 +- `anime-seg` 是可选的——若从不使用 `seg_model=anime-seg`,可以不放这个文件夹。 + +最省事的填充方式是用 `hf download`(它也会顺带拉几个用不到的文件如 `README.md`,无害): + +```bash +hf download hustvl/vitmatte-base-composition-1k --local-dir models/vitmatte-base-composition-1k +hf download ZhengPeng7/BiRefNet --local-dir models/BiRefNet +hf download skytnt/anime-seg --local-dir models/anime-seg +``` + +用 `BGFILTER_WEIGHTS_DIR` 可改基目录。`models/` 已被 gitignore。 + +### 墙内下载(国内网络) + +若直连 HuggingFace 被墙,用镜像 **并绕过本地代理、禁用 Xet**——这是稳定可用的组合: + +```bash +export HF_ENDPOINT=https://hf-mirror.com # 国内镜像 +export NO_PROXY='*' # 绕过代理;镜像是国内直连 +export HF_HUB_DISABLE_XET=1 # 这些 repo 是 Xet 存储;强制走经典 HTTP +``` + +这样能避开两个坑:(1) 镜像 + 走海外的代理会让镜像把 `resolve` 又重定向回 +`huggingface.co`,新版 `huggingface_hub` 会因此报 `FileMetadataError`; +(2) 装了 `hf-xet` 时 Xet 下载路径在此环境下会瞬间失败。 + +## 6. 配置(环境变量) + +| 变量 | 默认 | 作用 | +| --- | --- | --- | +| `BGFILTER_CONFIG` | `configs/default.yaml` | 管线配置文件 | +| `BGFILTER_DEVICE` | `cpu` | 同时覆盖 model 和 segmentation 的 device(GPU 用 `cuda`)| +| `BGFILTER_MAX_IMAGE_PIXELS` | `4194304` | 超过则返回 `413`(约 4 MP)| +| `BGFILTER_PRELOAD` | `1` | 启动时预加载默认模型(首个请求不用冷加载)| +| `BGFILTER_WEIGHTS_DIR` | `models` | 本地权重文件夹的查找目录(方案 B)| +| `HF_HOME` | — | HF 缓存位置(方案 A)| +| `HF_HUB_OFFLINE` / `TRANSFORMERS_OFFLINE` | — | 权重缓存好后,生产设为 `1` | +| `OMP_NUM_THREADS` / `MKL_NUM_THREADS` | — | 限制 CPU 线程数(如 `4`)| + +## 7. 运行服务 + +```bash +python -m uvicorn app:app --host 127.0.0.1 --port 18083 --workers 1 +``` + +- **必须保留 `--workers 1`。** 每个 worker 各加载一整套模型,多 worker 会成倍占内存 + (8 GB 机器会 OOM)。服务已用全局锁串行化推理。 +- `--host 127.0.0.1` 只对本机开放(面向同机调用方)。跨机访问见 §10。 + +## 8. 作为 systemd 服务运行 + +`/etc/systemd/system/genarrative-bgfilter.service`: + +```ini +[Unit] +Description=Genarrative BgFilter background removal service +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +WorkingDirectory=/opt/genarrative-image-host/bgfilter-service +Environment=HF_HOME=/opt/genarrative-image-host/bgfilter-service/model-cache/hf +Environment=HF_HUB_OFFLINE=1 +Environment=TRANSFORMERS_OFFLINE=1 +Environment=HF_HUB_DISABLE_XET=1 +Environment=BGFILTER_CONFIG=configs/default.yaml +Environment=BGFILTER_DEVICE=cpu +Environment=BGFILTER_MAX_IMAGE_PIXELS=4194304 +Environment=BGFILTER_PRELOAD=1 +Environment=OMP_NUM_THREADS=4 +Environment=MKL_NUM_THREADS=4 +ExecStart=/opt/genarrative-image-host/bgfilter-service/.venv/bin/uvicorn app:app --host 127.0.0.1 --port 18083 --workers 1 +Restart=on-failure +RestartSec=5 +MemoryMax=8G +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=full +ReadWritePaths=/opt/genarrative-image-host + +[Install] +WantedBy=multi-user.target +``` + +若用方案 B(本地 `models/` 文件夹)而非 HF 缓存,去掉 `HF_HOME` 和离线那几行—— +那种权重不需要缓存也不联网。 + +```bash +sudo systemctl daemon-reload +sudo systemctl enable --now genarrative-bgfilter +sudo systemctl status genarrative-bgfilter +journalctl -u genarrative-bgfilter -f # 等到 "Application startup complete" +``` + +## 9. 验证 + +```bash +curl -s http://127.0.0.1:18083/healthz +# {"ok":true,"service":"bgfilter","version":"0.1.0","defaultSegModel":"birefnet","device":"cpu"} + +curl -sS -F "file=@input.png" -F "screen_color=#CFEFFF" \ + http://127.0.0.1:18083/remove-background -o out.png +``` + +## 10. 对外暴露 + +单元里绑的是 `127.0.0.1`——面向同机调用方(例如 Genarrative Rust BFF,通过 +`GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_BASE_URL` 调用)。服务本身**没有鉴权**。 +要从其它主机访问: + +- **反向代理(推荐):** 前面放 nginx/caddy,终止 TLS、加鉴权/限流,转发到 + `127.0.0.1:18083`。 +- **直接暴露:** 把 `--host` 改成 `0.0.0.0`,并用防火墙限制来源 IP。 + 切勿把无鉴权的服务裸暴露到公网。 + +## 11. 容量与性能 + +- CPU 吞吐约 **45 秒/张(1.5 MP)**,用默认配置(base ViTMatte + BiRefNet);请求被全局锁 + 串行,所以单实例约 1–2 张/分钟。瓶颈是全分辨率的 ViTMatte 和 pymatting 前景估计。 +- 要提吞吐:上 GPU(`BGFILTER_DEVICE=cuda`,装 CUDA 版 torch),大约快一个数量级; + 和/或多实例 + 负载均衡(每个都 `--workers 1`)。 +- 内存:单个 worker 持有全部三个模型(约 1 GB 权重 + 运行时)。单 CPU worker 用 + `MemoryMax=8G` 作上限比较稳妥。 + +## 12. 故障排查 + +| 现象 | 原因 / 解决 | +| --- | --- | +| `pip` 拉了 `nvidia-*`/`cuda-*`(巨大) | §4 里漏了 CPU 源。用 `--index-url https://download.pytorch.org/whl/cpu` 装 torch。 | +| `import cv2` 报 `ImportError: libGL.so.1` | 在无 GUI 机器上用了 `opencv-python`。换成 `opencv-python-headless`(§4)。 | +| 建 venv 时 `ensurepip is not available` | 装 `python3-venv`(§1)。 | +| 下载时 `FileMetadataError: ... not on huggingface.co` | 镜像与代理冲突。绕过代理(`NO_PROXY='*'`)并设 `HF_HUB_DISABLE_XET=1`(§5)。 | +| `LocalEntryNotFoundError`、0 字节 | Xet 下载路径失败;设 `HF_HUB_DISABLE_XET=1`。 | +| `503 service not ready` | 启动时模型还在加载;等 "Application startup complete"。 | +| 负载下 OOM / 被 kill | worker 多于一个,或 `MemoryMax` 太低。保持 `--workers 1`。 | +| `400 could not auto-detect background colour` | 输入没有干净的纯色边框;传 `screen_color=#RRGGBB`。 |