#!/usr/bin/env python3 """ Base class for Face Masker plugins Plugins should inherit from this class See the override methods for which methods are required. The plugin will receive a :class:`~plugins.extract.pipeline.ExtractMedia` object. For each source item, the plugin must pass a dict to finalize containing: >>> {"filename": , >>> "detected_faces": } """ import cv2 import numpy as np from plugins.extract._base import Extractor, ExtractMedia, logger class Masker(Extractor): # pylint:disable=abstract-method """ Masker plugin _base Object All Masker plugins must inherit from this class Parameters ---------- git_model_id: int The second digit in the github tag that identifies this model. See https://github.com/deepfakes-models/faceswap-models for more information model_filename: str The name of the model file to be loaded image_is_aligned: bool, optional Indicates that the passed in image is an aligned face rather than a frame. Default: ``False`` Other Parameters ---------------- configfile: str, optional Path to a custom configuration ``ini`` file. Default: Use system configfile See Also -------- plugins.extract.pipeline : The extraction pipeline for calling plugins plugins.extract.align : Aligner plugins plugins.extract._base : Parent class for all extraction plugins plugins.extract.detect._base : Detector parent class for extraction plugins. plugins.extract.align._base : Aligner parent class for extraction plugins. """ def __init__(self, git_model_id=None, model_filename=None, configfile=None, instance=0, image_is_aligned=False): logger.debug("Initializing %s: (configfile: %s, )", self.__class__.__name__, configfile) super().__init__(git_model_id, model_filename, configfile=configfile, instance=instance) self.input_size = 256 # Override for model specific input_size self.coverage_ratio = 1.0 # Override for model specific coverage_ratio self._plugin_type = "mask" self._image_is_aligned = image_is_aligned self._storage_name = self.__module__.split(".")[-1].replace("_", "-") self._storage_size = 128 # Size to store masks at. Leave this at default self._faces_per_filename = dict() # Tracking for recompiling face batches self._rollover = None # Items that are rolled over from the previous batch in get_batch self._output_faces = [] logger.debug("Initialized %s", self.__class__.__name__) def get_batch(self, queue): """ Get items for inputting into the masker from the queue in batches Items are returned from the ``queue`` in batches of :attr:`~plugins.extract._base.Extractor.batchsize` Items are received as :class:`~plugins.extract.pipeline.ExtractMedia` objects and converted to ``dict`` for internal processing. To ensure consistent batch sizes for masker the items are split into separate items for each :class:`~lib.faces_detect.DetectedFace` object. Remember to put ``'EOF'`` to the out queue after processing the final batch Outputs items in the following format. All lists are of length :attr:`~plugins.extract._base.Extractor.batchsize`: >>> {'filename': [], >>> 'detected_faces': [[>> # @staticmethod def _resize(image, target_size): """ resize input and output of mask models appropriately """ height, width, channels = image.shape image_size = max(height, width) scale = target_size / image_size if scale == 1.: return image method = cv2.INTER_CUBIC if scale > 1. else cv2.INTER_AREA # pylint: disable=no-member resized = cv2.resize(image, (0, 0), fx=scale, fy=scale, interpolation=method) resized = resized if channels > 1 else resized[..., None] return resized