How the Faceswap Preview System Handles Mask Toggling During Training

The Faceswap preview system uses a thread-safe buffer to stream training batches to a separate UI thread, allowing users to toggle between raw faces, masked faces, and predicted masks in real-time without interrupting the training loop.

During model training in the deepfakes/faceswap repository, the preview system provides continuous visual feedback by displaying sample batches from the current training iteration. Understanding how mask toggling works within this system requires examining the interaction between the training loop's data pipeline, the preview buffer architecture, and the UI control panels that manage mask selection.

The Preview Architecture: Buffering and Threading

The preview system decouples the training loop from the rendering process to prevent UI operations from slowing down model training. At the core of this architecture is the PreviewBuffer class defined in lib/training/preview_cv.py (lines 29-55).

The training loop continuously pushes the most recent batch of aligned faces into this thread-safe buffer:


# Training loop pushes samples without blocking

self.preview_buffer.push(sample)  # lib/training/preview_cv.py

A separate preview thread reads from this buffer and handles all rendering. The system supports two rendering backends: Tkinter (lib/training/preview_tk.py, lines 119-136) when available, and OpenCV (lib/training/preview_cv.py, lines 122-140) as a fallback. This threading model ensures that expensive mask calculations and GUI updates never stall the gradient computation.

Configuring Mask Types in the Control Panel

The mask selection UI is built by tools/preview/control_panels.py, specifically within the _create_mask_choices method (lines 14-42). This function dynamically populates the Mask Type dropdown based on two data sources:

  • Alignment file masks: Extracted from alignments.mask_summary (e.g., "components", "extended")
  • Predicted masks: Added only when the training configuration reports has_predicted_mask=True
  • None: Always available to display raw, unmasked faces

The system maintains sensible defaults. If the saved default mask type from a previous session is no longer available (for example, if the alignment file changed), the code automatically falls back to the first valid mask in the list.

Applying Masks to Preview Images

When a user selects a mask from the dropdown, the preview system applies it to the buffered image before display. This logic resides in tools/sort/sort_methods.py within the _mask_face method (lines 436-475).

The function performs three critical operations:

  1. Retrieves the mask tensor using det_face.mask.get("components") or the selected type
  2. Resizes the mask to 256×256 to match the aligned face dimensions
  3. Blends the images using np.minimum(aln_face.face, nmask)

This visualization pipeline is completely isolated from the training loss calculation. While the preview shows masked versions of faces, the underlying training code in lib/training/cache.py and lib/training/generator.py continues to process raw mask tensors according to the model's configuration.

Real-Time Display Updates

After mask application, the updated image flows back through the buffer system. The preview thread retrieves the modified image and refreshes the canvas immediately:


# Preview thread retrieves and displays

img = self.preview_buffer.get()  # lib/training/preview_cv.py

# Apply selected mask based on UI state

masked_img = SortMethods._mask_face(img, alignments)  # tools/sort/sort_methods.py

# Update the canvas (Tkinter example)

canvas.itemconfig(self._image_id, image=ImageTk.PhotoImage(masked_img))

If the user changes the mask type again, the pipeline re-executes starting from the buffered raw image, providing instant visual feedback. For CLI-based preview launches, tools/preview/preview.py (line 541) validates the selected mask_type against available options before the UI initializes.

Summary

  • The training loop pushes samples to a PreviewBuffer (lib/training/preview_cv.py) while a separate thread handles rendering via Tkinter or OpenCV.
  • Mask choices are populated from alignment files and training config via _create_mask_choices in tools/preview/control_panels.py, always including "none" and conditionally including "predicted".
  • Mask application occurs in tools/sort/sort_methods.py::_mask_face, which resizes masks to 256×256 and blends them using np.minimum.
  • Mask toggling affects only visualization; the training loss continues using raw mask tensors from lib/training/cache.py and lib/training/generator.py.

Frequently Asked Questions

Does mask toggling affect the model's training loss?

No, mask toggling in the preview system is purely for visualization. The underlying training loop continues using raw mask tensors defined in the training configuration and handled by lib/training/cache.py and lib/training/generator.py. The _mask_face function only modifies the image displayed in the preview window.

What mask types are available in the preview system?

The dropdown includes masks defined in the alignment file (such as "components" or "extended"), a "predicted" option if the model was trained with mask prediction enabled, and "none" to show raw faces. The _create_mask_choices function in tools/preview/control_panels.py dynamically builds this list based on alignments.mask_summary and the has_predicted_mask configuration flag.

How does the preview system maintain performance while training?

The system decouples training from rendering using a thread-safe PreviewBuffer that stores the most recent batch. The training loop pushes samples via push() without blocking, while the preview thread retrieves them via get() and handles the expensive mask application and GUI rendering separately in lib/training/preview_tk.py or lib/training/preview_cv.py.

Where is the mask actually applied to the face image?

The masking logic resides in tools/sort/sort_methods.py within the _mask_face method (lines 436-475). This function retrieves the requested mask from the alignment data, resizes it to 256×256, and applies it using np.minimum(aln_face.face, nmask) to produce the final preview image.

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 →