LDAIR.py 5.83 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :

import facerec2010
import pyvision
import PIL
import numpy
import bob.bio.base

import logging
logger = logging.getLogger("bob.bio.csu")

from .. import utils

class LDAIR (bob.bio.base.extractor.Extractor):
16
  """This class defines a wrapper for the `facerec2010.baseline.lda.LRLDA` class to be used as an image :py:class:`bob.bio.base.extractor.Extractor`.
17

Manuel Günther's avatar
Manuel Günther committed
18
  **Parameters:**
19

Manuel Günther's avatar
Manuel Günther committed
20 21
  REGION_ARGS : list
    The region arguments as taken from :py:attr:`facerec2010.baseline.lda.CohortLDA_REGIONS`.
22

Manuel Günther's avatar
Manuel Günther committed
23 24 25
  REGION_KEYWORDS : dict
    The region keywords as taken from :py:attr:`facerec2010.baseline.lda.CohortLDA_KEYWORDS`.
  """
26
  def __init__(self, REGION_ARGS = facerec2010.baseline.lda.CohortLDA_REGIONS, REGION_KEYWORDS = facerec2010.baseline.lda.CohortLDA_KEYWORDS):
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
    bob.bio.base.extractor.Extractor.__init__(self, requires_training=True, split_training_data_by_client=True, **REGION_KEYWORDS)
    self.ldair = facerec2010.baseline.lda.LRLDA(REGION_ARGS, **REGION_KEYWORDS)
    self.layers = len(REGION_ARGS)
    self.use_cohort = 'cohort_adjust' not in REGION_ARGS[0] or REGION_ARGS[0]['cohort_adjust']

    # overwrite the training image list generation from the file selector
    # since LRPCA needs training data to be split up into identities
    self.use_training_images_sorted_by_identity = True


  def _check_image(self, image):
    """Checks that the input data is in the expected format"""
    assert isinstance(image, numpy.ndarray)
    assert image.ndim == 3
    assert image.dtype == numpy.uint8

  def _py_image(self, image):
    """Generates a 4D structure used for LDA-IR feature extraction"""

    pil_image = PIL.Image.new("RGB",(image.shape[2], image.shape[1]))
    # TODO: Test if there is any faster method to convert the image type
    for y in range(image.shape[1]):
      for x in range(image.shape[2]):
        # copy image content (re-order [y,x] to (x,y) and add the colors as (r,g,b))
        pil_image.putpixel((x,y),(image[0,y,x], image[1,y,x], image[2,y,x]))

    # convert to pyvision image
    py_image = pyvision.Image(pil_image)
    # generate some copies of the image
    return [py_image.copy() for i in range(self.layers)]


Manuel Günther's avatar
Manuel Günther committed
59 60 61 62 63 64 65 66 67 68 69 70 71 72
  def train(self, training_images, extractor_file):
    """Trains the LDA-IR module with the given image list.

    The resulting object will be saved into the given ``extractor_file`` using the :py:func:`bob.bio.csu.save_pickle` function.

    **Parameters:**

    training_images : [[numpy.ndarray]]
      The list of training images, which is split into images of the same clients.

    extractor_file : str
      The file to write into.
    """
    [self._check_image(image) for client_images in training_images for image in client_images]
73
    train_count = 0
Manuel Günther's avatar
Manuel Günther committed
74
    for client_index, client_images in enumerate(training_images):
75
      # Initializes an arrayset for the data
Manuel Günther's avatar
Manuel Günther committed
76
      for image in client_images:
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
        # create PIL image (since there are differences in the
        # implementation of pyvision according to different image types)
        # Additionally, PIL used pixels in (x,y) order
        pyimage = self._py_image(image)

        # append training data to the LDA-IR training
        # (the None parameters are due to the fact that preprocessing happened before)
        self.ldair.addTraining(str(client_index), pyimage, None, None, None)

        train_count += 1

    logger.info("  -> Training LDA-IR with %d images", train_count)
    self.ldair.train()

    if self.use_cohort:
      logger.info("  -> Adding cohort images")
      # add image cohort for score normalization
Manuel Günther's avatar
Manuel Günther committed
94
      for client_images in training_images:
95 96 97 98 99 100 101 102 103 104 105
        # Initializes an arrayset for the data
        for image in client_images:
          pyimage = self._py_image(image)
          self.ldair.addCohort(pyimage, None, None, None)


    # and write the result to file, which in this case simply used pickle
    utils.save_pickle(self.ldair, extractor_file)


  def load(self, extractor_file):
Manuel Günther's avatar
Manuel Günther committed
106 107 108 109 110 111 112 113
    """Loads the LDA-IR from the given extractor file using the :py:func:`bob.bio.csu.load_pickle` function.

    **Parameters:**


    extractor_file : str
      The file to be read, which has been written by the :py:meth:`train` function.
    """
114 115 116 117 118
    # read LDA-IR extractor
    self.ldair = utils.load_pickle(extractor_file)


  def __call__(self, image):
Manuel Günther's avatar
Manuel Günther committed
119 120 121 122 123 124 125 126 127 128 129
    """__call__(image) -> extracted

    Extracts image features using LDA-IR.

    **Parameters:**

    image : 3D :py:class:`numpy.ndarray`
      The color image to project.

    **Returns:**

130
    extracted : `facerec2010.baseline.common.FaceRecord`
Manuel Günther's avatar
Manuel Günther committed
131 132
      The extracted image feature.
    """
133 134 135 136 137 138 139 140 141 142
    self._check_image(image)
    # create pvimage
    pyimage = self._py_image(image)
    # Projects the data (by creating a "Face Record"
    face_record = self.ldair.getFaceRecord(pyimage, None, None, None, compute_cohort_scores = self.use_cohort)

    return face_record


  def write_feature(self, feature, feature_file):
Manuel Günther's avatar
Manuel Günther committed
143 144 145 146
    """Saves the extracted LDA-IR feature to file using :py:func:`bob.bio.csu.save_pickle`.

    **Parameters:**

147
    feature : `facerec2010.baseline.common.FaceRecord`
Manuel Günther's avatar
Manuel Günther committed
148 149 150 151 152
      The extracted feature to be written.

    feature_file : str or :py:class:`bob.io.base.HDF5File`
      The name of the file, or the file opened for writing.
    """
153 154 155 156 157 158
    # write the feature to a .pkl file
    # (since FaceRecord does not have a save method)
    utils.save_pickle(feature, feature_file)


  def read_feature(self, feature_file):
Manuel Günther's avatar
Manuel Günther committed
159 160 161 162 163 164 165 166 167 168 169
    """read_feature(feature_file) -> feature

    Reads the extracted LDA-IR feature from file using :py:func:`bob.bio.csu.load_pickle`.

    **Parameters:**

    feature_file : str or :py:class:`bob.io.base.HDF5File`
      The name of the file, or the file opened for reading.

    **Returns:**

170
    feature : `facerec2010.baseline.common.FaceRecord`
Manuel Günther's avatar
Manuel Günther committed
171 172
      The read feature.
    """
173 174
    # read the feature from .pkl file
    return utils.load_pickle(feature_file)