scikit-image
312 строк · 13.3 Кб
1#cython: cdivision=True
2#cython: boundscheck=False
3#cython: nonecheck=False
4#cython: wraparound=False
5
6import numpy as np
7
8cimport numpy as cnp
9from libcpp.vector cimport vector
10
11from .._shared.fused_numerics cimport np_real_numeric
12from .._shared.transform cimport integrate
13
14FEATURE_TYPE = {'type-2-x': 0, 'type-2-y': 1,
15'type-3-x': 2, 'type-3-y': 3,
16'type-4': 4}
17
18N_RECTANGLE = {'type-2-x': 2, 'type-2-y': 2,
19'type-3-x': 3, 'type-3-y': 3,
20'type-4': 4}
21
22
23cdef vector[vector[Rectangle]] _haar_like_feature_coord(
24Py_ssize_t width,
25Py_ssize_t height,
26unsigned int feature_type) noexcept nogil:
27"""Private function to compute the coordinates of all Haar-like features.
28"""
29cdef:
30vector[vector[Rectangle]] rect_feat
31Rectangle single_rect
32Py_ssize_t n_rectangle
33Py_ssize_t x, y, dx, dy
34
35if feature_type == 0 or feature_type == 1:
36n_rectangle = 2
37elif feature_type == 2 or feature_type == 3:
38n_rectangle = 3
39else:
40n_rectangle = 4
41
42# Allocate for the number of rectangle (we know from the start)
43rect_feat = vector[vector[Rectangle]](n_rectangle)
44
45for y in range(height):
46for x in range(width):
47for dy in range(1, height + 1):
48for dx in range(1, width + 1):
49# type -> 2 rectangles split along x axis
50if (feature_type == 0 and
51(y + dy <= height and x + 2 * dx <= width)):
52set_rectangle_feature(&single_rect,
53y, x,
54y + dy - 1, x + dx - 1)
55rect_feat[0].push_back(single_rect)
56set_rectangle_feature(&single_rect,
57y, x + dx,
58y + dy - 1, x + 2 * dx - 1)
59rect_feat[1].push_back(single_rect)
60# type -> 2 rectangles split along y axis
61elif (feature_type == 1 and
62(y + 2 * dy <= height and x + dx <= width)):
63set_rectangle_feature(&single_rect,
64y, x,
65y + dy - 1, x + dx - 1)
66rect_feat[0].push_back(single_rect)
67set_rectangle_feature(&single_rect,
68y + dy, x,
69y + 2 * dy - 1, x + dx - 1)
70rect_feat[1].push_back(single_rect)
71# type -> 3 rectangles split along x axis
72elif (feature_type == 2 and
73(y + dy <= height and x + 3 * dx <= width)):
74set_rectangle_feature(&single_rect,
75y, x,
76y + dy - 1, x + dx - 1)
77rect_feat[0].push_back(single_rect)
78set_rectangle_feature(&single_rect,
79y, x + dx,
80y + dy - 1, x + 2 * dx - 1)
81rect_feat[1].push_back(single_rect)
82set_rectangle_feature(&single_rect,
83y, x + 2 * dx,
84y + dy - 1, x + 3 * dx - 1)
85rect_feat[2].push_back(single_rect)
86# type -> 3 rectangles split along y axis
87elif (feature_type == 3 and
88(y + 3 * dy <= height and x + dx <= width)):
89set_rectangle_feature(&single_rect,
90y, x,
91y + dy - 1, x + dx - 1)
92rect_feat[0].push_back(single_rect)
93set_rectangle_feature(&single_rect,
94y + dy, x,
95y + 2 * dy - 1, x + dx - 1)
96rect_feat[1].push_back(single_rect)
97set_rectangle_feature(&single_rect,
98y + 2 * dy, x,
99y + 3 * dy - 1, x + dx - 1)
100rect_feat[2].push_back(single_rect)
101# type -> 4 rectangles split along x and y axis
102elif (feature_type == 4 and
103(y + 2 * dy <= height and x + 2 * dx <= width)):
104set_rectangle_feature(&single_rect,
105y, x,
106y + dy - 1, x + dx - 1)
107rect_feat[0].push_back(single_rect)
108set_rectangle_feature(&single_rect,
109y, x + dx,
110y + dy - 1, x + 2 * dx - 1)
111rect_feat[1].push_back(single_rect)
112set_rectangle_feature(&single_rect,
113y + dy, x,
114y + 2 * dy - 1, x + dx - 1)
115rect_feat[3].push_back(single_rect)
116set_rectangle_feature(&single_rect,
117y + dy, x + dx,
118y + 2 * dy - 1, x + 2 * dx - 1)
119rect_feat[2].push_back(single_rect)
120
121return rect_feat
122
123
124cpdef haar_like_feature_coord_wrapper(width, height, feature_type):
125"""Compute the coordinates of Haar-like features.
126
127Parameters
128----------
129width : int
130Width of the detection window.
131height : int
132Height of the detection window.
133feature_type : str
134The type of feature to consider:
135
136- 'type-2-x': 2 rectangles varying along the x axis;
137- 'type-2-y': 2 rectangles varying along the y axis;
138- 'type-3-x': 3 rectangles varying along the x axis;
139- 'type-3-y': 3 rectangles varying along the y axis;
140- 'type-4': 4 rectangles varying along x and y axis.
141
142Returns
143-------
144feature_coord : (n_features, n_rectangles, 2, 2), ndarray of list of \
145tuple coord
146Coordinates of the rectangles for each feature.
147feature_type : (n_features,), ndarray of str
148The corresponding type for each feature.
149
150"""
151cdef:
152vector[vector[Rectangle]] rect
153Py_ssize_t n_rectangle, n_feature
154Py_ssize_t i, j
155# cast the height and width to the right type
156Py_ssize_t height_win = <Py_ssize_t> height
157Py_ssize_t width_win = <Py_ssize_t> width
158
159rect = _haar_like_feature_coord(width_win, height_win,
160FEATURE_TYPE[feature_type])
161n_feature = rect[0].size()
162n_rectangle = rect.size()
163
164# allocate the output based on the number of rectangle
165output = np.empty((n_feature,), dtype=object)
166for j in range(n_feature):
167coord_feature = []
168for i in range(n_rectangle):
169coord_feature.append([(rect[i][j].top_left.row,
170rect[i][j].top_left.col),
171(rect[i][j].bottom_right.row,
172rect[i][j].bottom_right.col)])
173output[j] = coord_feature
174
175return output, np.array([feature_type] * n_feature, dtype=object)
176
177
178cdef np_real_numeric[:, ::1] _haar_like_feature(
179np_real_numeric[:, ::1] int_image,
180vector[vector[Rectangle]] coord,
181Py_ssize_t n_rectangle, Py_ssize_t n_feature):
182"""Private function releasing the GIL to compute the integral for the
183different rectangle."""
184cdef:
185np_real_numeric[:, ::1] rect_feature = np.empty(
186(n_rectangle, n_feature), dtype=int_image.base.dtype)
187
188Py_ssize_t idx_rect, idx_feature
189
190with nogil:
191for idx_rect in range(n_rectangle):
192for idx_feature in range(n_feature):
193rect_feature[idx_rect, idx_feature] = integrate(
194int_image,
195coord[idx_rect][idx_feature].top_left.row,
196coord[idx_rect][idx_feature].top_left.col,
197coord[idx_rect][idx_feature].bottom_right.row,
198coord[idx_rect][idx_feature].bottom_right.col)
199
200return rect_feature
201
202
203cpdef haar_like_feature_wrapper(
204cnp.ndarray[np_real_numeric, ndim=2] int_image,
205r, c, width, height, feature_type, feature_coord):
206"""Compute the Haar-like features for a region of interest (ROI) of an
207integral image.
208
209Haar-like features have been successfully used for image classification and
210object detection [1]_. It has been used for real-time face detection
211algorithm proposed in [2]_.
212
213Parameters
214----------
215int_image : (M, N) ndarray
216Integral image for which the features need to be computed.
217r : int
218Row-coordinate of top left corner of the detection window.
219c : int
220Column-coordinate of top left corner of the detection window.
221width : int
222Width of the detection window.
223height : int
224Height of the detection window.
225feature_type : str
226The type of feature to consider:
227
228- 'type-2-x': 2 rectangles varying along the x axis;
229- 'type-2-y': 2 rectangles varying along the y axis;
230- 'type-3-x': 3 rectangles varying along the x axis;
231- 'type-3-y': 3 rectangles varying along the y axis;
232- 'type-4': 4 rectangles varying along x and y axis.
233
234Returns
235-------
236haar_features : (n_features,) ndarray
237Resulting Haar-like features. Each value is equal to the subtraction of
238sums of the positive and negative rectangles. The data type depends of
239the data type of `int_image`: `int` when the data type of `int_image`
240is `uint` or `int` and `float` when the data type of `int_image` is
241`float`.
242
243References
244----------
245.. [1] https://en.wikipedia.org/wiki/Haar-like_feature
246.. [2] Oren, M., Papageorgiou, C., Sinha, P., Osuna, E., & Poggio, T.
247(1997, June). Pedestrian detection using wavelet templates.
248In Computer Vision and Pattern Recognition, 1997. Proceedings.,
2491997 IEEE Computer Society Conference on (pp. 193-199). IEEE.
250http://tinyurl.com/y6ulxfta
251:DOI:`10.1109/CVPR.1997.609319`
252.. [3] Viola, Paul, and Michael J. Jones. "Robust real-time face
253detection." International journal of computer vision 57.2
254(2004): 137-154.
255https://www.merl.com/publications/docs/TR2004-043.pdf
256:DOI:`10.1109/CVPR.2001.990517`
257
258"""
259cdef:
260vector[vector[Rectangle]] coord
261Py_ssize_t n_rectangle, n_feature
262Py_ssize_t idx_rect, idx_feature
263np_real_numeric[:, ::1] rect_feature
264# FIXME: currently cython does not support read-only memory views.
265# Those are used with joblib when using Parallel. Therefore, we use
266# ndarray as input. We take a copy of this ndarray to create a memory
267# view to be able to release the GIL in some later processing.
268# Check the following issue to check the status of read-only memory
269# views in cython:
270# https://github.com/cython/cython/issues/1605 to be resolved
271np_real_numeric[:, ::1] int_image_memview = int_image[
272r : r + height, c : c + width].copy()
273
274if feature_coord is None:
275# compute all possible coordinates with a specific type of feature
276coord = _haar_like_feature_coord(width, height,
277FEATURE_TYPE[feature_type])
278n_feature = coord[0].size()
279n_rectangle = coord.size()
280else:
281# build the coordinate from the set provided
282n_rectangle = N_RECTANGLE[feature_type]
283n_feature = len(feature_coord)
284
285# the vector can be directly pre-allocated since that the size is known
286coord = vector[vector[Rectangle]](n_rectangle,
287vector[Rectangle](n_feature))
288
289for idx_rect in range(n_rectangle):
290for idx_feature in range(n_feature):
291set_rectangle_feature(
292&coord[idx_rect][idx_feature],
293feature_coord[idx_feature][idx_rect][0][0],
294feature_coord[idx_feature][idx_rect][0][1],
295feature_coord[idx_feature][idx_rect][1][0],
296feature_coord[idx_feature][idx_rect][1][1])
297
298rect_feature = _haar_like_feature(int_image_memview,
299coord, n_rectangle, n_feature)
300
301# convert the memory view to numpy array and convert it to signed array if
302# necessary to avoid overflow during subtraction
303rect_feature_ndarray = np.asarray(rect_feature)
304data_type = rect_feature_ndarray.dtype
305if 'uint' in data_type.name:
306rect_feature_ndarray = rect_feature_ndarray.astype(
307data_type.name.replace('u', ''))
308
309# the rectangles with odd indices can always be subtracted to the rectangle
310# with even indices
311return (np.sum(rect_feature_ndarray[1::2], axis=0) -
312np.sum(rect_feature_ndarray[::2], axis=0))
313