123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051 |
"""演示4: RANSAC 从含离群噪声的角点/点集中拟合圆。
生成一个带高斯噪声的圆 + 大量随机离群点, 用 skimage.measure.ransac + CircleModel 拟合。
"""
import os
import numpy as np
import matplotlib.pyplot as plt
from skimage.measure import CircleModel, ransac
from figure_style import figures_dir, configure
configure()
OUT = figures_dir()
os.makedirs(OUT, exist_ok=True)
np.random.seed(0)
true_center = (100, 100)
true_radius = 50
theta = np.linspace(0, 2 * np.pi, 120)
circle_pts = np.array([
[true_center[0] + true_radius * np.cos(t) + np.random.normal(0, 4),
true_center[1] + true_radius * np.sin(t) + np.random.normal(0, 4)]
for t in theta
])
outliers = np.random.rand(80, 2) * 200 # 大量离群点
points = np.vstack([circle_pts, outliers])
model_robust, inliers = ransac(points, CircleModel, 3,
residual_threshold=8, max_trials=2000)
cy, cx, r = model_robust.params
fig, ax = plt.subplots(figsize=(7, 7))
inlier_pts = points[inliers]
outlier_pts = points[~inliers]
ax.plot(inlier_pts[:, 0], inlier_pts[:, 1], ".", c="green", label=f"内点 ({inlier_pts.shape[0]})")
ax.plot(outlier_pts[:, 0], outlier_pts[:, 1], ".", c="red", alpha=0.5, label=f"离群点 ({outlier_pts.shape[0]})")
circle = plt.Circle((cx, cy), r, color="blue", fill=False, lw=2, label="RANSAC 拟合圆")
ax.add_patch(circle)
ax.plot(cx, cy, "b+", ms=12, mew=2)
ax.legend(loc="upper left")
ax.set_aspect("equal")
ax.set_title(f"示例三: RANSAC — 拟合圆心 ({cx:.1f}, {cy:.1f}) 半径 {r:.1f}\n"
f"真实: 圆心 (100, 100) 半径 50", fontsize=12)
fig.tight_layout()
out_path = os.path.join(OUT, "fig_ransac_circle.png")
fig.savefig(out_path, dpi=150)
print(f"拟合圆心 ({cx:.1f}, {cy:.1f}), 半径 {r:.1f}, 内点数 {inlier_pts.shape[0]}"
f" -> {out_path}")
|