杰瑞科技汇

如何用Python实现Uniform LBP特征提取?

Of course! Here is a comprehensive guide to implementing Uniform Local Binary Patterns (Uniform LBP) in Python, including the theory, code from scratch, and using a popular library.

如何用Python实现Uniform LBP特征提取?-图1
(图片来源网络,侵删)

What is LBP and why "Uniform"?

Local Binary Pattern (LBP) is a powerful texture descriptor used in computer vision. The basic idea is to describe the local texture of an image by comparing each pixel with its neighbors.

The Standard LBP Process:

  1. For a given pixel (the center pixel), consider its P neighbors arranged in a circle of radius R.
  2. For each neighbor, compare its intensity value with the center pixel's value.
    • If the neighbor's value is greater than or equal to the center, assign a 1.
    • Otherwise, assign a 0.
  3. This results in a binary number (a string of P bits).
  4. Convert this binary number to a decimal value. This value is the LBP code for that pixel.
  5. Repeat for all pixels in the image to get an LBP image, where each pixel's value is its LBP code (an integer from 0 to 2^P - 1).

The Problem with Standard LBP: The number of possible LBP codes is 2^P. For P=8 neighbors, this results in 256 possible codes. This high dimensionality can be problematic for tasks like classification, as many of these codes correspond to very similar or rare patterns.

The Solution: Uniform LBP A binary pattern is called "Uniform" if it contains at most two bitwise transitions (from 0 to 1 or 1 to 0) when traversing the circular neighborhood.

如何用Python实现Uniform LBP特征提取?-图2
(图片来源网络,侵删)
  • Example (P=8):
    • 11111111 (0 transitions) -> Uniform
    • 00000000 (0 transitions) -> Uniform
    • 11100000 (1 transition) -> Uniform
    • 10101010 (4 transitions) -> Non-Uniform
    • 11001100 (2 transitions) -> Uniform

How Uniform LBP Works:

  1. Calculate the standard LBP code for every pixel.
  2. Count the number of bitwise transitions in the binary pattern.
  3. If the number of transitions is ≤ 2, the pattern is uniform. It gets a unique label from 0 to P+1 (for P=8, labels 0 to 9).
  4. If the number of transitions is > 2, the pattern is non-uniform. All non-uniform patterns are grouped into a single "miscellaneous" bin, labeled P+1 (for P=8, this is 10).

Result: Instead of 256 possible codes, Uniform LBP with P=8 has only 59 possible codes (0 to 58), making it much more robust and efficient.


Implementing Uniform LBP from Scratch

This is a great way to understand the underlying mechanics. We'll use NumPy for efficient array operations.

import numpy as np
import cv2
import matplotlib.pyplot as plt
def get_uniform_lbp_codes(P, R):
    """
    Pre-calculates the LBP codes and their uniformity for a given P and R.
    This is an optimization to avoid recalculating for every pixel.
    """
    num_codes = 2 ** P
    lbp_codes = np.arange(num_codes, dtype=np.uint8)
    # Create a binary representation of each code
    binary_repr = np.unpackbits(lbp_codes[:, np.newaxis], axis=1, count=P)
    # Calculate bitwise transitions (circular)
    # Shift the binary representation to the left and compare with the original
    shifted = np.roll(binary_repr, -1, axis=1)
    transitions = np.sum(np.abs(binary_repr - shifted), axis=1)
    # Determine if a code is uniform
    is_uniform = (transitions <= 2)
    # Map uniform codes to their new labels (0 to P+1)
    # Non-uniform codes are mapped to P+1
    uniform_labels = np.where(is_uniform, lbp_codes, P + 1)
    return uniform_labels
def calculate_uniform_lbp(image, P=8, R=1):
    """
    Calculates the Uniform Local Binary Pattern for a given grayscale image.
    Args:
        image (np.ndarray): Input grayscale image.
        P (int): Number of neighboring points.
        R (int): Radius of the circle.
    Returns:
        np.ndarray: The Uniform LBP image.
    """
    h, w = image.shape
    lbp_image = np.zeros((h, w), dtype=np.uint8)
    # Pre-calculate the mapping from standard LBP codes to uniform labels
    uniform_labels = get_uniform_lbp_codes(P, R)
    # Pad the image to handle borders
    padded_image = cv2.copyMakeBorder(image, R, R, R, R, cv2.BORDER_CONSTANT, value=0)
    # Iterate over the original image size
    for i in range(R, h + R):
        for j in range(R, w + R):
            center = padded_image[i, j]
            # Get the P neighbors in a circle
            # We can sample angles for P points
            angles = 2 * np.pi * np.arange(P) / P
            neighbor_coords = []
            for angle in angles:
                x = int(i + R * np.cos(angle))
                y = int(j + R * np.sin(angle))
                neighbor_coords.append((x, y))
            # Compare neighbors to center and form binary string
            binary_string = 0
            for k, (x, y) in enumerate(neighbor_coords):
                if padded_image[x, y] >= center:
                    binary_string |= (1 << k) # Set the k-th bit
            # Get the uniform label from the pre-calculated map
            lbp_image[i - R, j - R] = uniform_labels[binary_string]
    return lbp_image
# --- Example Usage ---
# Load an image and convert to grayscale
image_path = 'path/to/your/image.jpg' # Replace with your image path
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# Resize for faster processing (optional)
image = cv2.resize(image, (256, 256))
# Calculate Uniform LBP
P = 8
R = 1
uniform_lbp_image = calculate_uniform_lbp(image, P, R)
# Display results
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(image, cmap='gray')'Original Image')
plt.axis('off')
plt.subplot(1, 2, 2)
# Use a colormap that makes sense for integer labels
plt.imshow(uniform_lbp_image, cmap='viridis')f'Uniform LBP (P={P}, R={R})')
plt.axis('off')
plt.show()

Implementing Uniform LBP using scikit-image

The scikit-image library has a built-in, highly optimized function for this. This is the recommended approach for practical applications.

如何用Python实现Uniform LBP特征提取?-图3
(图片来源网络,侵删)

The function is local_binary_pattern from skimage.feature. We need to set the method='uniform' parameter.

import numpy as np
import cv2
import matplotlib.pyplot as plt
from skimage.feature import local_binary_pattern
# Load an image and convert to grayscale
image_path = 'path/to/your/image.jpg' # Replace with your image path
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
image = cv2.resize(image, (256, 256))
# Parameters
P = 8  # Number of neighbors
R = 1  # Radius of the circle
# Calculate Uniform LBP using scikit-image
# The function returns an image where pixel values are the LBP codes.
# With method='uniform', the codes will be in the range [0, P+1].
uniform_lbp_image = local_binary_pattern(
    image, 
    P, 
    R, 
    method='uniform'
)
# Display results
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(image, cmap='gray')'Original Image')
plt.axis('off')
plt.subplot(1, 2, 2)
# 'viridis' or 'nipy_spectral' are good for showing distinct integer labels
plt.imshow(uniform_lbp_image, cmap='viridis')f'Uniform LBP (P={P}, R={R}) - skimage')
plt.axis('off')
plt.show()
# You can also get a histogram of the LBP codes, which is often used as a feature vector
hist, _ = np.histogram(uniform_lbp_image.ravel(), bins=P+2, range=(0, P+2))
# Normalize the histogram
hist = hist.astype("float")
hist /= (hist.sum() + 1e-7)
print("LBP Histogram (Feature Vector):")
print(hist)

How to Use Uniform LBP (Typical Workflow)

The LBP image itself is often not the final goal. The goal is usually to use it as a feature descriptor for tasks like face recognition or object detection.

  1. Divide the Image: Split the input image into small, non-overlapping regions (e.g., 16x16 pixel blocks).
  2. Compute LBP for Each Block: For each block, calculate its Uniform LBP image.
  3. Create a Histogram for Each Block: From the LBP image of each block, compute a histogram of the LBP codes. Since there are P+2 bins (59 for P=8), this histogram is a 59-dimensional vector.
  4. Concatenate Histograms: Concatenate all the block histograms to form a single, long feature vector for the entire image. This vector represents the texture distribution of the whole image.
  5. Use for Classification: This final feature vector can be fed into a machine learning classifier (like SVM, k-NN, etc.).

Summary of Methods

Method Pros Cons Best For
From Scratch Excellent for learning the algorithm. No external dependencies. Slow for large images. Prone to implementation errors. Education, understanding the core concepts.
scikit-image Fast, optimized, and well-tested. Easy to use. Part of a larger ecosystem. Requires an external library. Practical applications, research, and production code.

For any serious project, always prefer the scikit-image implementation. Use the "from scratch" version only for learning purposes.

分享:
扫描分享到社交APP
上一篇
下一篇