Source code for bob.ip.facedetect.detector.sampler


import math
from .._library import BoundingBox

import bob.ip.base


[docs]class Sampler: """This class generates (samples) bounding boxes for different scales and locations in the image. It computes different scales of the image and provides a tight set of :py:class:`BoundingBox` of a given patch size for the given image. **Constructor Documentation:** Generates a patch-sampler, which will scan images and sample bounding boxes. **Parameters:** ``patch_size`` : (int, int) the size of the patch (i.e., the bounding box) to sample ``scale_factor`` : float image pyramids are computed using the given scale factor between two scales ``lowest_scale`` : float or None patches which will be lower than the given scale times the image resolution will not be taken into account; if 0. all possible patches will be considered ``distance`` : int the distance in both horizontal and vertical direction to generate samples """ def __init__(self, patch_size = (24,20), scale_factor = math.pow(2., -1./16.), lowest_scale = math.pow(2., -6.), distance = 2): self.m_patch_box = BoundingBox((0, 0), patch_size) self.m_scale_factor = scale_factor self.m_lowest_scale = lowest_scale self.m_distance = distance
[docs] def scales(self, image): """scales(image) -> scale, shape Computes the all possible scales for the given image and yields a tuple of the scale and the scaled image shape as an iterator. **Parameters::** ``image`` : array_like(2D or 3D) The image, for which the scales should be computed **Yields:** ``scale`` : float The next scale of the image to be considered ``shape`` : (int, int) or (int, int, int) The shape of the image, when scaled with the current ``scale`` """ # compute the minimum scale so that the patch size still fits into the given image minimum_scale = max(self.m_patch_box.size_f[0] / image.shape[-2], self.m_patch_box.size_f[1] / image.shape[-1]) if self.m_lowest_scale: maximum_scale = min(minimum_scale / self.m_lowest_scale, 1.) else: maximum_scale = 1. current_scale_power = 0. # iterate over all possible scales while True: # scale the image scale = minimum_scale * math.pow(self.m_scale_factor, current_scale_power) if scale > maximum_scale: # image is smaller than the requested minimum size break current_scale_power -= 1. scaled_image_shape = bob.ip.base.scaled_output_shape(image, scale) # return both the scale and the scaled image size yield scale, scaled_image_shape
[docs] def sample_scaled(self, shape): """sample_scaled(shape) -> bounding_box Yields an iterator that iterates over all sampled bounding boxes in the given (scaled) image shape. **Parameters:** ``shape`` : (int, int) or (int, int, int) The (current) shape of the (scaled) image **Yields:** ``bounding_box`` : :py:class:`BoundingBox` An iterator iterating over all bounding boxes that are valid for the given shape """ for y in range(0, shape[-2]-self.m_patch_box.bottomright[0], self.m_distance): for x in range(0, shape[-1]-self.m_patch_box.bottomright[1], self.m_distance): # create bounding box for the current shift yield self.m_patch_box.shift((y,x))
[docs] def sample(self, image): """sample(image) -> bounding_box Yields an iterator over all bounding boxes in different scales that are sampled for the given image. **Parameters:** ``image`` : array_like(2D or 3D) The image, for which the bounding boxes should be generated **Yields:** ``bounding_box`` : :py:class:`BoundingBox` An iterator iterating over all bounding boxes for the given ``image`` """ for scale, scaled_image_shape in self.scales(image): # prepare the feature extractor to extract features from the given image for bb in self.sample_scaled(scaled_image_shape): # extract features for yield bb.scale(1./scale)
[docs] def iterate(self, image, feature_extractor, feature_vector): """iterate(image, feature_extractor, feature_vector) -> bounding_box Scales the given image, and extracts features from all possible bounding boxes. For each of the sampled bounding boxes, this function fills the given pre-allocated feature vector and yields the current bounding box. **Parameters:** ``image`` : array_like(2D) The given image to extract features for ``feature_extractor`` : :py:class:`FeatureExtractor` The feature extractor to use to extract the features for the sampled patches ``feature_vector`` : :py:class:`numpy.ndarray` (1D, uint16) The pre-allocated feature vector that will be filled inside this function; needs to be of size :py:attr:`FeatureExtractor.number_of_features` **Yields:** ``bounding_box`` : :py:class:`BoundingBox` The bounding box for which the current features are extracted for """ for scale, scaled_image_shape in self.scales(image): # prepare the feature extractor to extract features from the given image feature_extractor.prepare(image, scale) for bb in self.sample_scaled(scaled_image_shape): # extract features for feature_extractor.extract_indexed(bb, feature_vector) yield bb.scale(1./scale)
[docs] def iterate_cascade(self, cascade, image, threshold = None): """iterate_cascade(self, cascade, image, [threshold]) -> prediction, bounding_box Iterates over the given image and computes the cascade of classifiers. This function will compute the cascaded classification result for the given ``image`` using the given ``cascade``. It yields a tuple of prediction value and the according bounding box. If a ``threshold`` is specified, only those ``prediction``\s are returned, which exceed the given ``threshold``. .. note:: The ``threshold`` does not overwrite the cascade thresholds `:py:attr:`Cascade.thresholds`, but only threshold the final prediction. Specifying the ``threshold`` here is just slightly faster than thresholding the yielded prediction. **Parameters:** ``cascade`` : :py:class:`Cascade` The cascade that performs the predictions ``image`` : array_like(2D) The image for which the predictions should be computed ``threshold`` : float The threshold, which limits the number of predictions **Yields:** ``prediction`` : float The prediction value for the current bounding box ``bounding_box`` : :py:class:`BoundingBox` An iterator over all possible sampled bounding boxes (which exceed the prediction ``threshold``, if given) """ for scale, scaled_image_shape in self.scales(image): # prepare the feature extractor to extract features from the given image cascade.prepare(image, scale) for bb in self.sample_scaled(scaled_image_shape): # return the prediction and the bounding box, if the prediction is over threshold prediction = cascade(bb) if threshold is None or prediction > threshold: yield prediction, bb.scale(1./scale)