Coverage for src/bob/bio/vein/preprocessor/normalize.py: 92%
52 statements
« prev ^ index » next coverage.py v7.6.0, created at 2024-07-12 23:27 +0200
« prev ^ index » next coverage.py v7.6.0, created at 2024-07-12 23:27 +0200
1#!/usr/bin/env python
2# vim: set fileencoding=utf-8 :
4"""Base utilities for normalization"""
6import math
8import numpy
10from PIL import Image
13class Normalizer(object):
14 """Objects of this class normalize the input image orientation and scale"""
16 def __init__(self):
17 pass
19 def __call__(self, image, mask):
20 """Inputs image and mask and outputs a normalized version of those
23 Parameters:
25 image (numpy.ndarray): raw image to normalize as 2D array of unsigned
26 8-bit integers
28 mask (numpy.ndarray): mask to normalize as 2D array of booleans
31 Returns:
33 numpy.ndarray: A 2D boolean array with the same shape and data type of
34 the input image representing the newly aligned image.
36 numpy.ndarray: A 2D boolean array with the same shape and data type of
37 the input mask representing the newly aligned mask.
39 """
41 raise NotImplementedError("You must implement the __call__ slot")
44class NoNormalization(Normalizer):
45 """Trivial implementation with no normalization"""
47 def __init__(self):
48 pass
50 def __call__(self, image, mask):
51 """Returns the input parameters, without changing them
54 Parameters:
56 image (numpy.ndarray): raw image to normalize as 2D array of unsigned
57 8-bit integers
59 mask (numpy.ndarray): mask to normalize as 2D array of booleans
62 Returns:
64 numpy.ndarray: A 2D boolean array with the same shape and data type of
65 the input image representing the newly aligned image.
67 numpy.ndarray: A 2D boolean array with the same shape and data type of
68 the input mask representing the newly aligned mask.
70 """
72 return image, mask
75class HuangNormalization(Normalizer):
76 """Simple finger normalization from Huang et. al
78 Based on B. Huang, Y. Dai, R. Li, D. Tang and W. Li, Finger-vein
79 authentication based on wide line detector and pattern normalization,
80 Proceedings on 20th International Conference on Pattern Recognition (ICPR),
81 2010.
83 This implementation aligns the finger to the centre of the image using an
84 affine transformation. Elliptic projection which is described in the
85 referenced paper is **not** included.
87 In order to defined the affine transformation to be performed, the
88 algorithm first calculates the center for each edge (column wise) and
89 calculates the best linear fit parameters for a straight line passing
90 through those points.
91 """
93 def __init__(self, padding_width=5, padding_constant=51):
94 self.padding_width = padding_width
95 self.padding_constant = padding_constant
97 def __call__(self, image, mask):
98 """Inputs image and mask and outputs a normalized version of those
101 Parameters:
103 image (numpy.ndarray): raw image to normalize as 2D array of unsigned
104 8-bit integers
106 mask (numpy.ndarray): mask to normalize as 2D array of booleans
109 Returns:
111 numpy.ndarray: A 2D boolean array with the same shape and data type of
112 the input image representing the newly aligned image.
114 numpy.ndarray: A 2D boolean array with the same shape and data type of
115 the input mask representing the newly aligned mask.
117 """
119 img_h, img_w = image.shape
121 # Calculates the mask edges along the columns
122 edges = numpy.zeros((2, mask.shape[1]), dtype=int)
124 edges[0, :] = mask.argmax(axis=0) # get upper edges
125 edges[1, :] = len(mask) - numpy.flipud(mask).argmax(axis=0) - 1
127 bl = edges.mean(axis=0) # baseline
128 x = numpy.arange(0, edges.shape[1])
129 A = numpy.vstack([x, numpy.ones(len(x))]).T
131 # Fit a straight line through the base line points
132 w = numpy.linalg.lstsq(A, bl)[0] # obtaining the parameters
134 angle = -1 * math.atan(w[0]) # Rotation
135 tr = img_h / 2 - w[1] # Translation
136 scale = 1.0 # Scale
138 # Affine transformation parameters
139 sx = sy = scale
140 cosine = math.cos(angle)
141 sine = math.sin(angle)
143 a = cosine / sx
144 b = -sine / sy
145 # b = sine/sx
146 c = 0 # Translation in x
148 d = sine / sx
149 e = cosine / sy
150 f = tr # Translation in y
151 # d = -sine/sy
152 # e = cosine/sy
153 # f = 0
155 g = 0
156 h = 0
157 # h=tr
158 i = 1
160 T = numpy.matrix([[a, b, c], [d, e, f], [g, h, i]])
161 Tinv = numpy.linalg.inv(T)
162 Tinvtuple = (
163 Tinv[0, 0],
164 Tinv[0, 1],
165 Tinv[0, 2],
166 Tinv[1, 0],
167 Tinv[1, 1],
168 Tinv[1, 2],
169 )
171 def _afftrans(img):
172 """Applies the affine transform on the resulting image"""
174 t = Image.fromarray(img.astype("uint8"))
175 w, h = t.size # pillow image is encoded w, h
176 w += 2 * self.padding_width
177 h += 2 * self.padding_width
178 t = t.transform(
179 (w, h),
180 Image.AFFINE,
181 Tinvtuple,
182 resample=Image.BICUBIC,
183 fill=self.padding_constant,
184 )
186 return numpy.array(t).astype(img.dtype)
188 return _afftrans(image), _afftrans(mask)