INormLBP.py 5.29 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# @author: Manuel Guenther <Manuel.Guenther@idiap.ch>
# @date: Thu May 24 10:41:42 CEST 2012
#
# Copyright (C) 2011-2012 Idiap Research Institute, Martigny, Switzerland
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import bob.ip.base

import numpy
from .Base import Base
from .utils import load_cropper
Tiago de Freitas Pereira's avatar
Tiago de Freitas Pereira committed
25
26
from sklearn.utils import check_array
from bob.pipelines.sample import SampleBatch
27

28

29
30
class INormLBP(Base):
    """Performs I-Norm LBP on the given image"""
31

32
33
34
35
36
37
38
39
40
41
42
    def __init__(
        self,
        face_cropper,
        radius=2,  # Radius of the LBP
        is_circular=True,  # use circular LBP?
        compare_to_average=False,
        elbp_type="regular",
        **kwargs
    ):

        """Parameters of the constructor of this preprocessor:
43

44
    face_cropper : str or :py:class:`bob.bio.face.preprocessor.FaceCrop` or :py:class:`bob.bio.face.preprocessor.FaceDetect` or ``None``
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
      The face image cropper that should be applied to the image.
      It might be specified as a registered resource, a configuration file, or an instance of a preprocessor.

      .. note:: The given class needs to contain a ``crop_face`` method.

    radius : int
      The radius of the LBP features to extract

    is_circular : bool
      Whether to extract circular LBP features, or square features

    compare_to_average : bool
      Compare to the average value of all pixels, or to the central one

    elbp_type : str
      The way, LBP features are extracted, see :py:class:`bob.ip.base.LBP` for more details.

    kwargs
      Remaining keyword parameters passed to the :py:class:`Base` constructor, such as ``color_channel`` or ``dtype``.
    """

66
67
68
        # call base class constructors
        Base.__init__(self, **kwargs)

69
70
71
72
73
        self.face_cropper = face_cropper
        self.radius = radius
        self.is_circular = is_circular
        self.compare_to_average = compare_to_average
        self.elbp_type = elbp_type
74
75
76
77

        self.radius = radius
        self.is_circular = is_circular
        self.compare_to_average = compare_to_average
78
        self.elbp_type = elbp_type
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
        self.cropper = load_cropper(face_cropper)

        self._init_non_pickables()

    def _init_non_pickables(self):
        # lbp extraction
        self.lbp_extractor = bob.ip.base.LBP(
            neighbors=8,
            radius=self.radius,
            circular=self.is_circular,
            to_average=self.compare_to_average,
            add_average_bit=False,
            uniform=False,
            elbp_type=self.elbp_type,
            border_handling="wrap",
        )

96
    def transform(self, X, annotations=None):
97
        """__call__(image, annotations = None) -> face
98

99
    Aligns the given image according to the given annotations.
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
    First, the desired color channel is extracted from the given image.
    Afterward, the face is eventually cropped using the ``face_cropper`` specified in the constructor.
    Then, the image is photometrically enhanced by extracting LBP features [HRM06]_.
    Finally, the resulting face is converted to the desired data type.

    **Parameters:**

    image : 2D or 3D :py:class:`numpy.ndarray`
      The face image to be processed.

    annotations : dict or ``None``
      The annotations that fit to the given image.
      Might be ``None``, when the ``face_cropper`` is ``None`` or of type :py:class:`FaceDetect`.

    **Returns:**

    face : 2D :py:class:`numpy.ndarray`
      The cropped and photometrically enhanced face.
    """
120

121
        def _crop_one_sample(image, annotations=None):
122

123
            if self.cropper is not None:
124
125
                # TODO: USE THE TAG `ALLOW_ANNOTATIONS`
                image = (
126
                    self.cropper.transform([image])
127
                    if annotations is None
128
                    else self.cropper.transform([image], [annotations])
129
                )
130
                # LBP's doesn't work with batches, so we have to work this out
131
132
133
                # Also, we change the color channel *after* cropping : some croppers use MTCNN internally, that works on multichannel images
                image = self.change_color_channel(image[0])
                image = self.lbp_extractor(image)
134
135
            else:
                # Handle with the cropper is None
136
                image = self.change_color_channel(image)
137
138
                image = self.lbp_extractor(image)

139
            return self.data_type(image)
140

141
        if annotations is None:
142
            return [_crop_one_sample(data) for data in X]
143
        else:
144
145
146
            return [
                _crop_one_sample(data, annot) for data, annot in zip(X, annotations)
            ]
147

148
149
150
151
    def __getstate__(self):
        d = dict(self.__dict__)
        d.pop("lbp_extractor")
        return d
152

153
154
155
    def __setstate__(self, d):
        self.__dict__ = d
        self._init_non_pickables()