How to Use Frame Ranges to Selectively Convert Video Portions in FaceSwap

Use the --frame-ranges flag in FaceSwap's convert command to process only specific frame segments, drastically reducing processing time when you need to convert just a portion of a video.

FaceSwap's conversion pipeline supports frame ranges to let you selectively process specific portions of a video without converting the entire sequence. This feature is particularly valuable when working with long videos where you only need to apply face swapping to specific segments, saving significant processing time and computational resources.

Understanding Frame Ranges in FaceSwap

Frame ranges allow you to define one or more contiguous segments of frames that FaceSwap will process, while skipping everything else. When you specify ranges, the tool creates a whitelist of frame indices and filters the input stream accordingly.

How Frame Ranges Work Internally

The implementation follows a five-stage pipeline inside the DiskIO class in scripts/convert.py:

  1. Argument parsing – The CLI captures your range strings in args.frame_ranges as a list of strings (e.g., ["100-500", "800-1200"]).

  2. Range calculation – DiskIO._get_frame_ranges() (lines 2100-2146) parses these strings into tuples of (start, end) integers. The method automatically clips these values to the actual frame boundaries detected in your source (minframe to maxframe).

  3. Skip-frame logic – As each input file loads, _check_skipframe() (lines 2850-2856) extracts the frame number from the filename using regex. If the number falls outside your defined ranges, the frame is either discarded or passed through unchanged (depending on --keep-unchanged).

  4. Total-frame count – The _total_count property (lines 2720-2729) calculates the exact number of frames to process. When ranges are active and --keep-unchanged is not set, this equals the sum of all range lengths; otherwise, it reports the full input count.

  5. Writer initialization – _get_writer() (lines 1990-2008) passes the frame range list to the output writer (FFmpeg or GIF). This ensures the final video is stitched correctly without gaps or timing errors.

Using Frame Ranges from the Command Line

FaceSwap's convert command accepts frame ranges through the --frame-ranges parameter followed by space-separated start-end pairs.

Converting Specific Frame Segments

To process only frames 100 through 500 and frames 800 through 1200:

python faceswap.py convert \
    -i /path/to/input_frames \
    -o /path/to/output_frames \
    -m /path/to/model \
    --frame-ranges 100-500 800-1200
  • Frames outside these ranges are skipped entirely and will not appear in the output directory.
  • The ranges are inclusive (both start and end frames are processed).

Preserving Unchanged Frames Outside Ranges

Add the --keep-unchanged flag to copy unprocessed frames to the output while only converting frames within your specified ranges:

python faceswap.py convert \
    -i video_frames \
    -o swapped_frames \
    -m model_dir \
    --frame-ranges 300-600 \
    --keep-unchanged
  • Frames 300-600 undergo face swapping.
  • All other frames are copied verbatim to maintain video timing and duration.
  • This is essential when you need to re-encode the full video but only want to process specific segments.

Working with Video Files Directly

FaceSwap treats video files as virtual frame sources. The same --frame-ranges syntax applies:

python faceswap.py convert \
    -i source_video.mp4 \
    -o result_video.mp4 \
    -m model_dir \
    --frame-ranges 0-2500
  • When using video input, minframe is forced to 1 and maxframe equals the total frame count detected by ImagesLoader (defined in scripts/fsmedia.py).
  • Out-of-bounds range values are automatically clipped to valid frame numbers, preventing errors.

Programmatic Access to Frame Ranges

If you are embedding FaceSwap in a Python application, you can trigger frame-range filtering programmatically:

from scripts.convert import Convert
import argparse

# Set up the argument parser with FaceSwap's expected parameters

parser = argparse.ArgumentParser()
parser.add_argument('-i', '--input', required=True, help='Input directory or video')
parser.add_argument('-o', '--output', required=True, help='Output directory or video')
parser.add_argument('-m', '--model', required=True, help='Model directory')
parser.add_argument('--frame-ranges', nargs='*', default=None,
                    help='Space-separated start-end pairs, e.g., 10-200 400-600')
parser.add_argument('--keep-unchanged', action='store_true',
                    help='Copy unprocessed frames to output')

# Parse example arguments

args = parser.parse_args([
    '-i', 'frames/', 
    '-o', 'out/', 
    '-m', 'model/', 
    '--frame-ranges', '10-200', '400-600'
])

# Execute the conversion with frame range filtering

Convert(args).process()

The Convert class instantiates DiskIO, which internally calls _get_frame_ranges() to parse your range strings and enforce the filtering logic.

Key Implementation Details

The frame range functionality is distributed across several core files:

File Role Relevant Sections
scripts/convert.py Core conversion driver that parses CLI arguments, creates DiskIO, and manages predictor and writer threads. Convert.__init__, DiskIO._get_frame_ranges (lines 2100-2146), DiskIO._check_skipframe (lines 2850-2856), DiskIO._total_count (lines 2720-2729), DiskIO._get_writer (lines 1990-2008)
scripts/fsmedia.py Handles image and video loading via ImagesLoader; provides is_video flag used to determine minframe and maxframe boundaries. ImagesLoader class
plugins/convert/writer/ffmpeg.py FFmpeg writer plugin that receives frame_ranges to ensure correct video stitching without gaps. FFmpegWriter.__init__
plugins/convert/writer/gif.py GIF writer plugin that similarly accepts frame range metadata for proper animation timing. GifWriter.__init__
lib/utils.py Utility functions for folder handling and error reporting used throughout the pipeline. get_folder, FaceswapError

Summary

  • Frame ranges allow you to process specific video segments using the --frame-ranges flag followed by start-end pairs (e.g., 100-500 800-1200).
  • Internally, DiskIO._get_frame_ranges() in scripts/convert.py parses and validates these ranges against actual frame boundaries.
  • Use --keep-unchanged to copy unprocessed frames to the output, preserving video timing while only converting selected segments.
  • The feature works with both extracted frame sequences and direct video file inputs, with automatic clipping to valid frame numbers.
  • Writer plugins (FFmpeg, GIF) receive range metadata to ensure seamless video reconstruction without gaps.

Frequently Asked Questions

What happens if I specify frame ranges outside my video's duration?

FaceSwap automatically clips your requested ranges to the actual frame boundaries detected in the source. In DiskIO._get_frame_ranges() (lines 2100-2146), the code compares your start-end values against minframe and maxframe, adjusting any out-of-bounds numbers to valid indices. This prevents errors and ensures you process every available frame within your specified ranges.

Can I use multiple frame ranges in a single command?

Yes. The --frame-ranges argument accepts multiple space-separated start-end pairs. For example, --frame-ranges 100-500 800-1200 1500-2000 processes three distinct segments while skipping everything else. Internally, these are stored as a list of tuples and checked sequentially during the _check_skipframe validation (lines 2850-2856).

Does using frame ranges affect the output video timing?

Without the --keep-unchanged flag, the output contains only the processed frames, resulting in a shorter video that jumps between your selected segments. When you include --keep-unchanged, frames outside the ranges are copied verbatim to the output, preserving the original timing and duration while only the specified ranges undergo face conversion. The writer plugins (FFmpeg/GIF) use the frame_ranges metadata to ensure proper frame sequencing in both scenarios.

How does --keep-unchanged differ from omitting the flag?

Omitting --keep-unchanged causes FaceSwap to discard any frames outside your specified ranges, producing an output that contains only the converted segments. Adding --keep-unchanged triggers the skip-frame logic to copy unprocessed frames directly to the output directory (or include them in the video writer), ensuring you retain the full frame sequence with only your selected ranges modified. This is controlled in the _check_skipframe method (lines 2850-2856) and affects the _total_count calculation (lines 2720-2729).

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 →