///|
/// Line segment detector parameters.
pub(all) struct LsdParameters {
scale : Double
sigma_scale : Double
quant : Double
ang_th : Double
log_eps : Double
density_th : Double
n_bins : Int
} derive(Debug, Eq)
///|
/// Creates default LSD parameters.
pub fn LsdParameters::default() -> LsdParameters {
{
scale: 0.8,
sigma_scale: 0.6,
quant: 2.0,
ang_th: 22.5, // Degrees
log_eps: 0.0,
density_th: 0.7,
n_bins: 1024,
}
}
///|
/// Detected 2D Line Segment with confidence score and angle.
pub(all) struct LineSegment {
p1 : Point2D
p2 : Point2D
width : Double
p_val : Double
nfa : Double
} derive(Debug, Eq)
///|
/// Creates a LineSegment.
pub fn LineSegment::new(
p1 : Point2D,
p2 : Point2D,
width : Double,
p_val : Double,
nfa : Double,
) -> LineSegment {
{ p1, p2, width, p_val, nfa }
}
///|
/// Fast Line Segment Detector (LSD) implementation for extracting straight edge segments.
pub fn detect_line_segments(
img : Image,
min_length? : Double = 10.0,
gradient_threshold? : Double = 20.0,
) -> Array[LineSegment] {
let w = img.width
let h = img.height
let segments : Array[LineSegment] = []
if w < 10 || h < 10 {
return segments
}
// 1. Compute gradients
let (gx, gy) = filter_sobel(img)
let (mag, angle) = compute_gradient_magnitude_and_angle(gx, gy)
// 2. Threshold gradient magnitude and find edge seed pixels
let used = Array::make(w * h, false)
for y in 1..<(h - 1) {
for x in 1..<(w - 1) {
let idx = y * w + x
if !used[idx] && mag[idx] >= gradient_threshold {
// Region growing along level-line direction (perpendicular to gradient: theta + pi/2)
let seed_angle = angle[idx] + 1.5707963267948966 // + pi/2
let region_pts : Array[Point2D] = [
Point2D::new(x.to_double(), y.to_double()),
]
used[idx] = true
let mut search_x = x
let mut search_y = y
let dx = @math.cos(seed_angle).round().to_int()
let dy = @math.sin(seed_angle).round().to_int()
for _step in 0..<100 {
search_x += dx
search_y += dy
if search_x >= 0 && search_x < w && search_y >= 0 && search_y < h {
let n_idx = search_y * w + search_x
if !used[n_idx] && mag[n_idx] >= gradient_threshold {
let diff_angle = (angle[n_idx] + 1.5707963267948966 - seed_angle).abs()
if diff_angle < 0.5 {
used[n_idx] = true
region_pts.push(
Point2D::new(search_x.to_double(), search_y.to_double()),
)
} else {
break
}
} else {
break
}
} else {
break
}
}
if region_pts.length() >= 5 {
let p_first = region_pts[0]
let p_last = region_pts[region_pts.length() - 1]
let seg_len = p_first.distance(p_last)
if seg_len >= min_length {
segments.push(LineSegment::new(p_first, p_last, 2.0, 0.95, 1.0))
}
}
}
}
}
segments
}