Обнаружение и рисование контуров
В этой демонстрации показано, как обнаруживать контуры на двоичных изображениях. Применяется алгоритм топологического структурного анализа оцифрованных двоичных изображений по границам, разработанный Сузуки и Абэ (такой же, как в OpenCV).#
Точки представлены в виде CartesianIndex. Контуры представлены в виде вектора точек. Направление — это одно число от 1 до 8. Шаги внутри функций обозначены так же, как в оригинальной работе:
using Images, TestImages, FileIO
# С СВ В ЮВ Ю ЮЗ З СЗ
# направление между двумя пикселями
# направление поворота по часовой стрелке
function clockwise(dir)
return (dir)%8 + 1
end
# направление поворота против часовой стрелки
function counterclockwise(dir)
return (dir+6)%8 + 1
end
# переходим от текущего пикселя к следующему в заданном направлении
function move(pixel, image, dir, dir_delta)
newp = pixel + dir_delta[dir]
height, width = size(image)
if (0 < newp[1] <= height) && (0 < newp[2] <= width)
if image[newp]!=0
return newp
end
end
return CartesianIndex(0, 0)
end
# находит направление между двумя заданными пикселями
function from_to(from, to, dir_delta)
delta = to-from
return findall(x->x == delta, dir_delta)[1]
end
function detect_move(image, p0, p2, nbd, border, done, dir_delta)
dir = from_to(p0, p2, dir_delta)
moved = clockwise(dir)
p1 = CartesianIndex(0, 0)
while moved != dir ## 3.1
newp = move(p0, image, moved, dir_delta)
if newp[1]!=0
p1 = newp
break
end
moved = clockwise(moved)
end
if p1 == CartesianIndex(0, 0)
return
end
p2 = p1 ## 3.2
p3 = p0 ## 3.2
done .= false
while true
dir = from_to(p3, p2, dir_delta)
moved = counterclockwise(dir)
p4 = CartesianIndex(0, 0)
done .= false
while true ## 3.3
p4 = move(p3, image, moved, dir_delta)
if p4[1] != 0
break
end
done[moved] = true
moved = counterclockwise(moved)
end
push!(border, p3) ## 3.4
if p3[1] == size(image, 1) || done[3]
image[p3] = -nbd
elseif image[p3] == 1
image[p3] = nbd
end
if (p4 == p0 && p3 == p1) ## 3.5
break
end
p2 = p3
p3 = p4
end
end
function find_contours(image)
nbd = 1
lnbd = 1
image = Float64.(image)
contour_list = Vector{typeof(CartesianIndex[])}()
done = [false, false, false, false, false, false, false, false]
# Окрестность Мура по часовой стрелке.
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)]
height, width = size(image)
for i=1:height
lnbd = 1
for j=1:width
fji = image[i, j]
is_outer = (image[i, j] == 1 && (j == 1 || image[i, j-1] == 0)) ## 1 (a)
is_hole = (image[i, j] >= 1 && (j == width || image[i, j+1] == 0))
if is_outer || is_hole
# 2
border = CartesianIndex[]
from = CartesianIndex(i, j)
if is_outer
nbd += 1
from -= CartesianIndex(0, 1)
else
nbd += 1
if fji > 1
lnbd = fji
end
from += CartesianIndex(0, 1)
end
p0 = CartesianIndex(i,j)
detect_move(image, p0, from, nbd, border, done, dir_delta) ## 3
if isempty(border) ##TODO
push!(border, p0)
image[p0] = -nbd
end
push!(contour_list, border)
end
if fji != 0 && fji != 1
lnbd = abs(fji)
end
end
end
return contour_list
end
# контур — это вектор из 2 массивов чисел типа int
function draw_contour(image, color, contour)
for ind in contour
image[ind] = color
end
end
function draw_contours(image, color, contours)
for cnt in contours
draw_contour(image, color, cnt)
end
end
# загружаем изображения
img1 = testimage("mandrill")
img2 = testimage("lighthouse")
# преобразуем в оттенки серого
imgg1 = Gray.(img1)
imgg2 = Gray.(img2)
# порог
imgg1 = imgg1 .> 0.45
imgg2 = imgg2 .> 0.45
# вызываем find_contours
cnts1 = find_contours(imgg1)
cnts2 = find_contours(imgg2)
img3 = copy(img1)
img4 = copy(img2)
# наконец, рисуем обнаруженные контуры
draw_contours(img3, RGB(1,0,0), cnts1)
draw_contours(img4, RGB(1,0,0), cnts2)
vcat([img1 img2], [img3 img4])
Эта страница была создана с помощью DemoCards.jl и Literate.jl.