# Detect lines with Hough transform

**URL:** <https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307>\
**Category:** General Usage\
**Tags:** question, images\
**Created:** [October 30, 2020, 1:52pm UTC](https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307 "2020-10-30T13:52:53Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![yakir12](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yakir12/32/297_2.png) [@yakir12](https://discourse.julialang.org/u/yakir12)\
**Post date:** [October 30, 2020, 1:52pm UTC](https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307/1 "2020-10-30T13:52:53Z")

</div>

I have an image with 3 lines in it:  
 ![tf2](https://global.discourse-cdn.com/julialang/original/3X/c/f/cffd2246ad5265f3c630b3db335320a62428468d.png)

For each of the three lines, I’d like to find the coordinates of their ends (i.e. where in the image are the start and end of each line). I thought that the best tool for the job would be [`ImageFeatures.hough_transform_standard`](https://juliaimages.org/stable/function_reference/#ImageFeatures.hough_transform_standard).

But I seem to always get an empty tuple back. Here’s everything you need to run it yourself:

```julia
import Pkg
Pkg.add(["ImageFeatures", "FileIO", "ImageIO"])
using ImageFeatures, FileIO
file = download("https://vision-group-temporary.s3.eu-central-1.amazonaws.com/tf.png")
tf = Bool.(FileIO.load(file))
hough_transform_standard(tf)

```

I’m puzzled because the image is fairly simple: no noise and the lines are straight and continuous. So I’m not sure why this won’t work…

Any ideas?

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [October 30, 2020, 2:27pm UTC](https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307/2 "2020-10-30T14:27:09Z")

</div>

It looks like it has some parameters, did you try playing with those?

---

<div class="post-metadata">

**Author:** ![yakir12](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yakir12/32/297_2.png) [@yakir12](https://discourse.julialang.org/u/yakir12)\
**Post date:** [October 30, 2020, 2:31pm UTC](https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307/3 "2020-10-30T14:31:57Z")

</div>

> [@tim.holy](#):
>
> It looks like it has some parameters

😆

> [@tim.holy](#):
>
> did you try playing with those?

The defaults seemed to be so reasonable I didn’t think it would matter:

```julia
    stepsize=1,
    angles=range(0,stop=pi,length=minimum(size(img))),
    vote_threshold=minimum(size(img)) / stepsize -1,
    max_linecount=typemax(Int))

```

Adding more angles to 1920 is a bit excessive I thought. I probably misunderstood the others though…

---

<div class="post-metadata">

**Author:** ![yakir12](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yakir12/32/297_2.png) [@yakir12](https://discourse.julialang.org/u/yakir12)\
**Post date:** [October 30, 2020, 2:39pm UTC](https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307/4 "2020-10-30T14:39:00Z")

</div>

OK, after playing around with it more I found out that `vote_threshold` has little effect if any, and `stepsize` makes the biggest difference. Here’s a run with some results:

```julia
julia> hough_transform_standard(tf, max_linecount = 3, stepsize = 2)
3-element Array{Tuple{Float64,Float64},1}:
 (1059.0, 0.6204604563369107)
 (1053.0, 0.6286459504838356)
 (1059.0, 0.6253717528250656)

```

Although I’m not entirely sure if that’s what I want… I’ll try to correlate that to the line’s endpoints.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [October 30, 2020, 2:39pm UTC](https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307/5 "2020-10-30T14:39:58Z")

</div>

I’d be surprised if `vote_threshold` has no effect. Usually, if a parameter has the word `threshold` in it, try setting it to something tiny (like 0) and see what happens.

---

<div class="post-metadata">

**Author:** ![yakir12](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yakir12/32/297_2.png) [@yakir12](https://discourse.julialang.org/u/yakir12)\
**Post date:** [October 30, 2020, 2:49pm UTC](https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307/6 "2020-10-30T14:49:25Z")

</div>

From the doc string, it seems like `vote_threshold` is

```julia
julia> minimum(size(tf)) / stepsize -1
1919.0

```

if the `stepsize` is 1. So I assumed it can be large too.

In any case, I tried with 0 to 1:

```julia
julia> for i in 0:0.1:1
       @show hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i)
       end
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]

```

and for larger magnitudes of `vote_threshold`:

```julia
julia> for i in range(1, 1919, length = 10)
       @show hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i)
       end
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = [(1059.0, 0.6204604563369107), (1053.0, 0.6286459504838356), (1059.0, 0.6253717528250656)]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = Tuple{Float64,Float64}[]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = Tuple{Float64,Float64}[]
hough_transform_standard(tf, max_linecount = 3, stepsize = 2, vote_threshold = i) = Tuple{Float64,Float64}[]

```

I don’t even know what `vote_threshold` does exactly.

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [October 30, 2020, 2:51pm UTC](https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307/7 "2020-10-30T14:51:06Z")

</div>

> **[Hough transform | Theory](https://en.wikipedia.org/wiki/Hough_transform#Theory)**
>
> In automated analysis of digital images, a subproblem often arises of detecting simple shapes, such as straight lines, circles or ellipses. In many cases an edge detector can be used as a pre-processing stage to obtain image points or image pixels that are on the desired curve in the image space. Due to imperfections in either the image data or the edge detector, however, there may be missing points or pixels on the desired curves as well as spatial deviations between the ideal line/circle/ellips...

A docstring PR would be gratefully accepted.

---

<div class="post-metadata">

**Author:** ![yakir12](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yakir12/32/297_2.png) [@yakir12](https://discourse.julialang.org/u/yakir12)\
**Post date:** [October 30, 2020, 2:53pm UTC](https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307/8 "2020-10-30T14:53:09Z")

</div>

Oooh, thank you, I’ll see what I can gleam from that and gladly PR an improvement to the docstring!

---

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [October 30, 2020, 6:11pm UTC](https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307/9 "2020-10-30T18:11:26Z")

</div>

AFAIU, this default value for the threshold means that for lines to be detected, they have to be at least as long as the smallest image dimension (i.e. lines have to cross the entire image, and mere segments are not detected). When stepsize is 1, the value of the threshold should roughly indicate the minimal length (in pixels) that a straight segment should have for it to be detected. More precisely, it should correspond to the minimal number of pixels that have to be aligned for a line to be detected (so that in a noisy image, or if line segments are “wavy” and only a fraction of the pixels are aligned, the line is still detected).

These interpretations come from vague recollections I have from of a previous life, when I implemented a Hough transform in OCaml for a CS class… So please take it with a grain of salt!

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [October 31, 2020, 8:06am UTC](https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307/10 "2020-10-31T08:06:15Z")

</div>

If you are still having problems detecting lines, ping me here or on Slack. I spent a good portion of my postdoctoral career on this problem and dealing with the situation where the lines intersect.

---

<div class="post-metadata">

**Author:** ![yakir12](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yakir12/32/297_2.png) [@yakir12](https://discourse.julialang.org/u/yakir12)\
**Post date:** [October 31, 2020, 8:17am UTC](https://discourse.julialang.org/t/detect-lines-with-hough-transform/49307/11 "2020-10-31T08:17:44Z")

</div>

Thanks a lot for all the help! I got a lot further:

```julia
import Pkg
Pkg.add(["ImageFeatures", "FileIO", "ImageIO", "ImageDraw", "ImageView", "ColorVectorSpace", "ImageCore", "Clustering", "ImageMorphology"])

using ImageFeatures, FileIO, ImageDraw, ImageView, ColorVectorSpace, ImageCore, Clustering, ImageMorphology
file = download("https://vision-group-temporary.s3.eu-central-1.amazonaws.com/tf.png")
tf = Bool.(FileIO.load(file))
t = thinning(tf)

rθ = hough_transform_standard(t, stepsize = 3, vote_threshold = 500, max_linecount = 100, angles = range(0, pi, length = 10000))

R = kmeans(Matrix(hcat(vcat.(rθ...)...)'),3)
M = R.centers 
m = Tuple.(eachcol(M))

img = RGB.(tf);
for (r, θ) in m
    draw!(img, LineNormal(r, θ), RGB{N0f8}(1, 0, 0))
end
imshow(img)

```

 ![img](https://global.discourse-cdn.com/julialang/original/3X/e/2/e2f7fc1f61fd26b2211e3f46ec97b69ec9c30fa7.png)

@ffevotte, this terminology is super helpful, and it’s what Matlab used as well:  
`stepsize`: “line thickness”  
`vote_threshold`: “line length”

@mkitti, wow, thanks! checkout the [issue](https://github.com/JuliaImages/ImageFeatures.jl/issues/83) I opened concerning improving this whole process.
