  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  # SOFTWARE.
  
  
  def layer(operator):
      """Decorator for composable network layers."""
  
      def layer_decorated(self, *args, **kwargs):
          # Automatically set a name if not provided.
          name = kwargs.setdefault('name', self.get_unique_name(operator.__name__))
          # Figure out the layer inputs.
 
</s> remove # The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
 </s> add # The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software. </s> remove     def layer_decorated(self, *args, **kwargs):
        # Automatically set a name if not provided.
        name = kwargs.setdefault('name', self.get_unique_name(operator.__name__))
        # Figure out the layer inputs.
        if len(self.terminals) == 0:  # pylint: disable=len-as-condition
            raise RuntimeError('No input variables found for layer %s.' % name)
        elif len(self.terminals) == 1:
            layer_input = self.terminals[0]
        else:
            layer_input = list(self.terminals)
        # Perform the operation and get the output.
        layer_output = operator(self, layer_input, *args, **kwargs)
        # Add to layer LUT.
        self.layers[name] = layer_output
        # This output is now the input for the next layer.
        self.feed(layer_output)
        # Return self for chained calls.
        return self

    return layer_decorated


class Network():
    """ Tensorflow Network """
    def __init__(self, inputs, trainable=True):
        # The input nodes for this network
        self.inputs = inputs
        # The current list of terminal nodes
        self.terminals = []
        # Mapping from layer names to layers
        self.layers = dict(inputs)
        # If true, the resulting variables are set as trainable
        self.trainable = trainable

        self.setup()

    def setup(self):
        """Construct the network. """
        raise NotImplementedError('Must be implemented by the subclass.')
 </s> add     @staticmethod
    def model_definition():
        """ Keras PNetwork for MTCNN """
        input_ = Input(shape=(None, None, 3))
        var_x = Conv2D(10, (3, 3), strides=1, padding='valid', name='conv1')(input_)
        var_x = PReLU(shared_axes=[1, 2], name='PReLU1')(var_x)
        var_x = MaxPool2D(pool_size=2)(var_x)
        var_x = Conv2D(16, (3, 3), strides=1, padding='valid', name='conv2')(var_x)
        var_x = PReLU(shared_axes=[1, 2], name='PReLU2')(var_x)
        var_x = Conv2D(32, (3, 3), strides=1, padding='valid', name='conv3')(var_x)
        var_x = PReLU(shared_axes=[1, 2], name='PReLU3')(var_x)
        classifier = Conv2D(2, (1, 1), activation='softmax', name='conv4-1')(var_x)
        bbox_regress = Conv2D(4, (1, 1), name='conv4-2')(var_x)
        return [input_], [classifier, bbox_regress]


class RNet(KSession):
    """ Keras RNet model for MTCNN """
    def __init__(self, model_path):
        super().__init__("MTCNN-RNet", model_path)
        self.define_model(self.model_definition)
        self.load_model_weights()

    @staticmethod
    def model_definition():
        """ Keras RNetwork for MTCNN """
        input_ = Input(shape=(24, 24, 3))
        var_x = Conv2D(28, (3, 3), strides=1, padding='valid', name='conv1')(input_)
        var_x = PReLU(shared_axes=[1, 2], name='prelu1')(var_x)
        var_x = MaxPool2D(pool_size=3, strides=2, padding='same')(var_x)

        var_x = Conv2D(48, (3, 3), strides=1, padding='valid', name='conv2')(var_x)
        var_x = PReLU(shared_axes=[1, 2], name='prelu2')(var_x)
        var_x = MaxPool2D(pool_size=3, strides=2)(var_x)

        var_x = Conv2D(64, (2, 2), strides=1, padding='valid', name='conv3')(var_x)
        var_x = PReLU(shared_axes=[1, 2], name='prelu3')(var_x)
        var_x = Permute((3, 2, 1))(var_x)
        var_x = Flatten()(var_x)
        var_x = Dense(128, name='conv4')(var_x)
        var_x = PReLU(name='prelu4')(var_x)
        classifier = Dense(2, activation='softmax', name='conv5-1')(var_x)
        bbox_regress = Dense(4, name='conv5-2')(var_x)
        return [input_], [classifier, bbox_regress]


class ONet(KSession):
    """ Keras ONet model for MTCNN """
    def __init__(self, model_path):
        super().__init__("MTCNN-ONet", model_path)
        self.define_model(self.model_definition)
        self.load_model_weights() </s> remove         assert len(args) != 0  # pylint: disable=len-as-condition
        self.terminals = []
        for fed_layer in args:
            if isinstance(fed_layer, string_types):
                try:
                    fed_layer = self.layers[fed_layer]
                except KeyError:
                    raise KeyError('Unknown layer name fed: %s' % fed_layer)
            self.terminals.append(fed_layer)
        return self

    def get_output(self):
        """Returns the current network output."""
        return self.terminals[-1]

    def get_unique_name(self, prefix):
        """Returns an index-suffixed unique name for the given prefix.
        This is used for auto-generating layer names based on the type-prefix.
 </s> add         logger.debug("Initializing: %s: (model_path: '%s')",
                     self.__class__.__name__, model_path)
        self.minsize = minsize
        self.threshold = threshold
        self.factor = factor

        self.pnet = PNet(model_path[0])
        self.rnet = RNet(model_path[1])
        self.onet = ONet(model_path[2])
        logger.debug("Initialized: %s", self.__class__.__name__)

    def detect_faces(self, batch):
        """Detects faces in an image, and returns bounding boxes and points for them.
        batch: input batch </s> remove 

def terminate_processes():
    """ Join all active processes on unexpected shutdown

        If the process is doing long running work, make sure you
        have a mechanism in place to terminate this work to avoid
        long blocks
    """

    logger.debug("Processes to join: %s", [process
                                           for process in _launched_processes
                                           if isinstance(process, mp.pool.Pool)
                                           or process.is_alive()])
    for process in list(_launched_processes):
        if isinstance(process, mp.pool.Pool):
            process.terminate()
        if isinstance(process, mp.pool.Pool) or process.is_alive():
            process.join()
 </s> add  </s> remove     def initialize(self, *args, **kwargs):
        """ Create the mtcnn detector """
        super().initialize(*args, **kwargs)
        logger.info("Initializing Manual Detector...")
        self.init.set()
        logger.info("Initialized Manual Detector.")

    def detect_faces(self, *args, **kwargs):
        """ Return the given bounding box in a bounding box dict """
        super().detect_faces(*args, **kwargs)
        while True:
            item = self.get_item()
            if item == "EOF":
                break
            face = item["face"]

            bounding_box = [self.to_bounding_box_dict(face[0], face[1], face[2], face[3])]
            item["detected_faces"] = bounding_box
            self.finalize(item)

        self.queues["out"].put("EOF")
 </s> add     def _compile_detection_image(self, input_image):
        """ Override compile detection image for manual. No face is actually fed into a model """
        return input_image, 1, (0, 0)

    def init_model(self):
        """ No model for Manual """
        return

    def process_input(self, batch):
        """ No pre-processing for Manual. Just set a dummy feed """
        batch["feed"] = batch["scaled_image"]
        return batch

    def predict(self, batch):
        """ No prediction for Manual """
        batch["prediction"] = [np.array(batch["manual_face"])]
        return batch

    def process_output(self, batch):
        """ Post process the detected faces """
        return batch
class PNet(KSession):
    """ Keras PNet model for MTCNN """
    def __init__(self, model_path):
        super().__init__("MTCNN-PNet", model_path)
        self.define_model(self.model_definition)
        self.load_model_weights()
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  # SOFTWARE.
  
  
class PNet(KSession):
    """ Keras PNet model for MTCNN """
    def __init__(self, model_path):
        super().__init__("MTCNN-PNet", model_path)
        self.define_model(self.model_definition)
        self.load_model_weights()
class PNet(KSession):
    """ Keras PNet model for MTCNN """
    def __init__(self, model_path):
        super().__init__("MTCNN-PNet", model_path)
        self.define_model(self.model_definition)
        self.load_model_weights()
  
      def layer_decorated(self, *args, **kwargs):
          # Automatically set a name if not provided.
          name = kwargs.setdefault('name', self.get_unique_name(operator.__name__))
          # Figure out the layer inputs.
keep keep keep keep replace replace keep keep keep keep keep
 De-Multiprocess Extract (#871)

* requirements.txt: - Pin opencv to 4.1.1 (fixes cv2-dnn error)

* lib.face_detect.DetectedFace: change LandmarksXY to landmarks_xy. Add left, right, top, bottom attributes

* lib.model.session: Session manager for loading models into different graphs (for Nvidia + CPU)

* plugins.extract._base: New parent class for all extract plugins

* plugins.extract.pipeline. Remove MultiProcessing. Dynamically limit batchsize for Nvidia cards. Remove loglevel input

* S3FD + FAN plugins. Standardise to Keras version for all backends

* Standardize all extract plugins to new threaded codebase

* Documentation. Start implementing Numpy style docstrings for Sphinx Documentation

* Remove s3fd_amd. Change convert OTF to expect DetectedFace object

* faces_detect - clean up and documentation

* Remove PoolProcess

* Migrate manual tool to new extract workflow

* Remove AMD specific extractor code from cli and plugins

* Sort tool to new extract workflow

* Remove multiprocessing from project

* Remove multiprocessing queues from QueueManager

* Remove multiprocessing support from logger

* Move face_filter to new extraction pipeline

* Alignments landmarksXY > landmarks_xy and legacy handling

* Intercept get_backend for sphinx doc build

# Add Sphinx documentation 
plugins/extract/detect/mtcnn.py