123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
"""演示2: 霍夫梯度法检测断裂圆弧。
生成一张"断成多段"的圆环图像, 用 cv2.HoughCircles 识别出完整圆,
输出 原图 | 中值滤波+边缘 | 检测结果 三栏对比图。
"""
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from figure_style import figures_dir, configure
configure()
OUT = figures_dir()
os.makedirs(OUT, exist_ok=True)
H, W = 320, 400
img = np.zeros((H, W), dtype=np.uint8)
cx, cy, R = 200, 160, 90
def draw_broken_circle(im, center, r, arcs, thickness=3):
"""arcs: 每段为 (start_deg, end_deg), 角度之间留缺口。"""
for s, e in arcs:
cv2.ellipse(im, center, (r, r), 0, s, e, 255, thickness)
# 圆周被切掉 6 段, 但每段覆盖较宽的角度, 保证霍夫投票足够
draw_broken_circle(img, (cx, cy), R, [
(0, 70), (95, 165), (190, 260), (285, 360),
])
# 加一个内圈小圆(干扰)与少量噪点
cv2.circle(img, (cx, cy), 25, 255, 1)
rng = np.random.default_rng(1)
noise = rng.random((H, W)) > 0.998
img[noise] = 255
# 中值滤波去噪
blur = cv2.medianBlur(img, 5)
# 边缘图(用于展示)
edges = cv2.Canny(blur, 50, 150)
# 霍夫梯度法检测圆
circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, dp=1, minDist=60,
param1=100, param2=20, minRadius=60, maxRadius=120)
res = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
n = 0
if circles is not None:
circles = np.uint16(np.around(circles))
for c in circles[0, :]:
x, y, r = int(c[0]), int(c[1]), int(c[2])
cv2.circle(res, (x, y), r, (0, 255, 0), 2) # 绿圆
cv2.circle(res, (x, y), 2, (0, 0, 255), 3) # 红圆心
n += 1
fig, axes = plt.subplots(1, 3, figsize=(13, 4.5))
for ax, im, title in zip(axes, (img, edges, res),
("原图(断裂圆弧)", "Canny 边缘图", f"霍夫圆检测结果 ({n} 个圆)")):
ax.imshow(im, cmap="gray")
ax.set_title(title, fontsize=11)
ax.axis("off")
fig.suptitle("示例一(续): 霍夫梯度法 — 断裂圆的识别", fontsize=14)
fig.tight_layout(rect=(0, 0, 1, 0.95))
out_path = os.path.join(OUT, "fig_hough_circles.png")
fig.savefig(out_path, dpi=150)
print(f"检测到 {n} 个圆 -> {out_path}")
|