Python:实现线段聚类,将相邻线段合并为组

2025-04-28  本文已影响0人  大龙10

一、“相邻线段”

二、如何衡量线段之间的“相邻”程度

可能的思路包括:

考虑到实现复杂度,可能先采用一个简单的方法,比如基于线段的中点或端点的距离来进行聚类。
比如,计算每条线段的中点,然后根据中点之间的距离进行聚类。
或者,对于线段端点之间的最小距离进行判断

三、如何聚类

四、合并线段

五、程序

# -*- coding: utf-8 -*-
"""
Created on Sat Apr 26 10:08:50 2025

实现线段聚类,将相邻线段合并为组ds008.py
"""

import cv2
import numpy as np
from sklearn.cluster import DBSCAN

def merge_lines(cluster_lines):
    """ 合并同一簇内的线段 """
    points = []
    for line in cluster_lines:
        x1, y1, x2, y2 = line[0]
        points.append([x1, y1])
        points.append([x2, y2])
    points = np.array(points)
    
    if len(points) < 2:
        return cluster_lines[0]
    
    # PCA计算主方向
    mean = np.mean(points, axis=0)
    centered = points - mean
    cov = np.cov(centered.T)
    
    if np.isnan(cov).any():
        return cluster_lines[0]
    
    eigenvals, eigenvecs = np.linalg.eigh(cov)
    primary_vec = eigenvecs[:, np.argmax(eigenvals)]
    
    # 计算投影极值点
    proj = np.dot(centered, primary_vec)
    min_proj = np.min(proj)
    max_proj = np.max(proj)
    
    # 生成合并后的线段
    point_min = mean + min_proj * primary_vec
    point_max = mean + max_proj * primary_vec
    return np.array([[int(point_min[0]), int(point_min[1]), 
                     int(point_max[0]), int(point_max[1])]])

def find_rightmost_segments(image_path, num_segments=3):
    # 读取图像并检测边缘
    image = cv2.imread(image_path)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    edges = cv2.Canny(blurred, 50, 150)
    
    # 霍夫变换检测线段
    lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=50,
                           minLineLength=50, maxLineGap=10)
    
    rightmost_lines = []
    if lines is not None:
        # 初选最右侧线段
        lines_with_right = []
        for line in lines:
            x1, y1, x2, y2 = line[0]
            right_x = max(x1, x2)
            lines_with_right.append((right_x, line))
        
        lines_with_right.sort(reverse=True, key=lambda x: x[0])
        selected = lines_with_right[:num_segments*2]  # 扩大初选范围
        pre_merge = [line for (_, line) in selected]
        
        # 线段聚类
        features = []
        for line in pre_merge:
            x1, y1, x2, y2 = line[0]
            features.append([(x1+x2)/2, (y1+y2)/2])
        
        features = np.array(features)
        if len(features) > 0:
            # DBSCAN聚类(核心参数:eps控制聚类半径)
            dbscan = DBSCAN(eps=30, min_samples=1)
            clusters = dbscan.fit_predict(features)
            
            # 合并线段
            merged_lines = []
            for cluster_id in np.unique(clusters):
                cluster_lines = [pre_merge[i] for i in np.where(clusters == cluster_id)[0]]
                merged = merge_lines(cluster_lines)
                merged_lines.append(merged)
            
            # 最终筛选最右侧线段
            merged_with_right = []
            for line in merged_lines:
                x1, y1, x2, y2 = line[0]
                merged_with_right.append((max(x1, x2), line))
            
            merged_with_right.sort(reverse=True, key=lambda x: x[0])
            rightmost_lines = [line for (_, line) in merged_with_right[:num_segments]]
    
    # 可视化结果
    result = image.copy()
    colors = [(0,255,0), (0,0,255), (255,0,0)]  # 不同线段用不同颜色
    for idx, line in enumerate(rightmost_lines):
        x1, y1, x2, y2 = line[0]
        cv2.line(result, (x1,y1), (x2,y2), colors[idx%3], 2)
    
    return result

# 使用示例
output_image = find_rightmost_segments("D:/OpenCVpic/p03.jpg", num_segments=5)

# 显示并保存结果
cv2.imshow("Detected Rightmost Lines", output_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite("output.jpg", output_image)

六、说明

自动合并相邻线段,消除重复检测;有效处理存在多个相邻线段的情况,特别适合需要识别明显独立结构的场景(如检测多根立柱、多车道线等)。

上一篇 下一篇

猜你喜欢

热点阅读