How Deep-Live-Cam Processes Multiple Faces and Pairs Source-to-Target Faces
Deep-Live-Cam handles multiple faces through three distinct modes—simple one-to-one swapping, "many faces" mode that applies a default source to every detected target, and "map-faces" mode that uses explicit source-to-target pairings stored in source_target_map.
Deep-Live-Cam (hacksider/Deep-Live-Cam) is an open-source real-time face swapping application that supports complex multi-face scenarios. The system can detect an arbitrary number of faces in a target frame and intelligently pair them with source faces using either automatic defaults or user-defined mappings. This article examines the source code architecture in modules/processors/frame/face_swapper.py to explain exactly how the application manages detection, pairing, and execution across different operational modes.
Understanding the Three Face Processing Modes
The application logic branches based on two global flags defined in modules/globals.py: many_faces and map_faces. These flags determine which detection and pairing strategy executes inside process_frame_v2.
Simple Mode (One-to-One)
In simple mode, the system detects a single source face from the CLI --source argument and a single target face from the current frame. The swapper creates exactly one pair and applies the transformation once per frame. This is the default behavior when both many_faces and source_target_map are disabled.
Many Faces Mode (Default Source to All Targets)
When the global flag many_faces is set to True, Deep-Live-Cam calls get_many_faces() from modules/face_analyser.py instead of get_one_face(). This returns a list of all detected faces in the frame. The system then retrieves a default source face (via default_source_face() from the first map entry) and pairs it with every detected target face. This mode is toggled from the UI via the "Swap every detected face, not just the primary one" switch or by passing --many-faces on the command line.
Map-Faces Mode (Explicit Many-to-Many)
Map-faces mode activates when source_target_map (or its simplified variant simple_map) is populated. This structure contains explicit dictionaries mapping specific source faces to specific target faces. The system supports two sub-variants:
- One-to-one explicit: Each map entry contains one source and one specific target
- Default source with many targets: When
many_facesis active alongside a map, the system uses the default source for all mapped targets
Face Detection Architecture in Deep-Live-Cam
Face detection is handled by modules/face_analyser.py, which wraps the InsightFace library. The core detection helper in face_swapper.py branches based on the global configuration:
def get_faces_optimized(frame: Frame, use_cache: bool = True) -> Optional[List[Face]]:
if modules.globals.many_faces:
return get_many_faces(frame) # Returns list of all detected faces
else:
face = get_one_face(frame)
return [face] if face else None
The get_many_faces() function invokes the underlying InsightFace analyzer on the entire frame, returning all detected face embeddings and bounding boxes. When many_faces is False, the system uses get_one_face(), which typically returns the largest or most centered face in the frame.
Source-to-Target Pairing Algorithm
The pairing logic resides in process_frame_v2 within modules/processors/frame/face_swapper.py. The algorithm constructs a list of tuples called source_target_pairs, then iterates through them to perform swaps.
Pair Construction Logic
The code evaluates three conditions in sequence to build the pairing list:
- Map-faces with many-faces enabled: Uses
default_source_face()for every entry insource_target_map - Map-faces without many-faces: Uses one-to-one mappings from the map structure
- Simple many-faces: Applies the default source to every face returned by
get_many_faces() - Simple single-face: Uses the pre-loaded source face with the single detected target
source_target_pairs = []
# 1️⃣ Map-faces mode with many_faces flag
if source_target_map and modules.globals.many_faces:
source_face = default_source_face()
for map_data in source_target_map:
target_face = map_data.get("target", {}).get("face")
if target_face:
source_target_pairs.append((source_face, target_face))
# 2️⃣ Map-faces mode one-to-one
elif source_target_map:
for map_data in source_target_map:
source_face = map_data["source"]["face"]
target_face = map_data["target"]["face"]
source_target_pairs.append((source_face, target_face))
# 3️⃣ Simple mode many faces
elif modules.globals.many_faces:
source_face = default_source_face()
detected_faces = get_many_faces(processed_frame)
for target_face in detected_faces:
source_target_pairs.append((source_face, target_face))
# 4️⃣ Simple mode single face
else:
source_face = pre_loaded_source_face
target_face = get_one_face(processed_frame)
if source_face and target_face:
source_target_pairs.append((source_face, target_face))
Once assembled, the swapper executes sequential transformations:
for source_face, target_face in source_target_pairs:
current_swap_target = swap_face(source_face, target_face, current_swap_target)
Creating and Managing Face Maps
Deep-Live-Cam provides two mechanisms for populating source_target_map: manual UI mapping and automatic video analysis.
Image-to-Image Mapping
Users select a source image and target image in the interface (modules/ui.py), then click "Add mapping". The system extracts face embeddings using modules/face_analyser.py and stores the pairing in modules.globals.source_target_map as a list of dictionaries containing source and target face objects.
Video Mapping with Clustering
For video targets, the function get_unique_faces_from_target_video() extracts face embeddings across all frames, clusters them using find_cluster_centroids, and assigns a source face to each cluster centroid. The resulting structure stores target faces per frame index in source_target_map[i]['target_faces_in_frame']. During processing, the system matches detected faces to the nearest centroid using find_closest_centroid.
Simplified Maps
The simplify_maps() function converts the full map into parallel lists (source_faces and target_embeddings). When simple_map is active, the algorithm uses nearest-neighbor matching against these simplified embeddings rather than the full map structure.
Runtime Execution Flow
The complete data flow from configuration to output involves:
| Step | Component | Action |
|---|---|---|
| Configuration | modules/core.py |
Parses CLI arguments (--many-faces, --map-faces, --analyse-video) and sets flags in modules/globals.py |
| Detection | modules/face_analyser.py |
Executes get_many_faces() or get_one_face() based on the many_faces boolean |
| Pairing | face_swapper.py |
Executes process_frame_v2 to build source_target_pairs using map or default logic |
| Swapping | face_swapper.py |
Iterates pairs and calls swap_face() for each source-target combination |
| Post-processing | face_swapper.py |
Applies sharpening and interpolation via apply_post_processing() |
| Output | process_frames |
Writes to disk or streams live through process_video() or process_image() |
Command Line Examples
Enable many-faces mode for automatic swapping onto every detected face:
python run.py --source path/to/source.jpg \
--target path/to/video.mp4 \
--many-faces
Generate and use a face map for explicit pairing:
# Analyze target video to build clusters
python run.py --target path/to/video.mp4 --analyse-video
# Run swap using generated map
python run.py --source path/to/source.jpg --target path/to/video.mp4 --map-faces
Programmatic Usage
Control the processing pipeline directly from Python:
from modules import globals, processors
from modules.face_analyser import get_many_faces
# Enable many-faces mode
globals.many_faces = True
# Load pre-built map (normally generated by UI)
globals.source_target_map = [
{"source": {"face": src_face_obj}, "target": {"face": tgt_face_obj}}
]
# Process numpy array
result = processors.frame.core.process_frame_v2(frame_np)
Summary
- Three operational modes control detection scope: simple (single face), many-faces (all faces with default source), and map-faces (explicit pairings).
- Detection routing occurs in
get_faces_optimized(), which selects betweenget_one_face()andget_many_faces()based onmodules.globals.many_faces. - Pair construction happens inside
process_frame_v2inmodules/processors/frame/face_swapper.py, supporting both automatic defaults and user-definedsource_target_mapstructures. - Video analysis uses clustering (
find_cluster_centroids) to identify unique faces across frames, enabling persistent identity mapping throughout a video sequence. - UI and CLI both manipulate the same global flags in
modules/globals.py, ensuring consistent behavior across interface modes.
Frequently Asked Questions
How does Deep-Live-Cam choose which source face to use when multiple faces are detected?
When many_faces mode is active without an explicit map, the system calls default_source_face() from modules/face_analyser.py, which returns the face from the first entry in the source map or the primary source image. This single source face is then paired with every detected target face in the frame. In map-faces mode, the specific source face defined in each map entry is used instead.
What is the difference between source_target_map and simple_map in the codebase?
source_target_map is a list of dictionaries containing full face objects and metadata, supporting complex many-to-many relationships and per-frame video mappings. simple_map is a processed variant created by simplify_maps() that contains parallel lists of source faces and target embeddings, optimized for nearest-neighbor matching using find_closest_centroid(). The simple map reduces lookup overhead during real-time processing.
Can Deep-Live-Cam maintain consistent face swapping for the same person across different frames in a video?
Yes, when using map-faces mode with video analysis. The function get_unique_faces_from_target_video() extracts embeddings across all frames, clusters them into unique identities using find_cluster_centroids, and assigns a consistent source face to each cluster. During playback, process_frame_v2 matches detected faces to these centroids, ensuring the same source face swaps onto the same target identity throughout the video sequence.
Where is the face detection backend implemented in the source code?
The detection backend is implemented in modules/face_analyser.py, which wraps the InsightFace library. This module provides get_one_face() for single-face detection and get_many_faces() for multi-face detection. The detection results (face embeddings and bounding boxes) are then consumed by modules/processors/frame/face_swapper.py to execute the actual swapping logic.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →