3. 基于纹理的材质识别

任务目标

构建一个基于局部二值模式(LBP)纹理特征的材质分类器,用于区分木材、金属、塑料三种材质表面图像。输入为材质表面灰度图像,输出为材质类别标签,为后续分拣提供决策依据。

实现方案

采用圆形邻域LBP算子提取纹理特征,计算归一化直方图作为特征向量,使用支持向量机(SVM)完成三分类。代码基于Python + OpenCV + scikit-learn实现,包含完整的特征提取、模型训练与评估流程。

完整代码

import cv2
import numpy as np
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.preprocessing import StandardScaler
import os

# ---------- LBP特征提取 ----------
def compute_lbp_histogram(image, radius=1, n_points=8):
    """计算圆形LBP直方图(归一化)"""
    # 使用OpenCV的LBP实现(等价于圆形邻域采样)
    lbp = np.zeros_like(image, dtype=np.uint8)
    for i in range(radius, image.shape[0] - radius):
        for j in range(radius, image.shape[1] - radius):
            center = image[i, j]
            code = 0
            # 按角度采样邻域点(此处简化为8邻域)
            neighbors = [
                image[i-1, j-1], image[i-1, j], image[i-1, j+1],
                image[i, j+1],   image[i+1, j+1], image[i+1, j],
                image[i+1, j-1], image[i, j-1]
            ]
            for k, val in enumerate(neighbors):
                if val >= center:
                    code |= (1 << k)
            lbp[i, j] = code
    
    # 直方图归一化
    hist = cv2.calcHist([lbp], [0], None, [256], [0, 256]).flatten()
    hist = hist / (hist.sum() + 1e-6)
    return hist

# ---------- 数据加载 ----------
def load_dataset(data_dir):
    """从目录加载图像,目录结构:data_dir/类别名/*.jpg"""
    X, y = [], []
    class_names = sorted(os.listdir(data_dir))
    for label, cls in enumerate(class_names):
        cls_dir = os.path.join(data_dir, cls)
        for fname in os.listdir(cls_dir):
            if not fname.endswith(('.jpg', '.png')):
                continue
            img = cv2.imread(os.path.join(cls_dir, fname), cv2.IMREAD_GRAYSCALE)
            if img is None:
                continue
            img = cv2.resize(img, (128, 128))  # 统一尺寸
            hist = compute_lbp_histogram(img)
            X.append(hist)
            y.append(label)
    return np.array(X), np.array(y), class_names

# ---------- 主流程 ----------
if __name__ == '__main__':
    # 数据集路径(按实际路径修改)
    DATA_DIR = './material_dataset'
    
    # 加载数据
    X, y, classes = load_dataset(DATA_DIR)
    print(f'加载完成:{len(X)}张图像,类别:{classes}')
    
    # 划分训练/测试集
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.3, random_state=42, stratify=y
    )
    
    # 特征标准化
    scaler = StandardScaler()
    X_train = scaler.fit_transform(X_train)
    X_test = scaler.transform(X_test)
    
    # 训练SVM分类器
    clf = SVC(kernel='rbf', C=10, gamma='scale', random_state=42)
    clf.fit(X_train, y_train)
    
    # 评估
    y_pred = clf.predict(X_test)
    print('\n分类结果:')
    print(classification_report(y_test, y_pred, target_names=classes))
    
    # 单张图像预测示例
    def predict_single(image_path):
        img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
        img = cv2.resize(img, (128, 128))
        hist = compute_lbp_histogram(img).reshape(1, -1)
        hist = scaler.transform(hist)
        pred = clf.predict(hist)[0]
        return classes[pred]
    
    # 测试示例(取消注释使用)
    # print('预测结果:', predict_single('./test_sample.jpg'))

运行与验证

环境依赖:Python 3.8+,安装 opencv-python、numpy、scikit-learn。

数据集准备:按以下目录结构放置图像(每类建议≥30张,图像尺寸不限,代码会自动缩放):

material_dataset/
├── wood/    # 木材表面图像
├── metal/   # 金属表面图像
└── plastic/ # 塑料表面图像

运行:修改代码末尾的 DATA_DIR 为实际路径,执行 python material_classifier.py。

通过标准:控制台输出分类报告,三类材质的精确率(precision)和召回率(recall)均达到0.85以上,即认为纹理特征有效区分三种材质。若未达标,可增加每类样本数量或调整LBP半径参数(radius)重新实验。

关键说明

  • LBP特征对光照变化具有一定鲁棒性,但采集图像时尽量保持光照条件一致。
  • 代码中的LBP实现为简化版8邻域,如需更优效果可替换为skimage.feature.local_binary_pattern的圆形邻域版本。
  • SVM参数(C=10, RBF核)为默认合理值,实际使用时可结合交叉验证调优。