Julia中轮廓最短距离计算、合并及杂点去除问题咨询
Julia轮廓检测:距离计算、轮廓合并与毛刺去除方案
一、两个折线轮廓的最短距离计算
可以借助GeometryBasics.jl库实现线段间的距离计算,核心思路是将轮廓转换为线段集合,遍历所有线段对找到最小距离。
实现步骤:
- 安装依赖:
using Pkg; Pkg.add(["GeometryBasics", "LinearAlgebra"])
- 转换轮廓为线段数组并计算最短距离:
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
相关产品推荐
相关产品推荐

