OpenCV C++中旋转不变LBP的直方图计算方法问询
Hey there! I've tackled rotation-invariant LBP (RI-LBP) for texture feature extraction before, so let's walk through exactly how to implement this for your OpenCV project—from converting your basic LBP image to rotation-invariant values, to computing a usable histogram for training/classification.
The key behind RI-LBP is that for any given LBP pattern (a binary number representing pixel comparisons), we rotate it to find the smallest possible binary value. This way, no matter how the texture is rotated in the image, the pattern will map to the same "canonical" value. For example, a pattern like 10010000 (binary) would rotate to 00001001, which is the smallest possible rotation of that pattern.
Let's break this down into actionable steps:
1. Get Your Basic LBP Image First
If you haven't already computed the basic LBP image, you can implement a custom version for full control (OpenCV's built-in cv2.LBP has limited customization). For 8-neighbor, radius=1 LBP (the most common setup), here's a quick implementation:
import cv2 import numpy as np def compute_basic_lbp(gray_img, radius=1, num_points=8): height, width = gray_img.shape lbp_img = np.zeros((height, width), dtype=np.uint8) for y in range(radius, height - radius): for x in range(radius, width - radius): center_pixel = gray_img[y, x] lbp_code = 0 # Calculate each neighbor's position and compare to center for i in range(num_points): angle = 2 * np.pi * i / num_points px = int(x + radius * np.cos(angle)) py = int(y - radius * np.sin(angle)) if gray_img[py, px] >= center_pixel: lbp_code |= (1 << (num_points - 1 - i)) lbp_img[y, x] = lbp_code return lbp_img # Example usage: load grayscale image and compute basic LBP gray_img = cv2.imread("your_image.jpg", cv2.IMREAD_GRAYSCALE) basic_lbp = compute_basic_lbp(gray_img)
2. Convert Basic LBP to Rotation-Invariant LBP
Next, process every pixel in the basic LBP image to get its rotation-invariant equivalent. We'll write a helper function that takes an LBP code, generates all possible rotations, and returns the smallest one:
def get_rotation_invariant_lbp(lbp_code, num_bits=8): min_code = lbp_code current_code = lbp_code # Generate all rotations of the code and track the minimum for _ in range(num_bits - 1): # Perform a circular right shift (adjust for left shift if needed) current_code = (current_code >> 1) | ((current_code & 1) << (num_bits - 1)) if current_code < min_code: min_code = current_code return min_code # Apply this to the entire basic LBP image ri_lbp_img = np.zeros_like(basic_lbp) for y in range(basic_lbp.shape[0]): for x in range(basic_lbp.shape[1]): ri_lbp_img[y, x] = get_rotation_invariant_lbp(basic_lbp[y, x])
Optional: Reduce Feature Dimension
For 8-bit LBP, there are only 36 unique rotation-invariant patterns (instead of 256). You can precompute a mapping table to convert each RI-LBP value to a 0-35 index, which shrinks your histogram size and speeds up processing:
# Precompute the mapping table ri_lbp_mapping = np.zeros(256, dtype=np.uint8) current_label = 0 for code in range(256): min_code = get_rotation_invariant_lbp(code) if ri_lbp_mapping[min_code] == 0: ri_lbp_mapping[min_code] = current_label current_label += 1 ri_lbp_mapping[code] = ri_lbp_mapping[min_code] # Apply mapping to get compact RI-LBP image compact_ri_lbp = ri_lbp_mapping[ri_lbp_img]
3. Compute the RI-LBP Histogram
Now you can compute the histogram of the RI-LBP image. For better classification performance, block-based histograms (splitting the image into grids and concatenating each block's histogram) are preferred over global histograms—they preserve spatial texture information.
Option 1: Global Histogram
# Compute and normalize the global histogram global_hist = cv2.calcHist([ri_lbp_img], [0], None, [256], [0, 256]) global_hist = cv2.normalize(global_hist, global_hist).flatten()
Option 2: Block-Based Histogram (Recommended)
# Split image into 4x4 grid (adjust grid size as needed) grid_rows, grid_cols = 4, 4 h, w = ri_lbp_img.shape cell_h, cell_w = h // grid_rows, w // grid_cols block_histograms = [] for row in range(grid_rows): for col in range(grid_cols): # Extract the current cell cell = ri_lbp_img[row*cell_h : (row+1)*cell_h, col*cell_w : (col+1)*cell_w] # Compute histogram for the cell cell_hist = cv2.calcHist([cell], [0], None, [256], [0, 256]) # Normalize and flatten cell_hist = cv2.normalize(cell_hist, cell_hist).flatten() block_histograms.append(cell_hist) # Concatenate all block histograms into a single feature vector feature_vector = np.concatenate(block_histograms)
- Video Frames: For video processing, simply repeat the above steps for each frame—treat each frame as a standalone grayscale image.
- Normalization: Always normalize your histograms (as shown) to ensure features are scale-invariant, which is critical for training/classification.
- Performance: If processing speed is an issue, vectorize the RI-LBP conversion (using NumPy operations instead of nested loops) to speed things up.
内容的提问来源于stack exchange,提问作者theburningfire

