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核)为默认合理值,实际使用时可结合交叉验证调优。