scikit-image
368 строк · 13.3 Кб
1import numpy as np
2import pytest
3
4from skimage._shared.testing import expected_warnings, run_in_parallel
5from skimage.feature import (
6graycomatrix,
7graycoprops,
8local_binary_pattern,
9multiblock_lbp,
10)
11from skimage.transform import integral_image
12
13
14class TestGLCM:
15def setup_method(self):
16self.image = np.array(
17[[0, 0, 1, 1], [0, 0, 1, 1], [0, 2, 2, 2], [2, 2, 3, 3]], dtype=np.uint8
18)
19
20@run_in_parallel()
21def test_output_angles(self):
22result = graycomatrix(
23self.image, [1], [0, np.pi / 4, np.pi / 2, 3 * np.pi / 4], 4
24)
25assert result.shape == (4, 4, 1, 4)
26expected1 = np.array(
27[[2, 2, 1, 0], [0, 2, 0, 0], [0, 0, 3, 1], [0, 0, 0, 1]], dtype=np.uint32
28)
29np.testing.assert_array_equal(result[:, :, 0, 0], expected1)
30expected2 = np.array(
31[[1, 1, 3, 0], [0, 1, 1, 0], [0, 0, 0, 2], [0, 0, 0, 0]], dtype=np.uint32
32)
33np.testing.assert_array_equal(result[:, :, 0, 1], expected2)
34expected3 = np.array(
35[[3, 0, 2, 0], [0, 2, 2, 0], [0, 0, 1, 2], [0, 0, 0, 0]], dtype=np.uint32
36)
37np.testing.assert_array_equal(result[:, :, 0, 2], expected3)
38expected4 = np.array(
39[[2, 0, 0, 0], [1, 1, 2, 0], [0, 0, 2, 1], [0, 0, 0, 0]], dtype=np.uint32
40)
41np.testing.assert_array_equal(result[:, :, 0, 3], expected4)
42
43def test_output_symmetric_1(self):
44result = graycomatrix(self.image, [1], [np.pi / 2], 4, symmetric=True)
45assert result.shape == (4, 4, 1, 1)
46expected = np.array(
47[[6, 0, 2, 0], [0, 4, 2, 0], [2, 2, 2, 2], [0, 0, 2, 0]], dtype=np.uint32
48)
49np.testing.assert_array_equal(result[:, :, 0, 0], expected)
50
51def test_error_raise_float(self):
52for dtype in [float, np.double, np.float16, np.float32, np.float64]:
53with pytest.raises(ValueError):
54graycomatrix(self.image.astype(dtype), [1], [np.pi], 4)
55
56def test_error_raise_int_types(self):
57for dtype in [np.int16, np.int32, np.int64, np.uint16, np.uint32, np.uint64]:
58with pytest.raises(ValueError):
59graycomatrix(self.image.astype(dtype), [1], [np.pi])
60
61def test_error_raise_negative(self):
62with pytest.raises(ValueError):
63graycomatrix(self.image.astype(np.int16) - 1, [1], [np.pi], 4)
64
65def test_error_raise_levels_smaller_max(self):
66with pytest.raises(ValueError):
67graycomatrix(self.image - 1, [1], [np.pi], 3)
68
69def test_image_data_types(self):
70for dtype in [np.uint16, np.uint32, np.uint64, np.int16, np.int32, np.int64]:
71img = self.image.astype(dtype)
72result = graycomatrix(img, [1], [np.pi / 2], 4, symmetric=True)
73assert result.shape == (4, 4, 1, 1)
74expected = np.array(
75[[6, 0, 2, 0], [0, 4, 2, 0], [2, 2, 2, 2], [0, 0, 2, 0]],
76dtype=np.uint32,
77)
78np.testing.assert_array_equal(result[:, :, 0, 0], expected)
79
80return
81
82def test_output_distance(self):
83im = np.array(
84[[0, 0, 0, 0], [1, 0, 0, 1], [2, 0, 0, 2], [3, 0, 0, 3]], dtype=np.uint8
85)
86result = graycomatrix(im, [3], [0], 4, symmetric=False)
87expected = np.array(
88[[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], dtype=np.uint32
89)
90np.testing.assert_array_equal(result[:, :, 0, 0], expected)
91
92def test_output_combo(self):
93im = np.array([[0], [1], [2], [3]], dtype=np.uint8)
94result = graycomatrix(im, [1, 2], [0, np.pi / 2], 4)
95assert result.shape == (4, 4, 2, 2)
96
97z = np.zeros((4, 4), dtype=np.uint32)
98e1 = np.array(
99[[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1], [0, 0, 0, 0]], dtype=np.uint32
100)
101e2 = np.array(
102[[0, 0, 1, 0], [0, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0]], dtype=np.uint32
103)
104
105np.testing.assert_array_equal(result[:, :, 0, 0], z)
106np.testing.assert_array_equal(result[:, :, 1, 0], z)
107np.testing.assert_array_equal(result[:, :, 0, 1], e1)
108np.testing.assert_array_equal(result[:, :, 1, 1], e2)
109
110def test_output_empty(self):
111result = graycomatrix(self.image, [10], [0], 4)
112np.testing.assert_array_equal(
113result[:, :, 0, 0], np.zeros((4, 4), dtype=np.uint32)
114)
115result = graycomatrix(self.image, [10], [0], 4, normed=True)
116np.testing.assert_array_equal(
117result[:, :, 0, 0], np.zeros((4, 4), dtype=np.uint32)
118)
119
120def test_normed_symmetric(self):
121result = graycomatrix(
122self.image, [1, 2, 3], [0, np.pi / 2, np.pi], 4, normed=True, symmetric=True
123)
124for d in range(result.shape[2]):
125for a in range(result.shape[3]):
126np.testing.assert_almost_equal(result[:, :, d, a].sum(), 1.0)
127np.testing.assert_array_equal(
128result[:, :, d, a], result[:, :, d, a].transpose()
129)
130
131def test_contrast(self):
132result = graycomatrix(self.image, [1, 2], [0], 4, normed=True, symmetric=True)
133result = np.round(result, 3)
134contrast = graycoprops(result, 'contrast')
135np.testing.assert_almost_equal(contrast[0, 0], 0.585, decimal=3)
136
137def test_dissimilarity(self):
138result = graycomatrix(
139self.image, [1], [0, np.pi / 2], 4, normed=True, symmetric=True
140)
141result = np.round(result, 3)
142dissimilarity = graycoprops(result, 'dissimilarity')
143np.testing.assert_almost_equal(dissimilarity[0, 0], 0.418, decimal=3)
144
145def test_dissimilarity_2(self):
146result = graycomatrix(
147self.image, [1, 3], [np.pi / 2], 4, normed=True, symmetric=True
148)
149result = np.round(result, 3)
150dissimilarity = graycoprops(result, 'dissimilarity')[0, 0]
151np.testing.assert_almost_equal(dissimilarity, 0.665, decimal=3)
152
153def test_non_normalized_glcm(self):
154img = (np.random.random((100, 100)) * 8).astype(np.uint8)
155p = graycomatrix(img, [1, 2, 4, 5], [0, 0.25, 1, 1.5], levels=8)
156np.testing.assert_(np.max(graycoprops(p, 'correlation')) < 1.0)
157
158def test_invalid_property(self):
159result = graycomatrix(self.image, [1], [0], 4)
160with pytest.raises(ValueError):
161graycoprops(result, 'ABC')
162
163def test_homogeneity(self):
164result = graycomatrix(self.image, [1], [0, 6], 4, normed=True, symmetric=True)
165homogeneity = graycoprops(result, 'homogeneity')[0, 0]
166np.testing.assert_almost_equal(homogeneity, 0.80833333)
167
168def test_energy(self):
169result = graycomatrix(self.image, [1], [0, 4], 4, normed=True, symmetric=True)
170energy = graycoprops(result, 'energy')[0, 0]
171np.testing.assert_almost_equal(energy, 0.38188131)
172
173def test_correlation(self):
174result = graycomatrix(self.image, [1, 2], [0], 4, normed=True, symmetric=True)
175energy = graycoprops(result, 'correlation')
176np.testing.assert_almost_equal(energy[0, 0], 0.71953255)
177np.testing.assert_almost_equal(energy[1, 0], 0.41176470)
178
179def test_mean(self):
180result = graycomatrix(
181self.image, [1], [0, np.pi / 2], 4, normed=True, symmetric=True
182)
183mean = graycoprops(result, 'mean')[0, 0]
184
185# Reference value was calculated by hand and is close to original source if precision 3 is used.
186np.testing.assert_almost_equal(mean, 1.29166667)
187
188def test_variance(self):
189result = graycomatrix(
190self.image, [1], [0, np.pi / 2], 4, normed=True, symmetric=True
191)
192variance = graycoprops(result, 'variance')[0, 0]
193
194# Reference value was calculated by hand and is close to original source if precision 3 is used.
195np.testing.assert_almost_equal(variance, 1.03993055)
196
197def test_std(self):
198result = graycomatrix(
199self.image, [1], [0, np.pi / 2], 4, normed=True, symmetric=True
200)
201std = graycoprops(result, 'std')[0, 0]
202
203# Reference value was calculated by hand and is close to original source if precision 3 is used.
204np.testing.assert_almost_equal(std, 1.01976985)
205
206def test_entropy(self):
207result = graycomatrix(
208self.image, [1], [0, np.pi / 2], 4, normed=True, symmetric=True
209)
210entropy = graycoprops(result, 'entropy')[0, 0]
211
212# Reference value was calculated by hand and is close to original source if precision 3 is used.
213np.testing.assert_almost_equal(entropy, 2.09472904)
214
215def test_uniform_properties(self):
216im = np.ones((4, 4), dtype=np.uint8)
217result = graycomatrix(
218im, [1, 2, 8], [0, np.pi / 2], 4, normed=True, symmetric=True
219)
220for prop in [
221'contrast',
222'dissimilarity',
223'homogeneity',
224'energy',
225'correlation',
226'ASM',
227'mean',
228'variance',
229'std',
230'entropy',
231]:
232graycoprops(result, prop)
233
234
235class TestLBP:
236def setup_method(self):
237self.image = np.array(
238[
239[255, 6, 255, 0, 141, 0],
240[48, 250, 204, 166, 223, 63],
241[8, 0, 159, 50, 255, 30],
242[167, 255, 63, 40, 128, 255],
243[0, 255, 30, 34, 255, 24],
244[146, 241, 255, 0, 189, 126],
245],
246dtype=np.uint8,
247)
248
249@run_in_parallel()
250def test_default(self):
251lbp = local_binary_pattern(self.image, 8, 1, 'default')
252ref = np.array(
253[
254[0, 251, 0, 255, 96, 255],
255[143, 0, 20, 153, 64, 56],
256[238, 255, 12, 191, 0, 252],
257[129, 64.0, 62, 159, 199, 0],
258[255, 4, 255, 175, 0, 254],
259[3, 5, 0, 255, 4, 24],
260]
261)
262np.testing.assert_array_equal(lbp, ref)
263
264def test_ror(self):
265lbp = local_binary_pattern(self.image, 8, 1, 'ror')
266ref = np.array(
267[
268[0, 127, 0, 255, 3, 255],
269[31, 0, 5, 51, 1, 7],
270[119, 255, 3, 127, 0, 63],
271[3, 1, 31, 63, 31, 0],
272[255, 1, 255, 95, 0, 127],
273[3, 5, 0, 255, 1, 3],
274]
275)
276np.testing.assert_array_equal(lbp, ref)
277
278@pytest.mark.parametrize('dtype', [np.float16, np.float32, np.float64])
279def test_float_warning(self, dtype):
280image = self.image.astype(dtype)
281msg = "Applying `local_binary_pattern` to floating-point images"
282with expected_warnings([msg]):
283lbp = local_binary_pattern(image, 8, 1, 'ror')
284ref = np.array(
285[
286[0, 127, 0, 255, 3, 255],
287[31, 0, 5, 51, 1, 7],
288[119, 255, 3, 127, 0, 63],
289[3, 1, 31, 63, 31, 0],
290[255, 1, 255, 95, 0, 127],
291[3, 5, 0, 255, 1, 3],
292]
293)
294np.testing.assert_array_equal(lbp, ref)
295
296def test_uniform(self):
297lbp = local_binary_pattern(self.image, 8, 1, 'uniform')
298ref = np.array(
299[
300[0, 7, 0, 8, 2, 8],
301[5, 0, 9, 9, 1, 3],
302[9, 8, 2, 7, 0, 6],
303[2, 1, 5, 6, 5, 0],
304[8, 1, 8, 9, 0, 7],
305[2, 9, 0, 8, 1, 2],
306]
307)
308np.testing.assert_array_equal(lbp, ref)
309
310def test_var(self):
311# Test idea: mean of variance is estimate of overall variance.
312
313# Fix random seed for test stability.
314np.random.seed(13141516)
315
316# Create random image with known variance.
317image = np.random.rand(500, 500)
318target_std = 0.3
319image = image / image.std() * target_std
320
321# Use P=4 to avoid interpolation effects
322P, R = 4, 1
323msg = "Applying `local_binary_pattern` to floating-point images"
324with expected_warnings([msg]):
325lbp = local_binary_pattern(image, P, R, 'var')
326
327# Take central part to avoid border effect.
328lbp = lbp[5:-5, 5:-5]
329
330# The LBP variance is biased (ddof=0), correct for that.
331expected = target_std**2 * (P - 1) / P
332
333np.testing.assert_almost_equal(lbp.mean(), expected, 4)
334
335def test_nri_uniform(self):
336lbp = local_binary_pattern(self.image, 8, 1, 'nri_uniform')
337ref = np.array(
338[
339[0, 54, 0, 57, 12, 57],
340[34, 0, 58, 58, 3, 22],
341[58, 57, 15, 50, 0, 47],
342[10, 3, 40, 42, 35, 0],
343[57, 7, 57, 58, 0, 56],
344[9, 58, 0, 57, 7, 14],
345]
346)
347np.testing.assert_array_almost_equal(lbp, ref)
348
349
350class TestMBLBP:
351def test_single_mblbp(self):
352# Create dummy matrix where first and fifth rectangles have greater
353# value than the central one. Therefore, the following bits
354# should be 1.
355test_img = np.zeros((9, 9), dtype='uint8')
356test_img[3:6, 3:6] = 1
357test_img[:3, :3] = 255
358test_img[6:, 6:] = 255
359
360# MB-LBP is filled in reverse order. So the first and fifth bits from
361# the end should be filled.
362correct_answer = 0b10001000
363
364int_img = integral_image(test_img)
365
366lbp_code = multiblock_lbp(int_img, 0, 0, 3, 3)
367
368np.testing.assert_equal(lbp_code, correct_answer)
369