Skip to content

Commit 6d45e32

Browse files
tejus-guptatimholy
authored andcommitted
added hough circle function (#29)
1 parent 71c95af commit 6d45e32

File tree

3 files changed

+159
-3
lines changed

3 files changed

+159
-3
lines changed

src/ImageFeatures.jl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ export
6060
gaussian_local,
6161
center_sample,
6262

63-
#Lines and Ellipses
64-
hough_transform_standard
63+
#Lines
64+
hough_transform_standard,
65+
66+
#Circles
67+
hough_circle_gradient
6568
end

src/houghtransform.jl

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
using Images
22
"""
33
```
44
lines = hough_transform_standard(image, ρ, θ, threshold, linesMax)
@@ -86,3 +86,130 @@ function hough_transform_standard{T<:Union{Bool,Gray{Bool}}}(
8686
lines
8787

8888
end
89+
90+
"""
91+
```
92+
circle_centers, circle_radius = hough_circle_gradient(img_edges, img_phase, scale, min_dist, vote_thres, min_radius:max_radius)
93+
```
94+
Returns two vectors, corresponding to circle centers and radius.
95+
96+
The circles are generated using a hough transform variant in which a non-zero point only votes for circle
97+
centers perpendicular to the local gradient. In case of concentric circles, only the largest circle is detected.
98+
99+
Parameters:
100+
- `img_edges` = edges of the image
101+
- `img_phase` = phase of the gradient image
102+
- `scale` = relative accumulator resolution factor
103+
- `min_dist` = minimum distance between detected circle centers
104+
- `vote_thres` = accumulator threshold for circle detection
105+
- `min_radius:max_radius` = circle radius range
106+
107+
[`canny`](@ref) and [`phase`](@ref) can be used for obtaining img_edges and img_phase respectively.
108+
109+
# Example
110+
```julia
111+
img = load("circle.png")
112+
113+
img_edges = canny(img, 1, 0.99, 0.97)
114+
dx, dy=imgradients(img, KernelFactors.ando5)
115+
img_phase = phase(dx, dy)
116+
117+
centers, radii=hough_circle_gradient(img_edges, img_phase, 1, 60, 60, 3:50)
118+
```
119+
"""
120+
121+
function hough_circle_gradient{T<:Number}(
122+
img_edges::AbstractArray{Bool,2}, img_phase::AbstractArray{T,2},
123+
scale::Number, min_dist::Number,
124+
vote_thres::Number, radii::AbstractVector{Int})
125+
126+
rows,cols=size(img_edges)
127+
128+
non_zeros=CartesianIndex{2}[]
129+
centers=CartesianIndex{2}[]
130+
circle_centers=CartesianIndex{2}[]
131+
circle_radius=Int[]
132+
accumulator_matrix=zeros(Int, Int(floor(rows/scale))+1, Int(floor(cols/scale))+1)
133+
134+
function vote!(accumulator_matrix, x, y)
135+
fx = Int(floor(x))
136+
fy = Int(floor(y))
137+
138+
for i in fx:fx+1
139+
for j in fy:fy+1
140+
if checkbounds(Bool, accumulator_matrix, i, j)
141+
@inbounds accumulator_matrix[i, j] += 1
142+
end
143+
end
144+
end
145+
end
146+
147+
for j in indices(img_edges, 2)
148+
for i in indices(img_edges, 1)
149+
if img_edges[i,j]
150+
sinθ = -cos(img_phase[i,j]);
151+
cosθ = sin(img_phase[i,j]);
152+
153+
for r in radii
154+
x=(i+r*sinθ)/scale
155+
y=(j+r*cosθ)/scale
156+
vote!(accumulator_matrix, x, y)
157+
158+
x=(i-r*sinθ)/scale
159+
y=(j-r*cosθ)/scale
160+
vote!(accumulator_matrix, x, y)
161+
end
162+
push!(non_zeros, CartesianIndex{2}(i,j));
163+
end
164+
end
165+
end
166+
167+
for i in findlocalmaxima(accumulator_matrix)
168+
if accumulator_matrix[i]>vote_thres
169+
push!(centers, i);
170+
end
171+
end
172+
173+
@noinline sort_by_votes(centers, accumulator_matrix) = sort!(centers, lt=(a, b) -> accumulator_matrix[a]>accumulator_matrix[b])
174+
175+
sort_by_votes(centers, accumulator_matrix)
176+
177+
dist(a, b) = sqrt(sum(abs2, (a-b).I))
178+
179+
f = CartesianIndex(map(r->first(r), indices(accumulator_matrix)))
180+
l = CartesianIndex(map(r->last(r), indices(accumulator_matrix)))
181+
radius_accumulator=Vector{Int}(Int(floor(dist(f,l)/scale)+1))
182+
183+
for center in centers
184+
center=(center-1)*scale
185+
fill!(radius_accumulator, 0)
186+
187+
too_close=false
188+
for circle_center in circle_centers
189+
if dist(center, circle_center)< min_dist
190+
too_close=true
191+
break
192+
end
193+
end
194+
if too_close
195+
continue;
196+
end
197+
198+
for point in non_zeros
199+
r=Int(floor(dist(center, point)/scale))
200+
if radii.start/scale<=r<=radii.stop/scale
201+
radius_accumulator[r+1]+=1
202+
end
203+
end
204+
205+
voters, radius = findmax(radius_accumulator)
206+
radius=(radius-1)*scale;
207+
208+
if voters>vote_thres
209+
push!(circle_centers, center)
210+
push!(circle_radius, radius)
211+
end
212+
end
213+
return circle_centers, circle_radius
214+
end
215+

test/houghtransform.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,30 @@ using ImageFeatures
3636
er = sum(map((t1,t2) -> abs(t1-t2), theta, [0, π/2, π/2, 0]))
3737
@test er <= 0.1
3838
end
39+
40+
@testset "Hough Circle Gradient" begin
41+
42+
dist(a, b) = sqrt(sum(abs2, (a-b).I))
43+
44+
img=zeros(Int, 300, 300)
45+
for i in CartesianRange(size(img))
46+
if dist(i, CartesianIndex(100, 100))<25 || dist(i, CartesianIndex(200, 200))<50
47+
img[i]=1
48+
else
49+
img[i]=0
50+
end
51+
end
52+
53+
img_edges = canny(img, 1, 0.2, 0.1, percentile=false)
54+
dx, dy=imgradients(img, KernelFactors.ando3)
55+
img_phase = phase(dx, dy)
56+
57+
centers, radii=hough_circle_gradient(img_edges, img_phase, 1, 40, 40, 5:75)
58+
59+
@test dist(centers[1], CartesianIndex(200,200))<5
60+
@test dist(centers[2], CartesianIndex(100,100))<5
61+
@test abs(radii[1]-50)<5
62+
@test abs(radii[2]-25)<5
63+
64+
end
3965
end

0 commit comments

Comments
 (0)