SPPAS 4.20

Module sppas.src.annotations

Class sppasFaceDetection

Description

SPPAS integration of the automatic face detection systems.

Can detect faces in an image or in all images of a video.

For this annotation, the user can't change the output pattern; it is

intentional. The pattern is either '-face' or '-portrait' if the option

portrait is enabled.

Constructor

Create a new automatic annotation instance.

Parameters
  • log: (sppasLog) Human-readable logs.
Raises

sppasEnableFeatureError

View Source
def __init__(self, log=None):
    """Create a new automatic annotation instance.

    :param log: (sppasLog) Human-readable logs.
    :raise: sppasEnableFeatureError

    """
    if cfg.feature_installed('facedetect') is False:
        raise sppasEnableFeatureError('facedetect')
    super(sppasFaceDetection, self).__init__('facedetect.json', log)
    self.__fdi = ImageFaceDetection()
    self.__img_writer = sppasCoordsImageWriter()
    self.__fdv = VideoFaceDetection(self.__fdi)
    self.__video_writer = sppasCoordsVideoWriter(self.__img_writer)

Public functions

load_resources

Fix the model files.

Currently, both HaarCascade classifiers and DNN are supported. Add

as many models as wished; their results are combined.

The given model(s) is/are enabled if there's an option to indicate it,

i.e. one starting by "model:".

Parameters
  • model1: (str) Filename of a model
  • args: other models for face detection
View Source
def load_resources(self, model1, *args, **kwargs):
    """Fix the model files.

        Currently, both HaarCascade classifiers and DNN are supported. Add
        as many models as wished; their results are combined.
        The given model(s) is/are enabled if there's an option to indicate it,
        i.e. one starting by "model:".

        :param model1: (str) Filename of a model
        :param args: other models for face detection

        """
    self.__fdi.load_model(model1, *args)
    all_models = [model1]
    for filename in args:
        all_models.append(filename)
    for key in self._options:
        if key.startswith('model:'):
            model_name = key.split(':')[1]
            if any([model_name in m for m in all_models]) is True:
                self.set_enable_model(model_name, self._options[key])
fix_options

Fix all options.

Parameters
  • options: (sppasOption)
View Source
def fix_options(self, options):
    """Fix all options.

        :param options: (sppasOption)

        """
    for opt in options:
        key = opt.get_key()
        if key == 'nbest':
            self.set_max_faces(opt.get_value())
        elif key == 'score':
            self.set_min_score(opt.get_value())
        elif key == 'csv':
            self.set_out_csv(opt.get_value())
        elif key == 'tag':
            self.set_img_tag(opt.get_value())
        elif key == 'crop':
            self.set_img_crop(opt.get_value())
        elif key == 'portrait':
            self.set_img_portrait(opt.get_value())
        elif key == 'folder':
            self.set_out_folder(opt.get_value())
        elif key == 'width':
            self.set_img_width(opt.get_value())
        elif key == 'height':
            self.set_img_height(opt.get_value())
        elif key.startswith('model:'):
            self._options[key] = opt.get_value()
            self.set_enable_model(key.split(':')[1], opt.get_value())
        elif 'pattern' in key:
            self._options[key] = opt.get_value()
        else:
            raise AnnotationOptionError(key)
set_max_faces

Fix the maximum number of expected faces in an image.

Parameters
  • value: (int) Number of faces
View Source
def set_max_faces(self, value):
    """Fix the maximum number of expected faces in an image.

        :param value: (int) Number of faces

        """
    value = int(value)
    self.__fdv.set_filter_best(value)
    self.__fdi.filter_best(value)
    self._options['nbest'] = value
set_min_score

Fix the minimum score to accept a face in an image.

Parameters
  • value: (float) Min confidence score of face detection result
View Source
def set_min_score(self, value):
    """Fix the minimum score to accept a face in an image.

        :param value: (float) Min confidence score of face detection result

        """
    value = float(value)
    self.__fdv.set_filter_confidence(value)
    self.__fdi.filter_confidence(value)
    self._options['score'] = value
set_out_csv

The result includes a CSV file.

Parameters
  • out_csv: (bool) Create a CSV file when detecting
View Source
def set_out_csv(self, out_csv=False):
    """The result includes a CSV file.

        :param out_csv: (bool) Create a CSV file when detecting

        """
    self.__img_writer.set_options(csv=out_csv)
    self.__video_writer.set_options(csv=out_csv)
    self._options['csv'] = out_csv
    self.__img_writer.set_options(xra=not out_csv)
    self.__video_writer.set_options(xra=not out_csv)
    self._options['xra'] = not out_csv
set_img_tag

Surround the faces with a square.

Parameters
  • value: (bool) Tag the images
View Source
def set_img_tag(self, value=True):
    """Surround the faces with a square.

        :param value: (bool) Tag the images

        """
    value = bool(value)
    self.__video_writer.set_options(tag=value)
    self._options['tag'] = value
set_img_crop

Create an image/video for each detected person.

Parameters
  • value: (bool) Crop the images
View Source
def set_img_crop(self, value=True):
    """Create an image/video for each detected person.

        :param value: (bool) Crop the images

        """
    value = bool(value)
    self.__video_writer.set_options(crop=value)
    self._options['crop'] = value
set_img_width

Width of the resulting images/video.

Parameters
  • value: (int) Number of pixels
View Source
def set_img_width(self, value):
    """Width of the resulting images/video.

        :param value: (int) Number of pixels

        """
    self.__video_writer.set_options(width=value)
    self._options['width'] = value
set_img_height

Height of the resulting images/video.

Parameters
  • value: (int) Number of pixel
View Source
def set_img_height(self, value):
    """Height of the resulting images/video.

        :param value: (int) Number of pixel

        """
    self.__video_writer.set_options(height=value)
    self._options['height'] = value
set_img_portrait

Result is the portrait instead of the face.

Parameters
  • value: (bool) True
View Source
def set_img_portrait(self, value):
    """Result is the portrait instead of the face.

        :param value: (bool) True

        """
    value = bool(value)
    self._options['portrait'] = value
    self.__fdv.set_portrait(value)
set_out_folder

The result includes a folder with image files -- if video input.

Parameters
  • out_folder: (bool) Create a folder with image files when detecting
View Source
def set_out_folder(self, out_folder=False):
    """The result includes a folder with image files -- if video input.

        :param out_folder: (bool) Create a folder with image files when detecting

        """
    self.__video_writer.set_options(folder=out_folder)
    self._options['folder'] = out_folder
set_enable_model

Enable or disable the use of a detection model.

Parameters
  • model_name: (str) model filename without path (model basename).
  • value: (bool) True to enable.
View Source
def set_enable_model(self, model_name, value=True):
    """Enable or disable the use of a detection model.

        :param model_name: (str) model filename without path (model basename).
        :param value: (bool) True to enable.

        """
    value = bool(value)
    success = self.__fdi.enable_recognizer(model_name, value)
    if success is False and value is True:
        self.logfile.print_message("Model {:s} can't be enabled.".format(model_name), indent=2, status=annots.warning)
get_nb_recognizers

Return the number of initialized object recognizers for video.

View Source
def get_nb_recognizers(self):
    """Return the number of initialized object recognizers for video."""
    return self.__fdi.get_nb_recognizers()
get_nb_enabled_recognizers

Return the number of enabled object recognizers.

View Source
def get_nb_enabled_recognizers(self):
    """Return the number of enabled object recognizers."""
    return self.__fdi.get_nb_enabled_recognizers()
get_recognizer_names

Return the name of all the initialized recognizers.

View Source
def get_recognizer_names(self):
    """Return the name of all the initialized recognizers."""
    return self.__fdi.get_recognizer_names()
get_enabled_recognizer_names

Return the name of all the initialized recognizers.

View Source
def get_enabled_recognizer_names(self):
    """Return the name of all the initialized recognizers. """
    return self.__fdi.get_enabled_recognizer_names()
image_face_detect

Get the image, detect faces and write results.

Parameters
  • image: (str or sppasImage) Image filename
  • output: (str) The output name for the image
Returns
  • (list) the coordinates of all detected faces or created filenames
View Source
def image_face_detect(self, image, output=None):
    """Get the image, detect faces and write results.

        :param image: (str or sppasImage) Image filename
        :param output: (str) The output name for the image

        :return: (list) the coordinates of all detected faces or created filenames

        """
    if isinstance(image, sppasImage) is False:
        image = sppasImage(filename=image)
    self.__fdi.detect(image)
    if self._options['portrait'] is True:
        try:
            self.__fdi.to_portrait(image)
        except Exception as e:
            self.logfile.print_message("Faces can't be scaled to portrait: {}".format(str(e)), indent=2, status=annots.error)
    coords = [c.copy() for c in self.__fdi]
    if output is not None:
        output_file = self.fix_out_file_ext(output, out_format='IMAGE')
        new_files = self.__img_writer.write(image, coords, output_file, self.get_output_pattern())
        return new_files
    return coords
get_inputs

Return the media filenames.

Parameters
  • input_files: (list)
Raises

NoInputError

Returns
  • (str) Name of the media file
View Source
def get_inputs(self, input_files):
    """Return the media filenames.

        :param input_files: (list)
        :raise: NoInputError
        :return: (str) Name of the media file

        """
    media_ext = self.get_input_extensions()
    media = None
    for filename in input_files:
        fn, fe = os.path.splitext(filename)
        if media is None and fe in media_ext[0]:
            return filename
    logging.error('Neither a video nor an image was found.')
    raise NoInputError
run

Run the automatic annotation process on a single input.

Parameters
  • input_files: (list of str) Video or image file
  • output: (str) the output name
Returns
  • (list of sppasCoords) Coordinates of detected faces or filenames
View Source
def run(self, input_files, output=None):
    """Run the automatic annotation process on a single input.

        :param input_files: (list of str) Video or image file
        :param output: (str) the output name
        :returns: (list of sppasCoords) Coordinates of detected faces or filenames

        """
    media_file = self.get_inputs(input_files)
    self.__video_writer.set_image_extension(self._out_extensions['IMAGE'])
    self.__video_writer.set_video_extension(self._out_extensions['VIDEO'])
    self.__video_writer.close()
    fn, ext = os.path.splitext(media_file)
    result = list()
    if ext in video_extensions:
        try:
            result = self.__fdv.video_face_detect(media_file, self.__video_writer, output)
        except:
            print(traceback.format_exc())
            raise
        self.__video_writer.close()
    elif ext in image_extensions:
        result = self.image_face_detect(media_file, output)
    return result
get_output_pattern

Pattern this annotation uses in an output filename.

View Source
def get_output_pattern(self):
    """Pattern this annotation uses in an output filename."""
    pattern = '-face'
    if self._options['portrait'] is True:
        pattern = '-portrait'
    return self._options.get('outputpattern', pattern)
get_input_extensions

Extensions that the annotation expects for its input filename.

Priority is given to video files, then image files.

View Source
@staticmethod
def get_input_extensions():
    """Extensions that the annotation expects for its input filename.

        Priority is given to video files, then image files.

        """
    out_ext = list(sppasFiles.get_informat_extensions('VIDEO'))
    for img_ext in sppasFiles.get_informat_extensions('IMAGE'):
        out_ext.append(img_ext)
    return [out_ext]