使用霍夫变换检测矩形角点时坐标显示异常,请求排查问题
问题排查与修正方案
核心错误分析
你的代码存在4个关键问题,直接导致角点检测异常:
直线方程转换错误
霍夫变换的直线公式为x·cosθ + y·sinθ = dist,你错误地将斜率计算为np.tan(angle),正确斜率应为-np.cos(angle)/np.sin(angle)(sinθ≠0时)。用tanθ会完全颠倒直线的倾斜方向,导致直线模型彻底错误。坐标映射完全颠倒
你将求解得到的交点(x,y)存储为(y, -x),这不仅颠倒了x/y轴对应关系,还引入了不必要的负号,直接让绘制的角点偏离真实位置。未处理垂直直线的数值异常
当θ接近±π/2时,sinθ趋近于0,用y=kx+b形式计算会产生无穷大斜率,导致NaN或异常值,无法正确表示垂直直线。未过滤冗余直线
hough_line_peaks可能检测到多条近似平行的噪声直线,直接两两求交会生成大量无效交点,而非矩形的4个角点。
修正后的完整代码
import cv2 import numpy as np import matplotlib.pyplot as plt from skimage.transform import hough_line, hough_line_peaks # 图像预处理 image = cv2.imread('/rect_pics/5.png') gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # cv2.imread默认是BGR格式,修正转换参数 gray = cv2.GaussianBlur(gray, (3, 3), 0) cv2.imwrite("gray.png", gray) edged = cv2.Canny(gray, 50, 150) # 调整Canny阈值,减少噪声边缘 cv2.imwrite("edged.png", edged) # 霍夫变换 using_angles = np.linspace(-np.pi / 2, np.pi / 2, 1800) halfspace, theta, dist = hough_line(edged, using_angles) # 霍夫累加器可视化 plt.figure(figsize=(15, 10)) plt.title('Hough Accumulator', size=30) plt.imshow(halfspace, cmap='gray') plt.savefig('accumulator.png') # 结果可视化 fig, axes = plt.subplots(1, 2, figsize=(20, 8)) ax = axes.ravel() ax[0].imshow(edged, cmap='gray') ax[0].set_title('Edged rectangle', size=30) ax[0].set_axis_off() ax[1].imshow(edged, cmap='gray') origin = np.array((0, edged.shape[1])) lines = [] # 直接存储霍夫直线的(theta, dist)参数,避免转换误差 for _, angle, dist_val in zip(*hough_line_peaks(halfspace, theta, dist)): y0, y1 = (dist_val - origin * np.cos(angle)) / np.sin(angle) ax[1].plot(origin, (y0, y1), '-r') lines.append((angle, dist_val)) print(f"Angle: {np.degrees(angle):.2f}°, Dist: {dist_val:.2f}") # 过滤近似平行的冗余直线,保留矩形的4条有效边 angle_tolerance = np.deg2rad(1) # 1度的角度公差 clustered_lines = [] used_indices = set() for i in range(len(lines)): if i in used_indices: continue current_angle = lines[i][0] cluster = [lines[i]] used_indices.add(i) # 收集所有近似平行的直线 for j in range(i+1, len(lines)): if abs(lines[j][0] - current_angle) < angle_tolerance: cluster.append(lines[j]) used_indices.add(j) # 取聚类中距离的中位数作为代表直线 cluster_dists = [d for _, d in cluster] median_dist = np.median(cluster_dists) clustered_lines.append((current_angle, median_dist)) # 确保只保留4条有效直线(可根据实际情况调整) if len(clustered_lines) > 4: clustered_lines = clustered_lines[:4] # 正确计算两条霍夫直线的交点 def hough_line_intersection(angle1, dist1, angle2, dist2): # 直线方程:x*cosθ + y*sinθ = dist A = np.array([ [np.cos(angle1), np.sin(angle1)], [np.cos(angle2), np.sin(angle2)] ]) b = np.array([dist1, dist2]) try: x, y = np.linalg.solve(A, b) return x, y except np.linalg.LinAlgError: # 平行直线,无有效交点 return None, None inter = [] for i in range(len(clustered_lines)): for j in range(i+1, len(clustered_lines)): angle1, dist1 = clustered_lines[i] angle2, dist2 = clustered_lines[j] # 跳过平行直线 if abs(angle1 - angle2) < angle_tolerance: continue x, y = hough_line_intersection(angle1, dist1, angle2, dist2) # 只保留图像范围内的有效交点 if x is not None and 0 <= x < edged.shape[1] and 0 <= y < edged.shape[0]: inter.append((x, y)) print(f"Valid intersection: ({x:.2f}, {y:.2f})") # 正确绘制角点 ax[1].scatter([p[0] for p in inter], [p[1] for p in inter], color='b', s=100, marker='o') ax[1].set_xlim(0, edged.shape[1]) ax[1].set_ylim(edged.shape[0], 0) ax[1].set_title('Detected rectangle', size=30) plt.savefig('detection.png') plt.show()
关键修正说明
- 保留原始霍夫参数:直接用
(theta, dist)存储直线,避免转换为y=kx+b时的数值不稳定问题,用标准直线方程求解交点更准确。 - 直线聚类过滤:通过角度公差筛选出4条有效直线(矩形的两组对边),去除噪声导致的冗余直线。
- 交点范围校验:只保留图像边界内的交点,排除无穷远处的无效点。
- 坐标映射修正:直接使用求解得到的
(x,y)绘制角点,不再做错误的轴反转和取负操作。 - 预处理优化:调整Canny阈值减少噪声,修正BGR转灰度的参数(cv2.imread默认是BGR格式)。
内容的提问来源于stack exchange,提问作者edit edit
相关产品推荐
相关产品推荐

