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