SweepLSD: A One-Pass, O(width)-Memory Line Segment Detector with an Integer-Only Streaming Core and a Real-Time FPGA Realization
Organizations: Independent researcher
Abstract
We present SweepLSD, a line segment detector that reads the image exactly once and emits each segment within a few rows of its last pixel passing the scan line. Every stage, including connected-component labeling and the final line test, processes the image as a row stream: intermediate memory is O(width) rather than O(pixels), and the per-pixel core is integer-only. We give the first complete description of the algorithm, designed in the author's 2014 master's thesis but never published, together with an open-source C++17 implementation and an FPGA realization -- held bit-exact against the software in its hardware configuration -- detecting segments in live 1080p30 video on 2009-era silicon without frame buffer or external memory. On structure-rich public 4K photographs downscaled to Full-HD, one CPU thread detects segments in ~11 ms -- 4.6x/5.2x/25x faster than the original authors' implementations of ELSED, EDLines, and LSD -- with the tightest frame-time distribution and the best per-segment direction accuracy of the four detectors, and curve rejection by design, while trailing ELSED in F-score on synthetic ground truth. A Manhattan-frame vanishing-point study on York Urban and NYU-VP scores every detector under a selection/evaluation-separated best-estimator-per-detector protocol, under which SweepLSD leads on NYU-VP by ~0.3 degrees and trails by 0.1 degrees on York Urban, with the fastest end-to-end pipeline of the four detectors on both. A single-frame camera-attitude application, evaluated on synthetic scenes with exact ground truth and on EuRoC and TUM-VI, matches the baselines' accuracy at a fraction of their memory, and drives a 4K horizon lock to 0.06 degrees median attitude error at 32 ms median per frame.
Figures & tables
| SweepLSD (this work) | Manabe 2022 [ 24 ] | Hough/FPGA [ 22 ] | |
|---|---|---|---|
| Output | finite segments | finite segments | infinite lines |
| Processing | row stream | row stream | frame accumulator |
| Ext. memory | none | none | none (on-chip) |
| Resolution | 1080p30 / 720p60 | VGA @60 fps | 1024 768 |
| Device (era) | Spartan-6 (2009) | Zynq-7000 (2011) | Cyclone IV (2009) |
| Mechanism | endpoint runs, union–find | stream-adapted region grow | vote accumulation |
| detector | median time per frame | relative | intermediates |
|---|---|---|---|
| SweepLSD (one-pass) | 11.3 ms | 1 | |
| SweepLSD (multi-pass driver) | 14.6 ms | 1.3 | |
| ELSED | 51.4 ms | 4.6 | |
| EDLines (ED_Lib) | 58.4 ms | 5.2 | |
| LSD | 277.8 ms | 25 |
| resolution | SweepLSD (one-pass) | ELSED | EDLines | LSD |
|---|---|---|---|---|
| (0.23 MP) | 1.5 ms | 7.3 ms | 8.0 ms | 31.9 ms |
| (0.92 MP) | 5.3 ms | 24.8 ms | 27.5 ms | 124 ms |
| (2.07 MP) | 11.3 ms | 51.4 ms | 58.4 ms | 278 ms |
| (3.69 MP) | 19.5 ms | 89.3 ms | 102 ms | 535 ms |
| (8.29 MP) | 44.5 ms | 219 ms | 255 ms | 1253 ms |
| detector | median | SD | CV | p95 | worst frame |
|---|---|---|---|---|---|
| SweepLSD (one-pass) | 11.3 ms | 2.7 ms | 24.4% | 1.33 | 1.66 |
| ELSED | 51.4 ms | 21.4 ms | 41.8% | 1.64 | 2.14 |
| EDLines (ED_Lib) | 58.4 ms | 22.5 ms | 38.2% | 1.70 | 2.55 |
| LSD | 277.8 ms | 102 ms | 37.0% | 1.59 | 2.33 |
| detector | 360p | 720p | 1080p | 1440p | 4K |
|---|---|---|---|---|---|
| SweepLSD (one-pass) | 5.9 | 7.4 | 10.0 | 13.3 | 22.4 |
| SweepLSD (multi-pass) | 7.6 | 14.3 | 25.8 | 41.2 | 85.5 |
| ELSED | 9.9 | 22.4 | 37.7 | 69.1 | 146.7 |
| EDLines (ED_Lib) | 10.4 | 22.2 | 41.3 | 66.9 | 154.1 |
| LSD | 12.6 | 33.6 | 67.3 | 114.1 | 255.0 |
| noise | SweepLSD | LSD | EDLines (ED_Lib) | ELSED |
|---|---|---|---|---|
| 0 (clean) | 0.958 | 0.936 | 0.955 | 0.991 |
| 5 | 0.959 | 0.938 | 0.950 | 0.992 |
| 10 | 0.948 | 0.788 | 0.952 | 0.990 |
| 20 | 0.905 | 0.510 | 0.951 | 0.990 |
| detector | lateral error (px) | direction error ( ∘ ) |
|---|---|---|
| SweepLSD | 0.12–0.13 | 0.02–0.04 |
| ELSED | 0.12–0.13 | 0.07–0.09 |
| EDLines (ED_Lib) | 0.12–0.13 | 0.13–0.14 |
| LSD | 0.15 | 0.02–0.12 |
| detector | pooled F (95% CI) | F, contrast 20 | F, | dir. ( ∘ ) |
|---|---|---|---|---|
| ELSED | 0.807 [0.769, 0.840] | 0.457 | 0.701 | 0.15 |
| EDLines (ED_Lib) | 0.776 [0.739, 0.809] | 0.303 | 0.719 | 0.19 |
| SweepLSD linker | 0.699 [0.664, 0.732] | 0.420 | 0.567 | 0.13 |
| SweepLSD | 0.654 [0.617, 0.686] | 0.382 | 0.519 | 0.13 |
| LSD | 0.600 [0.565, 0.633] | 0.248 | 0.455 | 0.20 |
| SweepLSD | LSD | EDLines (ED_Lib) | ELSED | |||||
|---|---|---|---|---|---|---|---|---|
| pattern | segs | CV | segs | CV | segs | CV | segs | CV |
| Siemens star (straight spokes) | 110 | 0.74 | 108 | 0.51 | 108 | 0.59 | 108 | 0.50 |
| Angular fan (straight lines) | 198 | 0.21 | 302 | 0.18 | 193 | 0.20 | 192 | 0.22 |
| Concentric circles (no lines) | 0 | — | 1,796 | 0.13 | 1,768 | 0.17 | 1,443 | 0.29 |
| Zone plate (curved everywhere) | 27 | 1.58 | 859 | 0.18 | 675 | 0.24 | 475 | 0.31 |
| detector | hflip | vflip | ∘ | ∘ | ∘ |
|---|---|---|---|---|---|
| SweepLSD | 97.7 | 96.3 | 96.4 | 97.0 | 96.9 |
| ELSED | 65.7 | 81.7 | 64.4 | 70.4 | 70.2 |
| EDLines (ED_Lib) | 79.2 | 80.6 | 71.4 | 78.4 | 77.1 |
| LSD | 71.2 | 71.3 | 66.6 | 70.9 | 71.5 |
| segments into the fixed estimator | York Urban | NYU (indoor) |
|---|---|---|
| LSD | 0.92 ∘ | 10.7 ∘ |
| EDLines (ED_Lib) | 0.98 ∘ | 10.4 ∘ |
| ELSED | 0.96 ∘ | 10.4 ∘ |
| SweepLSD | 1.04 ∘ | 12.7 ∘ |
| SweepLSD + linker | 0.99 ∘ | 10.7 ∘ |
| hand-labeled lines (ceiling) | 0.59 ∘ | 6.0 ∘ |
| dataset | detector | median err (cross-val.) | ∘ | ∘ | time (ms) | ||
|---|---|---|---|---|---|---|---|
| detect | VP est. | total | |||||
| NYU (indoor) | SweepLSD | 6.08 ∘ | 33.2% | 48.6% | 1.5 | 2.1 | 3.7 |
| ELSED | 7.67 ∘ | 32.4% | 47.1% | 4.0 | 1.6 | 5.7 | |
| LSD | 7.87 ∘ | 29.2% | 46.4% | 27.1 | 3.0 | 30.1 | |
| EDLines (ED_Lib) | 8.09 ∘ | 30.5% | 45.7% | 5.8 | 2.9 | 8.9 | |
| York Urban | ELSED | 0.82 ∘ | 84.3% | 96.1% | 5.5 | 2.8 | 8.4 |
| HLS core | RTL system | |
|---|---|---|
| (Artix-7 XC7A35T) | (Spartan-6 XC6SLX45) | |
| Clock | 100 MHz, II | 74.25 MHz pixel clock |
| Slice LUTs | 19,789 (95%) | 12,664 (46%) |
| Slice registers | 14,684 (35%) | 8,813 (16%) |
| Occupied slices | — | 4,109 (60%) |
| Block RAM | 79 (79%) | 74 RAMB16 26 RAMB8 (75%) |
Appendix figures & tables5 assets
Supplementary material from the paper’s appendix.
Appendix
| stage | median time | share |
|---|---|---|
| Gaussian (separable) | 1.7 ms | 13% |
| gradient | 2.3 ms | 17% |
| thresholding + sub-pixel NMS + adaptive hysteresis | 1.4 ms | 11% |
| endpoint candidates | 1.9 ms | 14% |
| streaming labeler (CCA + judgment) | 6.0 ms | 45% |
| model | [ms/MP] | [ s per edge px] | |
|---|---|---|---|
| 2.84 | 16.0 | 0.963 | |
| 5.12 | — | 0.891 |
| toolchain | vectorizes | relative time |
|---|---|---|
| GCC 15.2 | all five stage kernels | 1 |
| GCC 8.1 | all five, less well | 1.11 |
| Clang 22.1 | all five | 1.35 |
| clang-cl 22.1 | all five | 1.38 |
| MSVC cl 19.34 (VS 2022) | endpoint kernel stays scalar | 4.1 |
| configuration | F-max | dir. ( ∘ ) | lat. (px) | York U. ( ∘ ) | segs | time | |
|---|---|---|---|---|---|---|---|
| (ms) | |||||||
| 2014 pipeline † | 0.951 | 0.898 | 0.05–0.08 | 0.10–0.11 | 1.25 | 1,646 | 7.8 |
| strict NMS tie-break | 0.951 | 0.901 | 0.05–0.08 | 0.10–0.11 | 1.24 | 1,621 | 7.6 |
| streaming hysteresis | 0.951 | 0.901 | 0.05–0.08 | 0.10–0.11 | 1.18 | 1,621 | 8.1 |
| sub-pixel NMS | 0.951 | 0.901 | 0.02–0.04 | 0.12–0.13 | 1.18 | 1,621 | 11.2 |
| proj.-extreme endpoints | 0.951 | 0.901 | 0.02–0.04 | 0.12–0.13 | 1.12 | 1,621 | 11.3 |
| stage | parameter | default | § |
|---|---|---|---|
| edge | gradient power threshold | 256 at the scale ( 4 gray levels) | 3.2 |
| edge | hysteresis low threshold | adaptive ( the 80th-percentile power), | 3.2 |
| clamped to | |||
| edge | adaptive-histogram decay | per row | 3.2 |
| edge | sub-pixel NMS resolution | px | 3.2 |
| edge | border exclusion | 3 px | 3.2 |