Texture Classification Using Local Binary Pattern



Local Binary Pattern (LBP) is a powerful texture descriptor used in image analysis. It involves comparing the pixel values of a central point with those of its neighboring pixels, resulting in a binary outcome. This tutorial explores how to perform texture classification using LBP.

LBP examines the points surrounding a central pixel and determines whether these surrounding points are greater or less than the central point, resulting in a binary representation.

The scikit image library offers the local_binary_pattern() function within its feature module to perform this task.

Using the skimage.feature.local_binary_pattern() function

The feature.local_binary_pattern() function is used to compute the Local Binary Pattern (LBP) of a 2D grayscale image. LBP is a visual descriptor often used in texture classification.

Syntax

Following is the syntax of this function −

skimage.feature.local_binary_pattern(image, P, R, method='default')

Parameters

The function accepts the following parameters −

  • image (M, N) array: This parameter is the input 2D grayscale image on which to compute the LBP.

  • P (int): It specifies the number of circularly symmetric neighbor set points. This quantizes the angular space.

  • R (float): This parameter defines the radius of the circle used for spatial resolution in the LBP operator.

  • method (str, optional): This parameter is used to determine the LBP pattern. The available options are −

    • default: This is the original local binary pattern. It is grayscale invariant but not rotation invariant.

    • ror: This is an extension of the default pattern and is both grayscale and rotation invariant.

    • uniform: This method produces a uniform pattern that is both grayscale and rotation invariant. It offers finer quantization of the angular space, making it suitable for texture classification tasks.

    • nri_uniform: This is a variant of the uniform pattern that is grayscale invariant but not rotation invariant.

    • var: This method computes the variance of local image texture, which is related to contrast. It is rotation invariant but not grayscale invariant.

The function returns an output LBP image of the same dimensions as the input image, i.e., an (M, N).

Example

Before applying Local Binary Pattern (LBP) to an image, it can be beneficial to visualize the structure of LBPs. The following code is designed to create a schematic representation of LBPs.

import numpy as np
import matplotlib.pyplot as plt

# Define LBP method to 'uniform'
METHOD = 'uniform'

# Function to plot a circle on the given axis
def plot_circle(ax, center, radius, color):
   circle = plt.Circle(center, radius, facecolor=color, edgecolor='0.5')
   ax.add_patch(circle)

# Function to draw the schematic for a local binary pattern
def plot_lbp_model(ax, binary_values):
   # Define geometric parameters
   theta = np.deg2rad(45)
   R = 1
   r = 0.15
   w = 1.5
   gray = '0.5'
   
   # Draw the central pixel
   plot_circle(ax, (0, 0), radius=r, color=gray)
   
   # Draw the surrounding pixels
   for i, facecolor in enumerate(binary_values):
      x = R * np.cos(i * theta)
      y = R * np.sin(i * theta)
      plot_circle(ax, (x, y), radius=r, color=str(facecolor))
   
   # Draw the pixel grid
   for x in np.linspace(-w, w, 4):
      ax.axvline(x, color=gray)
      ax.axhline(x, color=gray)
   
   # Adjust the layout
   ax.axis('image')
   ax.axis('off')
   size = w + 0.2
   ax.set_xlim(-size, size)
   ax.set_ylim(-size, size)

# Create a figure with 5 subplots
fig, axes = plt.subplots(ncols=5, figsize=(7, 2))

# Titles for the subplots
titles = ['flat', 'flat', 'edge', 'corner', 'non-uniform']

# Binary patterns for each subplot
binary_patterns = [np.zeros(8),
   np.ones(8),
   np.hstack([np.ones(4), np.zeros(4)]),
   np.hstack([np.zeros(3), np.ones(5)]),
   [1, 0, 0, 1, 1, 1, 0, 0]]

# Plot each binary pattern
for ax, values, name in zip(axes, binary_patterns, titles):
   plot_lbp_model(ax, values)
   ax.set_title(name)

plt.show()

Output

local binary pattern

The figure above illustrates results where black (or white) pixels indicate lower (or higher) intensity compared to the central pixel. When all surrounding pixels are uniformly black or white, the image region is flat and lacks distinctive features. Continuous sequences of black or white pixels form "uniform" patterns, which can be interpreted as corners or edges. Patterns that alternate between black and white pixels are categorized as "non-uniform.

Example

Let's apply the Local Binary Pattern (LBP) to analyze a brick texture. The resulting plot will highlight the flat, edge-like, and corner-like regions within the image.

from skimage.transform import rotate
from skimage.feature import local_binary_pattern
from skimage import data
from skimage.color import label2rgb
import numpy as np
import matplotlib.pyplot as plt

# Define LBP method to 'uniform'
METHOD = 'uniform'

# LBP settings
radius = 3
n_points = 8 * radius

# Function to overlay labels on an image
def overlay_labels(image, lbp, labels):
   mask = np.logical_or.reduce([lbp == each for each in labels])
   return label2rgb(mask, image=image, bg_label=0, alpha=0.5)

# Function to highlight bars in a histogram
def highlight_bars(bars, indexes):
   for i in indexes:
      bars[i].set_facecolor('r')

image = data.brick()
lbp = local_binary_pattern(image, n_points, radius, METHOD)

# Function to plot histograms of LBP of textures
def hist(ax, lbp):
   n_bins = int(lbp.max() + 1)
   return ax.hist(lbp.ravel(), density=True, bins=n_bins, range=(0, n_bins),
      facecolor='0.5')

# Create subplots for image and histograms
fig, (ax_img, ax_hist) = plt.subplots(nrows=2, ncols=3, figsize=(9, 6))
plt.gray()

titles = ('edge', 'flat', 'corner')
w = width = radius - 1
edge_labels = range(n_points // 2 - w, n_points // 2 + w + 1)
flat_labels = list(range(0, w + 1)) + list(range(n_points - w, n_points + 2))
i_14 = n_points // 4            # 1/4th of the histogram
i_34 = 3 * (n_points // 4)      # 3/4th of the histogram
corner_labels = (list(range(i_14 - w, i_14 + w + 1)) +
   list(range(i_34 - w, i_34 + w + 1)))

label_sets = (edge_labels, flat_labels, corner_labels)

# Display labeled images and histograms
for ax, labels in zip(ax_img, label_sets):
   ax.imshow(overlay_labels(image, lbp, labels))

for ax, labels, name in zip(ax_hist, label_sets, titles):
   counts, _, bars = hist(ax, lbp)
   highlight_bars(bars, labels)
   ax.set_ylim(top=np.max(counts[:-1]))
   ax.set_xlim(right=n_points + 2)
   ax.set_title(name)

ax_hist[0].set_ylabel('Percentage')
for ax in ax_img:
   ax.axis('off')

Output

lbp to brick texture
Advertisements