From 3c82d9505f7b01c684f114b119df17825ea78737 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 29 May 2015 17:22:05 +0200 Subject: [PATCH] plain python multiblock lbp implemented with test coverage --- skimage/feature/__init__.py | 6 +- skimage/feature/tests/test_texture.py | 27 +++++- skimage/feature/texture.py | 129 ++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 3 deletions(-) diff --git a/skimage/feature/__init__.py b/skimage/feature/__init__.py index 567f561f..dc25f55d 100644 --- a/skimage/feature/__init__.py +++ b/skimage/feature/__init__.py @@ -1,7 +1,9 @@ from ._canny import canny from ._daisy import daisy from ._hog import hog -from .texture import greycomatrix, greycoprops, local_binary_pattern +from .texture import (greycomatrix, greycoprops, + local_binary_pattern, multiblock_local_binary_pattern, + visualize_multiblock_lbp) from .peak import peak_local_max from .corner import (corner_kitchen_rosenfeld, corner_harris, corner_shi_tomasi, corner_foerstner, corner_subpix, @@ -25,6 +27,8 @@ __all__ = ['canny', 'greycomatrix', 'greycoprops', 'local_binary_pattern', + 'multiblock_local_binary_pattern', + 'visualize_multiblock_lbp', 'peak_local_max', 'structure_tensor', 'structure_tensor_eigvals', diff --git a/skimage/feature/tests/test_texture.py b/skimage/feature/tests/test_texture.py index 10444aa8..8635c805 100644 --- a/skimage/feature/tests/test_texture.py +++ b/skimage/feature/tests/test_texture.py @@ -1,8 +1,15 @@ import numpy as np -from skimage.feature import greycomatrix, greycoprops, local_binary_pattern -from skimage._shared.testing import test_parallel +from skimage.feature import ( + greycomatrix, + greycoprops, + local_binary_pattern, + multiblock_local_binary_pattern + ) +from skimage._shared.testing import test_parallel +from skimage.transform import integral_image + class TestGLCM(): def setup(self): @@ -228,6 +235,22 @@ class TestLBP(): [ 9, 58, 0, 57, 7, 14]]) np.testing.assert_array_almost_equal(lbp, ref) + def test_multiblock_lbp(self): + + # Create dummy matrix where first and fifth + # rectangles have greater value than the central one + # Therefore, the following bits should be 1. + test_img = np.zeros((9, 9), dtype='uint8') + test_img[3:6, 3:6] = 1 + test_img[:3, :3] = 255 + test_img[6:, 6:] = 255 + + int_img = integral_image(test_img) + + lbp_code = multiblock_local_binary_pattern(int_img, 0, 0, 3, 3) + + np.testing.assert_equal(lbp_code, 17) + if __name__ == '__main__': np.testing.run_module_suite() diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index beb66ff9..60f185a9 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -6,6 +6,11 @@ import numpy as np from .._shared.utils import assert_nD from ._texture import _glcm_loop, _local_binary_pattern +# My imports below +from ..transform import integrate +import matplotlib.patches as patches +import matplotlib.pyplot as plt + def greycomatrix(image, distances, angles, levels=256, symmetric=False, normed=False): @@ -291,3 +296,127 @@ def local_binary_pattern(image, P, R, method='default'): image = np.ascontiguousarray(image, dtype=np.double) output = _local_binary_pattern(image, P, R, methods[method.lower()]) return output + +def multiblock_local_binary_pattern(int_image, x, y, width, height): + """Multi-block local binary pattern. + + MB-LBP is an extension of LBP that can be computed on many + scales in a constant time using integral image. It consists of + 9 equal-sized rectangles. Sum of pixels' intensity values + in each of them are compared to the central rectangle and + depending on comparison result, the feature descriptor is + computed. + + Parameters + ---------- + int_image : (N, M) array + Integral image. + x : int + X-coordinate of top left corner of a rectangle containing feature. + y : int + Y-coordinate of top left corner of a rectangle containing feature. + width : int + Width of one of 9 equal rectangles that will be used to compute + a feature. + height : int + Height of one of 9 equal rectangles that will be used to compute + a feature. + + Returns + ------- + output : int + 8bit MB-LBP feature descriptor. + + References + ---------- + .. [1] Face Detection Based on Multi-Block LBP + Representation. Lun Zhang, Rufeng Chu, Shiming Xiang, Shengcai Liao, + Stan Z. Li + http://www.cbsr.ia.ac.cn/users/scliao/papers/Zhang-ICB07-MBLBP.pdf + """ + + # Top-left coordinates of central rectangle + central_rect_x = x + width + central_rect_y = y + height + + # Sum of intensity values of central rectangle + central_rect_val = integrate(int_image, + central_rect_y, + central_rect_x, + central_rect_y + height - 1, + central_rect_x + width - 1) + + # Offsets of neighbour rectangles relative to central one. + # It has order starting from top left and going clockwise + neighbour_rect_offsets = ((-1, -1), (0, -1), (1, -1), + (1, 0), (1, 1), (0, 1), + (-1, 1), (-1, 0)) + + lbp_code = 0 + + for element_num, offset in enumerate(neighbour_rect_offsets): + + offset_x, offset_y = offset + + current_rect_x = central_rect_x + offset_x * width + current_rect_y = central_rect_y + offset_y * height + current_rect_val = integrate(int_image, + current_rect_y, + current_rect_x, + current_rect_y + height - 1, + current_rect_x + width - 1) + + has_greater_value = current_rect_val >= central_rect_val + + # If current rectangle's intensity value is bigger + # make corresponding bit to 1. + lbp_code |= has_greater_value << element_num + + print lbp_code + + return lbp_code + +def visualize_multiblock_lbp(img, x, y, width, height, lbp_code=0): + + plt.imshow(img) + img_desc = plt.gca() + plt.set_cmap('gray') + + # Offsets of neighbour rectangles relative to central one. + # It has order starting from top left and going clockwise + neighbour_rect_offsets = ((-1, -1), (0, -1), (1, -1), + (1, 0), (1, 1), (0, 1), + (-1, 1), (-1, 0)) + + # Top-left coordinates of central rectangle + central_rect_x = x + width + central_rect_y = y + height + + for element_num, offset in enumerate(neighbour_rect_offsets): + + offset_x, offset_y = offset + + current_rect_x = central_rect_x + offset_x * width + current_rect_y = central_rect_y + offset_y * height + + has_greater_value = lbp_code & (1 << element_num) + + # Hatch the rectangles that has less + # intensity than the central rectangle. + hatch = '\\' + + if has_greater_value: + hatch = '' + + img_desc.add_patch( + patches.Rectangle( + (current_rect_x, current_rect_y), + width, + height, + fill=False, + hatch=hatch, + color='w' + ) + ) + + plt.show()