DeepSwapFace · Face Swap Video & Image CLI
A small, readable face swap tool for video and stills, built on InsightFace + ONNX Runtime.
No Colab notebook, no 40-node ComfyUI graph to debug. One Python file, two models, one command.
# video
python swap.py --source face.jpg --target clip.mp4 --output out.mp4
# image
python swap.py --source face.jpg --target group.jpg --output out.jpg --all-faces
⚠️ Ethical use & disclaimer
This is a technical tool for research, VFX prototyping, and identity-preservation experiments.
By downloading or using it you agree that:
- Consent is mandatory. You must have explicit permission from every person whose likeness you process, and from anyone whose face you insert.
- No deception. Do not use it to impersonate, defraud, harass, or create non-consensual intimate imagery. In many jurisdictions (EU AI Act, US state deepfake laws, China's deep synthesis regulations) publishing synthetic media of real people without disclosure is illegal.
- You are liable. The software is provided "as is", without warranty of any kind. The authors accept no responsibility for misuse.
- Model licences apply.
inswapper_128.onnxfrom InsightFace is released for non-commercial research use only. Check the upstream licence before any commercial deployment.
🚀 Quickstart
1. Install
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
Requires Python 3.10+. CPU is enough; a GPU is roughly 10–30× faster.
2. Get the models
mkdir -p models ~/.insightface/models
# face detector + recognition (buffalo_l, ~289 MB)
curl -L -o /tmp/buffalo_l.zip \
https://github.com/deepinsight/insightface/releases/download/v0.7/buffalo_l.zip
unzip -q /tmp/buffalo_l.zip -d ~/.insightface/models/buffalo_l
# the swap model (~529 MB)
curl -L -o models/inswapper_128.onnx \
https://huggingface.co/ezioruan/inswapper_128.onnx/resolve/main/inswapper_128.onnx
The models are not mirrored in this repo — inswapper_128.onnx is non-commercial research-only and stays with its upstream host.
3. Swap a video
python swap.py --source examples/source-01.jpg \
--target examples/demo-clip.mp4 \
--output out.mp4 \
--det-size 320
4. Swap a still
python swap.py --source examples/source-01.jpg \
--target examples/target-01.jpg \
--output out.jpg
🧩 CLI options
| Flag | Default | Description |
|---|---|---|
--source |
required | Image containing the face to insert |
--target |
required | Image or video to put that face into |
--output |
output.jpg |
Output path (use .mp4 for video) |
--model |
models/inswapper_128.onnx |
Path to the swap model |
--source-face |
0 |
Which source face to use (left-to-right order) |
--target-face |
0 |
Which target face to replace |
--all-faces |
off | Swap every face detected in the target |
--det-size |
640 |
Detector input size; 320 is ~2× faster on video |
--every |
1 |
Video only: swap every Nth frame, reuse the result in between |
--max-frames |
0 |
Video only: stop after N frames (0 = whole clip) |
--execution-provider |
CPUExecutionProvider |
Set to CUDAExecutionProvider for GPU |
🎬 Video example
Input clip (synthetic slow zoom, 24 frames @ 8 fps) → swapped with this script, default settings except --det-size 320:
| Input | Output |
|---|---|
Identity check on the decoded output frames — ArcFace cosine similarity of the result face vs the source face:
| Frame | source ↔ target (before) | source ↔ result (after) |
|---|---|---|
| 0 | -0.071 | 0.822 |
| 12 | -0.082 | 0.846 |
| 23 | -0.058 | 0.828 |
The identity stays stable across the clip because detection runs per frame and the swap target is resolved by the same left-to-right rule each time.
🖼️ Still-image examples
Four pairs generated with the released model, then verified the same way.
1 · Colour portrait → vintage studio photo
before -0.036 → after 0.893
2 · Same source, different target
before 0.107 → after 0.855
3 · Role-reversed pair
before 0.068 → after 0.851
4 · Reverse direction again
before 0.068 → after 0.820
Reproduce any of them:
python swap.py --source examples/source-01.jpg \
--target examples/target-01.jpg \
--output /tmp/check.jpg
⏱️ Performance
Measured on a 2 vCPU / 1.9 GB RAM cloud instance, CPUExecutionProvider:
| Workload | Result |
|---|---|
Still, 960×1696, det-size 640 |
~27 s (cold start ~50 s incl. model load) |
| Still, peak RSS | ~1.6 GB |
Video, 378×600, det-size 320 |
4.2 s per frame (24 frames → 100 s) |
A modern GPU (CUDA EP) brings a single frame down to well under a second.
Rule of thumb on CPU: frames × 4s. Pass --every 2 or --every 3 when you need speed more than smoothness.
🔬 How it works
- Detect — SCRFD (
det_10g.onnx) finds face boxes and 5-point landmarks, once per frame. - Align — faces are warped to the canonical 112×112 template.
- Embed — ArcFace (
w600k_r50.onnx) produces a 512-d identity vector for the source face. - Swap —
inswapper_128.onnxmaps the source identity onto the target's aligned crop. - Paste back — the 128×128 result is inverse-warped into the full-resolution frame, so framing, lighting gradient and background are preserved.
- Encode — frames are written back at the source fps.
The swapped region is inherently 128×128 — that is the model's bottleneck, not the script's. Faces smaller than ~80 px will look soft.
🚧 Known limitations
- No temporal smoothing. Each frame is swapped independently, so identity can flicker under fast head motion or partial occlusion. Adding a tracker / stabilising pass is the obvious next step.
- Yaw / profile views degrade badly; the model is trained mostly on frontal faces.
- Heavy occlusion (masks, hands, hair) leaves artefacts at the seam.
- Glasses and beards are not transferred — you keep the target's accessories.
- Skin-tone mismatch between very different sources produces a visible edge.
- Multiple faces need
--all-facesor an explicit--target-faceindex; ordering is left-to-right by bounding box, so it can jump if people cross. - Audio is dropped. The video writer does not mux audio; re-attach it with ffmpeg:
ffmpeg -i out.mp4 -i original.mp4 -c copy -map 0:v -map 1:a final.mp4.
📜 Credits & licences
Code in this repository: MIT.
Example imagery is public-domain / no-known-restrictions material from Wikimedia Commons, used unmodified as inputs:
source-01— Cornelia Montgomery, 3/4 face portrait, Library of Congress via Wikimedia Commons, Public domaintarget-01— Studio portrait of a young man, Internet Archive book images, No known copyright restrictions- third portrait (Portrait photograph of Rose Stahl), Wikimedia Commons, Public domain
demo-clip.mp4— synthetic slow zoom generated fromtarget-01for testing purposes
Upstream projects:
- InsightFace — detector, recognition model and
inswapper_128(non-commercial research licence) - ONNX Runtime — inference
- OpenCV — image/video I/O and warping
🙏 Acknowledgements
This project is sponsored by deepswapface.ai — they cover the compute used to build and benchmark the examples in this repository.
If you would rather not set up a local environment, they run a hosted version of the same workflow:
📄 Citation
@misc{deepswapface_cli,
title = {DeepSwapFace: a minimal face swap CLI for video and images},
author = {deepswapface.ai contributors},
year = {2026},
url = {https://deepswapface.ai}
}



