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