1from random import shuffle
2
3import pytest
4
5import numpy as np
6from numpy.testing import assert_allclose
7from numpy.testing import assert_array_equal
8
9from skimage.transform import integral_image
10from skimage.feature import haar_like_feature
11from skimage.feature import haar_like_feature_coord
12from skimage.feature import draw_haar_like_feature
13
14
15def test_haar_like_feature_error():
16    img = np.ones((5, 5), dtype=np.float32)
17    img_ii = integral_image(img)
18
19    feature_type = 'unknown_type'
20    with pytest.raises(ValueError):
21        haar_like_feature(img_ii, 0, 0, 5, 5, feature_type=feature_type)
22        haar_like_feature_coord(5, 5, feature_type=feature_type)
23        draw_haar_like_feature(img, 0, 0, 5, 5, feature_type=feature_type)
24
25    feat_coord, feat_type = haar_like_feature_coord(5, 5, 'type-2-x')
26    with pytest.raises(ValueError):
27        haar_like_feature(img_ii, 0, 0, 5, 5, feature_type=feat_type[:3],
28                          feature_coord=feat_coord)
29
30
31@pytest.mark.parametrize("dtype", [np.uint8, np.int8,
32                                   np.float32, np.float64])
33@pytest.mark.parametrize("feature_type,shape_feature,expected_feature_value",
34                         [('type-2-x', (84,), [0.]),
35                          ('type-2-y', (84,), [0.]),
36                          ('type-3-x', (42,), [-5, -4., -3., -2., -1.]),
37                          ('type-3-y', (42,), [-5, -4., -3., -2., -1.]),
38                          ('type-4', (36,), [0.])])
39def test_haar_like_feature(feature_type, shape_feature,
40                           expected_feature_value, dtype):
41    # test Haar-like feature on a basic one image
42    img = np.ones((5, 5), dtype=dtype)
43    img_ii = integral_image(img)
44    haar_feature = haar_like_feature(img_ii, 0, 0, 5, 5,
45                                     feature_type=feature_type)
46    assert_allclose(np.sort(np.unique(haar_feature)), expected_feature_value)
47
48
49@pytest.mark.parametrize("dtype", [np.uint8, np.int8,
50                                   np.float32, np.float64])
51@pytest.mark.parametrize("feature_type", ['type-2-x', 'type-2-y',
52                                          'type-3-x', 'type-3-y',
53                                          'type-4'])
54def test_haar_like_feature_fused_type(dtype, feature_type):
55    # check that the input type is kept
56    img = np.ones((5, 5), dtype=dtype)
57    img_ii = integral_image(img)
58    expected_dtype = img_ii.dtype
59    # to avoid overflow, unsigned type are converted to signed
60    if 'uint' in expected_dtype.name:
61        expected_dtype = np.dtype(expected_dtype.name.replace('u', ''))
62    haar_feature = haar_like_feature(img_ii, 0, 0, 5, 5,
63                                     feature_type=feature_type)
64    assert haar_feature.dtype == expected_dtype
65
66
67def test_haar_like_feature_list():
68    img = np.ones((5, 5), dtype=np.int8)
69    img_ii = integral_image(img)
70    feature_type = ['type-2-x', 'type-2-y', 'type-3-x', 'type-3-y', 'type-4']
71    haar_list = haar_like_feature(img_ii, 0, 0, 5, 5,
72                                  feature_type=feature_type)
73    haar_all = haar_like_feature(img_ii, 0, 0, 5, 5)
74    assert_array_equal(haar_list, haar_all)
75
76
77@pytest.mark.parametrize("feature_type", ['type-2-x', 'type-2-y',
78                                          'type-3-x', 'type-3-y',
79                                          'type-4',
80                                          ['type-2-y', 'type-3-x',
81                                           'type-4']])
82def test_haar_like_feature_precomputed(feature_type):
83    img = np.ones((5, 5), dtype=np.int8)
84    img_ii = integral_image(img)
85    if isinstance(feature_type, list):
86        # shuffle the index of the feature to be sure that we are output
87        # the features in the same order
88        shuffle(feature_type)
89        feat_coord, feat_type = zip(*[haar_like_feature_coord(5, 5, feat_t)
90                                      for feat_t in feature_type])
91        feat_coord = np.concatenate(feat_coord)
92        feat_type = np.concatenate(feat_type)
93    else:
94        feat_coord, feat_type = haar_like_feature_coord(5, 5, feature_type)
95    haar_feature_precomputed = haar_like_feature(img_ii, 0, 0, 5, 5,
96                                                 feature_type=feat_type,
97                                                 feature_coord=feat_coord)
98    haar_feature = haar_like_feature(img_ii, 0, 0, 5, 5, feature_type)
99    assert_array_equal(haar_feature_precomputed, haar_feature)
100
101
102@pytest.mark.parametrize("feature_type,height,width,expected_coord",
103                         [('type-2-x', 2, 2,
104                           [[[(0, 0), (0, 0)], [(0, 1), (0, 1)]],
105                            [[(0, 0), (1, 0)], [(0, 1), (1, 1)]],
106                            [[(1, 0), (1, 0)], [(1, 1), (1, 1)]]]),
107                          ('type-2-y', 2, 2,
108                           [[[(0, 0), (0, 0)], [(1, 0), (1, 0)]],
109                            [[(0, 0), (0, 1)], [(1, 0), (1, 1)]],
110                            [[(0, 1), (0, 1)], [(1, 1), (1, 1)]]]),
111                          ('type-3-x', 3, 3,
112                           [[[(0, 0), (0, 0)], [(0, 1), (0, 1)],
113                             [(0, 2), (0, 2)]],
114                            [[(0, 0), (1, 0)], [(0, 1), (1, 1)],
115                             [(0, 2), (1, 2)]],
116                            [[(0, 0), (2, 0)], [(0, 1), (2, 1)],
117                             [(0, 2), (2, 2)]],
118                            [[(1, 0), (1, 0)], [(1, 1), (1, 1)],
119                             [(1, 2), (1, 2)]],
120                            [[(1, 0), (2, 0)], [(1, 1), (2, 1)],
121                             [(1, 2), (2, 2)]],
122                            [[(2, 0), (2, 0)], [(2, 1), (2, 1)],
123                             [(2, 2), (2, 2)]]]),
124                          ('type-3-y', 3, 3,
125                           [[[(0, 0), (0, 0)], [(1, 0), (1, 0)],
126                             [(2, 0), (2, 0)]],
127                            [[(0, 0), (0, 1)], [(1, 0), (1, 1)],
128                             [(2, 0), (2, 1)]],
129                            [[(0, 0), (0, 2)], [(1, 0), (1, 2)],
130                             [(2, 0), (2, 2)]],
131                            [[(0, 1), (0, 1)], [(1, 1), (1, 1)],
132                             [(2, 1), (2, 1)]],
133                            [[(0, 1), (0, 2)], [(1, 1), (1, 2)],
134                             [(2, 1), (2, 2)]],
135                            [[(0, 2), (0, 2)], [(1, 2), (1, 2)],
136                             [(2, 2), (2, 2)]]]),
137                          ('type-4', 2, 2,
138                           [[[(0, 0), (0, 0)], [(0, 1), (0, 1)],
139                             [(1, 1), (1, 1)], [(1, 0), (1, 0)]]])])
140def test_haar_like_feature_coord(feature_type, height, width, expected_coord):
141    feat_coord, feat_type = haar_like_feature_coord(width, height,
142                                                    feature_type)
143    # convert the output to a full numpy array just for comparison
144    feat_coord = np.array([hf for hf in feat_coord])
145    assert_array_equal(feat_coord, expected_coord)
146    assert np.all(feat_type == feature_type)
147
148
149@pytest.mark.parametrize("max_n_features,nnz_values", [(None, 46),
150                                                       (1, 4)])
151def test_draw_haar_like_feature(max_n_features, nnz_values):
152    img = np.zeros((5, 5), dtype=np.float32)
153    coord, _ = haar_like_feature_coord(5, 5, 'type-4')
154    image = draw_haar_like_feature(img, 0, 0, 5, 5, coord,
155                                   max_n_features=max_n_features,
156                                   random_state=0)
157    assert image.shape == (5, 5, 3)
158    assert np.count_nonzero(image) == nnz_values
159