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_faces is 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:

  1. Map-faces with many-faces enabled: Uses default_source_face() for every entry in source_target_map
  2. Map-faces without many-faces: Uses one-to-one mappings from the map structure
  3. Simple many-faces: Applies the default source to every face returned by get_many_faces()
  4. 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 between get_one_face() and get_many_faces() based on modules.globals.many_faces.
  • Pair construction happens inside process_frame_v2 in modules/processors/frame/face_swapper.py, supporting both automatic defaults and user-defined source_target_map structures.
  • 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →