1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
"""演示1: 三维模型识别与分类 — 三大表示流派对比 + 经典方法.
1. 左: 三种输入表示(多视图 / 体素 / 点云)示意
2. 中: 经典"视图特征 + 形状描述子"分类流程(可运行) — 用 PCA 描述子做简单分类
3. 右: 各流派代表方法准确率对比柱状图
"""
import os
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from figure_style import figures_dir, configure
configure()
OUT = figures_dir()
os.makedirs(OUT, exist_ok=True)
# ============ 中间: 经典形状描述子分类 (可运行) ============
rng = np.random.default_rng(0)
def make_shape(shape, n=300):
"""生成三种简单 3D 形状的点云."""
if shape == 'sphere':
u = rng.uniform(0, 2*np.pi, n); v = rng.uniform(0, np.pi, n)
r = 1 + rng.normal(0, 0.02, n)
return np.stack([r*np.sin(v)*np.cos(u), r*np.sin(v)*np.sin(u), r*np.cos(v)], 1)
if shape == 'box':
p = rng.uniform(-1, 1, (n, 3)); p[np.random.rand(n) > 0.25] *= 0.7
return p
if shape == 'cylinder':
th = rng.uniform(0, 2*np.pi, n); z = rng.uniform(-1, 1, n)
r = 1 + rng.normal(0, 0.02, n)
return np.stack([r*np.cos(th), r*np.sin(th), z], 1)
def descriptor(pc):
"""PCA 形状描述子: 3 特征值归一化 + 紧凑度 + 扁平度 (可解释特征)."""
c = pc.mean(0)
ev = np.sort(np.maximum(np.linalg.eigvalsh(np.cov((pc - c).T)), 1e-6))[::-1] # 特征值(升序→倒序)
ev = ev / ev.sum()
# 紧凑度: 各轴标准差均值; 扁平度: 最大/最小特征值比
compact = pc.std(0).mean()
flat = float(ev[0]) / (float(ev[2]) + 1e-6)
return np.concatenate([ev, [compact, flat]])
X, y = [], []
for s in ['sphere', 'box', 'cylinder']:
for _ in range(40):
X.append(descriptor(make_shape(s)))
y.append(s)
X, y = np.array(X), np.array(y)
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=0)
svm = SVC(kernel='rbf').fit(Xtr, ytr)
acc = svm.score(Xte, yte)
print(f"经典形状描述子 + SVM: 3类分类 acc = {acc:.3f}")
# ============ 右: 流派对比 ============
fig = plt.figure(figsize=(15, 5))
# 左: 三种表示
ax = fig.add_subplot(1, 3, 1, projection='3d')
for s, c, off in [('sphere', 'tab:red', 0), ('box', 'tab:blue', 3), ('cylinder', 'tab:green', 6)]:
pc = make_shape(s, 150)
ax.scatter(pc[:, 0]+off, pc[:, 1], pc[:, 2], s=2, c=c, alpha=0.6)
ax.set_title("点云表示 (三种形状)")
ax.set_xticks([]); ax.set_yticks([]); ax.set_zticks([])
# 中: 描述子分类流程
ax = fig.add_subplot(1, 3, 2)
from sklearn.decomposition import PCA
pca2 = PCA(n_components=2).fit(X)
X2 = pca2.transform(X)
for s, c in [('sphere', 'tab:red'), ('box', 'tab:blue'), ('cylinder', 'tab:green')]:
ax.scatter(X2[np.array(y)==s, 0], X2[np.array(y)==s, 1], c=c, s=12, label=s, alpha=0.7)
ax.set_title(f"PCA 形状描述子分布 (2D)\n描述子→SVM acc={acc:.2f}")
ax.legend(fontsize=8); ax.grid(alpha=0.3)
# 右: 流派代表方法 ModelNet40 精度
ax = fig.add_subplot(1, 3, 3)
methods = ['3DShapeNets\n(体素)', 'VoxNet\n(体素)', 'MVCNN\n(视图)', 'PointNet\n(点云)', 'PointNet++\n(点云)', 'PointMLP\n(点云)']
accs = [77.3, 83.0, 90.1, 89.2, 90.7, 93.7]
cols = ['tab:gray', 'tab:gray', 'tab:orange', 'tab:blue', 'tab:blue', 'tab:blue']
bars = ax.barh(methods, accs, color=cols)
for b, a in zip(bars, accs):
ax.text(b.get_width()+0.5, b.get_y()+b.get_height()/2, f'{a:.1f}%', va='center', fontsize=8)
ax.set_xlim(0, 100); ax.set_xlabel('ModelNet40 OA (%)')
ax.set_title('三大流派代表方法精度对比\n(体素/视图/点云)')
ax.grid(axis='x', alpha=0.3)
fig.suptitle('三维模型识别与分类: 表示流派对比 + 经典描述子方法', fontsize=14)
fig.tight_layout(rect=(0, 0, 1, 0.93))
out = os.path.join(OUT, 'fig3d_classify.png')
fig.savefig(out, dpi=150)
print(f"-> {out}")
|