CofeehousePy/deps/scikit-image/skimage/feature/_haar.pyx

315 lines
14 KiB
Cython

#cython: cdivision=True
#cython: boundscheck=False
#cython: nonecheck=False
#cython: wraparound=False
#distutils: language=c++
import numpy as np
cimport numpy as cnp
from libcpp.vector cimport vector
from .._shared.fused_numerics cimport np_real_numeric
from .._shared.transform cimport integrate
FEATURE_TYPE = {'type-2-x': 0, 'type-2-y': 1,
'type-3-x': 2, 'type-3-y': 3,
'type-4': 4}
N_RECTANGLE = {'type-2-x': 2, 'type-2-y': 2,
'type-3-x': 3, 'type-3-y': 3,
'type-4': 4}
cdef vector[vector[Rectangle]] _haar_like_feature_coord(
Py_ssize_t width,
Py_ssize_t height,
unsigned int feature_type) nogil:
"""Private function to compute the coordinates of all Haar-like features.
"""
cdef:
Py_ssize_t max_feature = height * height * width * width
vector[vector[Rectangle]] rect_feat
Rectangle single_rect
Py_ssize_t n_rectangle
Py_ssize_t x, y, dx, dy
if feature_type == 0 or feature_type == 1:
n_rectangle = 2
elif feature_type == 2 or feature_type == 3:
n_rectangle = 3
else:
n_rectangle = 4
# Allocate for the number of rectangle (we know from the start)
rect_feat = vector[vector[Rectangle]](n_rectangle)
for y in range(height):
for x in range(width):
for dy in range(1, height):
for dx in range(1, width):
# type -> 2 rectangles split along x axis
if (feature_type == 0 and
(y + dy <= height and x + 2 * dx <= width)):
set_rectangle_feature(&single_rect,
y, x,
y + dy - 1, x + dx - 1)
rect_feat[0].push_back(single_rect)
set_rectangle_feature(&single_rect,
y, x + dx,
y + dy - 1, x + 2 * dx - 1)
rect_feat[1].push_back(single_rect)
# type -> 2 rectangles split along y axis
elif (feature_type == 1 and
(y + 2 * dy <= height and x + dx <= width)):
set_rectangle_feature(&single_rect,
y, x,
y + dy - 1, x + dx - 1)
rect_feat[0].push_back(single_rect)
set_rectangle_feature(&single_rect,
y + dy, x,
y + 2 * dy - 1, x + dx - 1)
rect_feat[1].push_back(single_rect)
# type -> 3 rectangles split along x axis
elif (feature_type == 2 and
(y + dy <= height and x + 3 * dx <= width)):
set_rectangle_feature(&single_rect,
y, x,
y + dy - 1, x + dx - 1)
rect_feat[0].push_back(single_rect)
set_rectangle_feature(&single_rect,
y, x + dx,
y + dy - 1, x + 2 * dx - 1)
rect_feat[1].push_back(single_rect)
set_rectangle_feature(&single_rect,
y, x + 2 * dx,
y + dy - 1, x + 3 * dx - 1)
rect_feat[2].push_back(single_rect)
# type -> 3 rectangles split along y axis
elif (feature_type == 3 and
(y + 3 * dy <= height and x + dx <= width)):
set_rectangle_feature(&single_rect,
y, x,
y + dy - 1, x + dx - 1)
rect_feat[0].push_back(single_rect)
set_rectangle_feature(&single_rect,
y + dy, x,
y + 2 * dy - 1, x + dx - 1)
rect_feat[1].push_back(single_rect)
set_rectangle_feature(&single_rect,
y + 2 * dy, x,
y + 3 * dy - 1, x + dx - 1)
rect_feat[2].push_back(single_rect)
# type -> 4 rectangles split along x and y axis
elif (feature_type == 4 and
(y + 2 * dy <= height and x + 2 * dx <= width)):
set_rectangle_feature(&single_rect,
y, x,
y + dy - 1, x + dx - 1)
rect_feat[0].push_back(single_rect)
set_rectangle_feature(&single_rect,
y, x + dx,
y + dy - 1, x + 2 * dx - 1)
rect_feat[1].push_back(single_rect)
set_rectangle_feature(&single_rect,
y + dy, x,
y + 2 * dy - 1, x + dx - 1)
rect_feat[3].push_back(single_rect)
set_rectangle_feature(&single_rect,
y + dy, x + dx,
y + 2 * dy - 1, x + 2 * dx - 1)
rect_feat[2].push_back(single_rect)
return rect_feat
cpdef haar_like_feature_coord_wrapper(width, height, feature_type):
"""Compute the coordinates of Haar-like features.
Parameters
----------
width : int
Width of the detection window.
height : int
Height of the detection window.
feature_type : str
The type of feature to consider:
- 'type-2-x': 2 rectangles varying along the x axis;
- 'type-2-y': 2 rectangles varying along the y axis;
- 'type-3-x': 3 rectangles varying along the x axis;
- 'type-3-y': 3 rectangles varying along the y axis;
- 'type-4': 4 rectangles varying along x and y axis.
Returns
-------
feature_coord : (n_features, n_rectangles, 2, 2), ndarray of list of \
tuple coord
Coordinates of the rectangles for each feature.
feature_type : (n_features,), ndarray of str
The corresponding type for each feature.
"""
cdef:
vector[vector[Rectangle]] rect
Py_ssize_t n_rectangle, n_feature
Py_ssize_t i, j
# cast the height and width to the right type
Py_ssize_t height_win = <Py_ssize_t> height
Py_ssize_t width_win = <Py_ssize_t> width
rect = _haar_like_feature_coord(width_win, height_win,
FEATURE_TYPE[feature_type])
n_feature = rect[0].size()
n_rectangle = rect.size()
# allocate the output based on the number of rectangle
output = np.empty((n_feature,), dtype=object)
for j in range(n_feature):
coord_feature = []
for i in range(n_rectangle):
coord_feature.append([(rect[i][j].top_left.row,
rect[i][j].top_left.col),
(rect[i][j].bottom_right.row,
rect[i][j].bottom_right.col)])
output[j] = coord_feature
return output, np.array([feature_type] * n_feature, dtype=object)
cdef np_real_numeric[:, ::1] _haar_like_feature(
np_real_numeric[:, ::1] int_image,
vector[vector[Rectangle]] coord,
Py_ssize_t n_rectangle, Py_ssize_t n_feature):
"""Private function releasing the GIL to compute the integral for the
different rectangle."""
cdef:
np_real_numeric[:, ::1] rect_feature = np.empty(
(n_rectangle, n_feature), dtype=int_image.base.dtype)
Py_ssize_t idx_rect, idx_feature
with nogil:
for idx_rect in range(n_rectangle):
for idx_feature in range(n_feature):
rect_feature[idx_rect, idx_feature] = integrate(
int_image,
coord[idx_rect][idx_feature].top_left.row,
coord[idx_rect][idx_feature].top_left.col,
coord[idx_rect][idx_feature].bottom_right.row,
coord[idx_rect][idx_feature].bottom_right.col)
return rect_feature
cpdef haar_like_feature_wrapper(
cnp.ndarray[np_real_numeric, ndim=2] int_image,
r, c, width, height, feature_type, feature_coord):
"""Compute the Haar-like features for a region of interest (ROI) of an
integral image.
Haar-like features have been successfully used for image classification and
object detection [1]_. It has been used for real-time face detection
algorithm proposed in [2]_.
Parameters
----------
int_image : (M, N) ndarray
Integral image for which the features need to be computed.
r : int
Row-coordinate of top left corner of the detection window.
c : int
Column-coordinate of top left corner of the detection window.
width : int
Width of the detection window.
height : int
Height of the detection window.
feature_type : str
The type of feature to consider:
- 'type-2-x': 2 rectangles varying along the x axis;
- 'type-2-y': 2 rectangles varying along the y axis;
- 'type-3-x': 3 rectangles varying along the x axis;
- 'type-3-y': 3 rectangles varying along the y axis;
- 'type-4': 4 rectangles varying along x and y axis.
Returns
-------
haar_features : (n_features,) ndarray
Resulting Haar-like features. Each value is equal to the subtraction of
sums of the positive and negative rectangles. The data type depends of
the data type of `int_image`: `int` when the data type of `int_image`
is `uint` or `int` and `float` when the data type of `int_image` is
`float`.
References
----------
.. [1] https://en.wikipedia.org/wiki/Haar-like_feature
.. [2] Oren, M., Papageorgiou, C., Sinha, P., Osuna, E., & Poggio, T.
(1997, June). Pedestrian detection using wavelet templates.
In Computer Vision and Pattern Recognition, 1997. Proceedings.,
1997 IEEE Computer Society Conference on (pp. 193-199). IEEE.
http://tinyurl.com/y6ulxfta
:DOI:`10.1109/CVPR.1997.609319`
.. [3] Viola, Paul, and Michael J. Jones. "Robust real-time face
detection." International journal of computer vision 57.2
(2004): 137-154.
https://www.merl.com/publications/docs/TR2004-043.pdf
:DOI:`10.1109/CVPR.2001.990517`
"""
cdef:
vector[vector[Rectangle]] coord
Py_ssize_t n_rectangle, n_feature
Py_ssize_t idx_rect, idx_feature
np_real_numeric[:, ::1] rect_feature
# FIXME: currently cython does not support read-only memory views.
# Those are used with joblib when using Parallel. Therefore, we use
# ndarray as input. We take a copy of this ndarray to create a memory
# view to be able to release the GIL in some later processing.
# Check the following issue to check the status of read-only memory
# views in cython:
# https://github.com/cython/cython/issues/1605 to be resolved
np_real_numeric[:, ::1] int_image_memview = int_image[
r : r + height, c : c + width].copy()
if feature_coord is None:
# compute all possible coordinates with a specific type of feature
coord = _haar_like_feature_coord(width, height,
FEATURE_TYPE[feature_type])
n_feature = coord[0].size()
n_rectangle = coord.size()
else:
# build the coordinate from the set provided
n_rectangle = N_RECTANGLE[feature_type]
n_feature = len(feature_coord)
# the vector can be directly pre-allocated since that the size is known
coord = vector[vector[Rectangle]](n_rectangle,
vector[Rectangle](n_feature))
for idx_rect in range(n_rectangle):
for idx_feature in range(n_feature):
set_rectangle_feature(
&coord[idx_rect][idx_feature],
feature_coord[idx_feature][idx_rect][0][0],
feature_coord[idx_feature][idx_rect][0][1],
feature_coord[idx_feature][idx_rect][1][0],
feature_coord[idx_feature][idx_rect][1][1])
rect_feature = _haar_like_feature(int_image_memview,
coord, n_rectangle, n_feature)
# convert the memory view to numpy array and convert it to signed array if
# necessary to avoid overflow during subtraction
rect_feature_ndarray = np.asarray(rect_feature)
data_type = rect_feature_ndarray.dtype
if 'uint' in data_type.name:
rect_feature_ndarray = rect_feature_ndarray.astype(
data_type.name.replace('u', ''))
# the rectangles with odd indices can always be subtracted to the rectangle
# with even indices
return (np.sum(rect_feature_ndarray[1::2], axis=0) -
np.sum(rect_feature_ndarray[::2], axis=0))