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

Julia中轮廓最短距离计算、合并及杂点去除问题咨询

Julia轮廓检测:距离计算、轮廓合并与毛刺去除方案

一、两个折线轮廓的最短距离计算

可以借助GeometryBasics.jl库实现线段间的距离计算,核心思路是将轮廓转换为线段集合,遍历所有线段对找到最小距离。

实现步骤:

  1. 安装依赖:
using Pkg; Pkg.add(["GeometryBasics", "LinearAlgebra"])
  1. 转换轮廓为线段数组并计算最短距离:
using GeometryBasics, LinearAlgebra

# 将CartesianIndex轮廓转为线段集合(默认处理闭合轮廓)
function contour_to_segments(contour::Vector{CartesianIndex{2}})
    segments = Segment{Point2{Int}}[]
    for i in 1:length(contour)-1
        push!(segments, Segment(Point(contour[i][1], contour[i][2]), Point(contour[i+1][1], contour[i+1][2])))
    end
    # 闭合轮廓需补充最后一点到第一点的线段
    push!(segments, Segment(Point(contour[end][1], contour[end][2]), Point(contour[1][1], contour[1][2])))
    return segments
end

# 计算两个轮廓的最短距离
function shortest_contour_distance(contour1::Vector{CartesianIndex{2}}, contour2::Vector{CartesianIndex{2}})
    segs1 = contour_to_segments(contour1)
    segs2 = contour_to_segments(contour2)
    min_dist = Inf
    for s1 in segs1, s2 in segs2
        dist = distance(s1, s2)
        dist < min_dist && (min_dist = dist)
    end
    return min_dist
end

二、两个轮廓的合并

合并逻辑需基于轮廓的最近点对拼接,确保轮廓方向一致(顺时针/逆时针),以下是基础闭合轮廓的实现:

# 找到两个轮廓的最近点索引对
function find_closest_points(contour1::Vector{CartesianIndex{2}}, contour2::Vector{CartesianIndex{2}})
    min_dist = Inf
    idx1, idx2 = 1, 1
    for (i, p1) in enumerate(contour1), (j, p2) in enumerate(contour2)
        dist = norm(Point(p1[1], p1[2]) - Point(p2[1], p2[2]))
        if dist < min_dist
            min_dist = dist
            idx1, idx2 = i, j
        end
    end
    return idx1, idx2
end

# 合并两个闭合轮廓
function merge_contours(contour1::Vector{CartesianIndex{2}}, contour2::Vector{CartesianIndex{2}})
    idx1, idx2 = find_closest_points(contour1, contour2)
    # 按顺序拼接:遍历完contour1所有点,再遍历contour2除连接点外的所有点
    merged = vcat(
        contour1[idx1:end],
        contour1[1:idx1-1],
        contour2[idx2+1:end],
        contour2[1:idx2]
    )
    # 去重避免重复连接点
    unique!(merged)
    return merged
end

如果是非闭合轮廓,只需调整拼接逻辑,直接在最近点处连接两个轮廓的端点即可。

三、去除错误轮廓点/毛刺

推荐两种高效方法:

方法1:Ramer-Douglas-Peucker算法(折线简化)

使用Simplify.jl库实现,通过设定阈值保留轮廓关键特征点,去除冗余毛刺:

using Pkg; Pkg.add("Simplify")
using Simplify

# 转换轮廓为坐标数组
function contour_to_points(contour::Vector{CartesianIndex{2}})
    return [(p[1], p[2]) for p in contour]
end

# 简化轮廓,epsilon为简化阈值(值越大简化程度越高)
function simplify_contour(contour::Vector{CartesianIndex{2}}, epsilon::Float64=2.0)
    simplified_points = simplify(contour_to_points(contour), epsilon)
    return CartesianIndex.(first.(simplified_points), last.(simplified_points))
end

方法2:角度突变过滤

通过计算连续三点的夹角,去除角度突变的毛刺点:

# 计算三点间的夹角(弧度)
function angle_between(p1::CartesianIndex{2}, p2::CartesianIndex{2}, p3::CartesianIndex{2})
    v1 = (p1[1]-p2[1], p1[2]-p2[2])
    v2 = (p3[1]-p2[1], p3[2]-p2[2])
    dot_prod = v1[1]*v2[1] + v1[2]*v2[2]
    mag1 = sqrt(v1[1]^2 + v1[2]^2)
    mag2 = sqrt(v2[1]^2 + v2[2]^2)
    mag1 == 0 || mag2 == 0 && return π
    cos_theta = clamp(dot_prod/(mag1*mag2), -1.0, 1.0)
    return acos(cos_theta)
end

# 去除夹角过小的毛刺点,angle_threshold为阈值(默认45度)
function remove_burrs(contour::Vector{CartesianIndex{2}}, angle_threshold::Float64=π/4)
    filtered = copy(contour)
    i = 2
    while i < length(filtered)
        prev, curr, next_p = filtered[i-1], filtered[i], filtered[i+1]
        ang = angle_between(prev, curr, next_p)
        ang < angle_threshold ? deleteat!(filtered, i) : (i += 1)
    end
    # 处理闭合轮廓的首尾夹角
    length(filtered) >=3 && begin
        ang = angle_between(filtered[end], filtered[1], filtered[2])
        ang < angle_threshold && deleteat!(filtered, 1)
    end
    return filtered
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.11 01:50:32