You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于角度连接Julia中带标签的图像线条?

可行实现思路

1. 提取每个标签对应的线条像素坐标

先把每个标签对应的所有像素坐标提取出来,形成点集,方便后续处理:

# 假设thinned是二值图像,labels是标签矩阵
label_list = unique(labels)[2:end]  # 剔除背景标签0
line_points = Dict()
for lbl in label_list
    coords = findall(x -> x == lbl, labels)
    # 转换为(x,y)坐标(Julia矩阵是行优先,CartesianIndex的第一个元素是行即y,第二个是列即x)
    line_points[lbl] = [(c[2], c[1]) for c in coords]
end

2. 计算每条线条的主方向角度

因为线条可能是非直线,用**主成分分析(PCA)**求主方向更准确,能反映线条的整体走向:

using LinearAlgebra

function get_line_angle(points)
    # 转换为矩阵格式,每行对应一个点的坐标
    mat = hcat([p[1] for p in points], [p[2] for p in points])'
    # 坐标去均值,消除位置影响
    centered = mat .- mean(mat, dims=2)
    # 计算协方差矩阵
    cov_mat = cov(centered')
    # 求解特征值和特征向量,最大特征值对应主方向
    eigvals, eigvecs = eigen(cov_mat)
    main_vec = eigvecs[:, argmax(eigvals)]
    # 弧度转角度,统一到0-180°范围(避免方向相反的角度被误判)
    angle_rad = atan(main_vec[2], main_vec[1])
    angle_deg = rad2deg(angle_rad)
    return angle_deg < 0 ? angle_deg + 180 : angle_deg
end

# 批量计算所有线条的角度
line_angles = Dict(lbl => get_line_angle(points) for (lbl, points) in line_points)

3. 筛选角度相近的线条对

设定角度阈值(比如5°),遍历所有线条对,判断角度差是否在阈值范围内:

angle_threshold = 5.0  # 可根据实际图像调整
similar_pairs = []
lbls = collect(keys(line_angles))
for i in 1:length(lbls)
    for j in i+1:length(lbls)
        angle_diff = abs(line_angles[lbls[i]] - line_angles[lbls[j]])
        # 处理180°循环的情况,比如178°和2°的实际差是4°
        angle_diff = min(angle_diff, 180 - angle_diff)
        if angle_diff <= angle_threshold
            push!(similar_pairs, (lbls[i], lbls[j]))
        end
    end
end

4. 插值连接角度相近的线条

4.1 提取线条端点

通过邻域像素计数识别端点,闭合线条则取离中心最远的两个点:

function get_line_endpoints(points, thinned_img)
    endpoints = []
    for (x,y) in points
        # 统计3x3邻域内的非零像素数
        y_range = max(1,y-1):min(size(thinned_img,1),y+1)
        x_range = max(1,x-1):min(size(thinned_img,2),x+1)
        neighbors = thinned_img[y_range, x_range]
        count = sum(neighbors)
        # 端点只有1个相邻像素(自身为1,所以总和是2)
        if count == 2
            push!(endpoints, (x,y))
        end
    end
    # 处理闭合线条无端点的情况
    if length(endpoints) < 2
        center = mean(points)
        # 取离中心最远的两个点作为"端点"
        sorted_points = sort(points, by=p -> norm(p .- center), rev=true)
        endpoints = sorted_points[1:2]
    end
    return endpoints
end

# 提取所有线条的端点
line_endpoints = Dict(lbl => get_line_endpoints(points, thinned) for (lbl, points) in line_points)

4.2 插值连接端点

找到两条线条距离最近的端点对,用线性插值生成中间点并标记到图像:

function connect_lines(p1, p2, num_points=10)
    # 生成线性插值点
    x_vals = range(p1[1], p2[1], length=num_points)
    y_vals = range(p1[2], p2[2], length=num_points)
    return collect(zip(x_vals, y_vals))
end

# 对相似线条对进行连接
connected_img = copy(thinned)
for (lbl1, lbl2) in similar_pairs
    eps1 = line_endpoints[lbl1]
    eps2 = line_endpoints[lbl2]
    # 筛选距离最近的端点组合
    min_dist = Inf
    best_pair = (eps1[1], eps2[1])
    for e1 in eps1
        for e2 in eps2
            dist = norm(e1 .- e2)
            if dist < min_dist
                min_dist = dist
                best_pair = (e1, e2)
            end
        end
    end
    # 生成插值点并更新图像
    interp_points = connect_lines(best_pair[1], best_pair[2])
    for (x,y) in interp_points
        # 转换为矩阵索引并标记
        connected_img[round(Int,y), round(Int,x)] = 1
    end
end

额外注意事项

  • 若线条弯曲程度大,可考虑分段计算角度,或用霍夫变换检测直线段后再求角度
  • 角度阈值需根据实际场景调整,避免误连或漏连
  • 插值方式可替换为贝塞尔曲线等,实现更平滑的连接效果

内容的提问来源于stack exchange,提问作者F612

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 05:21:55