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