Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Hough Line Transform #601

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Images.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ include("distances.jl")
include("bwdist.jl")
using .FeatureTransform
include("deprecated.jl")
include("houghtranform.jl")

export # types
BlobLoG,
Expand Down Expand Up @@ -240,6 +241,7 @@ Algorithms:
- Morphological operations: `dilate`, `erode`, `closing`, `opening`, `tophat`, `bothat`, `morphogradient`, `morpholaplace`, `feature_transform`, `distance_transform`
- Connected components: `label_components`, `component_boxes`, `component_lengths`, `component_indices`, `component_subscripts`, `component_centroids`
- Interpolation: `bilinear_interpolation`
- Feature detection: `hough_transform_standard`

Test images and phantoms (see also TestImages.jl):

Expand Down
107 changes: 107 additions & 0 deletions src/houghtranform.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@


function is_binary_image{T}(img::AbstractArray{T,2})
for pix in CartesianRange(size(img))
if img[pix] != 0.0 && img[pix] != 1.0
return false
end
end
return true
end

"""
lines = hough_transform_standard(image, rho, theta, min_theta, max_theta, threshold, linesMax)

Returns an array of tuples corresponding to the tuples of (r,t) where r and t are parameters for normal form of line:
x*cos(t) + y*sin(t) = r

r = length of perpendicular from (0,0) to the line
t = angle between perpendicular from (0,0) to the line and axis

The lines are generated by applying hough transform on the image.

Parameters:
image = image to apply hough transform on
rho = discrete steps for radius
theta = discrete steps for theta
min_theta = minimum theta of lines
max_theta = maximum theta of lines
threshold = no of points to pass through line for considering it valid
linesMax = maximum no of lines to return

"""

function hough_transform_standard{T}(
img::AbstractArray{T,2},
rho::Float64, theta::Float64,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too restrictive on the types? Particularly for people who have 32-bit machines, Int64 is not native (better to use Int). Number and Integer might even be best.

min_theta::Float64, max_theta::Float64,
threshold::Int64, linesMax::Int64)

theta > 0 || error("theta threshold must be positive")
rho > 0 || error("radius threshold must be positive")
min_theta < max_theta || error("max_theta must be greater than min_theta")

if ! is_binary_image(img)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to just require an T<:Union{Bool,Gray{Bool}} eltype.

img = canny(img)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks orthogonality; what if someone wants a different algorithm than canny? Will be solved if we simply require a Bool eltype.

Hmm, I hadn't noticed that canny returns an Array{Float64}. We should probably change it to return an Array{Bool}.

end

width::Int64 = size(img)[2]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to declare types like this.

height::Int64 = size(img)[1]
irho::Float64 = 1 / rho;
numangle::Int64 = round((max_theta - min_theta)/theta)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

round(Int, x) returns an Int. You can use this in several places below, too.

numrho::Int64 = round(((width + height)*2 + 1)/rho)

accumulator_matrix = Array{Int64}(numangle + 2, numrho + 2)
fill!(accumulator_matrix, 0)

#Pre-Computed sines and cosines in tables
tabsin = Array{Float64}(numangle)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even better, Vector{Float64} (here and below).

tabcos = Array{Float64}(numangle)
for i in 1:numangle
tabsin[i] = (sin(min_theta + (i-1)*theta) * irho)
tabcos[i] = (cos(min_theta + (i-1)*theta) * irho)
end


#Hough Transform implementation
for pix in CartesianRange(size(img))
if img[pix] == 1.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're using Bool eltypes, this becomes just if img[pix]

for i in 1:numangle
dist::Int64 = round((pix[1] * tabsin[i] + pix[2] * tabcos[i]))
dist += round((numrho -1)/2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This constant could be hoisted out of the loop.

accumulator_matrix[i + 1, dist + 1] += 1
end
end
end

#Finding local maximum lines
validLines = Array{CartesianIndex}(0)
for val in CartesianRange(size(accumulator_matrix))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should add a function input to findlocalmaxima and just use that?

if accumulator_matrix[val] > threshold &&
accumulator_matrix[val] > accumulator_matrix[val[1],val[2] - 1] &&
accumulator_matrix[val] >= accumulator_matrix[val[1],val[2] + 1] &&
accumulator_matrix[val] > accumulator_matrix[val[1] - 1,val[2]] &&
accumulator_matrix[val] >= accumulator_matrix[val[1] + 1,val[2]]
push!(validLines,val)
end
end

#Sorting by value in accumulator_matrix
sort!(validLines, by = (x)->accumulator_matrix[x], rev = true)

linesMax = min(linesMax, size(validLines)[1])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can just use length instead of size(validLines)[1] (they're equivalent if validLines is a vector).


lines = Array{Tuple{Float64,Float64}}(0)

#Getting lines with Maximum value in accumulator_matrix && size(lines) < linesMax
for l in 1:linesMax
lrho = ((validLines[l][2]-1) - (numrho - 1)*0.5)*rho
langle = ((validLines[l][1]-1)*theta + min_theta)
push!(lines,(lrho,langle))
end

lines

end