inswapper-Openvino / README.md
HelloSun's picture
InsightFace inswapper -> OpenVINO FP16/INT8/INT4 (+INT4-mixed) models, single-file swapper, samples with PD NASA portraits
c6ec17d verified
|
Raw History Blame Contribute Delete
10.2 kB
---
license: other
library_name: openvino
tags:
- inswapper
- insightface
- face-swap
- openvino
- nncf
- int4
- quantization
- deepfake
pipeline_tag: image-to-image
---
# inswapper (InsightFace face swap) → OpenVINO 16 / 8 / 4 bit
把 [haofanwang/inswapper](https://github.com/haofanwang/inswapper) / [InsightFace `inswapper_128`](https://github.com/deepinsight/insightface)
的整條換臉流程轉成 **OpenVINO IR**,並用 NNCF 量化成 **16-bit / 8-bit / 4-bit**。
推論時**不需要 PyTorch / onnxruntime**,只要 `openvino + opencv + numpy`。
> ⚠️ **授權與使用範圍(請先讀)**
> `inswapper_128`、ArcFace、RetinaFace 都是 **InsightFace** 訓練並發佈的模型,
> 依其官方授權**僅限非商業研究用途**(non-commercial research use only)。
> 本 repo 只做格式轉換與量化測試,**請勿用於冒充他人、散布謠言或任何未經當事人同意的用途**;
> 商用請先取得 InsightFace 官方授權。
## 這條流程用到三個模型
| 網路 | 用途 | 原始 ONNX |
|---|---|---|
| `retinaface_10g` | 人臉偵測 + 5 點關鍵點 | 16.9 MB |
| `arcface_w600k_r50` | 人臉辨識,輸出 512 維 embedding | 174.4 MB |
| `inswapper_128` | **換臉本體**(target 128×128 + source 512 維 embedding → 換好的臉) | 554.3 MB |
三者都已轉成 OpenVIR,各有 **fp16 / int8 / int4** 三種版本(`int4-mixed` 只有 swapper 版本)。
## 模型清單(`models/`)
| 檔案 | 精度 | 大小 (xml+bin) |
|---|---|---|
| `inswapper_fp16.*` | 16-bit FP16 | 276.9 MB |
| `inswapper_int8.*` | 8-bit W8A8(NNCF PTQ + SmoothQuant) | 139.1 MB |
| `inswapper_int4.*` | 4-bit `u4` 權重(NNCF LWC) | **69.7 MB** |
| `inswapper_int4_mixed.*` | 4-bit/8-bit 混合(75% 權重為 4-bit) | 91.7 MB |
| `retinaface_fp16.* / _int8.* / _int4.*` | 偵測 | 8.7 / 4.6 / 2.5 MB |
| `arcface_fp16.* / _int8.* / _int4.*` | 辨識 | 87.5 / 44.2 / 22.3 MB |
| `inswapper_emap.npy` | 512×512 的 embedding 投影矩陣(原模型最後一個 initializer) | 1.0 MB |
## 快速開始(單一 py 檔)
```bash
pip install openvino opencv-python numpy huggingface_hub
git clone https://huggingface.co/HelloSun/inswapper
cd inswapper
python inswapper_ov.py -s input/source_face_nasa_astronaut.jpg \
-t input/target_photo_nasa_astronaut.jpg \
-o result.jpg -m int8
```
模型會在第一次執行時自動從本 repo 下載並快取到 `~/.cache/inswapper_openvino`
(可用 `INSWAPPER_OV_CACHE` 改路徑;也可下載後用 `--model-dir models` 指定本機資料夾)。
```python
# 最小可運作範例
import cv2
from inswapper_ov import FaceSwapperOpenVINO
swapper = FaceSwapperOpenVINO(precision='int8') # fp16 / int8 / int4 / int4-mixed
out = swapper.swap('source.jpg', 'target.jpg', out='result.jpg')
```
### 命令列選項
| 選項 | 說明 |
|---|---|
| `-s, --source` | 提供臉孔的來源圖 |
| `-t, --target` | 要被換臉的目標圖 |
| `-o, --output` | 輸出路徑(預設 `result.jpg`) |
| `-m, --model` | `fp16`(16-bit) / `int8`(8-bit) / `int4`(4-bit) / `int4-mixed` |
| `-d, --device` | `CPU` / `GPU` / `NPU` / `AUTO` |
| `--all-faces` | 目標圖中**每一張臉**都換 |
| `--face-index N` | 只換第 N 張臉(依置信度排序,預設 0 = 最大張) |
| `--det-size W H` | 偵測輸入尺寸,預設 `640 640` |
| `--model-dir` | 使用本機 `models/` 資料夾,不連網 |
| `--num-threads` | CPU 執行緒數 |
### Python API
```python
from inswapper_ov import FaceSwapperOpenVINO
sw = FaceSwapperOpenVINO(precision='int4-mixed', device='CPU', det_size=(640, 640))
bboxes, kpss = sw.detect(img_bgr) # 偵測
emb, kps = sw.embedding(img_bgr) # 512 維 embedding(ArcFace)
out = sw.swap('src.jpg', 'tgt.jpg', all_faces=True)
```
## 實測結果(16 / 8 / 4 bit)
素材為 NASA **公有領域**太空人肖像照片(見 `samples/README.md`),
以 **ONNX Runtime FP32**(完全相同的預處理與貼回程式碼)為參考基準:
![face montage](samples/20_face_montage.jpg)
| 精度 | swapper 大小 | 臉部 PSNR ↑ | 臉部 SSIM ↑ | 全圖 PSNR ↑ | embedding cos ↑ | 單次換臉 ↑ |
|---|---|---|---|---|---|---|
| ONNX Runtime FP32(參考) | 554.3 MB | – | – | – | 1.000 | 8.08 s |
| **fp16 (16-bit)** | 276.9 MB | **56.29 dB** | **0.9994** | 71.02 dB | 1.000 | 0.47 s |
| **int8 (8-bit)** | 139.1 MB | 19.28 dB | 0.556 | 37.09 dB | 0.979 | **0.09 s** |
| **int4-mixed (4-bit 主體)** | 91.7 MB | 17.28 dB | 0.672 | **37.70 dB** | 0.947 | 0.40 s |
| **int4 (純 4-bit)** | **69.7 MB** | 14.79 dB | 0.532 | 32.29 dB | 0.947 | 0.43 s |
(測試機:Xeon Platinum 8559C 16 vCPU、OpenVINO 2026.4.1、1280×1024 照片、單張臉。)
### 怎麼選
1. **fp16(16-bit)**:與原模型幾乎一模一樣(56 dB),又快 17×,**大多數情況請用這個**。
2. **int8(8-bit)**:**CPU 上最快(0.09 s)**,模型只有原版 1/4;臉部會有細微雜訊,
但辨識度完全沒問題。注意:**校準資料一定要用真實人臉**(見下方匯出說明),
用隨機雜訊校準會讓 ArcFace embedding 偏移到 cosine 0.78(會換到錯的人)。
3. **int4-mixed(4-bit 主體)**:視覺品質比純 4-bit 好很多,幾乎達到 int8 的水準,
模型 91.7 MB(FP32 的 1/6)。**要在 4-bit 路線上兼顧品質就用這個**。
4. **int4(純 4-bit)**:最小(69.7 MB),但 swapper 是類 GAN 網路,
4-bit 誤差會讓膚色嚴重偏色(見 montage 右下),**不建議實際使用**。
> INT4 在 **CPU 上不會比 INT8 快**(0.43 s vs 0.09 s):CPU plugin 會在 runtime 把 `u4` 權重
> 解壓回 FP32/FP16 再計算。INT4 的收益是**模型體積**,不是速度。
## Intel GPU / NPU
```bash
python inswapper_ov.py -s src.jpg -t tgt.jpg -o out.jpg -m int8 -d GPU
```
這三個 IR 都是標準 OpenVINO IR,不綁 plugin,`CPU / GPU / NPU / AUTO / HETERO` 都能跑。
GPU 上推薦 `int8`(plugin 對 INT8 卷積最佳化)與 `fp16`;
`int4` 在 GPU 上也不會有 4-bit 卷積加速,權重同樣是 runtime 解壓。
要產生針對 GPU plugin 調校的 INT8 模型:
```bash
python export_openvino_inswapper.py --onnx-dir onnx --target-device gpu \
--models inswapper --precisions int8 --calib-dir calib
```
> 本 repo 的數據是在**沒有 GPU 的機器**上量測的(`available_devices = ['CPU']`),
> GPU 的說明依 OpenVINO plugin 能力整理,非本機實測。
## 從原始 ONNX 重新轉換/量化
```bash
# 1) 先取得三個 ONNX(見 README 的來源)
# 2) 用真實人臉照片產生 NNCF 校準張量(強烈建議,否則 INT8 embedding 會偏移)
python make_calib_data.py --images "input/*.jpg" --samples 24 --out-dir calib
# 3) 轉換 + 量化(fp16 / int8 / int4 一次產出)
python export_openvino_inswapper.py --onnx-dir onnx --out-dir models --calib-dir calib
# 只做純 4-bit 或混合精度 4/8-bit
python export_openvino_inswapper.py --onnx-dir onnx --out-dir models \
--models inswapper --precisions int4 --int4-ratio 0.75
```
轉換管線裡處理了三個「不處理就會出包」的點:
1. **NNCF 的 `compress_weights(mode='int4_*')` 預設只壓 MatMul**,卷積會靜默退回 INT8
→ `enable_int4_for_convolutions()` 擴充 `_get_ratio_defining_params`,讓 Conv 也用 4-bit。
swapper 裡的 `Gemm` 會自動被壓,`Conv` 則需要這個 patch。
2. **NNCF 的 CPU hardware config 只允許 8/16-bit 權重** → `enable_int4_weights_in_cpu_hw_config()`
把 `q4_w` 加進 Convolution/MatMul 的 qspace,才會產生真正的 `u4` 常數。
(`--target-device gpu` 時 NNCF 本身就允許 4-bit,不需 patch。)
3. **`openvino.convert_model()` 預設把權重壓成 FP16**(舊版 Model Converter 的行為)
→ `convert_onnx_fp32()` 以 `compress_to_fp16=False` 轉換,量化才從真正的 FP32 出發。
另外,`emap`(embedding 投影矩陣)在推論時需要,但它是 ONNX 最後一個 initializer,
已匯出成 `models/inswapper_emap.npy`,執行期就不必安裝 `onnx` 了。
## 重新產生 `samples/`
```bash
python make_sample.py --onnx-dir onnx --models-dir models --out-dir samples
```
## 疑難排解
| 症狀 | 原因 / 解決 |
|---|---|
| 換錯人 / 換到奇怪的人 | ArcFace 精度太低或校準資料是隨機雜訊;用 `--model-dir` 指定本機 IR,並用 `int8` 以上 |
| 臉部出現彩色斑塊 | 用了純 `int4`,改用 `int4-mixed` 或 `int8` / `fp16` |
| `No face detected` | 把 `--det-size` 調大(例如 `1024 1024`),或換張臉比較大的圖 |
| 想對影片換臉 | 逐影格呼叫 `sw.swap(...)`,參考下方「影片」範例 |
| OpenVINO 對 FP32 graph 預設 bf16 | `inswapper_ov.py` 已固定 `INFERENCE_PRECISION_HINT=f32`;自己呼叫 `ov.Core().compile_model` 時記得傳入 |
### 影片換臉(逐影格)
```python
import cv2
from inswapper_ov import FaceSwapperOpenVINO
sw = FaceSwapperOpenVINO(precision='int8')
cap, out = cv2.VideoCapture('in.mp4'), cv2.VideoWriter('out.mp4', cv2.VideoWriter_fourcc(*'mp4v'), 25, (1920, 1080))
while True:
ok, frame = cap.read()
if not ok: break
out.write(sw.swap('src.jpg', frame, verbose=False))
out.release()
```
## 來源與授權
| 項目 | 來源 | 授權 |
|---|---|---|
| 換臉程式 | [haofanwang/inswapper](https://github.com/haofanwang/inswapper) | Apache-2.0 |
| `inswapper_128` | [deepinsight/insightface](https://github.com/deepinsight/insightface) | **非商業研究用途** |
| `arcface_w600k_r50`、`retinaface_10g` | InsightFace(buffalo_l 系列) | **非商業研究用途** |
| 量化 | [OpenVINO NNCF](https://github.com/openvinotoolkit/nncf) | Apache-2.0 |
| 測試照片 | NASA Johnson Space Center(公有領域) | Public Domain |
| `inswapper_ov.py` / 匯出腳本 / 本說明 | 本專案 | Apache-2.0 |
本 repo 的 `inswapper_ov.py` 內含從 InsightFace 移植的 `estimate_norm` / RetinaFace 後處理 /
換臉貼回(`paste_back`)邏輯,以確保 OpenVINO 版本與原版逐像素一致。