Skip to content

Commit e5ae871

Browse files
archit120johnnychen94
authored andcommitted
Add a demonstration for detecting contours (#106)
* Add a demonstration for detecting contours * Add comments * Fix implementation and use cartesianindex
1 parent 33f444a commit e5ae871

File tree

1 file changed

+192
-0
lines changed

1 file changed

+192
-0
lines changed
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# ---
2+
# cover: assets/contour_detection.png
3+
# title: Contour Detection and Drawing
4+
# ---
5+
6+
# This demonstration shows how to detect contours on binary images
7+
# The algorithm used is ["Topological Structural Analysis of Digitized Binary Images
8+
# by Border Following"](https://www.sciencedirect.com/science/article/pii/0734189X85900167) by Suzuki and Abe (Same as OpenCV).#
9+
10+
# Points are represented using CartesianIndex. Contours are represented as a vector of Points.
11+
# Direction is a single number between 1 to 8. Steps inside functions are marked as they are in the original paper
12+
13+
using Images, TestImages, FileIO
14+
15+
## N NE E SE S SW W NW
16+
## direction between two pixels
17+
18+
## rotate direction clocwise
19+
function clockwise(dir)
20+
return (dir)%8 + 1
21+
end
22+
23+
## rotate direction counterclocwise
24+
function counterclockwise(dir)
25+
return (dir+6)%8 + 1
26+
end
27+
28+
## move from current pixel to next in given direction
29+
function move(pixel, image, dir, dir_delta)
30+
newp = pixel + dir_delta[dir]
31+
height, width = size(image)
32+
if (0 < newp[1] <= height) && (0 < newp[2] <= width)
33+
if image[newp]!=0
34+
return newp
35+
end
36+
end
37+
return CartesianIndex(0, 0)
38+
end
39+
40+
## finds direction between two given pixels
41+
function from_to(from, to, dir_delta)
42+
delta = to-from
43+
return findall(x->x == delta, dir_delta)[1]
44+
end
45+
46+
47+
48+
function detect_move(image, p0, p2, nbd, border, done, dir_delta)
49+
dir = from_to(p0, p2, dir_delta)
50+
moved = clockwise(dir)
51+
p1 = CartesianIndex(0, 0)
52+
while moved != dir ## 3.1
53+
newp = move(p0, image, moved, dir_delta)
54+
if newp[1]!=0
55+
p1 = newp
56+
break
57+
end
58+
moved = clockwise(moved)
59+
end
60+
61+
if p1 == CartesianIndex(0, 0)
62+
return
63+
end
64+
65+
p2 = p1 ## 3.2
66+
p3 = p0 ## 3.2
67+
done .= false
68+
while true
69+
dir = from_to(p3, p2, dir_delta)
70+
moved = counterclockwise(dir)
71+
p4 = CartesianIndex(0, 0)
72+
done .= false
73+
while true ## 3.3
74+
p4 = move(p3, image, moved, dir_delta)
75+
if p4[1] != 0
76+
break
77+
end
78+
done[moved] = true
79+
moved = counterclockwise(moved)
80+
end
81+
push!(border, p3) ## 3.4
82+
if p3[1] == size(image, 1) || done[3]
83+
image[p3] = -nbd
84+
elseif image[p3] == 1
85+
image[p3] = nbd
86+
end
87+
88+
if (p4 == p0 && p3 == p1) ## 3.5
89+
break
90+
end
91+
p2 = p3
92+
p3 = p4
93+
end
94+
end
95+
96+
97+
function find_contours(image)
98+
nbd = 1
99+
lnbd = 1
100+
image = convert(Array{Float64}, image)
101+
contour_list = Vector{typeof(CartesianIndex[])}()
102+
done = [false, false, false, false, false, false, false, false]
103+
104+
## Clockwise Moore neighborhood.
105+
dir_delta = [CartesianIndex(-1, 0) , CartesianIndex(-1, 1), CartesianIndex(0, 1), CartesianIndex(1, 1), CartesianIndex(1, 0), CartesianIndex(1, -1), CartesianIndex(0, -1), CartesianIndex(-1,-1)]
106+
107+
height, width = size(image)
108+
109+
for i=1:height
110+
lnbd = 1
111+
for j=1:width
112+
fji = image[i, j]
113+
is_outer = (image[i, j] == 1 && (j == 1 || image[i, j-1] == 0)) ## 1 (a)
114+
is_hole = (image[i, j] >= 1 && (j == width || image[i, j+1] == 0))
115+
116+
if is_outer || is_hole
117+
## 2
118+
border = CartesianIndex[]
119+
120+
from = CartesianIndex(i, j)
121+
122+
if is_outer
123+
nbd += 1
124+
from -= CartesianIndex(0, 1)
125+
126+
else
127+
nbd += 1
128+
if fji > 1
129+
lnbd = fji
130+
end
131+
from += CartesianIndex(0, 1)
132+
end
133+
134+
p0 = CartesianIndex(i,j)
135+
detect_move(image, p0, from, nbd, border, done, dir_delta) ## 3
136+
if isempty(border) ##TODO
137+
push!(border, p0)
138+
image[p0] = -nbd
139+
end
140+
push!(contour_list, border)
141+
end
142+
if fji != 0 && fji != 1
143+
lnbd = abs(fji)
144+
end
145+
146+
end
147+
end
148+
149+
return contour_list
150+
151+
152+
end
153+
154+
## a contour is a vector of 2 int arrays
155+
function draw_contour(image, color, contour)
156+
for ind in contour
157+
image[ind] = color
158+
end
159+
end
160+
function draw_contours(image, color, contours)
161+
for cnt in contours
162+
draw_contour(image, color, cnt)
163+
end
164+
end
165+
166+
## load images
167+
img1 = testimage("mandrill")
168+
img2 = testimage("lighthouse")
169+
170+
## convert to grayscale
171+
imgg1 = Gray.(img1)
172+
imgg2 = Gray.(img2)
173+
174+
## threshold
175+
imgg1 = imgg1 .> 0.45
176+
imgg2 = imgg2 .> 0.45
177+
178+
## calling find_contours
179+
cnts1 = find_contours(imgg1)
180+
cnts2 = find_contours(imgg2)
181+
182+
img3 = copy(img1)
183+
img4 = copy(img2)
184+
185+
## finally, draw the detected contours
186+
draw_contours(img3, RGB(1,0,0), cnts1)
187+
draw_contours(img4, RGB(1,0,0), cnts2)
188+
189+
190+
vcat([img1 img2], [img3 img4])
191+
192+
save("assets/contour_detection.png", img3) #src

0 commit comments

Comments
 (0)