LBPHistogram.py 6.19 KB
Newer Older
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
1
from __future__ import division
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
2
3

from bob.ip.base import LBP, histogram
4
import numpy as np
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
5
6
from sklearn.base import BaseEstimator
from sklearn.base import TransformerMixin
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
7
8


Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
9
class LBPHistogram(TransformerMixin, BaseEstimator):
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
10
    """Calculates a normalized LBP histogram over an image.
11
    These features are implemented based on [CAM12]_.
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

    Parameters
    ----------
    lbptype : str
        The type of the LBP operator (regular, uniform or riu2)
    elbptype : str
        The type of extended version of LBP (regular if not extended version
        is used, otherwise transitional, direction_coded or modified)
    rad : float
        The radius of the circle on which the points are taken (for circular
        LBP)
    neighbors : int
        The number of points around the central point on which LBP is
        computed (4, 8, 16)
    circ : bool
        True if circular LBP is needed, False otherwise
28
29
30
31
32
33
    n_hor : int
        Number of blocks horizontally for spatially-enhanced LBP/MCT
        histograms. Default: 1
    n_vert
        Number of blocks vertically for spatially-enhanced LBP/MCT
        histograms. Default: 1
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
34
35
36
37
38
39

    Attributes
    ----------
    dtype : numpy.dtype
        If a ``dtype`` is specified in the contructor, it is assured that the
        resulting features have that dtype.
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
40
    lbp : LBP
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
41
42
43
        The LPB extractor object.
    """

Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
44
45
46
47
48
49
50
51
52
53
54
55
56
57
    def __init__(
        self,
        lbptype="uniform",
        elbptype="regular",
        rad=1,
        neighbors=8,
        circ=False,
        dtype=None,
        n_hor=1,
        n_vert=1,
        **kwargs,
    ):

        super().__init__(**kwargs)
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
58

59
        elbps = {
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
60
61
62
63
            "regular": "regular",
            "transitional": "trainsitional",
            "direction_coded": "direction-coded",
            "modified": "regular",
64
        }
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
65

Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
66
        if elbptype == "modified":
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
67
68
69
70
            mct = True
        else:
            mct = False

Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
71
        if lbptype == "uniform":
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
72
            if neighbors == 16:
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
73
                lbp = LBP(
74
75
76
77
78
                    neighbors=16,
                    uniform=True,
                    circular=circ,
                    radius=rad,
                    to_average=mct,
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
79
80
                    elbp_type=elbps[elbptype],
                )
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
81
            else:  # we assume neighbors==8 in this case
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
82
                lbp = LBP(
83
84
85
86
87
                    neighbors=8,
                    uniform=True,
                    circular=circ,
                    radius=rad,
                    to_average=mct,
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
88
89
90
                    elbp_type=elbps[elbptype],
                )
        elif lbptype == "riu2":
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
91
            if neighbors == 16:
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
92
                lbp = LBP(
93
94
95
96
97
98
                    neighbors=16,
                    uniform=True,
                    rotation_invariant=True,
                    radius=rad,
                    circular=circ,
                    to_average=mct,
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
99
100
                    elbp_type=elbps[elbptype],
                )
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
101
            else:  # we assume neighbors==8 in this case
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
102
                lbp = LBP(
103
104
105
106
107
108
                    neighbors=8,
                    uniform=True,
                    rotation_invariant=True,
                    radius=rad,
                    circular=circ,
                    to_average=mct,
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
109
110
                    elbp_type=elbps[elbptype],
                )
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
111
112
        else:  # regular LBP
            if neighbors == 16:
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
113
                lbp = LBP(
114
115
116
117
                    neighbors=16,
                    circular=circ,
                    radius=rad,
                    to_average=mct,
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
118
119
                    elbp_type=elbps[elbptype],
                )
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
120
            else:  # we assume neighbors==8 in this case
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
121
                lbp = LBP(
122
123
124
125
                    neighbors=8,
                    circular=circ,
                    radius=rad,
                    to_average=mct,
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
126
127
                    elbp_type=elbps[elbptype],
                )
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
128
129
130

        self.dtype = dtype
        self.lbp = lbp
131
132
        self.n_hor = n_hor
        self.n_vert = n_vert
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
133

134
135
136
    def comp_block_histogram(self, data):
        """
        Extracts LBP/MCT histograms from a gray-scale image/block.
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152

        Takes data of arbitrary dimensions and linearizes it into a 1D vector;
        Then, calculates the histogram.
        enforcing the data type, if desired.

        Parameters
        ----------
        data : numpy.ndarray
            The preprocessed data to be transformed into one vector.

        Returns
        -------
        1D :py:class:`numpy.ndarray`
            The extracted feature vector, of the desired ``dtype`` (if
            specified)
        """
153
        assert isinstance(data, np.ndarray)
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
154
155

        # allocating the image with lbp codes
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
156
        lbpimage = np.ndarray(self.lbp.lbp_shape(data), "uint16")
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
157
        self.lbp(data, lbpimage)  # calculating the lbp image
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
158
159
160
        hist = histogram(
            lbpimage, (0, self.lbp.max_label - 1), self.lbp.max_label
        )
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
161
162
163
164
        hist = hist / sum(hist)  # histogram normalization
        if self.dtype is not None:
            hist = hist.astype(self.dtype)
        return hist
165

Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
166
    def transform_one_image(self, data):
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
        """
        Extracts spatially-enhanced LBP/MCT histograms from a gray-scale image.

        Parameters
        ----------
        data : numpy.ndarray
            The preprocessed data to be transformed into one vector.

        Returns
        -------
        1D :py:class:`numpy.ndarray`
            The extracted feature vector, of the desired ``dtype`` (if
            specified)

        """

        # Make sure the data can be split into equal blocks:
        row_max = int(data.shape[0] / self.n_vert) * self.n_vert
        col_max = int(data.shape[1] / self.n_hor) * self.n_hor
        data = data[:row_max, :col_max]

Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
188
189
190
191
192
        blocks = [
            sub_block
            for block in np.hsplit(data, self.n_hor)
            for sub_block in np.vsplit(block, self.n_vert)
        ]
193
194
195
196
197
198
199
200

        hists = [self.comp_block_histogram(block) for block in blocks]

        hist = np.hstack(hists)

        hist = hist / len(blocks)  # histogram normalization

        return hist
Amir MOHAMMADI's avatar
Amir MOHAMMADI committed
201
202
203
204
205
206
207
208
209

    def transform(self, images):
        return [self.transform_one_image(img) for img in images]

    def _more_tags(self):
        return {"stateless": True, "requires_fit": False}

    def fit(self, X, y=None):
        return self