Skip to content

Commit de2ebab

Browse files
committed
helper function and test cases added
1 parent 6638323 commit de2ebab

File tree

2 files changed

+131
-65
lines changed

2 files changed

+131
-65
lines changed

src/houghtransform.jl

Lines changed: 90 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ img::AbstractArray{T,2},
156156
ρ::Real, θ::Range,
157157
threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) where T<:Union{Bool,Gray{Bool}}
158158

159-
ρ > 0 || error("Discrete step size must be positive")
159+
ρ > 0 || throw(ArgumentError("Discrete step size must be positive"))
160160
indsy, indsx = indices(img)
161161
ρinv = 1 / ρ
162162
numangle = length(θ)
@@ -165,48 +165,30 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
165165
accumulator_matrix = zeros(Int, numangle + 2, numrho + 2)
166166
h, w = size(img)
167167
mask = zeros(Bool, h, w)
168-
168+
x0, y0 = 0, 0
169169
#Pre-Computed sines and cosines in tables
170170
sinθ, cosθ = sin.(θ).*ρinv, cos.(θ).*ρinv
171171
nzloc = Vector{Tuple{Int64,Int64}}(0)
172172
lines = Vector{Tuple{Int64, Int64, Int64, Int64}}(0)
173-
174-
#collect non-zero image points
175-
for pix in CartesianRange(size(img))
176-
pix1 = (pix[1], pix[2])
177-
if(img[pix])
178-
push!(nzloc, pix1)
179-
mask[pix] = true
180-
else
181-
mask[pix] = false
173+
const shift = 16
174+
175+
#function to mark and collect all non zero points
176+
function collect_points()
177+
for pix in CartesianRange(size(img))
178+
pix1 = (pix[1], pix[2])
179+
if(img[pix])
180+
push!(nzloc, pix1)
181+
mask[pix] = true
182+
else
183+
mask[pix] = false
184+
end
182185
end
183-
end
186+
end
184187

185-
count_ = size(nzloc)[1]+1
186-
187-
# stage 2. process all the points in random order
188-
while(count_>1)
189-
count_-=1
190-
good_line = false
191-
# choose random point out of the remaining ones
192-
idx = rand(1:count_)
193-
max_val = threshold-1
188+
#function to update the accumulator matrix for every point selected
189+
function update_accumulator(point)
194190
max_n = 1
195-
point = nzloc[idx]
196-
line_end = [[0,0],[0,0]]
197-
i = point[1]-1
198-
j = point[2]-1
199-
x0, y0, dx0, dy0, xflag = 0, 0, 0, 0, 0
200-
const shift = 16
201-
202-
# "remove" it by overriding it with the last element
203-
nzloc[idx] = nzloc[count_]
204-
205-
if(!(mask[point[1], point[2]]))
206-
continue
207-
end
208-
209-
# update accumulator, find the most probable line
191+
max_val = threshold-1
210192
for n in 0:numangle-1
211193
dist = round(Int, point[2]*cosθ[n+1] + point[1]*sinθ[n+1])
212194
dist += constadd
@@ -218,32 +200,12 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
218200
max_n = n+1
219201
end
220202
end
203+
return max_n, max_val
204+
end
221205

222-
# if it is too "weak" candidate, continue with another point
223-
if(max_val < threshold)
224-
continue
225-
end
226-
227-
# from the current point walk in each direction along the found line
228-
a = -sinθ[max_n]
229-
b = cosθ[max_n]
230-
x0 = j
231-
y0 = i
232-
good_line = false
233-
234-
if(abs(a) > abs(b))
235-
xflag = 1
236-
dx0 = a > 0 ? 1 : -1
237-
dy0 = round(b*(1 << shift)/abs(a))
238-
y0 = (y0 << shift) + (1 << (shift-1))
239-
else
240-
xflag = 0
241-
dy0 = b > 0 ? 1 : -1
242-
dx0 = round( a*(1 << shift)/abs(b) );
243-
x0 = (x0 << shift) + (1 << (shift-1));
244-
end
245-
246-
# pass 1: walk the line, merging lines less than specified gap length
206+
#function to detect the line segment after merging lines within lineGap
207+
function pass_1(xflag, x0, y0, dx0, dy0)
208+
line_end = [[0,0],[0,0]]
247209
for k = 1:2
248210
gap = 0
249211
x = x0
@@ -286,10 +248,11 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
286248
y = Int64(y+dy)
287249
end
288250
end
289-
# confirm line length is sufficient
290-
good_line = abs(line_end[2][1] - line_end[1][1]) >= lineLength || abs(line_end[2][2] - line_end[1][2]) >= lineLength
251+
return line_end
252+
end
291253

292-
# pass 2: walk the line again and reset accumulator and mask
254+
#function to reset the mask and accumulator_matrix
255+
function pass_2(xflag, x0, y0, dx0, dy0, good_line, line_end)
293256
for k = 1:2
294257
x = x0
295258
y = y0
@@ -331,8 +294,70 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
331294
x = Int64(x+dx)
332295
y = Int64(y+dy)
333296
end
297+
end
298+
end
299+
300+
#collect non-zero image points
301+
collect_points()
302+
303+
count_ = size(nzloc)[1]+1
304+
305+
# stage 2. process all the points in random order
306+
while(count_>1)
307+
count_-=1
308+
good_line = false
309+
# choose random point out of the remaining ones
310+
idx = rand(1:count_)
311+
max_n = 1
312+
point = nzloc[idx]
313+
i = point[1]-1
314+
j = point[2]-1
315+
x0, y0, dx0, dy0, xflag = 0, 0, 0, 0, 0
316+
max_n = 1
317+
318+
# "remove" it by overriding it with the last element
319+
nzloc[idx] = nzloc[count_]
320+
321+
if(!(mask[point[1], point[2]]))
322+
continue
323+
end
324+
325+
# update accumulator, find the most probable line
326+
max_n, max_val = update_accumulator(point)
327+
328+
# if it is too "weak" candidate, continue with another point
329+
if(max_val < threshold)
330+
continue
331+
end
332+
333+
# from the current point walk in each direction along the found line
334+
a = -sinθ[max_n]
335+
b = cosθ[max_n]
336+
x0 = j
337+
y0 = i
338+
good_line = false
339+
340+
if(abs(a) > abs(b))
341+
xflag = 1
342+
dx0 = a > 0 ? 1 : -1
343+
dy0 = round(b*(1 << shift)/abs(a))
344+
y0 = (y0 << shift) + (1 << (shift-1))
345+
else
346+
xflag = 0
347+
dy0 = b > 0 ? 1 : -1
348+
dx0 = round( a*(1 << shift)/abs(b) );
349+
x0 = (x0 << shift) + (1 << (shift-1));
350+
end
351+
352+
# pass 1: walk the line, merging lines less than specified gap length
353+
line_end = pass_1(xflag, x0, y0, dx0, dy0)
354+
355+
# confirm line length is sufficient
356+
good_line = abs(line_end[2][1] - line_end[1][1]) >= lineLength || abs(line_end[2][2] - line_end[1][2]) >= lineLength
357+
358+
# pass 2: walk the line again and reset accumulator and mask
359+
pass_2(xflag, x0, y0, dx0, dy0, good_line, line_end)
334360

335-
end
336361
# add line to the result
337362
if(good_line)
338363
push!(lines, (line_end[1][1], line_end[1][2], line_end[2][1], line_end[2][2]))

test/houghtransform.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,47 @@ using ImageFeatures
3737
@test er <= 0.1
3838
end
3939

40+
@testset "Hough Line Probabilistic" begin
41+
#one horizontal line segment with few scattered points
42+
img = zeros(Bool,20,20)
43+
for j in 5:15
44+
img[15,j] = true
45+
end
46+
47+
srand(1234)
48+
for k in 1:5
49+
img[rand(1:13),rand(6:14)] = true
50+
end
51+
lines = hough_line_probabilistic(img, 1, linspace(0,π,180),7,5,10,4)
52+
@test length(lines) == 1
53+
@test lines[1] == (15,5,15,15)
54+
55+
#for a square image
56+
img = zeros(Bool, 20, 20)
57+
58+
for i in (3,14)
59+
for j in 3:14
60+
img[i,j] = true
61+
end
62+
end
63+
64+
for i in (3,14)
65+
for j in 3:14
66+
img[j,i] = true
67+
end
68+
end
69+
srand(1234)
70+
for k in 1:4
71+
img[rand(6:10),rand(6:10)] = true
72+
end
73+
# srand(1234)
74+
lines = hough_line_probabilistic(img, 1, linspace(0,π,180),9,7,15,4)
75+
lines_ = [line for line in lines]
76+
@test length(lines) == 4
77+
@test all(lines_ == [(3, 3, 3, 14), (14, 3, 14, 14), (13, 14, 4, 14), (13, 3, 4, 3)])
78+
79+
end
80+
4081
@testset "Hough Circle Gradient" begin
4182

4283
dist(a, b) = sqrt(sum(abs2, (a-b).I))

0 commit comments

Comments
 (0)