File size: 10,182 Bytes
c6ec17d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | ---
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**(完全相同的預處理與貼回程式碼)為參考基準:

| 精度 | 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 版本與原版逐像素一致。 |