如何检测图像中近似共线线段以修复重叠样本计数与长度测量问题
重叠样本计数修复:共线线段合并方案
针对重叠样本被误计数的问题,核心是把近似共线且距离接近的线段合并为单个样本,以下是具体实现方案:
核心思路
判断两条线段属于同一样本需要满足两个条件:
- 线段方向近似一致(共线)
- 线段之间的距离足够近(属于同一“直线簇”)
具体实现步骤
1. 计算每条线段的几何参数
先为每个线段提取关键参数:斜率、直线归一化一般式(用于距离计算)、中点坐标、线段长度。
2. 分组近似共线的线段
通过斜率阈值筛选方向一致的线段,垂直线单独处理避免除零错误。
3. 验证组内线段的距离
对同方向的线段,计算线段中点到基准直线的距离,只有距离小于阈值的才归为同一样本。
4. 合并线段并统计结果
将同组线段的所有端点整合,用最小外接矩形的长轴长度作为合并后样本的长度,最终统计样本数量。
代码示例
假设你的extremes_dict结构为{label: [(x1,y1), (x2,y2)]},对应每个线段的两个端点:
import numpy as np import cv2 def get_line_params(p1, p2): """计算线段的关键几何参数""" x1, y1 = p1 x2, y2 = p2 dx = x2 - x1 dy = y2 - y1 # 处理斜率,垂直线标记为无穷大 slope = np.inf if dx == 0 else dy / dx # 直线归一化一般式 Ax + By + C = 0 A, B, C = dy, -dx, dx*y1 - dy*x1 norm = np.sqrt(A**2 + B**2) if norm != 0: A, B, C = A/norm, B/norm, C/norm # 中点坐标和线段长度 mid = ((x1+x2)/2, (y1+y2)/2) length = np.sqrt(dx**2 + dy**2) return slope, (A,B,C), mid, length # 配置阈值(根据图像分辨率和样本粗细调整) SLOPE_TOLERANCE = 0.05 # 斜率容忍度 DISTANCE_TOLERANCE = 3 # 像素距离容忍度 # 预处理所有线段参数 line_params = {} for label, endpoints in extremes_dict.items(): slope, abc, mid, length = get_line_params(*endpoints) line_params[label] = { "slope": slope, "abc": abc, "mid": mid, "length": length, "endpoints": endpoints } # 分组共线且近距离的线段 groups = [] used_labels = set() for label1, params1 in line_params.items(): if label1 in used_labels: continue current_group = [label1] used_labels.add(label1) slope1, abc1 = params1["slope"], params1["abc"] for label2, params2 in line_params.items(): if label2 in used_labels: continue slope2 = params2["slope"] # 判断斜率是否近似一致 same_dir = False if np.isinf(slope1) and np.isinf(slope2): same_dir = True elif not np.isinf(slope1) and not np.isinf(slope2): same_dir = abs(slope1 - slope2) < SLOPE_TOLERANCE if not same_dir: continue # 判断中点到基准直线的距离是否符合要求 A, B, C = abc1 mx, my = params2["mid"] dist = abs(A*mx + B*my + C) if dist < DISTANCE_TOLERANCE: current_group.append(label2) used_labels.add(label2) groups.append(current_group) # 统计最终结果 final_count = len(groups) final_lengths = [] for group in groups: # 收集组内所有端点,计算最小外接矩形的长轴长度 all_points = [] for label in group: all_points.extend(line_params[label]["endpoints"]) all_points = np.array(all_points, dtype=np.float32) rect = cv2.minAreaRect(all_points) sample_length = max(rect[1]) final_lengths.append(round(sample_length, 2)) print(f"修正后样本数量: {final_count}") print(f"各样本长度(像素): {final_lengths}")
注意事项
- 阈值调整:
SLOPE_TOLERANCE和DISTANCE_TOLERANCE需要根据你的图像实际情况微调,比如样本越粗,距离阈值可以适当放大。 - 方向判断优化:如果样本有轻微弯曲,可以用方向向量的夹角替代斜率判断(计算两个线段方向向量的点积,夹角小于5°即视为同方向)。
- 端点衔接验证:可以额外加入端点距离判断,若组内线段的端点间距小于阈值,进一步确认属于同一样本。
内容的提问来源于stack exchange,提问作者pippo1980
相关产品推荐
相关产品推荐

