本次实验基于图像分类原理,选取 3 类数据用于训练与测试。利用 LM 滤波器组对数据提取特征。其原理是通过设定不同尺度、方向等参数生成多种滤波器,以获取图像不同特征。接着运用 SVM 算法进行分类,借助 libsvm - 3.21工具包,依据给定的多项式核函数的度和惩罚系数,用训练样本训练模型并对测试样本预测。最后通过评估函数计算 kappa 值、混淆矩阵等指标来评价,并分析 SVM 中两个关键参数对分类结果的影响,以此探究实验中图像分类的规律和性能 。
--------------------------------------------------------------------------------------------------------------------------------
LM 滤波器的特征提取原理
- 多分辨率分析:LM 滤波器利用拉普拉斯金字塔的构建来实现图像的多分辨率分解。首先,通过高斯滤波器对原始图像进行不同尺度的平滑处理,得到一系列不同分辨率的图像。然后,通过相邻尺度图像的差分来构建拉普拉斯金字塔,这些差分图像保留了图像在不同尺度下的细节信息,例如边缘、纹理等。这样可以捕捉到图像中不同大小和频率的特征,从粗到细地描述图像的结构。
- 边缘和纹理增强:拉普拉斯算子本身是一种二阶导数算子,对图像中的边缘和纹理等细节信息具有较强的响应。在 LM 滤波器中,通过在不同尺度上应用拉普拉斯算子,能够突出图像中各种尺度的边缘和纹理特征。边缘是图像中灰度变化剧烈的地方,而纹理则是由一系列重复的局部模式组成,LM 滤波器能够有效地将这些特征提取出来,使得图像的特征更加明显,便于后续的分类处理。
基于提取特征的分类原理
- 特征向量构建:将经过 LM 滤波器处理后得到的多尺度特征进行组合和量化,形成特征向量。这些特征向量包含了图像在不同尺度下的边缘、纹理等信息,能够全面地描述图像的特征。例如,可以将不同尺度下的拉普拉斯金字塔图像的统计信息(如均值、方差、能量等)作为特征向量的元素,或者使用一些局部特征描述子(如 SIFT、SURF 等)在 LM 滤波器处理后的图像上进行特征提取,然后将这些描述子组合成特征向量。
- 分类器训练与分类:使用已经标注好类别的图像数据集来训练分类器。常见的分类器有支持向量机(SVM)、神经网络(如卷积神经网络 CNN)、决策树等。在训练过程中,分类器学习特征向量与图像类别之间的映射关系。对于待分类的图像,首先通过 LM 滤波器提取其特征向量,然后将该特征向量输入到训练好的分类器中,分类器根据学习到的映射关系输出该图像所属的类别。
def gaussian1d(sigma, mean, x, ord):
x = np.array(x)
x_ = x - mean
var = sigma ** 2
g1 = (1 / np.sqrt(2 * np.pi * var)) * (np.exp((-1 * x_ * x_) / (2 * var)))
if ord == 0:
return g1
elif ord == 1:
return -g1 * ((x_) / (var))
else:
return g1 * (((x_ * x_) - var) / (var ** 2))
- 功能:计算一维高斯函数或其一阶、二阶导数。
- 参数:
sigma
:高斯函数的标准差,用于控制函数的宽度。mean
:高斯函数的均值,即函数的中心位置。x
:输入的自变量数组,可以是一维的数值序列。ord
:导数的阶数,ord=0
表示计算高斯函数本身,ord=1
表示计算一阶导数,ord=2
表示计算二阶导数。
- 实现步骤:
- 将输入的
x
转换为 NumPy 数组。 - 计算
x
与均值mean
的差值x_
。 - 计算方差
var
。 - 根据高斯函数的公式计算
g1
。 - 根据
ord
的值返回不同的结果:如果ord
为 0,返回高斯函数的值;如果ord
为 1,返回一阶导数的值;如果ord
为其他值(这里实际是 2),返回二阶导数的值。
- 将输入的
def gaussian2d(sup, scales):
var = scales * scales
shape = (sup, sup)
n, m = [(i - 1) / 2 for i in shape]
x, y = np.ogrid[-m:m + 1, -n:n + 1]
return (1 / np.sqrt(2 * np.pi * var)) * np.exp(-(x * x + y * y) / (2 * var))
- 功能:计算二维高斯函数。
- 参数:
sup
:生成的二维高斯函数的大小,即输出矩阵的边长。scales
:高斯函数的标准差,用于控制函数的宽度。
- 实现步骤:
- 计算方差
var
。 - 确定输出矩阵的形状
shape
。 - 计算矩阵中心的索引
n
和m
。 - 使用
np.ogrid
生成二维坐标网格x
和y
。 - 根据二维高斯函数的公式计算并返回结果。
- 计算方差
def log2d(sup, scales):
var = scales * scales
shape = (sup, sup)
n, m = [(i - 1) / 2 for i in shape]
x, y = np.ogrid[-m:m + 1, -n:n + 1]
g = (1 / np.sqrt(2 * np.pi * var)) * np.exp(-(x * x + y * y) / (2 * var))
return g * ((x * x + y * y) - var) / (var ** 2)
- 功能:计算二维拉普拉斯高斯(LoG)函数。
- 参数:
sup
:生成的二维 LoG 函数的大小,即输出矩阵的边长。scales
:高斯函数的标准差,用于控制函数的宽度。
- 实现步骤:
- 计算方差
var
。 - 确定输出矩阵的形状
shape
。 - 计算矩阵中心的索引
n
和m
。 - 使用
np.ogrid
生成二维坐标网格x
和y
。 - 计算二维高斯函数
g
。 - 根据二维 LoG 函数的公式计算并返回结果。
- 计算方差
def makefilter(scale, phasex, phasey, pts, sup):
gx = gaussian1d(3 * scale, 0, pts[0, ...], phasex)
gy = gaussian1d(scale, 0, pts[1, ...], phasey)
return np.reshape(gx * gy, (sup, sup))
- 功能:结合两个一维高斯函数生成一个二维滤波器。
- 参数:
scale
:用于控制高斯函数的尺度。phasex
:x
方向上高斯函数的导数阶数。phasey
:y
方向上高斯函数的导数阶数。pts
:包含两个数组的二维数组,分别表示x
和y
方向上的坐标点。sup
:生成的二维滤波器的大小,即输出矩阵的边长。
- 实现步骤:
- 调用
gaussian1d
函数计算x
方向上的一维高斯函数gx
,使用3 * scale
作为标准差,0
作为均值。 - 调用
gaussian1d
函数计算y
方向上的一维高斯函数gy
,使用scale
作为标准差,0
作为均值。 - 将
gx
和gy
相乘,并重新调整形状为(sup, sup)
后返回。
- 调用
def makeLMfilters():
sup = 49
scalex = np.sqrt(2) * np.array([1, 2, 3])
norient = 6
nrotinv = 12
nbar = len(scalex) * norient
nedge = len(scalex) * norient
nf = nbar + nedge + nrotinv
F = np.zeros([sup, sup, nf])
hsup = (sup - 1) / 2
x = [np.arange(-hsup, hsup + 1)]
y = [np.arange(-hsup, hsup + 1)]
[x, y] = np.meshgrid(x, y)
orgpts = np.array([x.flatten(), y.flatten()])
count = 0
num_filters_per_plot = 24
num_plots = (nf + num_filters_per_plot - 1) // num_filters_per_plot
for plot_num in range(num_plots):
fig = plt.figure(figsize=(10, 6))
for i in range(num_filters_per_plot):
if count >= nf:
break
scale = count // norient
scale = np.clip(scale, 0, len(scalex) - 1)
orient = count % norient
angle = (np.pi * orient) / norient
c, s = np.cos(angle), np.sin(angle)
rotpts = np.dot([[c, -s], [s, c]], orgpts)
F[:, :, count] = makefilter(scalex[scale], 0, 1, rotpts, sup)
ax = fig.add_subplot(4, 6, i+1)
plt.imshow(F[:, :, count], cmap='jet')
ax.set_title(f'Filter {count+1}')
ax.axis('off')
count += 1
plt.suptitle(f'LM滤波器 {plot_num * num_filters_per_plot + 1} 到 {min((plot_num + 1) * num_filters_per_plot, nf)}')
plt.tight_layout()
plt.show()
return F
- 功能:生成 LM(Leung-Malik)滤波器组,并可视化这些滤波器。
- 实现步骤:
- 初始化一些参数,包括滤波器的大小
sup
,尺度数组scalex
,方向数量norient
,旋转不变滤波器数量nrotinv
等。 - 计算不同类型滤波器的数量
nbar
、nedge
和总滤波器数量nf
,并初始化一个三维数组F
来存储所有滤波器。 - 生成原始的坐标网格
orgpts
。 - 计算需要绘制的滤波器数量和绘图数量。
- 遍历每个绘图:
- 创建一个新的图形。
- 遍历每个子图(滤波器):
- 计算当前滤波器的尺度
scale
和方向orient
。 - 计算旋转角度
angle
和旋转矩阵c
、s
。 - 对原始坐标点进行旋转得到
rotpts
。 - 调用
makefilter
函数生成当前滤波器,并存储到F
中。 - 将滤波器可视化,并设置子图的标题,关闭坐标轴。
- 计算当前滤波器的尺度
- 设置整个绘图的标题,调整布局并显示图形。
- 返回生成的 LM 滤波器组
F
。
- 初始化一些参数,包括滤波器的大小
def extract_features(images, F):
features = []
for img in images:
img_features = []
for i in range(F.shape[2]):
filtered = convolve2d(img, F[:, :, i], mode='same')
img_features.extend([filtered.mean(), filtered.std()])
features.append(img_features)
return np.array(features)
-
功能:该函数用于从给定的图像集合中提取特征。它通过将每个图像与一组滤波器(由
F
表示)进行卷积操作,然后计算卷积结果的均值和标准差,以此作为图像的特征。 -
参数:
images
:一个包含多个图像的列表或数组。每个图像应该是一个二维的数值数组,表示图像的像素值。F
:一个三维数组,表示一组滤波器。其中,F[:, :, i]
表示第i
个滤波器,它是一个二维数组。
-
实现步骤:
- 初始化一个空列表
features
,用于存储所有图像的特征。 - 遍历输入的图像列表
images
:- 对于每个图像
img
,初始化一个空列表img_features
,用于存储该图像的特征。 - 遍历滤波器数组
F
的第三个维度(即滤波器的数量):- 使用
convolve2d
函数对当前图像img
和第i
个滤波器F[:, :, i]
进行二维卷积操作,mode='same'
表示输出的卷积结果与输入图像大小相同。 - 计算卷积结果
filtered
的均值和标准差,并将这两个值添加到img_features
列表中。
- 使用
- 将当前图像的特征列表
img_features
添加到features
列表中。
- 对于每个图像
- 最后,将
features
列表转换为 NumPy 数组并返回。
- 初始化一个空列表
def evaluate_results(y_true, y_pred):
CM = confusion_matrix(y_true, y_pred)
CA = accuracy_score(y_true, y_pred)
OA = np.trace(CM) / np.sum(CM)
AA = np.mean(np.diag(CM) / np.sum(CM, axis=1))
kappa = cohen_kappa_score(y_true, y_pred)
return kappa, CM, CA, OA, AA
def confusion_matrix_plot(CM, class_names):
plt.figure(figsize=(8, 6))
sns.heatmap(CM, annot=True, fmt='d', xticklabels=class_names, yticklabels=class_names)
plt.xlabel('预测类别')
plt.ylabel('真实类别')
plt.title('混淆矩阵')
plt.show()
def main():
data_path = r"D:\LM_SVM_DTD_Classification\data\dtd\images"
selected_classes = ['banded', 'blotchy', 'braided']
try:
X, y = load_dtd_data(data_path, selected_classes)
except Exception as e:
print(f"数据加载失败: {str(e)}")
print("请检查:")
print(f"1. 路径是否存在: {os.path.exists(data_path)}")
print(f"2. 可用类别: {os.listdir(data_path) if os.path.exists(data_path) else '路径无效'}")
return
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
F = makeLMfilters()
X_train_feats = extract_features(X_train, F)
X_test_feats = extract_features(X_test, F)
print("\n训练SVM分类器...")
clf = svm.SVC(kernel='poly', degree=3, C=1.0)
clf.fit(X_train_feats, y_train)
y_pred = clf.predict(X_test_feats)
kappa, CM, CA, OA, AA = evaluate_results(y_test, y_pred)
print("\n分类结果统计表:")
print(f"{'指标':<15}{'值':<10}")
print("-" * 25)
print(f"{'Kappa系数':<15}{kappa:.4f}")
print(f"{'总体准确率(OA)':<15}{OA:.4f}")
print(f"{'平均准确率(AA)':<15}{AA:.4f}")
print(f"{'分类准确率(CA)':<15}{CA:.4f}")
plt.figure(figsize=(10, 8))
sns.heatmap(CM, annot=True, fmt='d', cmap='Blues',
xticklabels=selected_classes,
yticklabels=selected_classes)
plt.xlabel('预测类别', fontsize=12)
plt.ylabel('真实类别', fontsize=12)
plt.title('分类混淆矩阵', fontsize=15)
plt.xticks(rotation=45)
plt.yticks(rotation=45)
plt.tight_layout()
plt.savefig('confusion_matrix.png', dpi=300, bbox_inches='tight')
plt.show()
print("\n参数分析结果:")
degrees = range(1, 8) # 1-7
C_values = [10 ** i for i in range(-1, 7)] # 10^-1到10^6
results = []
best_acc = 0
best_params = {}
for degree in degrees:
for C in C_values:
clf = svm.SVC(kernel='poly', degree=degree, C=C)
clf.fit(X_train_feats, y_train)
y_pred = clf.predict(X_test_feats)
acc = accuracy_score(y_test, y_pred)
results.append({
'degree': degree,
'C': C,
'accuracy': acc
})
if acc > best_acc:
best_acc = acc
best_params = {'degree': degree, 'C': C}
print(f"degree={degree}, C={C:.1e}: 准确率={acc:.4f}")
print(f"\n最佳参数组合: {best_params}")
print(f"最高准确率: {best_acc:.4f}")