SPPAS 4.22

https://sppas.org/

Module sppas.src.imgdata

Class BaseObjectsDetector

Description

Abstract class to detect objects in an image.

This class allows to analyze an image in order to detect objects. It stores a list of sppasCoords() for each detected object. The detected object corresponds to the one of the trained model (human face, human profile, cat face, car...).

Example
Example
 >>> f = BaseObjectsDetector()
 >>> f.load_model(model_filename)
 >>> # Detect all the objects in an image
 >>> f.detect(sppasImage(filename="image path"))
 >>> # Get number of detected objects
 >>> len(f)
 >>> # Browse through the detected object coordinates:
 >>> for c in f:
 >>> print(c)

An object detector is instantiated from a model. It will be used to detect the objects matching the model in an image. Detected objects are stored in a list of coordinates. The confidence score of each detected object is expected to range [0., 1.].

Two filters are used to filter detected objects: - a min confidence score; - a min ratio between the object (w,h) related to the ones of the image.

Constructor

Create a new detector instance.

View Source
def __init__(self):
    """Create a new detector instance.

    """
    self._detector = None
    self._extension = ''
    self._coords = list()
    self.__min_ratio = BaseObjectsDetector.DEFAULT_MIN_RATIO
    self.__min_score = BaseObjectsDetector.DEFAULT_MIN_SCORE

Public functions

invalidate

Invalidate current list of detected object coordinates.

View Source
def invalidate(self):
    """Invalidate current list of detected object coordinates."""
    self._coords = list()

get_extension

Return the expected extension of the model filename.

View Source
def get_extension(self):
    """Return the expected extension of the model filename."""
    return self._extension

get_min_ratio

Return the minimum ratio of an object compared to the image.

View Source
def get_min_ratio(self):
    """Return the minimum ratio of an object compared to the image."""
    return self.__min_ratio

set_min_ratio

Set the minimum ratio of an object compared to the image.

It means that width and height of a detected object must be at least 'value' percent of the image width and height respectively

Parameters
  • value: (float) Value ranging [0., 1.]
Raises

ValueError

View Source
def set_min_ratio(self, value):
    """Set the minimum ratio of an object compared to the image.

        It means that width and height of a detected object must be at
        least 'value' percent of the image width and height respectively

        :param value: (float) Value ranging [0., 1.]
        :raise: ValueError

        """
    value = BaseObjectsDetector.to_dtype(value, dtype=float)
    if value < 0.0 or value > 1.0:
        raise IntervalRangeException(value, 0.0, 1.0)
    self.__min_ratio = value

get_min_score

Return the minimum score of a detected object to consider it.

View Source
def get_min_score(self):
    """Return the minimum score of a detected object to consider it."""
    return self.__min_score

set_min_score

Set the minimum score of a detected object to consider it.

It means that any detected object with a score lesser than the given one will be ignored. The score of detected objects are supposed to range between 0. and 1.

Parameters
  • value: (float) Value ranging [0., 1.]
Raises

ValueError

View Source
def set_min_score(self, value):
    """Set the minimum score of a detected object to consider it.

        It means that any detected object with a score lesser than the given
        one will be ignored. The score of detected objects are supposed to
        range between 0. and 1.

        :param value: (float) Value ranging [0., 1.]
        :raise: ValueError

        """
    value = BaseObjectsDetector.to_dtype(value, dtype=float)
    if value < 0.0 or value > 1.0:
        raise IntervalRangeException(value, 0.0, 1.0)
    self.__min_score = value

get_best

Return a copy of the coordinates with the n-best scores.

Add "None" if more objects are requested than those detected.

Parameters
  • nb: (int) number of coordinates to return
Returns
  • (list of sppasCoords or sppasCoords or None)
View Source
def get_best(self, nb=1):
    """Return a copy of the coordinates with the n-best scores.

        Add "None" if more objects are requested than those detected.

        :param nb: (int) number of coordinates to return
        :return: (list of sppasCoords or sppasCoords or None)

        """
    if len(self._coords) == 0:
        return None
    nb = BaseObjectsDetector.to_dtype(nb)
    if nb == 1:
        return self._coords[0]
    nbest = [c.copy() for c in self._coords[:nb]]
    if nb > len(nbest):
        nbest.extend([None] * (nb - len(nbest)))
    return nbest

get_confidence

Return a copy of the coordinate objects with the better scores.

Parameters
  • confidence: (float) Minimum confidence score ranging [0., 1.]
Returns
  • (list of sppasCoords or sppasCoords or None)
View Source
def get_confidence(self, confidence=0.2):
    """Return a copy of the coordinate objects with the better scores.

        :param confidence: (float) Minimum confidence score ranging [0., 1.]
        :return: (list of sppasCoords or sppasCoords or None)

        """
    if len(self._coords) == 0:
        return None
    score = BaseObjectsDetector.to_dtype(confidence, dtype=float)
    return [c.copy() for c in self._coords if c.get_confidence() > score]

load_model

Load at least a model to instantiate a detector.

Parameters
  • model: Filename of a model(DNN, HaarCascade, ...)
Raises

IOError if model is not loaded.

View Source
def load_model(self, model, *args):
    """Load at least a model to instantiate a detector.

        :param model: Filename of a model (DNN, HaarCascade, ...)
        :raise: IOError if model is not loaded.

        """
    if model.endswith(self._extension) is False:
        raise IOExtensionError(model)
    if os.path.exists(model) is False:
        raise sppasIOError(model)
    self._set_detector(model)

detect

Determine the coordinates of all the detected objects.

Parameters
  • image: (sppasImage or numpy.ndarray)
Raises
  • sppasTypeError: Given parameter is not an image.
  • sppasError: No model was instantiated.
View Source
def detect(self, image):
    """Determine the coordinates of all the detected objects.

        :param image: (sppasImage or numpy.ndarray)
        :raises: sppasTypeError: Given parameter is not an image.
        :raises: sppasError: No model was instantiated.

        """
    self.invalidate()
    if isinstance(image, numpy.ndarray) is True:
        image = sppasImage(input_array=image)
    if isinstance(image, sppasImage) is False:
        logging.debug('Object detection requires a sppasImage. Got {} instead.'.format(str(type(image))))
        raise sppasTypeError(str(type(image)), expected='sppasImage')
    if image.channel == 4:
        image = image.ibgra_to_bgr()
    if self._detector is None:
        raise sppasError(ERR_MODEL_MISS)
    self._detection(image)
    self.filter_confidence(self.get_min_score())
    self.sort_by_score()
    try:
        self.filter_overlapped()
        self.sort_by_score()
    except NotImplementedError:
        pass

sort_by_score

Sort the detected objects by their confidence score.

The highest the better.

View Source
def sort_by_score(self):
    """Sort the detected objects by their confidence score.

        The highest the better.

        """
    detected = list()
    for coord in self._coords:
        c = coord.copy()
        score = c.get_confidence()
        detected.append((c, score))
    self._coords = list()
    for coord, score in reversed(sorted(detected, key=lambda x: x[1])):
        self._coords.append(coord)

filter_best

Filter the coordinates to select only the n-best scores.

If there are less coordinates than those requested, nothing is changed.

Parameters
  • nb: (int) number of coordinates to keep
View Source
def filter_best(self, nb=1):
    """Filter the coordinates to select only the n-best scores.

        If there are less coordinates than those requested, nothing is
        changed.

        :param nb: (int) number of coordinates to keep

        """
    best = self.get_best(nb)
    if nb == 1:
        best = [best]
    if nb < len(self._coords):
        self._coords = best

filter_confidence

Filter the coordinates to select only the highest scores.

Parameters
  • confidence: (float) Minimum confidence score
View Source
def filter_confidence(self, confidence=0.2):
    """Filter the coordinates to select only the highest scores.

        :param confidence: (float) Minimum confidence score

        """
    if len(self._coords) == 0:
        return None
    best = self.get_confidence(confidence)
    if len(best) < len(self._coords):
        self._coords = best

to_dtype

Convert a value to dtype or raise the appropriate exception.

Parameters
  • value: (any type)
  • dtype: (type) Expected type of the value
Returns
  • Value of the given type
Raises

TypeError

View Source
@staticmethod
def to_dtype(value, dtype=int):
    """Convert a value to dtype or raise the appropriate exception.

        :param value: (any type)
        :param dtype: (type) Expected type of the value
        :returns: Value of the given type
        :raises: TypeError

        """
    try:
        value = dtype(value)
        if isinstance(value, dtype) is False:
            raise sppasTypeError(value, str(dtype))
    except ValueError:
        raise sppasTypeError(value, str(dtype))
    return value

filter_overlapped

Remove overlapping detected objects.

To be overridden.

Parameters
  • overlap
View Source
def filter_overlapped(self, overlap=50.0):
    """Remove overlapping detected objects.

        To be overridden.

        """
    pass

Private functions

_set_detector

Really load the model and instantiate the detector.

To be overridden.

Parameters
  • model
View Source
def _set_detector(self, model):
    """Really load the model and instantiate the detector.

        To be overridden.

        """
    raise NotImplementedError

_detection

Determine the coordinates of the detected objects.

To be overridden.

Parameters
  • image
View Source
def _detection(self, image):
    """Determine the coordinates of the detected objects.

        To be overridden.

        """
    raise NotImplementedError

Overloads

__len__

View Source
def __len__(self):
    return len(self._coords)

__iter__

View Source
def __iter__(self):
    for coordinates in self._coords:
        yield coordinates

__getitem__

View Source
def __getitem__(self, i):
    return self._coords[i]

__contains__

Return true if value in self.__coordinates.

Parameters
  • value
View Source
def __contains__(self, value):
    """Return true if value in self.__coordinates."""
    for c in self._coords:
        if c == value:
            return True
    return False

__str__

View Source
def __str__(self):
    return '\n'.join([str(c) for c in self._coords])

__repr__

View Source
def __repr__(self):
    return self.__class__.__name__

__format__

View Source
def __format__(self, fmt):
    return str(self).__format__(fmt)