From 3c82d9505f7b01c684f114b119df17825ea78737 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 29 May 2015 17:22:05 +0200 Subject: [PATCH 01/17] 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() From 086d8c667b02dea8c1238053d3b8d857c2123706 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 29 May 2015 23:03:12 +0200 Subject: [PATCH 02/17] imports now inside of the file --- skimage/feature/texture.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index 60f185a9..3d82bf44 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -6,10 +6,7 @@ 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, @@ -378,6 +375,9 @@ def multiblock_local_binary_pattern(int_image, x, y, width, height): def visualize_multiblock_lbp(img, x, y, width, height, lbp_code=0): + import matplotlib.patches as patches + import matplotlib.pyplot as plt + plt.imshow(img) img_desc = plt.gca() plt.set_cmap('gray') From 83c3bd10c88d39db8fb448777eab29a233b01e9d Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 8 Jun 2015 09:16:22 +0200 Subject: [PATCH 03/17] Cython implementation of MB-LBP. Updated MB-LBP visualization without matplotlib.Examples to gallery were added. Tests are made more easily readable. --- .../plot_multiblock_local_binary_pattern.py | 87 +++++++++ skimage/feature/__init__.py | 4 +- skimage/feature/_texture.pyx | 171 ++++++++++++++++++ skimage/feature/tests/test_texture.py | 17 +- skimage/feature/texture.py | 114 ++++-------- 5 files changed, 315 insertions(+), 78 deletions(-) create mode 100644 doc/examples/plot_multiblock_local_binary_pattern.py diff --git a/doc/examples/plot_multiblock_local_binary_pattern.py b/doc/examples/plot_multiblock_local_binary_pattern.py new file mode 100644 index 00000000..a05056b4 --- /dev/null +++ b/doc/examples/plot_multiblock_local_binary_pattern.py @@ -0,0 +1,87 @@ +""" +=========================================================== +Multi-Block Local Binary Pattern for texture classification +=========================================================== + +In this example, we will see how to compute the multi-block +local binary pattern at a specified image and how to visualize it. + +The features are calculated in a way similar to local binary +patterns, except that block summed up pixel values +rather than pixel values are used. + +`MB-LBP` is an extension of LBP that can be computed on any +scales in a constant time using integral image. It consists of +`9` equal-sized rectangles. They are used to compute a feature. +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. + +We will start with a simple image that we will generate by our +own to show how the `MB-LBP` works. We will create a `(9, 9)` +rectangle with and divide it into `9` blocks. After this +we will apply `MB-LBP` on it. + + +""" +from __future__ import print_function +from skimage.feature import multiblock_local_binary_pattern +import numpy as np +from skimage.util import img_as_float +from skimage.transform import integral_image + +# 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] = 50 +test_img[6:, 6:] = 50 + +# MB-LBP is filled in reverse order. +# So the first and fifth bits from the end should +# be filled. +correct_answer = 0b10001000 + +# The function accepts the float images. +# Also it has to be C-contiguous. +test_img = img_as_float(test_img) +int_img = integral_image(test_img) + +lbp_code = multiblock_local_binary_pattern(int_img, 0, 0, 3, 3) + +print(lbp_code == correct_answer) + +""" +Now let's apply the operator to a real image and see how the visualization works. +""" + +from __future__ import print_function +from skimage.feature import (multiblock_local_binary_pattern, + visualize_multiblock_lbp) +from skimage.util import img_as_float +from skimage.transform import integral_image +from skimage import data +from matplotlib import pyplot as plt + +test_img = data.coins() + +test_img = img_as_float(test_img) +int_img = integral_image(test_img) + +lbp_code = multiblock_local_binary_pattern(int_img, 0, 0, 90, 90) + +img = visualize_multiblock_lbp(test_img, 0, 0, 90, 90, + lbp_code=lbp_code) + + +plt.imshow(img, interpolation='nearest') + +""" +.. image:: PLOT2RST.current_figure + +On the above plot we see the result of computing a `MB-LBP` and visualization +of the computed feature. The rectangles that have less intensity than the central +rectangle are marked with cyan color. The ones that have bigger intensity values +are marked with white color. The central rectangle is left untouched. +""" diff --git a/skimage/feature/__init__.py b/skimage/feature/__init__.py index dc25f55d..7cac9954 100644 --- a/skimage/feature/__init__.py +++ b/skimage/feature/__init__.py @@ -2,8 +2,10 @@ from ._canny import canny from ._daisy import daisy from ._hog import hog from .texture import (greycomatrix, greycoprops, - local_binary_pattern, multiblock_local_binary_pattern, + local_binary_pattern, visualize_multiblock_lbp) + +from ._texture import multiblock_local_binary_pattern from .peak import peak_local_max from .corner import (corner_kitchen_rosenfeld, corner_harris, corner_shi_tomasi, corner_foerstner, corner_subpix, diff --git a/skimage/feature/_texture.pyx b/skimage/feature/_texture.pyx index d716e690..7add3fe0 100644 --- a/skimage/feature/_texture.pyx +++ b/skimage/feature/_texture.pyx @@ -264,3 +264,174 @@ def _local_binary_pattern(double[:, ::1] image, output[r, c] = lbp return np.asarray(output) + + +cdef inline Py_ssize_t _clip(Py_ssize_t x, Py_ssize_t low, + Py_ssize_t high) nogil: + """Clips coordinate between high and low. + + Parameters + ---------- + x : int + Coordinate to be clipped. + low : int + The lower bound. + high : int + The higher bound. + + Returns + ------- + x : int + `x` clipped between `high` and `low`. + """ + + if(x > high): + return high + if(x < low): + return low + return x + + +cdef inline cnp.double_t _integ( + cnp.double_t[:, ::1] img, Py_ssize_t r0, Py_ssize_t c0, + Py_ssize_t r1, Py_ssize_t c1) nogil: + """Integrate over the integral image in the given window + + This method was created so that `multiblock_local_binary_pattern` + does not have to make a Python call. + + Parameters + ---------- + img : array + The integral image over which to integrate. + r0 : int + The row number of the top left corner. + c0 : int + The column number of the top left corner. + r1 : int + The row number of the bottom right corner. + c1 : int + The column number of the bottom right corner. + + Returns + ------- + ans : double + The integral over the given window. + """ + + r = _clip(r0, 0, img.shape[0] - 1) + c = _clip(c0, 0, img.shape[1] - 1) + + r2 = _clip(r1, 0, img.shape[0] - 1) + c2 = _clip(c1, 0, img.shape[1] - 1) + + cdef cnp.double_t ans = img[r1, c1] + + if (r0 >= 1) and (c0 >= 1): + ans += img[r0 - 1, c0 - 1] + + if (r0 >= 1): + ans -= img[r0 - 1, c1] + + if (c0 >= 1): + ans -= img[r1, c0 - 1] + + return ans + + +def multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, + Py_ssize_t x, + Py_ssize_t y, + Py_ssize_t width, + Py_ssize_t height): + """Multi-block local binary pattern. + + The features are calculated in a way similar to local binary + patterns, except that block summed up pixel values + rather than pixel values are used. + + MB-LBP is an extension of LBP that can be computed on any + scales in a constant time using integral image. It consists of + 9 equal-sized rectangles. They are used to compute a feature. + 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) double 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 + cdef: + Py_ssize_t central_rect_x = x + width + Py_ssize_t central_rect_y = y + height + + # Sum of intensity values of central rectangle + cdef double central_rect_val = _integ(int_image, + central_rect_y, + central_rect_x, + central_rect_y + height - 1, + central_rect_x + width - 1) + + #print central_rect_x, central_rect_y + + # Offsets of neighbour rectangles relative to central one. + # It has order starting from top left and going clockwise + cdef: + Py_ssize_t *x_offsets = [-1, 0, 1, 1, 1, 0, -1, -1] + Py_ssize_t *y_offsets = [-1, -1, -1, 0, 1, 1, 1, 0] + Py_ssize_t element_num, offset_x, offset_y + Py_ssize_t current_rect_x, current_rect_y + double current_rect_val + int has_greater_value + int lbp_code = 0 + + for element_num in range(8): + + offset_x = x_offsets[element_num] + offset_y = y_offsets[element_num] + + + current_rect_x = central_rect_x + offset_x * width + current_rect_y = central_rect_y + offset_y * height + + + current_rect_val = _integ(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 << (7 - element_num) + + return lbp_code + diff --git a/skimage/feature/tests/test_texture.py b/skimage/feature/tests/test_texture.py index 8635c805..c9e2c7d5 100644 --- a/skimage/feature/tests/test_texture.py +++ b/skimage/feature/tests/test_texture.py @@ -6,9 +6,9 @@ from skimage.feature import ( multiblock_local_binary_pattern ) - from skimage._shared.testing import test_parallel from skimage.transform import integral_image +from skimage.util import img_as_float class TestGLCM(): @@ -235,7 +235,10 @@ class TestLBP(): [ 9, 58, 0, 57, 7, 14]]) np.testing.assert_array_almost_equal(lbp, ref) - def test_multiblock_lbp(self): + +class TestMBLBP(): + + def test_single_mblbp(self): # Create dummy matrix where first and fifth # rectangles have greater value than the central one @@ -245,11 +248,19 @@ class TestLBP(): test_img[:3, :3] = 255 test_img[6:, 6:] = 255 + # MB-LBP is filled in reverse order. + # So the first and fifth bits from the end should + # be filled. + correct_answer = 0b10001000 + + # The function accepts the float images. + # Also it has to be C-contiguous. + test_img = img_as_float(test_img) 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) + np.testing.assert_equal(lbp_code, correct_answer) if __name__ == '__main__': diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index 3d82bf44..7d827666 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -4,10 +4,9 @@ Methods to characterize image textures. import numpy as np from .._shared.utils import assert_nD +from ..util import img_as_float from ._texture import _glcm_loop, _local_binary_pattern -from ..transform import integrate - def greycomatrix(image, distances, angles, levels=256, symmetric=False, normed=False): @@ -294,8 +293,9 @@ def local_binary_pattern(image, P, R, method='default'): 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. + +def visualize_multiblock_lbp(img, x, y, width, height, lbp_code=0): + """Multi-block local binary pattern visualization. MB-LBP is an extension of LBP that can be computed on many scales in a constant time using integral image. It consists of @@ -304,10 +304,15 @@ def multiblock_local_binary_pattern(int_image, x, y, width, height): depending on comparison result, the feature descriptor is computed. + The blocks visualized in the following manner: the center block + is left untouched. The blocks that have higher are covered with + transparent white rectangles. The blocks that have less intensity + are covered with cyan rectangles. + Parameters ---------- - int_image : (N, M) array - Integral image. + img : + Image on which to visualize the pattern. x : int X-coordinate of top left corner of a rectangle containing feature. y : int @@ -318,11 +323,14 @@ def multiblock_local_binary_pattern(int_image, x, y, width, height): height : int Height of one of 9 equal rectangles that will be used to compute a feature. + lbp_code : int + The descriptor of feature to visualize. If not provided, + the descriptor with 0 value will be used. Returns ------- - output : int - 8bit MB-LBP feature descriptor. + output : + Float image with visualization. References ---------- @@ -332,55 +340,21 @@ def multiblock_local_binary_pattern(int_image, x, y, width, height): 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 + # Default colors for regions. + # White is for the blocks that are brighter. + # Cyan is for the blocks that has less intensity. + color_greater_block = np.asarray([1, 1, 1], dtype='float64') + color_less_block = np.asarray([0, 0.69, 0.96], dtype='float64') - # 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) + # Copy array to avoid the changes to the original one + output = np.copy(img) - # 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)) + # As the visualization uses RGB color we need 3 bands. + if len(img.shape) < 3: + output = np.dstack((img,) * 3) - 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): - - import matplotlib.patches as patches - import matplotlib.pyplot as plt - - plt.imshow(img) - img_desc = plt.gca() - plt.set_cmap('gray') + # Colors are specified in floats + output = img_as_float(output) # Offsets of neighbour rectangles relative to central one. # It has order starting from top left and going clockwise @@ -396,27 +370,19 @@ def visualize_multiblock_lbp(img, x, y, width, height, lbp_code=0): offset_x, offset_y = offset - current_rect_x = central_rect_x + offset_x * width - current_rect_y = central_rect_y + offset_y * height + curr_x = central_rect_x + offset_x * width + curr_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 = '\\' + has_greater_value = lbp_code & (1 << (7-element_num)) + # Mix-in the visualization colors if has_greater_value: - hatch = '' + output[curr_y:curr_y+height, curr_x:curr_x+width] = \ + 0.5 * output[curr_y:curr_y+height, curr_x:curr_x+width] \ + + 0.5 * color_greater_block + else: + output[curr_y:curr_y+height, curr_x:curr_x+width] = \ + 0.5 * output[curr_y:curr_y+height, curr_x:curr_x+width] \ + + 0.5 * color_less_block - img_desc.add_patch( - patches.Rectangle( - (current_rect_x, current_rect_y), - width, - height, - fill=False, - hatch=hatch, - color='w' - ) - ) - - plt.show() + return output From d39434c30e015bb4fd5839d6214cd5782ea5a2f1 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 9 Jun 2015 14:18:58 +0200 Subject: [PATCH 04/17] MBLBP optimized by taking out constants definition out of function. Visualization function of MBLBP now supports colors and opacity. Examples are updated. --- .../plot_multiblock_local_binary_pattern.py | 16 +++-- skimage/feature/__init__.py | 4 +- skimage/feature/_texture.pyx | 60 +++++-------------- skimage/feature/texture.py | 50 +++++++++++----- 4 files changed, 60 insertions(+), 70 deletions(-) diff --git a/doc/examples/plot_multiblock_local_binary_pattern.py b/doc/examples/plot_multiblock_local_binary_pattern.py index a05056b4..8c64554b 100644 --- a/doc/examples/plot_multiblock_local_binary_pattern.py +++ b/doc/examples/plot_multiblock_local_binary_pattern.py @@ -7,18 +7,18 @@ In this example, we will see how to compute the multi-block local binary pattern at a specified image and how to visualize it. The features are calculated in a way similar to local binary -patterns, except that block summed up pixel values +patterns, except that summed up pixel values rather than pixel values are used. `MB-LBP` is an extension of LBP that can be computed on any -scales in a constant time using integral image. It consists of +scale in a constant time using integral image. It consists of `9` equal-sized rectangles. They are used to compute a feature. 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. -We will start with a simple image that we will generate by our -own to show how the `MB-LBP` works. We will create a `(9, 9)` +We will start with a simple image that we will generate +to show how the `MB-LBP` works. We will create a `(9, 9)` rectangle with and divide it into `9` blocks. After this we will apply `MB-LBP` on it. @@ -55,10 +55,8 @@ print(lbp_code == correct_answer) """ Now let's apply the operator to a real image and see how the visualization works. """ - -from __future__ import print_function from skimage.feature import (multiblock_local_binary_pattern, - visualize_multiblock_lbp) + draw_multiblock_lbp) from skimage.util import img_as_float from skimage.transform import integral_image from skimage import data @@ -71,8 +69,8 @@ int_img = integral_image(test_img) lbp_code = multiblock_local_binary_pattern(int_img, 0, 0, 90, 90) -img = visualize_multiblock_lbp(test_img, 0, 0, 90, 90, - lbp_code=lbp_code) +img = draw_multiblock_lbp(test_img, 0, 0, 90, 90, + lbp_code=lbp_code, alpha=0.5) plt.imshow(img, interpolation='nearest') diff --git a/skimage/feature/__init__.py b/skimage/feature/__init__.py index 7cac9954..37954c92 100644 --- a/skimage/feature/__init__.py +++ b/skimage/feature/__init__.py @@ -3,7 +3,7 @@ from ._daisy import daisy from ._hog import hog from .texture import (greycomatrix, greycoprops, local_binary_pattern, - visualize_multiblock_lbp) + draw_multiblock_lbp) from ._texture import multiblock_local_binary_pattern from .peak import peak_local_max @@ -30,7 +30,7 @@ __all__ = ['canny', 'greycoprops', 'local_binary_pattern', 'multiblock_local_binary_pattern', - 'visualize_multiblock_lbp', + 'draw_multiblock_lbp', 'peak_local_max', 'structure_tensor', 'structure_tensor_eigvals', diff --git a/skimage/feature/_texture.pyx b/skimage/feature/_texture.pyx index 7add3fe0..8597cdee 100644 --- a/skimage/feature/_texture.pyx +++ b/skimage/feature/_texture.pyx @@ -266,35 +266,9 @@ def _local_binary_pattern(double[:, ::1] image, return np.asarray(output) -cdef inline Py_ssize_t _clip(Py_ssize_t x, Py_ssize_t low, - Py_ssize_t high) nogil: - """Clips coordinate between high and low. - - Parameters - ---------- - x : int - Coordinate to be clipped. - low : int - The lower bound. - high : int - The higher bound. - - Returns - ------- - x : int - `x` clipped between `high` and `low`. - """ - - if(x > high): - return high - if(x < low): - return low - return x - - -cdef inline cnp.double_t _integ( - cnp.double_t[:, ::1] img, Py_ssize_t r0, Py_ssize_t c0, - Py_ssize_t r1, Py_ssize_t c1) nogil: +cdef inline cnp.double_t _integ(cnp.double_t[:, ::1] img, + Py_ssize_t r0, Py_ssize_t c0, + Py_ssize_t r1, Py_ssize_t c1) nogil: """Integrate over the integral image in the given window This method was created so that `multiblock_local_binary_pattern` @@ -319,12 +293,6 @@ cdef inline cnp.double_t _integ( The integral over the given window. """ - r = _clip(r0, 0, img.shape[0] - 1) - c = _clip(c0, 0, img.shape[1] - 1) - - r2 = _clip(r1, 0, img.shape[0] - 1) - c2 = _clip(c1, 0, img.shape[1] - 1) - cdef cnp.double_t ans = img[r1, c1] if (r0 >= 1) and (c0 >= 1): @@ -339,6 +307,14 @@ cdef inline cnp.double_t _integ( return ans +# Constant values that are used by `multiblock_local_binary_pattern` function. +# These values are taken out for performance improvement. +# Values represent offsets of neighbour rectangles relative to central one. +# It has order starting from top left and going clockwise. +cdef: + Py_ssize_t[::1] mlbp_x_offsets = np.asarray([-1, 0, 1, 1, 1, 0, -1, -1]) + Py_ssize_t[::1] mlbp_y_offsets = np.asarray([-1, -1, -1, 0, 1, 1, 1, 0]) + def multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, Py_ssize_t x, Py_ssize_t y, @@ -347,11 +323,11 @@ def multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, """Multi-block local binary pattern. The features are calculated in a way similar to local binary - patterns, except that block summed up pixel values + patterns, except that summed up pixel values rather than pixel values are used. MB-LBP is an extension of LBP that can be computed on any - scales in a constant time using integral image. It consists of + scale in a constant time using integral image. It consists of 9 equal-sized rectangles. They are used to compute a feature. Sum of pixels' intensity values in each of them are compared to the central rectangle and depending on comparison result, @@ -397,13 +373,7 @@ def multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, central_rect_y + height - 1, central_rect_x + width - 1) - #print central_rect_x, central_rect_y - - # Offsets of neighbour rectangles relative to central one. - # It has order starting from top left and going clockwise cdef: - Py_ssize_t *x_offsets = [-1, 0, 1, 1, 1, 0, -1, -1] - Py_ssize_t *y_offsets = [-1, -1, -1, 0, 1, 1, 1, 0] Py_ssize_t element_num, offset_x, offset_y Py_ssize_t current_rect_x, current_rect_y double current_rect_val @@ -412,8 +382,8 @@ def multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, for element_num in range(8): - offset_x = x_offsets[element_num] - offset_y = y_offsets[element_num] + offset_x = mlbp_x_offsets[element_num] + offset_y = mlbp_y_offsets[element_num] current_rect_x = central_rect_x + offset_x * width diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index 7d827666..670c7ccb 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -294,7 +294,16 @@ def local_binary_pattern(image, P, R, method='default'): return output -def visualize_multiblock_lbp(img, x, y, width, height, lbp_code=0): +def draw_multiblock_lbp(img, + x, + y, + width, + height, + lbp_code=0, + color_greater_block=[1, 1, 1], + color_less_block=[0, 0.69, 0.96], + alpha=0.5 + ): """Multi-block local binary pattern visualization. MB-LBP is an extension of LBP that can be computed on many @@ -326,11 +335,24 @@ def visualize_multiblock_lbp(img, x, y, width, height, lbp_code=0): lbp_code : int The descriptor of feature to visualize. If not provided, the descriptor with 0 value will be used. + color_greater_block : list of 3 floats + Floats specifying the color for the block that + has greater intensity value. They should be + in the range [0, 1]. Corresponding values define + (R, G, B) values. Default value is white [1, 1, 1]. + color_greater_block : list of 3 floats + Floats specifying the color for the block that + has greater intensity value. They should be + in the range [0, 1]. Corresponding values define + (R, G, B) values. Default value is cyan [0, 0.69, 0.96]. + alpha : float + Value in the range [0, 1] that specifies opacity of + visualization. 1 - fully transparent, 0 - opaque. Returns ------- - output : - Float image with visualization. + output : ndarray of float + Image with visualization. References ---------- @@ -343,26 +365,26 @@ def visualize_multiblock_lbp(img, x, y, width, height, lbp_code=0): # Default colors for regions. # White is for the blocks that are brighter. # Cyan is for the blocks that has less intensity. - color_greater_block = np.asarray([1, 1, 1], dtype='float64') - color_less_block = np.asarray([0, 0.69, 0.96], dtype='float64') + color_greater_block = np.asarray(color_greater_block, dtype='float64') + color_less_block = np.asarray(color_less_block, dtype='float64') - # Copy array to avoid the changes to the original one + # Copy array to avoid the changes to the original one. output = np.copy(img) # As the visualization uses RGB color we need 3 bands. if len(img.shape) < 3: output = np.dstack((img,) * 3) - # Colors are specified in floats + # Colors are specified in floats. output = img_as_float(output) # Offsets of neighbour rectangles relative to central one. - # It has order starting from top left and going clockwise + # 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 + # Top-left coordinates of central rectangle. central_rect_x = x + width central_rect_y = y + height @@ -375,14 +397,14 @@ def visualize_multiblock_lbp(img, x, y, width, height, lbp_code=0): has_greater_value = lbp_code & (1 << (7-element_num)) - # Mix-in the visualization colors + # Mix-in the visualization colors. if has_greater_value: output[curr_y:curr_y+height, curr_x:curr_x+width] = \ - 0.5 * output[curr_y:curr_y+height, curr_x:curr_x+width] \ - + 0.5 * color_greater_block + (1-alpha) * output[curr_y:curr_y+height, curr_x:curr_x+width] \ + + alpha * color_greater_block else: output[curr_y:curr_y+height, curr_x:curr_x+width] = \ - 0.5 * output[curr_y:curr_y+height, curr_x:curr_x+width] \ - + 0.5 * color_less_block + (1-alpha) * output[curr_y:curr_y+height, curr_x:curr_x+width] \ + + alpha * color_less_block return output From fb6ef72a3195cfdba219ef7b1acb551df2a8181d Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 9 Jun 2015 14:56:29 +0200 Subject: [PATCH 05/17] public MBLBP function created for users to safely use it. --- .../plot_multiblock_local_binary_pattern.py | 4 +- skimage/feature/__init__.py | 2 +- skimage/feature/_texture.pyx | 10 +-- skimage/feature/texture.py | 65 ++++++++++++++++--- 4 files changed, 64 insertions(+), 17 deletions(-) diff --git a/doc/examples/plot_multiblock_local_binary_pattern.py b/doc/examples/plot_multiblock_local_binary_pattern.py index 8c64554b..e1ec7abb 100644 --- a/doc/examples/plot_multiblock_local_binary_pattern.py +++ b/doc/examples/plot_multiblock_local_binary_pattern.py @@ -55,12 +55,12 @@ print(lbp_code == correct_answer) """ Now let's apply the operator to a real image and see how the visualization works. """ -from skimage.feature import (multiblock_local_binary_pattern, - draw_multiblock_lbp) from skimage.util import img_as_float from skimage.transform import integral_image from skimage import data from matplotlib import pyplot as plt +from skimage.feature import (multiblock_local_binary_pattern, + draw_multiblock_lbp) test_img = data.coins() diff --git a/skimage/feature/__init__.py b/skimage/feature/__init__.py index 37954c92..11f5cd8c 100644 --- a/skimage/feature/__init__.py +++ b/skimage/feature/__init__.py @@ -5,7 +5,7 @@ from .texture import (greycomatrix, greycoprops, local_binary_pattern, draw_multiblock_lbp) -from ._texture import multiblock_local_binary_pattern +from .texture import multiblock_local_binary_pattern from .peak import peak_local_max from .corner import (corner_kitchen_rosenfeld, corner_harris, corner_shi_tomasi, corner_foerstner, corner_subpix, diff --git a/skimage/feature/_texture.pyx b/skimage/feature/_texture.pyx index 8597cdee..f71d08c1 100644 --- a/skimage/feature/_texture.pyx +++ b/skimage/feature/_texture.pyx @@ -315,11 +315,11 @@ cdef: Py_ssize_t[::1] mlbp_x_offsets = np.asarray([-1, 0, 1, 1, 1, 0, -1, -1]) Py_ssize_t[::1] mlbp_y_offsets = np.asarray([-1, -1, -1, 0, 1, 1, 1, 0]) -def multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, - Py_ssize_t x, - Py_ssize_t y, - Py_ssize_t width, - Py_ssize_t height): +def _multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, + Py_ssize_t x, + Py_ssize_t y, + Py_ssize_t width, + Py_ssize_t height): """Multi-block local binary pattern. The features are calculated in a way similar to local binary diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index 670c7ccb..1a9f6901 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -5,7 +5,9 @@ Methods to characterize image textures. import numpy as np from .._shared.utils import assert_nD from ..util import img_as_float -from ._texture import _glcm_loop, _local_binary_pattern +from ._texture import (_glcm_loop, + _local_binary_pattern, + _multiblock_local_binary_pattern) def greycomatrix(image, distances, angles, levels=256, symmetric=False, @@ -294,6 +296,57 @@ def local_binary_pattern(image, P, R, method='default'): return output +def multiblock_local_binary_pattern(int_image, + x, + y, + width, + height): + """Multi-block local binary pattern. + + The features are calculated in a way similar to local binary + patterns, except that summed up pixel values + rather than pixel values are used. + + MB-LBP is an extension of LBP that can be computed on any + scale in a constant time using integral image. It consists of + 9 equal-sized rectangles. They are used to compute a feature. + 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 + """ + + int_image = np.ascontiguousarray(int_image, dtype=np.double) + lbp_code = _multiblock_local_binary_pattern(int_image, x, y, width, height) + return lbp_code + + def draw_multiblock_lbp(img, x, y, @@ -306,17 +359,11 @@ def draw_multiblock_lbp(img, ): """Multi-block local binary pattern visualization. - 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. - The blocks visualized in the following manner: the center block is left untouched. The blocks that have higher are covered with transparent white rectangles. The blocks that have less intensity - are covered with cyan rectangles. + are covered with cyan rectangles. The colors can also be specified. + Opacity of visualization is controlled with `alpha` argument. Parameters ---------- From c4f8e0126b13cc241347ca19173b3fec85f0775c Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 9 Jun 2015 17:04:11 +0200 Subject: [PATCH 06/17] MBLBP is cdef function now. Corrected the example repeated imports. --- .../plot_multiblock_local_binary_pattern.py | 5 +- skimage/feature/__init__.py | 2 +- skimage/feature/_texture.pyx | 66 +++++++++++++++---- skimage/feature/texture.py | 55 +--------------- 4 files changed, 57 insertions(+), 71 deletions(-) diff --git a/doc/examples/plot_multiblock_local_binary_pattern.py b/doc/examples/plot_multiblock_local_binary_pattern.py index e1ec7abb..bb354b94 100644 --- a/doc/examples/plot_multiblock_local_binary_pattern.py +++ b/doc/examples/plot_multiblock_local_binary_pattern.py @@ -55,12 +55,9 @@ print(lbp_code == correct_answer) """ Now let's apply the operator to a real image and see how the visualization works. """ -from skimage.util import img_as_float -from skimage.transform import integral_image from skimage import data from matplotlib import pyplot as plt -from skimage.feature import (multiblock_local_binary_pattern, - draw_multiblock_lbp) +from skimage.feature import draw_multiblock_lbp test_img = data.coins() diff --git a/skimage/feature/__init__.py b/skimage/feature/__init__.py index 11f5cd8c..37954c92 100644 --- a/skimage/feature/__init__.py +++ b/skimage/feature/__init__.py @@ -5,7 +5,7 @@ from .texture import (greycomatrix, greycoprops, local_binary_pattern, draw_multiblock_lbp) -from .texture import multiblock_local_binary_pattern +from ._texture import multiblock_local_binary_pattern from .peak import peak_local_max from .corner import (corner_kitchen_rosenfeld, corner_harris, corner_shi_tomasi, corner_foerstner, corner_subpix, diff --git a/skimage/feature/_texture.pyx b/skimage/feature/_texture.pyx index f71d08c1..b1b90e8a 100644 --- a/skimage/feature/_texture.pyx +++ b/skimage/feature/_texture.pyx @@ -308,30 +308,20 @@ cdef inline cnp.double_t _integ(cnp.double_t[:, ::1] img, # Constant values that are used by `multiblock_local_binary_pattern` function. -# These values are taken out for performance improvement. # Values represent offsets of neighbour rectangles relative to central one. # It has order starting from top left and going clockwise. cdef: Py_ssize_t[::1] mlbp_x_offsets = np.asarray([-1, 0, 1, 1, 1, 0, -1, -1]) Py_ssize_t[::1] mlbp_y_offsets = np.asarray([-1, -1, -1, 0, 1, 1, 1, 0]) -def _multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, +cdef _multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, Py_ssize_t x, Py_ssize_t y, Py_ssize_t width, Py_ssize_t height): """Multi-block local binary pattern. - The features are calculated in a way similar to local binary - patterns, except that summed up pixel values - rather than pixel values are used. - - MB-LBP is an extension of LBP that can be computed on any - scale in a constant time using integral image. It consists of - 9 equal-sized rectangles. They are used to compute a feature. - 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. + Effcient implementation in Cython. Parameters ---------- @@ -405,3 +395,55 @@ def _multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, return lbp_code + +def multiblock_local_binary_pattern(int_image, + x, + y, + width, + height): + """Multi-block local binary pattern. + + The features are calculated in a way similar to local binary + patterns, except that summed up pixel values + rather than pixel values are used. + + MB-LBP is an extension of LBP that can be computed on any + scale in a constant time using integral image. It consists of + 9 equal-sized rectangles. They are used to compute a feature. + 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 + """ + + int_image = np.ascontiguousarray(int_image, dtype=np.double) + lbp_code = _multiblock_local_binary_pattern(int_image, x, y, width, height) + return lbp_code + + diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index 1a9f6901..dbadac94 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -5,9 +5,7 @@ Methods to characterize image textures. import numpy as np from .._shared.utils import assert_nD from ..util import img_as_float -from ._texture import (_glcm_loop, - _local_binary_pattern, - _multiblock_local_binary_pattern) +from ._texture import _glcm_loop, _local_binary_pattern def greycomatrix(image, distances, angles, levels=256, symmetric=False, @@ -296,57 +294,6 @@ def local_binary_pattern(image, P, R, method='default'): return output -def multiblock_local_binary_pattern(int_image, - x, - y, - width, - height): - """Multi-block local binary pattern. - - The features are calculated in a way similar to local binary - patterns, except that summed up pixel values - rather than pixel values are used. - - MB-LBP is an extension of LBP that can be computed on any - scale in a constant time using integral image. It consists of - 9 equal-sized rectangles. They are used to compute a feature. - 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 - """ - - int_image = np.ascontiguousarray(int_image, dtype=np.double) - lbp_code = _multiblock_local_binary_pattern(int_image, x, y, width, height) - return lbp_code - - def draw_multiblock_lbp(img, x, y, From 61442e040e57188ac2a384a67a94984b8c70a908 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 10 Jun 2015 11:38:20 +0200 Subject: [PATCH 07/17] Switched to using integrate Cython function from skimage._shared. All changes were made to make it fit. --- .../plot_multiblock_local_binary_pattern.py | 48 ++++---- skimage/feature/_texture.pyx | 103 +++++------------- skimage/feature/tests/test_texture.py | 4 - skimage/feature/texture.py | 14 +-- 4 files changed, 56 insertions(+), 113 deletions(-) diff --git a/doc/examples/plot_multiblock_local_binary_pattern.py b/doc/examples/plot_multiblock_local_binary_pattern.py index bb354b94..17102fe3 100644 --- a/doc/examples/plot_multiblock_local_binary_pattern.py +++ b/doc/examples/plot_multiblock_local_binary_pattern.py @@ -3,36 +3,33 @@ Multi-Block Local Binary Pattern for texture classification =========================================================== -In this example, we will see how to compute the multi-block -local binary pattern at a specified image and how to visualize it. +This example shows how to compute multi-block local binary +pattern (MB-LBP) features as well as how to visualize them. -The features are calculated in a way similar to local binary -patterns, except that summed up pixel values -rather than pixel values are used. +The features are calculated similarly to local binary patterns (LBPs), +except that summed blocks are used instead of individual pixel values. -`MB-LBP` is an extension of LBP that can be computed on any -scale in a constant time using integral image. It consists of -`9` equal-sized rectangles. They are used to compute a feature. -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. +MB-LBP is an extension of LBP that can be computed on multiple scales +in constant time using the integral image. +9 equally-sized rectangles are used to compute a feature. +For each rectangle, the sum of the pixel intensities is computed. +Comparisons of these sums to that of the central rectangle determine +the feature, similarly to LBP (See `LBP `_). -We will start with a simple image that we will generate -to show how the `MB-LBP` works. We will create a `(9, 9)` -rectangle with and divide it into `9` blocks. After this -we will apply `MB-LBP` on it. +First, we generate an image to illustrate the functioning of MB-LBP: +we take a (9, 9) rectangle and divide it into (3, 3) block, +upon which we then apply MB-LBP. """ from __future__ import print_function from skimage.feature import multiblock_local_binary_pattern import numpy as np -from skimage.util import img_as_float from skimage.transform import integral_image -# Create dummy matrix where first and fifth -# rectangles have greater value than the central one -# Therefore, the following bits should be 1. +# Create test matrix where first and fifth +# rectangles starting from top left clockwise +# have greater value than the central one. test_img = np.zeros((9, 9), dtype='uint8') test_img[3:6, 3:6] = 1 test_img[:3, :3] = 50 @@ -43,14 +40,12 @@ test_img[6:, 6:] = 50 # be filled. correct_answer = 0b10001000 -# The function accepts the float images. -# Also it has to be C-contiguous. -test_img = img_as_float(test_img) int_img = integral_image(test_img) lbp_code = multiblock_local_binary_pattern(int_img, 0, 0, 3, 3) -print(lbp_code == correct_answer) +print(correct_answer) +print(lbp_code) """ Now let's apply the operator to a real image and see how the visualization works. @@ -61,7 +56,6 @@ from skimage.feature import draw_multiblock_lbp test_img = data.coins() -test_img = img_as_float(test_img) int_img = integral_image(test_img) lbp_code = multiblock_local_binary_pattern(int_img, 0, 0, 90, 90) @@ -75,8 +69,8 @@ plt.imshow(img, interpolation='nearest') """ .. image:: PLOT2RST.current_figure -On the above plot we see the result of computing a `MB-LBP` and visualization +On the above plot we see the result of computing a MB-LBP and visualization of the computed feature. The rectangles that have less intensity than the central -rectangle are marked with cyan color. The ones that have bigger intensity values -are marked with white color. The central rectangle is left untouched. +rectangle are marked in cyan. The ones that have bigger intensity values +are marked in white. The central rectangle is left untouched. """ diff --git a/skimage/feature/_texture.pyx b/skimage/feature/_texture.pyx index b1b90e8a..f41e8363 100644 --- a/skimage/feature/_texture.pyx +++ b/skimage/feature/_texture.pyx @@ -6,6 +6,7 @@ import numpy as np cimport numpy as cnp from libc.math cimport sin, cos, abs from .._shared.interpolation cimport bilinear_interpolation, round +from .._shared.transform cimport integrate cdef extern from "numpy/npy_math.h": @@ -266,47 +267,6 @@ def _local_binary_pattern(double[:, ::1] image, return np.asarray(output) -cdef inline cnp.double_t _integ(cnp.double_t[:, ::1] img, - Py_ssize_t r0, Py_ssize_t c0, - Py_ssize_t r1, Py_ssize_t c1) nogil: - """Integrate over the integral image in the given window - - This method was created so that `multiblock_local_binary_pattern` - does not have to make a Python call. - - Parameters - ---------- - img : array - The integral image over which to integrate. - r0 : int - The row number of the top left corner. - c0 : int - The column number of the top left corner. - r1 : int - The row number of the bottom right corner. - c1 : int - The column number of the bottom right corner. - - Returns - ------- - ans : double - The integral over the given window. - """ - - cdef cnp.double_t ans = img[r1, c1] - - if (r0 >= 1) and (c0 >= 1): - ans += img[r0 - 1, c0 - 1] - - if (r0 >= 1): - ans -= img[r0 - 1, c1] - - if (c0 >= 1): - ans -= img[r1, c0 - 1] - - return ans - - # Constant values that are used by `multiblock_local_binary_pattern` function. # Values represent offsets of neighbour rectangles relative to central one. # It has order starting from top left and going clockwise. @@ -314,18 +274,16 @@ cdef: Py_ssize_t[::1] mlbp_x_offsets = np.asarray([-1, 0, 1, 1, 1, 0, -1, -1]) Py_ssize_t[::1] mlbp_y_offsets = np.asarray([-1, -1, -1, 0, 1, 1, 1, 0]) -cdef _multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, - Py_ssize_t x, - Py_ssize_t y, - Py_ssize_t width, - Py_ssize_t height): +cdef int _multiblock_local_binary_pattern(float[:, ::1] int_image, + Py_ssize_t x, + Py_ssize_t y, + Py_ssize_t width, + Py_ssize_t height): """Multi-block local binary pattern. - Effcient implementation in Cython. - Parameters ---------- - int_image : (N, M) double array + int_image : (N, M) float array Integral image. x : int X-coordinate of top left corner of a rectangle containing feature. @@ -341,7 +299,7 @@ cdef _multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, Returns ------- output : int - 8bit MB-LBP feature descriptor. + 8-bit MB-LBP feature descriptor. References ---------- @@ -357,11 +315,11 @@ cdef _multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, Py_ssize_t central_rect_y = y + height # Sum of intensity values of central rectangle - cdef double central_rect_val = _integ(int_image, - central_rect_y, - central_rect_x, - central_rect_y + height - 1, - central_rect_x + width - 1) + cdef float central_rect_val = integrate(int_image, + central_rect_y, + central_rect_x, + central_rect_y + height - 1, + central_rect_x + width - 1) cdef: Py_ssize_t element_num, offset_x, offset_y @@ -380,11 +338,11 @@ cdef _multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, current_rect_y = central_rect_y + offset_y * height - current_rect_val = _integ(int_image, - current_rect_y, - current_rect_x, - current_rect_y + height - 1, - current_rect_x + width - 1) + 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 @@ -396,23 +354,18 @@ cdef _multiblock_local_binary_pattern(cnp.double_t[:, ::1] int_image, return lbp_code -def multiblock_local_binary_pattern(int_image, - x, - y, - width, - height): +def multiblock_local_binary_pattern(int_image, x, y, width, height): """Multi-block local binary pattern. - The features are calculated in a way similar to local binary - patterns, except that summed up pixel values - rather than pixel values are used. + The features are calculated similarly to local binary patterns (LBPs), + except that summed blocks are used instead of individual pixel values. - MB-LBP is an extension of LBP that can be computed on any - scale in a constant time using integral image. It consists of - 9 equal-sized rectangles. They are used to compute a feature. - 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. + MB-LBP is an extension of LBP that can be computed on multiple scales + in constant time using the integral image. + 9 equally-sized rectangles are used to compute a feature. + For each rectangle, the sum of the pixel intensities is computed. + Comparisons of these sums to that of the central rectangle determine + the feature, similarly to LBP. Parameters ---------- @@ -442,7 +395,7 @@ def multiblock_local_binary_pattern(int_image, http://www.cbsr.ia.ac.cn/users/scliao/papers/Zhang-ICB07-MBLBP.pdf """ - int_image = np.ascontiguousarray(int_image, dtype=np.double) + int_image = np.ascontiguousarray(int_image, dtype=np.float32) lbp_code = _multiblock_local_binary_pattern(int_image, x, y, width, height) return lbp_code diff --git a/skimage/feature/tests/test_texture.py b/skimage/feature/tests/test_texture.py index c9e2c7d5..1647f881 100644 --- a/skimage/feature/tests/test_texture.py +++ b/skimage/feature/tests/test_texture.py @@ -8,7 +8,6 @@ from skimage.feature import ( from skimage._shared.testing import test_parallel from skimage.transform import integral_image -from skimage.util import img_as_float class TestGLCM(): @@ -253,9 +252,6 @@ class TestMBLBP(): # be filled. correct_answer = 0b10001000 - # The function accepts the float images. - # Also it has to be C-contiguous. - test_img = img_as_float(test_img) int_img = integral_image(test_img) lbp_code = multiblock_local_binary_pattern(int_img, 0, 0, 3, 3) diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index dbadac94..efa119eb 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -306,11 +306,11 @@ def draw_multiblock_lbp(img, ): """Multi-block local binary pattern visualization. - The blocks visualized in the following manner: the center block - is left untouched. The blocks that have higher are covered with - transparent white rectangles. The blocks that have less intensity - are covered with cyan rectangles. The colors can also be specified. - Opacity of visualization is controlled with `alpha` argument. + Blocks with higher sums are colored with transparent white rectangles, + whereas blocks with lower sums are colored cyan. + The blocks that have less intensity are covered with + cyan rectangles. The colors can also be specified. + Opacity of visualization is controlled with `alpha` parameter. Parameters ---------- @@ -359,8 +359,8 @@ def draw_multiblock_lbp(img, # Default colors for regions. # White is for the blocks that are brighter. # Cyan is for the blocks that has less intensity. - color_greater_block = np.asarray(color_greater_block, dtype='float64') - color_less_block = np.asarray(color_less_block, dtype='float64') + color_greater_block = np.asarray(color_greater_block, dtype=np.double) + color_less_block = np.asarray(color_less_block, dtype=np.double) # Copy array to avoid the changes to the original one. output = np.copy(img) From d20eae7cd1e0197a7769f3e2b5f7a3c04e586f84 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 10 Jun 2015 11:43:05 +0200 Subject: [PATCH 08/17] small correction in documentation. --- skimage/feature/_texture.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/feature/_texture.pyx b/skimage/feature/_texture.pyx index f41e8363..70d92c5c 100644 --- a/skimage/feature/_texture.pyx +++ b/skimage/feature/_texture.pyx @@ -385,7 +385,7 @@ def multiblock_local_binary_pattern(int_image, x, y, width, height): Returns ------- output : int - 8bit MB-LBP feature descriptor. + 8-bit MB-LBP feature descriptor. References ---------- From cc81bdb26bcc029568db0ffae11dd4837d7b6134 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 11 Jun 2015 07:52:06 +0200 Subject: [PATCH 09/17] moved multiblocl_local_binary_pattern in python file in order for sphinx to be able to correctly creat documentation. --- skimage/feature/__init__.py | 2 +- skimage/feature/_texture.pyx | 55 ++++-------------------------------- skimage/feature/texture.py | 50 +++++++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 52 deletions(-) diff --git a/skimage/feature/__init__.py b/skimage/feature/__init__.py index 37954c92..4c646235 100644 --- a/skimage/feature/__init__.py +++ b/skimage/feature/__init__.py @@ -3,9 +3,9 @@ from ._daisy import daisy from ._hog import hog from .texture import (greycomatrix, greycoprops, local_binary_pattern, + multiblock_local_binary_pattern, draw_multiblock_lbp) -from ._texture import multiblock_local_binary_pattern from .peak import peak_local_max from .corner import (corner_kitchen_rosenfeld, corner_harris, corner_shi_tomasi, corner_foerstner, corner_subpix, diff --git a/skimage/feature/_texture.pyx b/skimage/feature/_texture.pyx index 70d92c5c..98503534 100644 --- a/skimage/feature/_texture.pyx +++ b/skimage/feature/_texture.pyx @@ -274,11 +274,11 @@ cdef: Py_ssize_t[::1] mlbp_x_offsets = np.asarray([-1, 0, 1, 1, 1, 0, -1, -1]) Py_ssize_t[::1] mlbp_y_offsets = np.asarray([-1, -1, -1, 0, 1, 1, 1, 0]) -cdef int _multiblock_local_binary_pattern(float[:, ::1] int_image, - Py_ssize_t x, - Py_ssize_t y, - Py_ssize_t width, - Py_ssize_t height): +def _multiblock_local_binary_pattern(float[:, ::1] int_image, + Py_ssize_t x, + Py_ssize_t y, + Py_ssize_t width, + Py_ssize_t height): """Multi-block local binary pattern. Parameters @@ -354,49 +354,4 @@ cdef int _multiblock_local_binary_pattern(float[:, ::1] int_image, return lbp_code -def multiblock_local_binary_pattern(int_image, x, y, width, height): - """Multi-block local binary pattern. - - The features are calculated similarly to local binary patterns (LBPs), - except that summed blocks are used instead of individual pixel values. - - MB-LBP is an extension of LBP that can be computed on multiple scales - in constant time using the integral image. - 9 equally-sized rectangles are used to compute a feature. - For each rectangle, the sum of the pixel intensities is computed. - Comparisons of these sums to that of the central rectangle determine - the feature, similarly to LBP. - - 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 - 8-bit 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 - """ - - int_image = np.ascontiguousarray(int_image, dtype=np.float32) - lbp_code = _multiblock_local_binary_pattern(int_image, x, y, width, height) - return lbp_code - diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index efa119eb..5a3308a2 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -5,7 +5,9 @@ Methods to characterize image textures. import numpy as np from .._shared.utils import assert_nD from ..util import img_as_float -from ._texture import _glcm_loop, _local_binary_pattern +from ._texture import (_glcm_loop, + _local_binary_pattern, + _multiblock_local_binary_pattern) def greycomatrix(image, distances, angles, levels=256, symmetric=False, @@ -294,6 +296,52 @@ def local_binary_pattern(image, P, R, method='default'): return output +def multiblock_local_binary_pattern(int_image, x, y, width, height): + """Multi-block local binary pattern. + + The features are calculated similarly to local binary patterns (LBPs), + except that summed blocks are used instead of individual pixel values. + + MB-LBP is an extension of LBP that can be computed on multiple scales + in constant time using the integral image. + 9 equally-sized rectangles are used to compute a feature. + For each rectangle, the sum of the pixel intensities is computed. + Comparisons of these sums to that of the central rectangle determine + the feature, similarly to LBP. + + 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 + 8-bit 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 + """ + + int_image = np.ascontiguousarray(int_image, dtype=np.float32) + lbp_code = _multiblock_local_binary_pattern(int_image, x, y, width, height) + return lbp_code + + def draw_multiblock_lbp(img, x, y, From 0e96098e496f188fa3c44ade8bfc78f52e7da0c4 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 11 Jun 2015 08:01:26 +0200 Subject: [PATCH 10/17] small documentation correction --- skimage/feature/texture.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index 5a3308a2..c17c1a5b 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -356,8 +356,7 @@ def draw_multiblock_lbp(img, Blocks with higher sums are colored with transparent white rectangles, whereas blocks with lower sums are colored cyan. - The blocks that have less intensity are covered with - cyan rectangles. The colors can also be specified. + The colors can also be specified. Opacity of visualization is controlled with `alpha` parameter. Parameters From 360d384633dc4b2228b990613b14669c69b9c3a6 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 11 Jun 2015 08:38:18 +0200 Subject: [PATCH 11/17] used gray2rgb function. Code is more readable now. --- skimage/feature/texture.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index c17c1a5b..b311342d 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -5,6 +5,7 @@ Methods to characterize image textures. import numpy as np from .._shared.utils import assert_nD from ..util import img_as_float +from ..color import gray2rgb from ._texture import (_glcm_loop, _local_binary_pattern, _multiblock_local_binary_pattern) @@ -414,7 +415,7 @@ def draw_multiblock_lbp(img, # As the visualization uses RGB color we need 3 bands. if len(img.shape) < 3: - output = np.dstack((img,) * 3) + output = gray2rgb(img) # Colors are specified in floats. output = img_as_float(output) From 68f627aca73d3ef1730957debe25ef7b5a551326 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 12 Jun 2015 07:49:00 +0200 Subject: [PATCH 12/17] pep8. Documenatation correction. --- .../plot_multiblock_local_binary_pattern.py | 22 +++++++++---------- skimage/feature/_texture.pyx | 8 ++----- skimage/feature/texture.py | 18 ++++++--------- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/doc/examples/plot_multiblock_local_binary_pattern.py b/doc/examples/plot_multiblock_local_binary_pattern.py index 17102fe3..526e63c1 100644 --- a/doc/examples/plot_multiblock_local_binary_pattern.py +++ b/doc/examples/plot_multiblock_local_binary_pattern.py @@ -10,11 +10,11 @@ The features are calculated similarly to local binary patterns (LBPs), except that summed blocks are used instead of individual pixel values. MB-LBP is an extension of LBP that can be computed on multiple scales -in constant time using the integral image. -9 equally-sized rectangles are used to compute a feature. -For each rectangle, the sum of the pixel intensities is computed. -Comparisons of these sums to that of the central rectangle determine -the feature, similarly to LBP (See `LBP `_). +in constant time using the integral image. 9 equally-sized rectangles +are used to compute a feature. For each rectangle, the sum of the pixel +intensities is computed. Comparisons of these sums to that of the central +rectangle determine the feature, similarly to LBP +(See `LBP `_). First, we generate an image to illustrate the functioning of MB-LBP: we take a (9, 9) rectangle and divide it into (3, 3) block, @@ -25,6 +25,7 @@ upon which we then apply MB-LBP. from __future__ import print_function from skimage.feature import multiblock_local_binary_pattern import numpy as np +from numpy.testing import assert_equal from skimage.transform import integral_image # Create test matrix where first and fifth @@ -35,17 +36,16 @@ test_img[3:6, 3:6] = 1 test_img[:3, :3] = 50 test_img[6:, 6:] = 50 -# MB-LBP is filled in reverse order. -# So the first and fifth bits from the end should -# be filled. +# First and fifth bits should be filled. +# This correct value will be compared to +# the computed one. correct_answer = 0b10001000 int_img = integral_image(test_img) lbp_code = multiblock_local_binary_pattern(int_img, 0, 0, 3, 3) -print(correct_answer) -print(lbp_code) +assert_equal(correct_answer, lbp_code) """ Now let's apply the operator to a real image and see how the visualization works. @@ -71,6 +71,6 @@ plt.imshow(img, interpolation='nearest') On the above plot we see the result of computing a MB-LBP and visualization of the computed feature. The rectangles that have less intensity than the central -rectangle are marked in cyan. The ones that have bigger intensity values +rectangle are marked in cyan. The ones that have higher intensity values are marked in white. The central rectangle is left untouched. """ diff --git a/skimage/feature/_texture.pyx b/skimage/feature/_texture.pyx index 98503534..85b6d530 100644 --- a/skimage/feature/_texture.pyx +++ b/skimage/feature/_texture.pyx @@ -315,9 +315,7 @@ def _multiblock_local_binary_pattern(float[:, ::1] int_image, Py_ssize_t central_rect_y = y + height # Sum of intensity values of central rectangle - cdef float central_rect_val = integrate(int_image, - central_rect_y, - central_rect_x, + cdef float central_rect_val = integrate(int_image, central_rect_y, central_rect_x, central_rect_y + height - 1, central_rect_x + width - 1) @@ -338,9 +336,7 @@ def _multiblock_local_binary_pattern(float[:, ::1] int_image, current_rect_y = central_rect_y + offset_y * height - current_rect_val = integrate(int_image, - current_rect_y, - current_rect_x, + current_rect_val = integrate(int_image, current_rect_y, current_rect_x, current_rect_y + height - 1, current_rect_x + width - 1) diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index b311342d..f7e3baf9 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -343,11 +343,7 @@ def multiblock_local_binary_pattern(int_image, x, y, width, height): return lbp_code -def draw_multiblock_lbp(img, - x, - y, - width, - height, +def draw_multiblock_lbp(img, x, y, width, height, lbp_code=0, color_greater_block=[1, 1, 1], color_less_block=[0, 0.69, 0.96], @@ -441,12 +437,12 @@ def draw_multiblock_lbp(img, # Mix-in the visualization colors. if has_greater_value: - output[curr_y:curr_y+height, curr_x:curr_x+width] = \ - (1-alpha) * output[curr_y:curr_y+height, curr_x:curr_x+width] \ - + alpha * color_greater_block + new_value = ((1-alpha) * output[curr_y:curr_y+height, curr_x:curr_x+width] + + alpha * color_greater_block) + output[curr_y:curr_y+height, curr_x:curr_x+width] = new_value else: - output[curr_y:curr_y+height, curr_x:curr_x+width] = \ - (1-alpha) * output[curr_y:curr_y+height, curr_x:curr_x+width] \ - + alpha * color_less_block + new_value = ((1-alpha) * output[curr_y:curr_y+height, curr_x:curr_x+width] + + alpha * color_less_block) + output[curr_y:curr_y+height, curr_x:curr_x+width] = new_value return output From cbe8f99f301118f75d0decaa41ea0987b1bc9d7e Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 12 Jun 2015 07:50:38 +0200 Subject: [PATCH 13/17] Documenatation correction --- doc/examples/plot_multiblock_local_binary_pattern.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/examples/plot_multiblock_local_binary_pattern.py b/doc/examples/plot_multiblock_local_binary_pattern.py index 526e63c1..6c40292d 100644 --- a/doc/examples/plot_multiblock_local_binary_pattern.py +++ b/doc/examples/plot_multiblock_local_binary_pattern.py @@ -70,7 +70,7 @@ plt.imshow(img, interpolation='nearest') .. image:: PLOT2RST.current_figure On the above plot we see the result of computing a MB-LBP and visualization -of the computed feature. The rectangles that have less intensity than the central +of the computed feature. The rectangles that have less intensities' sum than the central rectangle are marked in cyan. The ones that have higher intensity values are marked in white. The central rectangle is left untouched. """ From 496e8c090e02dc1e73ea76eb56b206dd5b32d916 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 12 Jun 2015 20:41:38 +0200 Subject: [PATCH 14/17] the function name was shortened to multiblock_lbp --- doc/examples/plot_multiblock_local_binary_pattern.py | 6 +++--- skimage/feature/__init__.py | 4 ++-- skimage/feature/_texture.pyx | 10 +++++----- skimage/feature/tests/test_texture.py | 4 ++-- skimage/feature/texture.py | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/examples/plot_multiblock_local_binary_pattern.py b/doc/examples/plot_multiblock_local_binary_pattern.py index 6c40292d..c359a1e9 100644 --- a/doc/examples/plot_multiblock_local_binary_pattern.py +++ b/doc/examples/plot_multiblock_local_binary_pattern.py @@ -23,7 +23,7 @@ upon which we then apply MB-LBP. """ from __future__ import print_function -from skimage.feature import multiblock_local_binary_pattern +from skimage.feature import multiblock_lbp import numpy as np from numpy.testing import assert_equal from skimage.transform import integral_image @@ -43,7 +43,7 @@ correct_answer = 0b10001000 int_img = integral_image(test_img) -lbp_code = multiblock_local_binary_pattern(int_img, 0, 0, 3, 3) +lbp_code = multiblock_lbp(int_img, 0, 0, 3, 3) assert_equal(correct_answer, lbp_code) @@ -58,7 +58,7 @@ test_img = data.coins() int_img = integral_image(test_img) -lbp_code = multiblock_local_binary_pattern(int_img, 0, 0, 90, 90) +lbp_code = multiblock_lbp(int_img, 0, 0, 90, 90) img = draw_multiblock_lbp(test_img, 0, 0, 90, 90, lbp_code=lbp_code, alpha=0.5) diff --git a/skimage/feature/__init__.py b/skimage/feature/__init__.py index 4c646235..4d40e2c8 100644 --- a/skimage/feature/__init__.py +++ b/skimage/feature/__init__.py @@ -3,7 +3,7 @@ from ._daisy import daisy from ._hog import hog from .texture import (greycomatrix, greycoprops, local_binary_pattern, - multiblock_local_binary_pattern, + multiblock_lbp, draw_multiblock_lbp) from .peak import peak_local_max @@ -29,7 +29,7 @@ __all__ = ['canny', 'greycomatrix', 'greycoprops', 'local_binary_pattern', - 'multiblock_local_binary_pattern', + 'multiblock_lbp', 'draw_multiblock_lbp', 'peak_local_max', 'structure_tensor', diff --git a/skimage/feature/_texture.pyx b/skimage/feature/_texture.pyx index 85b6d530..6ff5d60b 100644 --- a/skimage/feature/_texture.pyx +++ b/skimage/feature/_texture.pyx @@ -274,11 +274,11 @@ cdef: Py_ssize_t[::1] mlbp_x_offsets = np.asarray([-1, 0, 1, 1, 1, 0, -1, -1]) Py_ssize_t[::1] mlbp_y_offsets = np.asarray([-1, -1, -1, 0, 1, 1, 1, 0]) -def _multiblock_local_binary_pattern(float[:, ::1] int_image, - Py_ssize_t x, - Py_ssize_t y, - Py_ssize_t width, - Py_ssize_t height): +def _multiblock_lbp(float[:, ::1] int_image, + Py_ssize_t x, + Py_ssize_t y, + Py_ssize_t width, + Py_ssize_t height): """Multi-block local binary pattern. Parameters diff --git a/skimage/feature/tests/test_texture.py b/skimage/feature/tests/test_texture.py index 1647f881..1bc44919 100644 --- a/skimage/feature/tests/test_texture.py +++ b/skimage/feature/tests/test_texture.py @@ -3,7 +3,7 @@ from skimage.feature import ( greycomatrix, greycoprops, local_binary_pattern, - multiblock_local_binary_pattern + multiblock_lbp ) from skimage._shared.testing import test_parallel @@ -254,7 +254,7 @@ class TestMBLBP(): int_img = integral_image(test_img) - lbp_code = multiblock_local_binary_pattern(int_img, 0, 0, 3, 3) + lbp_code = multiblock_lbp(int_img, 0, 0, 3, 3) np.testing.assert_equal(lbp_code, correct_answer) diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index f7e3baf9..1b9c8f1d 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -8,7 +8,7 @@ from ..util import img_as_float from ..color import gray2rgb from ._texture import (_glcm_loop, _local_binary_pattern, - _multiblock_local_binary_pattern) + _multiblock_lbp) def greycomatrix(image, distances, angles, levels=256, symmetric=False, @@ -297,7 +297,7 @@ def local_binary_pattern(image, P, R, method='default'): return output -def multiblock_local_binary_pattern(int_image, x, y, width, height): +def multiblock_lbp(int_image, x, y, width, height): """Multi-block local binary pattern. The features are calculated similarly to local binary patterns (LBPs), @@ -339,7 +339,7 @@ def multiblock_local_binary_pattern(int_image, x, y, width, height): """ int_image = np.ascontiguousarray(int_image, dtype=np.float32) - lbp_code = _multiblock_local_binary_pattern(int_image, x, y, width, height) + lbp_code = _multiblock_lbp(int_image, x, y, width, height) return lbp_code From fe85552596c5e76bf160992f5ed6004c17ead6c6 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 13 Jun 2015 07:45:26 +0200 Subject: [PATCH 15/17] changed documentation --- skimage/feature/texture.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index 1b9c8f1d..0ec13cb3 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -319,10 +319,10 @@ def multiblock_lbp(int_image, x, y, width, height): 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 + Width of one of the 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 + Height of one of the 9 equal rectangles that will be used to compute a feature. Returns From c3e23b0604d1198b4795287681b2164f7271a6f0 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 15 Jun 2015 22:04:09 +0200 Subject: [PATCH 16/17] Changed x/y to r/c notation. Documentation correction. --- .../plot_multiblock_local_binary_pattern.py | 21 ++-- skimage/feature/_texture.pyx | 69 ++++++------ skimage/feature/tests/test_texture.py | 17 ++- skimage/feature/texture.py | 101 +++++++++--------- 4 files changed, 106 insertions(+), 102 deletions(-) diff --git a/doc/examples/plot_multiblock_local_binary_pattern.py b/doc/examples/plot_multiblock_local_binary_pattern.py index c359a1e9..5029c99c 100644 --- a/doc/examples/plot_multiblock_local_binary_pattern.py +++ b/doc/examples/plot_multiblock_local_binary_pattern.py @@ -28,17 +28,15 @@ import numpy as np from numpy.testing import assert_equal from skimage.transform import integral_image -# Create test matrix where first and fifth -# rectangles starting from top left clockwise -# have greater value than the central one. +# Create test matrix where first and fifth rectangles starting +# from top left clockwise have greater value than the central one. test_img = np.zeros((9, 9), dtype='uint8') test_img[3:6, 3:6] = 1 test_img[:3, :3] = 50 test_img[6:, 6:] = 50 -# First and fifth bits should be filled. -# This correct value will be compared to -# the computed one. +# First and fifth bits should be filled. This correct value will +# be compared to the computed one. correct_answer = 0b10001000 int_img = integral_image(test_img) @@ -48,7 +46,8 @@ lbp_code = multiblock_lbp(int_img, 0, 0, 3, 3) assert_equal(correct_answer, lbp_code) """ -Now let's apply the operator to a real image and see how the visualization works. +Now let's apply the operator to a real image and see how the +visualization works. """ from skimage import data from matplotlib import pyplot as plt @@ -69,8 +68,8 @@ plt.imshow(img, interpolation='nearest') """ .. image:: PLOT2RST.current_figure -On the above plot we see the result of computing a MB-LBP and visualization -of the computed feature. The rectangles that have less intensities' sum than the central -rectangle are marked in cyan. The ones that have higher intensity values -are marked in white. The central rectangle is left untouched. +On the above plot we see the result of computing a MB-LBP and visualization of +the computed feature. The rectangles that have less intensities' sum than the +central rectangle are marked in cyan. The ones that have higher intensity +values are marked in white. The central rectangle is left untouched. """ diff --git a/skimage/feature/_texture.pyx b/skimage/feature/_texture.pyx index 6ff5d60b..b90ab6dd 100644 --- a/skimage/feature/_texture.pyx +++ b/skimage/feature/_texture.pyx @@ -267,28 +267,29 @@ def _local_binary_pattern(double[:, ::1] image, return np.asarray(output) -# Constant values that are used by `multiblock_local_binary_pattern` function. +# Constant values that are used by `_multiblock_lbp` function. # Values represent offsets of neighbour rectangles relative to central one. # It has order starting from top left and going clockwise. cdef: - Py_ssize_t[::1] mlbp_x_offsets = np.asarray([-1, 0, 1, 1, 1, 0, -1, -1]) - Py_ssize_t[::1] mlbp_y_offsets = np.asarray([-1, -1, -1, 0, 1, 1, 1, 0]) + Py_ssize_t[::1] mlbp_r_offsets = np.asarray([-1, -1, -1, 0, 1, 1, 1, 0]) + Py_ssize_t[::1] mlbp_c_offsets = np.asarray([-1, 0, 1, 1, 1, 0, -1, -1]) + def _multiblock_lbp(float[:, ::1] int_image, - Py_ssize_t x, - Py_ssize_t y, + Py_ssize_t r, + Py_ssize_t c, Py_ssize_t width, Py_ssize_t height): - """Multi-block local binary pattern. + """Multi-block local binary pattern (MB-LBP) [1]_. Parameters ---------- int_image : (N, M) float 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. + r : int + Row-coordinate of top left corner of a rectangle containing feature. + c : int + Column-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. @@ -309,36 +310,43 @@ def _multiblock_lbp(float[:, ::1] int_image, http://www.cbsr.ia.ac.cn/users/scliao/papers/Zhang-ICB07-MBLBP.pdf """ - # Top-left coordinates of central rectangle cdef: - Py_ssize_t central_rect_x = x + width - Py_ssize_t central_rect_y = y + height + # Top-left coordinates of central rectangle. + Py_ssize_t central_rect_r = r + height + Py_ssize_t central_rect_c = c + width - # Sum of intensity values of central rectangle - cdef float central_rect_val = integrate(int_image, central_rect_y, central_rect_x, - central_rect_y + height - 1, - central_rect_x + width - 1) + Py_ssize_t r_shift = height - 1 + Py_ssize_t c_shift = width - 1 - cdef: - Py_ssize_t element_num, offset_x, offset_y - Py_ssize_t current_rect_x, current_rect_y + # Copy offset array to multiply it by width and height later. + Py_ssize_t[::1] r_offsets = mlbp_r_offsets.copy() + Py_ssize_t[::1] c_offsets = mlbp_c_offsets.copy() + + Py_ssize_t current_rect_r, current_rect_c + Py_ssize_t element_num, i double current_rect_val int has_greater_value int lbp_code = 0 + # Pre-multiply offsets with width and height. + for i in range(8): + r_offsets[i] = r_offsets[i]*height + c_offsets[i] = c_offsets[i]*width + + # Sum of intensity values of central rectangle. + cdef float central_rect_val = integrate(int_image, central_rect_r, central_rect_c, + central_rect_r + r_shift, + central_rect_c + c_shift) + for element_num in range(8): - offset_x = mlbp_x_offsets[element_num] - offset_y = mlbp_y_offsets[element_num] + current_rect_r = central_rect_r + r_offsets[element_num] + current_rect_c = central_rect_c + c_offsets[element_num] - 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) + current_rect_val = integrate(int_image, current_rect_r, current_rect_c, + current_rect_r + r_shift, + current_rect_c + c_shift) has_greater_value = current_rect_val >= central_rect_val @@ -348,6 +356,3 @@ def _multiblock_lbp(float[:, ::1] int_image, lbp_code |= has_greater_value << (7 - element_num) return lbp_code - - - diff --git a/skimage/feature/tests/test_texture.py b/skimage/feature/tests/test_texture.py index 1bc44919..c095fdda 100644 --- a/skimage/feature/tests/test_texture.py +++ b/skimage/feature/tests/test_texture.py @@ -1,10 +1,8 @@ import numpy as np -from skimage.feature import ( - greycomatrix, +from skimage.feature import (greycomatrix, greycoprops, local_binary_pattern, - multiblock_lbp - ) + multiblock_lbp) from skimage._shared.testing import test_parallel from skimage.transform import integral_image @@ -239,17 +237,16 @@ class TestMBLBP(): def test_single_mblbp(self): - # Create dummy matrix where first and fifth - # rectangles have greater value than the central one - # Therefore, the following bits should be 1. + # 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 - # MB-LBP is filled in reverse order. - # So the first and fifth bits from the end should - # be filled. + # MB-LBP is filled in reverse order. So the first and fifth bits from + # the end should be filled. correct_answer = 0b10001000 int_img = integral_image(test_img) diff --git a/skimage/feature/texture.py b/skimage/feature/texture.py index 0ec13cb3..c26eb59d 100644 --- a/skimage/feature/texture.py +++ b/skimage/feature/texture.py @@ -297,27 +297,27 @@ def local_binary_pattern(image, P, R, method='default'): return output -def multiblock_lbp(int_image, x, y, width, height): - """Multi-block local binary pattern. +def multiblock_lbp(int_image, r, c, width, height): + """Multi-block local binary pattern (MB-LBP) [1]_. The features are calculated similarly to local binary patterns (LBPs), - except that summed blocks are used instead of individual pixel values. + (See :py:meth:`local_binary_pattern`) except that summed blocks are + used instead of individual pixel values. MB-LBP is an extension of LBP that can be computed on multiple scales - in constant time using the integral image. - 9 equally-sized rectangles are used to compute a feature. - For each rectangle, the sum of the pixel intensities is computed. - Comparisons of these sums to that of the central rectangle determine - the feature, similarly to LBP. + in constant time using the integral image. Nine equally-sized rectangles + are used to compute a feature. For each rectangle, the sum of the pixel + intensities is computed. Comparisons of these sums to that of the central + rectangle determine the feature, similarly to LBP. 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. + r : int + Row-coordinate of top left corner of a rectangle containing feature. + c : int + Column-coordinate of top left corner of a rectangle containing feature. width : int Width of one of the 9 equal rectangles that will be used to compute a feature. @@ -339,11 +339,11 @@ def multiblock_lbp(int_image, x, y, width, height): """ int_image = np.ascontiguousarray(int_image, dtype=np.float32) - lbp_code = _multiblock_lbp(int_image, x, y, width, height) + lbp_code = _multiblock_lbp(int_image, r, c, width, height) return lbp_code -def draw_multiblock_lbp(img, x, y, width, height, +def draw_multiblock_lbp(img, r, c, width, height, lbp_code=0, color_greater_block=[1, 1, 1], color_less_block=[0, 0.69, 0.96], @@ -351,19 +351,18 @@ def draw_multiblock_lbp(img, x, y, width, height, ): """Multi-block local binary pattern visualization. - Blocks with higher sums are colored with transparent white rectangles, - whereas blocks with lower sums are colored cyan. - The colors can also be specified. - Opacity of visualization is controlled with `alpha` parameter. + Blocks with higher sums are colored with alpha-blended white rectangles, + whereas blocks with lower sums are colored alpha-blended cyan. Colors + and the `alpha` parameter can be changed. Parameters ---------- - img : + img : ndarray of float or uint Image on which to visualize the pattern. - 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. + r : int + Row-coordinate of top left corner of a rectangle containing feature. + c : int + Column-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. @@ -371,26 +370,25 @@ def draw_multiblock_lbp(img, x, y, width, height, Height of one of 9 equal rectangles that will be used to compute a feature. lbp_code : int - The descriptor of feature to visualize. If not provided, - the descriptor with 0 value will be used. + The descriptor of feature to visualize. If not provided, the + descriptor with 0 value will be used. color_greater_block : list of 3 floats - Floats specifying the color for the block that - has greater intensity value. They should be - in the range [0, 1]. Corresponding values define - (R, G, B) values. Default value is white [1, 1, 1]. + Floats specifying the color for the block that has greater + intensity value. They should be in the range [0, 1]. + Corresponding values define (R, G, B) values. Default value + is white [1, 1, 1]. color_greater_block : list of 3 floats - Floats specifying the color for the block that - has greater intensity value. They should be - in the range [0, 1]. Corresponding values define + Floats specifying the color for the block that has greater intensity + value. They should be in the range [0, 1]. Corresponding values define (R, G, B) values. Default value is cyan [0, 0.69, 0.96]. alpha : float - Value in the range [0, 1] that specifies opacity of - visualization. 1 - fully transparent, 0 - opaque. + Value in the range [0, 1] that specifies opacity of visualization. + 1 - fully transparent, 0 - opaque. Returns ------- output : ndarray of float - Image with visualization. + Image with MB-LBP visualization. References ---------- @@ -403,8 +401,8 @@ def draw_multiblock_lbp(img, x, y, width, height, # Default colors for regions. # White is for the blocks that are brighter. # Cyan is for the blocks that has less intensity. - color_greater_block = np.asarray(color_greater_block, dtype=np.double) - color_less_block = np.asarray(color_less_block, dtype=np.double) + color_greater_block = np.asarray(color_greater_block, dtype=np.float64) + color_less_block = np.asarray(color_less_block, dtype=np.float64) # Copy array to avoid the changes to the original one. output = np.copy(img) @@ -418,31 +416,36 @@ def draw_multiblock_lbp(img, x, y, width, height, # 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)) + neighbour_rect_offsets = ((-1, -1), (-1, 0), (-1, 1), + (0, 1), (1, 1), (1, 0), + (1, -1), (0, -1)) + + # Pre-multiply the offsets with width and height. + neighbour_rect_offsets = np.array(neighbour_rect_offsets) + neighbour_rect_offsets[:, 0] *= height + neighbour_rect_offsets[:, 1] *= width # Top-left coordinates of central rectangle. - central_rect_x = x + width - central_rect_y = y + height + central_rect_r = r + height + central_rect_c = c + width for element_num, offset in enumerate(neighbour_rect_offsets): - offset_x, offset_y = offset + offset_r, offset_c = offset - curr_x = central_rect_x + offset_x * width - curr_y = central_rect_y + offset_y * height + curr_r = central_rect_r + offset_r + curr_c = central_rect_c + offset_c has_greater_value = lbp_code & (1 << (7-element_num)) # Mix-in the visualization colors. if has_greater_value: - new_value = ((1-alpha) * output[curr_y:curr_y+height, curr_x:curr_x+width] + new_value = ((1-alpha) * output[curr_r:curr_r+height, curr_c:curr_c+width] + alpha * color_greater_block) - output[curr_y:curr_y+height, curr_x:curr_x+width] = new_value + output[curr_r:curr_r+height, curr_c:curr_c+width] = new_value else: - new_value = ((1-alpha) * output[curr_y:curr_y+height, curr_x:curr_x+width] + new_value = ((1-alpha) * output[curr_r:curr_r+height, curr_c:curr_c+width] + alpha * color_less_block) - output[curr_y:curr_y+height, curr_x:curr_x+width] = new_value + output[curr_r:curr_r+height, curr_c:curr_c+width] = new_value return output From 88e3ac45fc80b8c6e9568041e507964a9e94fac5 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 15 Jun 2015 22:12:02 +0200 Subject: [PATCH 17/17] Documentation correction. --- .../plot_multiblock_local_binary_pattern.py | 26 +++++++++---------- skimage/feature/tests/test_texture.py | 6 ++--- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/doc/examples/plot_multiblock_local_binary_pattern.py b/doc/examples/plot_multiblock_local_binary_pattern.py index 5029c99c..f54d944e 100644 --- a/doc/examples/plot_multiblock_local_binary_pattern.py +++ b/doc/examples/plot_multiblock_local_binary_pattern.py @@ -3,23 +3,21 @@ Multi-Block Local Binary Pattern for texture classification =========================================================== -This example shows how to compute multi-block local binary -pattern (MB-LBP) features as well as how to visualize them. +This example shows how to compute multi-block local binary pattern (MB-LBP) +features as well as how to visualize them. -The features are calculated similarly to local binary patterns (LBPs), -except that summed blocks are used instead of individual pixel values. +The features are calculated similarly to local binary patterns (LBPs), except +that summed blocks are used instead of individual pixel values. -MB-LBP is an extension of LBP that can be computed on multiple scales -in constant time using the integral image. 9 equally-sized rectangles -are used to compute a feature. For each rectangle, the sum of the pixel -intensities is computed. Comparisons of these sums to that of the central -rectangle determine the feature, similarly to LBP -(See `LBP `_). - -First, we generate an image to illustrate the functioning of MB-LBP: -we take a (9, 9) rectangle and divide it into (3, 3) block, -upon which we then apply MB-LBP. +MB-LBP is an extension of LBP that can be computed on multiple scales in +constant time using the integral image. 9 equally-sized rectangles are used to +compute a feature. For each rectangle, the sum of the pixel intensities is +computed. Comparisons of these sums to that of the central rectangle determine +the feature, similarly to LBP (See `LBP `_). +First, we generate an image to illustrate the functioning of MB-LBP: consider +a (9, 9) rectangle and divide it into (3, 3) block, upon which we then apply +MB-LBP. """ from __future__ import print_function diff --git a/skimage/feature/tests/test_texture.py b/skimage/feature/tests/test_texture.py index c095fdda..ae54a6bb 100644 --- a/skimage/feature/tests/test_texture.py +++ b/skimage/feature/tests/test_texture.py @@ -1,8 +1,8 @@ import numpy as np from skimage.feature import (greycomatrix, - greycoprops, - local_binary_pattern, - multiblock_lbp) + greycoprops, + local_binary_pattern, + multiblock_lbp) from skimage._shared.testing import test_parallel from skimage.transform import integral_image