|
1 | | - |
| 1 | +using Images |
2 | 2 | """ |
3 | 3 | ``` |
4 | 4 | lines = hough_transform_standard(image, ρ, θ, threshold, linesMax) |
@@ -86,3 +86,130 @@ function hough_transform_standard{T<:Union{Bool,Gray{Bool}}}( |
86 | 86 | lines |
87 | 87 |
|
88 | 88 | 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 | + |
0 commit comments