@@ -103,6 +103,54 @@ threshold::Integer, linesMax::Integer) where T<:Union{Bool,Gray{Bool}}
103103
104104end
105105
106+ """
107+ ```
108+ lines = hough_line_probabilistic(image, ρ, θ, threshold, lineLength, lineGap, linesMax)
109+ ```
110+
111+ Returns lines :
112+ vector of lines identified, lines in format ((r0, c0), (r1, c1))
113+ indicating line start and end.
114+
115+ The lines are generated by applying hough transform on the image.
116+
117+ Parameters:
118+ - `image` = Image to be transformed (eltype should be `Bool`)
119+ - `ρ` = Discrete step size for perpendicular length of line
120+ - `θ` = List of angles for which the transform is computed
121+ - `threshold` = Accumulator threshold for line detection
122+ - 'lineLength' = minimum length of a good_line
123+ - 'lineGap' = minimum gap between two different lines.
124+ - `linesMax` = Maximum no of lines to return
125+
126+ # Example
127+ ```julia
128+ julia> img = load("line.jpg");
129+
130+ julia> img_edges = canny(img, (Percentile(0.99), Percentile(0.97)), 1);
131+
132+ julia> lines = hough_line_probabilistic(img_edges, 1, linspace(0,π,180),30,30,10,10)
133+ 10-element Array{NTuple{4,Int64},1}:
134+ (186, 283, 20, 283)
135+ (186, 20, 20, 20)
136+ (200, 218, 200, 291)
137+ (20, 68, 20, 180)
138+ (186, 85, 186, 197)
139+ (48, 59, 69, 199)
140+ (50, 58, 65, 160)
141+ (200, 35, 200, 147)
142+ (20, 186, 20, 282)
143+ (155, 138, 75, 198)
144+ ```
145+ May use LineSegment of ImageDraw to draw lines.
146+
147+ References
148+ ----------
149+ .. [1] C. Galamhos, J. Matas and J. Kittler, "Progressive probabilistic
150+ Hough transform for line detection", in IEEE Computer Society
151+ Conference on Computer Vision and Pattern Recognition, 1999.
152+ """
153+
106154function hough_line_probabilistic (
107155img:: AbstractArray{T,2} ,
108156ρ:: Real , θ:: Range ,
@@ -123,6 +171,7 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
123171 nzloc = Vector {Tuple{Int64,Int64}} (0 )
124172 lines = Vector {Tuple{Int64, Int64, Int64, Int64}} (0 )
125173
174+ # collect non-zero image points
126175 for pix in CartesianRange (size (img))
127176 pix1 = (pix[1 ], pix[2 ])
128177 if (img[pix])
@@ -134,9 +183,12 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
134183 end
135184
136185 count_ = size (nzloc)[1 ]+ 1
186+
187+ # stage 2. process all the points in random order
137188 while (count_> 1 )
138189 count_-= 1
139190 good_line = false
191+ # choose random point out of the remaining ones
140192 idx = rand (1 : count_)
141193 max_val = threshold- 1
142194 max_n = 1
@@ -147,12 +199,14 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
147199 x0, y0, dx0, dy0, xflag = 0 , 0 , 0 , 0 , 0
148200 const shift = 16
149201
202+ # "remove" it by overriding it with the last element
150203 nzloc[idx] = nzloc[count_]
151204
152205 if (! (mask[point[1 ], point[2 ]]))
153206 continue
154207 end
155208
209+ # update accumulator, find the most probable line
156210 for n in 0 : numangle- 1
157211 dist = round (Int, point[2 ]* cosθ[n+ 1 ] + point[1 ]* sinθ[n+ 1 ])
158212 dist += constadd
@@ -165,10 +219,12 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
165219 end
166220 end
167221
222+ # if it is too "weak" candidate, continue with another point
168223 if (max_val < threshold)
169224 continue
170225 end
171226
227+ # from the current point walk in each direction along the found line
172228 a = - sinθ[max_n]
173229 b = cosθ[max_n]
174230 x0 = j
@@ -187,6 +243,7 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
187243 x0 = (x0 << shift) + (1 << (shift- 1 ));
188244 end
189245
246+ # pass 1: walk the line, merging lines less than specified gap length
190247 for k = 1 : 2
191248 gap = 0
192249 x = x0
@@ -209,26 +266,30 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
209266 j1 = x>> shift
210267 i1 = y
211268 end
212-
269+
270+ # check when line exits image boundary
213271 if ( j1 < 0 || j1 >= w || i1 < 0 || i1 >= h )
214272 break ;
215273 end
216274 gap+= 1
275+
276+ # if non-zero point found, continue the line
217277 if (mask[i1+ 1 , j1+ 1 ])
218278 gap = 0
219279 line_end[k][1 ] = i1+ 1
220280 line_end[k][2 ] = j1+ 1
221-
281+ # if gap to this point was too large, end the line
222282 elseif (gap > lineGap)
223283 break
224284 end
225285 x = Int64 (x+ dx)
226286 y = Int64 (y+ dy)
227287 end
228288 end
229-
289+ # confirm line length is sufficient
230290 good_line = abs (line_end[2 ][1 ] - line_end[1 ][1 ]) >= lineLength || abs (line_end[2 ][2 ] - line_end[1 ][2 ]) >= lineLength
231291
292+ # pass 2: walk the line again and reset accumulator and mask
232293 for k = 1 : 2
233294 x = x0
234295 y = y0
@@ -240,6 +301,7 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
240301 dy = - dy
241302 end
242303
304+ # walk along the line using fixed-point arithmetics,
243305 while (true )
244306 i1, j1 = 0 ,0
245307
@@ -250,6 +312,8 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
250312 j1 = x >> shift
251313 i1 = y
252314 end
315+
316+ # if non-zero point found, continue the line
253317 if (mask[i1+ 1 , j1+ 1 ])
254318 if (good_line)
255319 for n = 0 : numangle- 1
@@ -260,7 +324,7 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
260324 end
261325 end
262326 end
263-
327+ # exit when the point is the line end
264328 if ((i1+ 1 ) == line_end[k][1 ] && (j1+ 1 ) == line_end[k][2 ])
265329 break
266330 end
@@ -269,6 +333,7 @@ threshold::Integer, lineLength::Integer, lineGap::Integer, linesMax::Integer) wh
269333 end
270334
271335 end
336+ # add line to the result
272337 if (good_line)
273338 push! (lines, (line_end[1 ][1 ], line_end[1 ][2 ], line_end[2 ][1 ], line_end[2 ][2 ]))
274339
0 commit comments