Документация Engee

Справка

Структурирующий элемент

strel([T], X::AbstractArray)

Преобразует структурирующий элемент (СЭ) X в соответствующий формат представления с типом элементов T. Это полезный инструмент для создания СЭ, которые принимают большинство функций ImageMorphology.

ImageMorphology в настоящее время поддерживает два распространенных представления:

  • T=CartesianIndex: смещения относительно центральной точки. Тип вывода — Vector{CartesianIndex{N}}.

  • T=Bool: маска связи, где true означает наличие связи с центральной точкой. Тип вывода — BitArray{N}.

julia> se_mask = centered(Bool[1 1 0; 1 1 0; 0 0 0]) # маска связи
3×3 OffsetArray(::Matrix{Bool}, -1:1, -1:1) with eltype Bool with indices -1:1×-1:1:
 1  1  0
 1  1  0
 0  0  0

julia> se_offsets = strel(CartesianIndex, se_mask) # смещения относительно центральной точки
3-element Vector{CartesianIndex{2}}:
 CartesianIndex(-1, -1)
 CartesianIndex(0, -1)
 CartesianIndex(-1, 0)

julia> se = strel(Bool, se_offsets)
3×3 OffsetArray(::BitMatrix, -1:1, -1:1) with eltype Bool with indices -1:1×-1:1:
 1  1  0
 1  1  0
 0  0  0

Конструкторы СЭ для двух особых случаев см. также в описании функций strel_diamond и strel_box.

strel_chain(A, B, ...)
strel_chain(As)

Объединяет два структурирующих элемента одинаковой размерности для построения структурирующего элемента большего размера.

Выходное измерение такое же, как входные. См. также функцию strel_product, которая возвращает декартово произведение СЭ.

Для некоторых морфологических операций f, таких как расширение и эрозия, если se можно разложить на меньшие элементы se1, se2, ..., seN, то f(img, se) эквивалентно f(...f(f(img, se1), se2), ..., seN). Так как применять f к меньшим СЭ эффективнее, чем к исходному большому, этот прием широко применяется в морфологии изображений.

julia> img = rand(512, 512);

julia> se1, se2 = [centered(rand(Bool, 3, 3)) for _ in 1:2];

julia> se = strel_chain(se1, se2);

julia> out_se = dilate(img, se);

julia> out_pipe = dilate(dilate(img, se1), se2);

julia> out_se[2:end-1, 2:end-1] == out_pipe[2:end-1, 2:end-1] # граница исключается
true
strel_product(A, B, ...)
strel_product(se_list)

Декартово произведение нескольких структурирующих элементов; выходное измерение ndims(out) == sum(ndims, se_list).

См. также описание функции strel_chain, которая объединяет СЭ в одном измерении.

julia> strel_product(strel_diamond((5, 5)), centered(Bool[1, 1, 1]))
5×5×3 SEChainArray{3, OffsetArrays.OffsetArray{Bool, 3, BitArray{3}}} with indices -2:2×-2:2×-1:1:
[:, :, -1] =
 0  0  1  0  0
 0  1  1  1  0
 1  1  1  1  1
 0  1  1  1  0
 0  0  1  0  0

[:, :, 0] =
 0  0  1  0  0
 0  1  1  1  0
 1  1  1  1  1
 0  1  1  1  0
 0  0  1  0  0

[:, :, 1] =
 0  0  1  0  0
 0  1  1  1  0
 1  1  1  1  1
 0  1  1  1  0
 0  0  1  0  0
strel_box(A; r=1)
strel_box(size; r=size .÷ 2)

Создает N-мерный структурирующий элемент (СЭ), все элементы которого в локальном окне связаны.

Если предоставлено изображение A, то СЭ будет иметь размер (2r+1, 2r+1, ...) с половинным размером по умолчанию r=1. Если предоставлено значение size, r по умолчанию будет равно size .÷ 2. В dims по умолчанию будут содержаться все измерения, то есть (1, 2, ..., length(size)).

julia> img = rand(64, 64);

julia> strel_box(img)
3×3 SEBoxArray{2, UnitRange{Int64}} with indices -1:1×-1:1:
 1  1  1
 1  1  1
 1  1  1

julia> strel_box(img; r=2)
5×5 SEBoxArray{2, UnitRange{Int64}} with indices -2:2×-2:2:
 1  1  1  1  1
 1  1  1  1  1
 1  1  1  1  1
 1  1  1  1  1
 1  1  1  1  1

julia> strel_box((5,5); r=(1,2))
5×5 SEBoxArray{2, UnitRange{Int64}} with indices -2:2×-2:2:
 0  0  0  0  0
 1  1  1  1  1
 1  1  1  1  1
 1  1  1  1  1
 0  0  0  0  0

SEBox — это специальный тип (прямоугольная форма), для которого многие морфологические алгоритмы могут предоставлять эффективные реализации. По этой причине при попытке поместить SEBoxArray в массив другого типа (например, в Array{Bool} с помощью collect) весьма вероятно существенное снижение производительности.

См. также описание функций strel и strel_box.

strel_diamond(A::AbstractArray, [dims]; r=1)
strel_diamond(size, [dims]; [r])

Создает N-мерный структурирующий элемент (СЭ) ромбовидной формы.

Если предоставлено изображение A, то СЭ будет иметь размер (2r+1, 2r+1, ...) с половинным размером по умолчанию r=1. Если предоставлено значение size, r по умолчанию будет равно maximum(size)÷2. В dims по умолчанию будут содержаться все измерения, то есть (1, 2, ..., length(size)).

julia> img = rand(64, 64);

julia> strel_diamond(img) # размер по умолчанию для входного изображения: (3, 3)
3×3 SEDiamondArray{2, 2, UnitRange{Int64}, 0} with indices -1:1×-1:1:
 0  1  0
 1  1  1
 0  1  0

julia> strel_diamond(img; r=2) # эквивалентно `strel_diamond((5,5))`
5×5 SEDiamondArray{2, 2, UnitRange{Int64}, 0} with indices -2:2×-2:2:
 0  0  1  0  0
 0  1  1  1  0
 1  1  1  1  1
 0  1  1  1  0
 0  0  1  0  0

julia> strel_diamond(img, (1,)) # маска по измерению 1
3×1 SEDiamondArray{2, 1, UnitRange{Int64}, 1} with indices -1:1×0:0:
 1
 1
 1

julia> strel_diamond((3,3), (1,)) # маска 3×3 по измерению 1
3×3 SEDiamondArray{2, 1, UnitRange{Int64}, 1} with indices -1:1×-1:1:
 0  1  0
 0  1  0
 0  1  0

SEDiamond — это специальный тип (ромбовидная форма), для которого многие морфологические алгоритмы могут предоставлять эффективные реализации. По этой причине при попытке поместить SEDiamondArray в массив другого типа (например, в Array{Bool} с помощью collect) весьма вероятно существенное снижение производительности.

См. также описание функций strel и strel_box.

strel_type(x)

Выводит тип структурирующего элемента для x.

Эта функция служит для диспетчеризации специальных типов СЭ, например SEBoxArray, в оптимизированную реализацию определенного морфологического фильтра. В этих целях для пользовательских типов массивов СЭ должен быть определен данный метод.

strel_size(x)

Вычисляет минимальный размер блока, в который вмещается структурирующий элемент. Результатом будет кортеж нечетных целых чисел.

julia> se = strel_diamond((5, 5); r=1)
5×5 SEDiamondArray{2, 2, UnitRange{Int64}, 0} with indices -2:2×-2:2:
 0  0  0  0  0
 0  0  1  0  0
 0  1  1  1  0
 0  0  1  0  0
 0  0  0  0  0

julia> strel_size(se) # не равно (5, 5)
(3, 3)

julia> strel(Bool, strel(CartesianIndex, se)) # поскольку проверяется только минимальный вмещающий блок
3×3 OffsetArray(::BitMatrix, -1:1, -1:1) with eltype Bool with indices -1:1×-1:1:
 0  1  0
 1  1  1
 0  1  0

julia> se = [CartesianIndex(1, 1), CartesianIndex(-2, -2)];

julia> strel_size(se) # не равно (4, 4)
(5, 5)

julia> strel(Bool, se) # поскольку маска связи должна иметь нечетный размер
5×5 OffsetArray(::BitMatrix, -2:2, -2:2) with eltype Bool with indices -2:2×-2:2:
 1  0  0  0  0
 0  0  0  0  0
 0  0  1  0  0
 0  0  0  1  0
 0  0  0  0  0

julia> se = strel_diamond((5, 5), (1, ); r=1)
5×5 SEDiamondArray{2, 1, UnitRange{Int64}, 1} with indices -2:2×-2:2:
 0  0  0  0  0
 0  0  1  0  0
 0  0  1  0  0
 0  0  1  0  0
 0  0  0  0  0

julia> strel_size(se)
(3, 1)
strel_ndims(x)::Int

Выводит измерение структурирующего элемента x.

upper, lower = strel_split([T], se)

Разделяет симметричный структурирующий элемент на верхнюю и нижнюю половины на основе центральной точки.

Для каждого элемента o в strel(CartesianIndex, upper) соответствующий отрицательный элемент -o входит в strel(CartesianIndex, lower). Эта функция не является обратной функции strel_chain.

Разделенные несимметричные части СЭ будут представлены массивом T, где T — это либо Bool, либо CartesianIndex. По умолчанию T = eltype(se).

julia> se = strel_diamond((3, 3))
3×3 SEDiamondArray{2, 2, UnitRange{Int64}, 0} with indices -1:1×-1:1:
 0  1  0
 1  1  1
 0  1  0

julia> upper, lower = strel_split(se);

julia> upper
3×3 OffsetArray(::Matrix{Bool}, -1:1, -1:1) with eltype Bool with indices -1:1×-1:1:
 0  1  0
 1  1  0
 0  0  0

julia> lower
3×3 OffsetArray(::Matrix{Bool}, -1:1, -1:1) with eltype Bool with indices -1:1×-1:1:
 0  0  0
 0  1  1
 0  1  0

Если элемент se представлен как массив смещений, то результат разделения также будет массивом смещений:

julia> se = strel(CartesianIndex, se)
4-element Vector{CartesianIndex{2}}:
 CartesianIndex(0, -1)
 CartesianIndex(-1, 0)
 CartesianIndex(1, 0)
 CartesianIndex(0, 1)

julia> upper, lower = strel_split(se);

julia> upper
2-element Vector{CartesianIndex{2}}:
 CartesianIndex(0, -1)
 CartesianIndex(-1, 0)

julia> lower
2-element Vector{CartesianIndex{2}}:
 CartesianIndex(1, 0)
 CartesianIndex(0, 1)
centered(A, cp=center(A)) -> Ao

Смещает центральную координату / точку cp массива A в точку (0, 0, ..., 0). Внутренне это эквивалентно OffsetArray(A, .-cp).

Совместимость: OffsetArrays 1.9

Для этого метода требуется версия OffsetArrays не ниже 1.9.

Примеры

julia> A = reshape(collect(1:9), 3, 3)
3×3 Matrix{Int64}:
 1  4  7
 2  5  8
 3  6  9

julia> Ao = OffsetArrays.centered(A); # оси (-1:1, -1:1)

julia> Ao[0, 0]
5

julia> Ao = OffsetArray(A, OffsetArrays.Origin(0)); # оси (0:2, 0:2)

julia> Aoo = OffsetArrays.centered(Ao); # оси (-1:1, -1:1)

julia> Aoo[0, 0]
5

Пользователи могут передавать cp, чтобы изменить интерпретацию «центральной точки», но значение выходного массива также должно быть переосмыслено. Например, если cp = map(last, axes(A)), то эта функция смещает уже не центральную точку, а правую нижнюю точку в точку (0, 0, ..., 0). Обычно cp используется для изменения поведения округления, когда массив имеет четное значение размера в некотором измерении:

julia> A = reshape(collect(1:4), 2, 2) # В идеале центр должен быть в точке (1.5, 1.5), но OffsetArrays поддерживают только целочисленные смещения.
2×2 Matrix{Int64}:
 1  3
 2  4

julia> OffsetArrays.centered(A, OffsetArrays.center(A, RoundUp)) # задание (2, 2) в качестве центральной точки
2×2 OffsetArray(::Matrix{Int64}, -1:0, -1:0) with eltype Int64 with indices -1:0×-1:0:
 1  3
 2  4

julia> OffsetArrays.centered(A, OffsetArrays.center(A, RoundDown)) # задание (1, 1) в качестве центральной точки
2×2 OffsetArray(::Matrix{Int64}, 0:1, 0:1) with eltype Int64 with indices 0:1×0:1:
 1  3
 2  4

См. также описание функции center.

center(A, [r::RoundingMode=RoundDown])::Dims

Возвращает координату центра заданного массива A. Если значение size(A, k) является четным числом, то будет применена процедура округления с режимом r.

Совместимость: OffsetArrays 1.9

Для этого метода требуется версия OffsetArrays не ниже 1.9.

Примеры

julia> A = reshape(collect(1:9), 3, 3)
3×3 Matrix{Int64}:
 1  4  7
 2  5  8
 3  6  9

julia> c = OffsetArrays.center(A)
(2, 2)

julia> A[c...]
5

julia> Ao = OffsetArray(A, -2, -2); # оси (-1:1, -1:1)

julia> c = OffsetArrays.center(Ao)
(0, 0)

julia> Ao[c...]
5

Чтобы сместить центральную координату заданного массива в точку (0, 0, ...), можно использовать centered.

is_symmetric(se)

Проверяет, является ли данный массив структурирующего элемента se симметричным относительно центрального пикселя.

Говоря более строго, эта функция проверяет равенство mask[I] == mask[-I] для любого допустимого I ∈ CartesianIndices(mask) в представлении маски связи mask = strel(Bool, se).

SEMask{N}()

Типажный тип (чистый) для представления структурирующего элемента как маски связи. Маска связи СЭ — это массив логических значений, где true означает, что позиция пикселя связана с центральной точкой.

julia> se = centered(Bool[0 1 0; 1 1 1; 0 1 0]) # широко известно как связь C4
3×3 OffsetArray(::Matrix{Bool}, -1:1, -1:1) with eltype Bool with indices -1:1×-1:1:
 0  1  0
 1  1  1
 0  1  0

julia> strel_type(se)
ImageMorphology.StructuringElements.SEMask{2}()

Сведения о представлении в виде смещений см. в описании типа SEOffset. Дополнительные сведения можно найти на странице документации Структурирующий элемент.

SEOffset{N}()

Типажный тип (чистый) для представления структурирующего элемента как смещений. Смещения СЭ — это массив CartesianIndex, в каждом элементе которого хранится смещение от центральной точки.

julia> se = [CartesianIndex(-1, 0), CartesianIndex(0, -1), CartesianIndex(1, 0), CartesianIndex(0, 1)]
4-element Vector{CartesianIndex{2}}:
 CartesianIndex(-1, 0)
 CartesianIndex(0, -1)
 CartesianIndex(1, 0)
 CartesianIndex(0, 1)

julia> strel_type(se)
ImageMorphology.StructuringElements.SEOffset{2}()

Сведения о представлении в виде маски связи см. в описании типа SEMask. Дополнительные сведения можно найти на странице документации Структурирующий элемент.

SEDiamond{N}(axes, [dims]; [r])

Типажный тип (чистый) для N-мерного структурирующего элемента ромбовидной формы. Это особый случай SEMask, для которого алгоритмы ImageMorphology могут предоставлять оптимизированную реализацию.

Рекомендуется использовать strel_diamond и strel_type:

julia> se = strel_diamond((3, 3)) # связь C4
3×3 SEDiamondArray{2, 2, UnitRange{Int64}, 0} with indices -1:1×-1:1:
 0  1  0
 1  1  1
 0  1  0

julia> strel_type(se)
SEDiamond{2, 2, UnitRange{Int64}}((-1:1, -1:1), (1, 2), 1)

julia> se = centered(collect(se)) # преобразование в обычный центрированный массив
3×3 OffsetArray(::Matrix{Bool}, -1:1, -1:1) with eltype Bool with indices -1:1×-1:1:
 0  1  0
 1  1  1
 0  1  0

julia> strel_type(se)
SEMask{2}()
SEBox{N}(axes; [r])

N-мерный структурирующий элемент, все элементы которого связаны. Это особый случай SEMask, для которого алгоритмы ImageMorphology могут предоставлять оптимизированную реализацию.

Рекомендуется использовать strel_box и strel_type:

julia> se = strel_box((3, 3)) # связь C8
3×3 SEBoxArray{2, UnitRange{Int64}} with indices -1:1×-1:1:
 1  1  1
 1  1  1
 1  1  1

julia> strel_type(se)
SEBox{2, UnitRange{Int64}}((-1:1, -1:1), (1, 1))

julia> se = centered(collect(se)) # преобразование в обычный центрированный массив
3×3 OffsetArray(::Matrix{Bool}, -1:1, -1:1) with eltype Bool with indices -1:1×-1:1:
 1  1  1
 1  1  1
 1  1  1

julia> strel_type(se)
SEMask{2}()
SEDiamondArray(se::SEDiamond)

Созданный экземпляр массива SEDiamond.

SEBoxArray(se::SEBox)

Созданный экземпляр массива SEBox.

Морфологические операции

extreme_filter(f, A; r=1, [dims]) -> out
extreme_filter(f, A, Ω) -> out

Фильтрует массив A с использованием функции выбора f(x, y) для каждой окрестности Ω. Слово extreme в названии присутствует по той причине, что в качестве функции выбора f обычно используются min и max.

Для каждого пикселя p в A функция выбора f применяется итеративно к его окрестности Ω следующим образом: f(...(f(f(A[p], A[p+Ω[1]]), A[p+Ω[2]]), ...). Например, в одномерном случае для каждого p по умолчанию выполняется операция out[p] = f(f(A[p], A[p-1]), A[p+1]).

Окрестность Ω определяется аргументом dims или Ω. Именованные аргументы r и dims определяют окрестность прямоугольной формы Ω с использованием функции strel_box. Ω также называется структурирующим элементом (СЭ) и может быть представлено смещениями или маской в виде массива логических значений; дополнительные сведения см. в описании функции strel.

Примеры

julia> M = [4 6 5 3 4; 8 6 9 4 8; 7 8 4 9 6; 6 2 2 1 7; 1 6 5 2 6]
5×5 Matrix{Int64}:
 4  6  5  3  4
 8  6  9  4  8
 7  8  4  9  6
 6  2  2  1  7
 1  6  5  2  6

julia> extreme_filter(max, M) # фильтр максимума с использованием 4 смежных элементов по обоим измерениям
5×5 Matrix{Int64}:
 8  9  9  9  8
 8  9  9  9  9
 8  9  9  9  9
 8  8  9  9  9
 6  6  6  7  7

julia> extreme_filter(max, M; dims=1) # фильтр максимума по первому измерению (столбцу)
5×5 Matrix{Int64}:
 8  6  9  4  8
 8  8  9  9  8
 8  8  9  9  8
 7  8  5  9  7
 6  6  5  2  7

Ω может быть либо массивом маски AbstractArray{Bool}, в котором значение true означает наличие связи, либо массивом AbstractArray{<:CartesianIndex}, в котором каждый элемент означает смещение относительно центрального элемента.

julia> Ω_mask = centered(Bool[1 1 0; 1 1 0; 1 0 0]) # пользовательская окрестность в формате маски
3×3 OffsetArray(::Matrix{Bool}, -1:1, -1:1) with eltype Bool with indices -1:1×-1:1:
 1  1  0
 1  1  0
 1  0  0

julia> out = extreme_filter(max, M, Ω_mask)
5×5 Matrix{Int64}:
 4  8  6  9  4
 8  8  9  9  9
 8  8  9  9  9
 7  8  8  9  9
 6  6  6  5  7

julia> Ω_offsets = strel(CartesianIndex, Ω_mask) # пользовательская окрестность в виде смещений
4-element Vector{CartesianIndex{2}}:
 CartesianIndex(-1, -1)
 CartesianIndex(0, -1)
 CartesianIndex(1, -1)
 CartesianIndex(-1, 0)

julia> out == extreme_filter(max, M, Ω_offsets) # обе версии работают одинаково
true

См. также выполняемую на месте версию: extreme_filter!. В пакете ImageFiltering есть функция, предоставляющая аналогичные возможности: ImageFiltering.mapwindow.

extreme_filter!(f, out, A; [r], [dims])
extreme_filter!(f, out, A, Ω)

Выполняемая на месте версия функции extreme_filter, где out — это выходной массив, подлежащий изменению.

dilate(img; dims=coords_spatial(img), r=1)
dilate(img, se)

Применяет фильтр максимума к окрестности img, определяемой структурирующим элементом se.

se — это структурирующий элемент, определяющий окрестность изображения. Дополнительные сведения см. в описании функции strel. Если элемент se не указан, используется функция strel_box с дополнительным именованным аргументом dims, определяющим измерения для фильтрации, и половинным размером r, определяющим размер ромба.

Примеры

julia> img = falses(5, 5); img[3, [2, 4]] .= true; img
5×5 BitMatrix:
 0  0  0  0  0
 0  0  0  0  0
 0  1  0  1  0
 0  0  0  0  0
 0  0  0  0  0

julia> dilate(img)
5×5 BitMatrix:
 0  0  0  0  0
 1  1  1  1  1
 1  1  1  1  1
 1  1  1  1  1
 0  0  0  0  0

julia> dilate(img; dims=1)
5×5 BitMatrix:
 0  0  0  0  0
 0  1  0  1  0
 0  1  0  1  0
 0  1  0  1  0
 0  0  0  0  0

julia> dilate(img, strel_diamond(img)) # используем СЭ ромбовидной формы
5×5 BitMatrix:
 0  0  0  0  0
 0  1  0  1  0
 1  1  1  1  1
 0  1  0  1  0
 0  0  0  0  0

Дополнительные материалы

  • dilate! is the in-place version of this function

  • erode is the dual operator of dilate in the sense that complement.(dilate(img)) == erode(complement.(img)).

Если элемент se симметричен относительно начала координат, то есть se[b] == se[-b] для любого b, то расширение сводится к сумме Минковского: A⊕B={a+b|a∈A, b∈B}.

dilate!(out, img; [dims], [r])
dilate!(out, img, se)

Выполняемая на месте версия dilate с входным изображением img и выходным изображением out.

out = erode(img; dims=coords_spatial(img), r=1)
out = erode(img, se)

Применяет фильтр минимума к окрестности img, определяемой структурирующим элементом se.

se — это структурирующий элемент, определяющий окрестность изображения. Дополнительные сведения см. в описании функции strel. Если элемент se не указан, используется функция strel_box с дополнительным именованным аргументом dims, определяющим измерения для фильтрации, и половинным размером r, определяющим размер ромба.

Примеры

julia> img = trues(5, 5); img[3, [2, 4]] .= false; img
5×5 BitMatrix:
 1  1  1  1  1
 1  1  1  1  1
 1  0  1  0  1
 1  1  1  1  1
 1  1  1  1  1

julia> erode(img)
5×5 BitMatrix:
 1  1  1  1  1
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 1  1  1  1  1

julia> erode(img; dims=1)
5×5 BitMatrix:
 1  1  1  1  1
 1  0  1  0  1
 1  0  1  0  1
 1  0  1  0  1
 1  1  1  1  1

julia> erode(img, strel_diamond(img)) # используем СЭ ромбовидной формы
5×5 BitMatrix:
 1  1  1  1  1
 1  0  1  0  1
 0  0  0  0  0
 1  0  1  0  1
 1  1  1  1  1

Дополнительные материалы

  • erode! is the in-place version of this function

  • dilate is the dual operator of erode in the sense that complement.(dilate(img)) == erode(complement.(img)).

Если элемент se симметричен относительно начала координат, то есть se[b] == se[-b] для любого b, то эрозия сводится к разности Минковского: A⊖B={a-b|a∈A, b∈B}.

erode!(out, img; [dims], [r])
erode!(out, img, se)

Выполняемая на месте версия erode с входным изображением img и выходным изображением out.

opening(img; dims=coords_spatial(img), r=1)
opening(img, se)

Выполняет морфологическое открытие для img. Операция открытия определяется как эрозия с последующим расширением: dilate(erode(img, se), se).

se — это структурирующий элемент, определяющий окрестность изображения. Дополнительные сведения см. в описании функции strel. Если элемент se не указан, используется функция strel_box с дополнительным именованным аргументом dims, определяющим измерения для фильтрации, и половинным размером r, определяющим размер ромба.

Примеры

julia> img = trues(7,7); img[2, 2] = false; img[3:5, 3:5] .= false; img[4, 4] = true; img
7×7 BitMatrix:
 1  1  1  1  1  1  1
 1  0  1  1  1  1  1
 1  1  0  0  0  1  1
 1  1  0  1  0  1  1
 1  1  0  0  0  1  1
 1  1  1  1  1  1  1
 1  1  1  1  1  1  1

julia> opening(img)
7×7 BitMatrix:
 0  0  1  1  1  1  1
 0  0  1  1  1  1  1
 1  1  0  0  0  1  1
 1  1  0  0  0  1  1
 1  1  0  0  0  1  1
 1  1  1  1  1  1  1
 1  1  1  1  1  1  1

julia> opening(img, strel_diamond(img)) # используем СЭ ромбовидной формы
7×7 BitMatrix:
 1  1  1  1  1  1  1
 1  0  1  1  1  1  1
 1  1  0  0  0  1  1
 1  1  0  0  0  1  1
 1  1  0  0  0  1  1
 1  1  1  1  1  1  1
 1  1  1  1  1  1  1

Дополнительные материалы

  • opening! is the in-place version of this function.

  • closing is the dual operator of opening in the sense that complement.(opening(img)) == closing(complement.(img)).

opening!(out, img, buffer; [dims], [r])
opening!(out, img, se, buffer)

Выполняемая на месте версия opening с входным изображением img и выходным изображением out. Промежуточный результат эрозии сохраняется в buffer.

closing(img; dims=coords_spatial(img), r=1)
closing(img, se)

Выполняет морфологическое закрытие для img. Операция закрытия определяется как расширение с последующей эрозией: erode(dilate(img, se), se).

se — это структурирующий элемент, определяющий окрестность изображения. Дополнительные сведения см. в описании функции strel. Если элемент se не указан, используется функция strel_box с дополнительным именованным аргументом dims, определяющим измерения для фильтрации, и половинным размером r, определяющим размер ромба.

Примеры

julia> img = falses(7,7); img[2, 2] = true; img[3:5, 3:5] .= true; img[4, 4] = false; img
7×7 BitMatrix:
 0  0  0  0  0  0  0
 0  1  0  0  0  0  0
 0  0  1  1  1  0  0
 0  0  1  0  1  0  0
 0  0  1  1  1  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0

julia> closing(img)
7×7 BitMatrix:
 1  1  0  0  0  0  0
 1  1  0  0  0  0  0
 0  0  1  1  1  0  0
 0  0  1  1  1  0  0
 0  0  1  1  1  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0

julia> closing(img, strel_diamond(img)) # # используем СЭ ромбовидной формы
7×7 BitMatrix:
 0  0  0  0  0  0  0
 0  1  0  0  0  0  0
 0  0  1  1  1  0  0
 0  0  1  1  1  0  0
 0  0  1  1  1  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0

Дополнительные материалы

  • opening! is the in-place version of this function.

  • closing is the dual operator of opening in the sense that complement.(opening(img)) == closing(complement.(img)).

closing!(out, img, buffer; [dims], [r])
closing!(out, img, se, buffer)

Выполняемая на месте версия closing с входным изображением img и выходным изображением out. Промежуточный результат расширения сохраняется в buffer.

tophat(img; dims=coords_spatial(img), r=1)
tophat(img, se)

Выполняет морфологическое преобразование «верх шляпы» (Top Hat) для данного изображения, то есть img - opening(img, se).

se — это структурирующий элемент, определяющий окрестность изображения. Дополнительные сведения см. в описании функции strel. Если элемент se не указан, используется функция strel_box с дополнительным именованным аргументом dims, определяющим измерения для фильтрации, и половинным размером r, определяющим размер ромба.

Это белое преобразование «верх шляпы» может использоваться для выделения небольших белых элементов и деталей на изображении. Для выделения черных деталей можно использовать черное преобразование «верх шляпы», также известное как преобразование «дно шляпы», bothat.

Примеры

julia> img = falses(5, 5); img[1, 1] = true; img[3:5, 3:5] .= true; img
5×5 BitMatrix:
 1  0  0  0  0
 0  0  0  0  0
 0  0  1  1  1
 0  0  1  1  1
 0  0  1  1  1

julia> Int.(tophat(img))
5×5 Matrix{Int64}:
 1  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0

julia> Int.(tophat(img, strel_diamond(img))) # используем СЭ ромбовидной формы
5×5 Matrix{Int64}:
 1  0  0  0  0
 0  0  0  0  0
 0  0  1  0  0
 0  0  0  0  0
 0  0  0  0  0
tophat!(out, img, buffer; [dims], [r])
tophat!(out, img, se, buffer)

Выполняемая на месте версия tophat с входным изображением img и выходным изображением out. Промежуточный результат эрозии сохраняется в buffer.

bothat(img; dims=coords_spatial(img), r=1)
bothat(img, se)

Выполняет морфологическое преобразование «дно шляпы» (Bottom Hat) для данного изображения, то есть closing(img, se) - img.

se — это структурирующий элемент, определяющий окрестность изображения. Дополнительные сведения см. в описании функции strel. Если элемент se не указан, используется функция strel_box с дополнительным именованным аргументом dims, определяющим измерения для фильтрации, и половинным размером r, определяющим размер ромба.

Преобразование «дно шляпы», также известное как черное преобразование «верх шляпы», может использоваться для выделения небольших черных элементов и деталей на изображении. Для выделения белых деталей можно использовать белое преобразование «верх шляпы», tophat.

Примеры

julia> img = falses(7, 7); img[3:5, 3:5] .= true; img[4, 6] = true; img[4, 4] = false; img
7×7 BitMatrix:
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  1  1  1  0  0
 0  0  1  0  1  1  0
 0  0  1  1  1  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0

julia> Int.(bothat(img))
7×7 Matrix{Int64}:
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  0  1  0  0  1
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0

julia> Int.(bothat(img, strel_diamond(img))) # используем СЭ ромбовидной формы
7×7 Matrix{Int64}:
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  0  1  0  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0

Есть также выполняемая на месте версия: bothat!.

bothat!(out, img, buffer; [dims], [r])
bothat!(out, img, se, buffer)

Выполняемая на месте версия bothat с входным изображением img и выходным изображением out. Промежуточный результат расширения сохраняется в buffer.

mgradient(img; mode=:beucher, dims=coords_spatial(img), r=1)
mgradient(img, se; mode=:beucher)

Вычисляет морфологический градиент изображения в указанном режиме.

Широко применяются три режима [1]:

  • :beucher: режим по умолчанию. Вычисляется арифметическая разность расширения и эрозии: dilate(img, se) - erode(img, se).

  • :internal: также называется половинным градиентом по эрозии. Вычисляется арифметическая разность исходного изображения и его эрозии: img - erode(img, se).

  • :external: также называется половинным градиентом по расширению. Вычисляется арифметическая разность расширения и исходного изображения: dilate(img, se) - se.

se — это структурирующий элемент, определяющий окрестность изображения. Дополнительные сведения см. в описании функции strel. Если элемент se не указан, используется функция strel_box с дополнительным именованным аргументом dims, определяющим измерения для фильтрации, и половинным размером r, определяющим размер ромба.

Примеры

julia> img = falses(7, 7); img[3:5, 3:5] .= true; img
7×7 BitMatrix:
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  1  1  1  0  0
 0  0  1  1  1  0  0
 0  0  1  1  1  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0

julia> Int.(mgradient(img)) # в режиме по умолчанию :beucher всегда создается граница шириной два пикселя
7×7 Matrix{Int64}:
 0  0  0  0  0  0  0
 0  1  1  1  1  1  0
 0  1  1  1  1  1  0
 0  1  1  0  1  1  0
 0  1  1  1  1  1  0
 0  1  1  1  1  1  0
 0  0  0  0  0  0  0

julia> Int.(mgradient(img; mode=:internal)) # половинный градиент -- граница находится внутри исходного изображения
7×7 Matrix{Int64}:
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  1  1  1  0  0
 0  0  1  0  1  0  0
 0  0  1  1  1  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0

julia> Int.(mgradient(img; mode=:external)) # половинный градиент -- граница находится вне исходного изображения
7×7 Matrix{Int64}:
 0  0  0  0  0  0  0
 0  1  1  1  1  1  0
 0  1  0  0  0  1  0
 0  1  0  0  0  1  0
 0  1  0  0  0  1  0
 0  1  1  1  1  1  0
 0  0  0  0  0  0  0

julia> Int.(mgradient(img, strel_diamond(img))) # используем СЭ ромбовидной формы
7×7 Matrix{Int64}:
 0  0  0  0  0  0  0
 0  0  1  1  1  0  0
 0  1  1  1  1  1  0
 0  1  1  0  1  1  0
 0  1  1  1  1  1  0
 0  0  1  1  1  0  0
 0  0  0  0  0  0  0

Оператор Боше является самодополняющимся в том смысле, что mgradient(img, se; mode=:beucher) == mgradient(complement.(img), se; mode=:beucher). При r>1 он обычно называется толстым градиентом. Если в качестве se используется отрезок прямой, то градиент становится направленным.

Дополнительные материалы

  • mgradient! is the in-place version of this function.

  • mlaplacian for the laplacian operator.

  • ImageBase.FiniteDiff также предоставляет несколько конечных разностных операторов, включая fdiff, fgradient и другие.

Справочные материалы

  • [1] Rivest, Jean-Francois, Pierre Soille, and Serge Beucher. Morphological gradients. Journal of Electronic Imaging 2.4 (1993): 326—​336.

mgradient!(out, img, buffer; [dims], [r], [mode])
mgradient!(out, img, se, buffer; [mode])

Выполняемая на месте версия mgradient с входным изображением img и выходным изображением out.

В режиме :beucher требуется массив buffer. В режимах :internal и :external buffer не требуется и может быть nothing.

mlaplacian(img; dims=coords_spatial(img), r=1)
mlaplacian(img, se)

Вычисляет морфологический лапласиан изображения.

Оператор морфологического лапласиана определяется как ∇⁺A - ∇⁻A, где ∇⁺A — внешний градиент A - erode(A, se), а ∇⁻A — внутренний градиент dilate(A, se) - A. Таким образом, он равен dilate(A, se) + erode(A, se) - 2A.

se — это структурирующий элемент, определяющий окрестность изображения. Дополнительные сведения см. в описании функции strel. Если элемент se не указан, используется функция strel_box с дополнительным именованным аргументом dims, определяющим измерения для фильтрации.

Примеры

julia> img = falses(7, 7); img[3:5, 3:5] .= true; img[4, 4] = false; img
7×7 BitMatrix:
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0
 0  0  1  1  1  0  0
 0  0  1  0  1  0  0
 0  0  1  1  1  0  0
 0  0  0  0  0  0  0
 0  0  0  0  0  0  0

julia> Int.(mlaplacian(img))
7×7 Matrix{Int64}:
 0  0   0   0   0  0  0
 0  1   1   1   1  1  0
 0  1  -1  -1  -1  1  0
 0  1  -1   1  -1  1  0
 0  1  -1  -1  -1  1  0
 0  1   1   1   1  1  0
 0  0   0   0   0  0  0

julia> Int.(mlaplacian(img, strel_diamond(img))) # используем СЭ ромбовидной формы
7×7 Matrix{Int64}:
 0  0   0   0   0  0  0
 0  0   1   1   1  0  0
 0  1  -1  -1  -1  1  0
 0  1  -1   1  -1  1  0
 0  1  -1  -1  -1  1  0
 0  0   1   1   1  0  0
 0  0   0   0   0  0  0

Дополнительные материалы

  • mlaplacian! is the in-place version of this function.

  • mgradient for the gradient operator.

  • ImageBase.FiniteDiff также предоставляет несколько конечных разностных операторов, включая fdiff, fgradient и другие.

mlaplacian!(out, img, buffer; [dims], [r])
mlaplacian!(out, img, se, buffer)

Выполняемая на месте версия mlaplacian с входным изображением img и выходным изображением out. Промежуточный результат эрозии сохраняется в buffer.

Геодезические операции

mreconstruct(op, marker, mask; [dims])
mreconstruct(op, marker, mask, se)

Морфологическая реконструкция изображения marker по операции op.

Аргумент op может иметь значение erode или dilate, означающее реконструкцию по эрозии или расширению. Аргумент mask имеет ту же форму, что и marker, и применяется для ограничения диапазона выходных значений.

Именованный аргумент dims служит для указания измерения, которое должно быть обработано путем построения структурирующего элемента прямоугольной формы strel_box(marker; dims). Для универсального структурирующего элемента половинный размер по каждому измерению должен быть равен 0 или 1.

По определению реконструкция выполняется путем многократного применения marker = select.(op(marker; dims), mask), пока не будет достигнута устойчивость. Для расширения op, select = dilate, min, а для эрозии op, select = erode, max.

Примеры

julia> marker = [0 0 0 0 0; 0 9 0 0 0; 0 0 0 0 0; 0 0 0 5 0; 0 0 0 0 0; 0 9 0 0 0]
6×5 Matrix{Int64}:
 0  0  0  0  0
 0  9  0  0  0
 0  0  0  0  0
 0  0  0  5  0
 0  0  0  0  0
 0  9  0  0  0

julia> mask = [9 0 0 0 0; 0 8 7 1 0; 0 9 0 4 0; 0 0 0 4 0; 0 0 6 5 6; 0 0 9 8 9]
6×5 Matrix{Int64}:
 9  0  0  0  0
 0  8  7  1  0
 0  9  0  4  0
 0  0  0  4  0
 0  0  6  5  6
 0  0  9  8  9

julia> mreconstruct(dilate, marker, mask) # эквивалентно underbuild(marker, mask)
6×5 Matrix{Int64}:
 8  0  0  0  0
 0  8  7  1  0
 0  8  0  4  0
 0  0  0  4  0
 0  0  4  4  4
 0  0  4  4  4

Дополнительные материалы

Выполняемая на месте версия этой функции — mreconstruct!. Существуют также псевдонимы underbuild для реконструкции по расширению и overbuild для реконструкции по эрозии.

Справочные материалы

  • [1] L. Vincent. Morphological grayscale reconstruction in image analysis: applications and efficient algorithms. IEEE Trans. on Image Process., vol. 2, no. 2, pp. 176—​201, Apr. 1993, doi: 10.1109/83.217222.

  • [2] P. Soille. Morphological Image Analysis. Berlin, Heidelberg: Springer Berlin Heidelberg, 2004. doi: 10.1007/978-3-662-05088-0.

mreconstruct!(op, out, marker, mask; [dims])

Выполняемая на месте версия морфологической реконструкции mreconstruct.

underbuild(marker, mask; [dims])
underbuild(marker, mask, se)

Реконструкция по расширению. Это псевдоним для mreconstruct с op=dilate.

См. также выполняемую на месте версию underbuild! и двойственный оператор overbuild.

underbuild!(out, marker, mask; [dims])
underbuild!(out, marker, mask, se)

Выполняемая на месте версия underbuild с выходным изображением out, изменяемым на месте.

overbuild(marker, mask; [dims])
overbuild(marker, mask, se)

Реконструкция по эрозии. Это псевдоним для mreconstruct с op=erode.

См. также выполняемую на месте версию overbuild! и двойственный оператор underbuild.

overbuild!(out, marker, mask; [dims])
overbuild!(out, marker, mask, se)

Выполняемая на месте версия overbuild с выходным изображением out, изменяемым на месте.

Компоненты и сегментация

label = label_components(A; [dims=coords_spatial(A)], [r=1], [bkg])
label = label_components(A, se; [bkg])

Находит и маркирует связанные компоненты массива A, где связь определяется структурирующим элементом se. Каждому компоненту присваивается уникальное целочисленное значение в качестве метки, где 0 представляет фон, указанный в bkg.

se — это структурирующий элемент, определяющий окрестность изображения. Дополнительные сведения см. в описании функции strel. Если элемент se не указан, используется функция strel_box с дополнительным именованным аргументом dims, определяющим измерения для фильтрации, и половинным размером r, определяющим размер ромба.

Примеры

julia> A = [false true false true  false;
            true false false  true  true]
2×5 Matrix{Bool}:
 0  1  0  1  0
 1  0  0  1  1

julia> label_components(A) # связь C4 по умолчанию ромбовидной формы
2×5 Matrix{Int64}:
 0  2  0  3  0
 1  0  0  3  3

julia> label_components(A; dims=2) # учитываются только строки
2×5 Matrix{Int64}:
 0  2  0  3  0
 1  0  0  4  4

julia> label_components(A, strel_box((3, 3))) # связь C8 прямоугольной формы
2×5 Matrix{Int64}:
 0  1  0  2  0
 1  0  0  2  2

Выполняемая на месте версия — label_components!. Основные свойства маркируемых компонентов см. в описании component_boxes, component_lengths, component_indices, component_centroids.

label_components!(out, A; [dims], [r] [bkg])
label_components!(out, A, se; [bkg])

Выполняемая на месте версия label_components.

boxes = component_boxes(labeled_array)

Вычисляет минимальные ограничивающие прямоугольники для каждой метки, включая метку фона. Метки можно вычислить с помощью функции label_components.

Каждый ограничивающий прямоугольник представлен в виде CartesianIndices. boxes смещается в вектор с индексацией с нуля так, что фоновый участок равен boxes[0].

julia> A = [2 2 2 2 2; 1 1 1 0 1; 1 0 2 1 1; 1 1 2 2 2; 1 0 2 2 2]
5×5 Matrix{Int64}:
 2  2  2  2  2
 1  1  1  0  1
 1  0  2  1  1
 1  1  2  2  2
 1  0  2  2  2

julia> label = label_components(A) # четыре несмежных компонента
5×5 Matrix{Int64}:
 1  1  1  1  1
 2  2  2  0  4
 2  0  3  4  4
 2  2  3  3  3
 2  0  3  3  3

julia> boxes = component_boxes(label) # получаем ограничивающие прямоугольники всех участков
5-element OffsetArray(::Vector{CartesianIndices{2, Tuple{UnitRange{Int64}, UnitRange{Int64}}}}, 0:4) with eltype CartesianIndices{2, Tuple{UnitRange{Int64}, UnitRange{Int64}}} with indices 0:4:
 CartesianIndices((2:5, 2:4))
 CartesianIndices((1:1, 1:5))
 CartesianIndices((2:5, 1:3))
 CartesianIndices((3:5, 3:5))
 CartesianIndices((2:3, 4:5))

julia> A[boxes[1]] # обрезаем участок изображения с меткой 1
1×5 Matrix{Int64}:
 2  2  2  2  2

julia> A[boxes[4]] # обрезаем участок изображения с меткой 4
2×2 Matrix{Int64}:
 0  1
 1  1
counts = component_lengths(labeled_array)

Подсчитывает количество каждой метки во входном маркированном массиве. counts смещается в вектор с индексацией с нуля так, что количество фоновых пикселей равно counts[0].

julia> A = [2 2 2 2 2; 1 1 1 0 1; 1 0 2 1 1; 1 1 2 2 2; 1 0 2 2 2]
5×5 Matrix{Int64}:
 2  2  2  2  2
 1  1  1  0  1
 1  0  2  1  1
 1  1  2  2  2
 1  0  2  2  2

julia> label = label_components(A) # четыре несмежных компонента
5×5 Matrix{Int64}:
 1  1  1  1  1
 2  2  2  0  4
 2  0  3  4  4
 2  2  3  3  3
 2  0  3  3  3

julia> component_lengths(label)
5-element OffsetArray(::Vector{Int64}, 0:4) with eltype Int64 with indices 0:4:
 3
 5
 7
 7
 3

Для изображений в оттенках серого метки можно вычислить с помощью функции label_components.

indices = component_indices([T], labeled_array)

Возвращает индексы каждой метки во входном маркированном массиве. indices смещается в вектор с индексацией с нуля так, что индексы фоновых пикселей равны indices[0].

Необязательным типом T, определяющим тип индексов, может быть либо Int/IndexLinear(), либо CartesianIndex/IndexCartesian(). По умолчанию используется IndexStyle(labeled_array).

julia> A = [2 2 2 2 2; 1 1 1 0 1; 1 0 2 1 1; 1 1 2 2 2; 1 0 2 2 2]
5×5 Matrix{Int64}:
 2  2  2  2  2
 1  1  1  0  1
 1  0  2  1  1
 1  1  2  2  2
 1  0  2  2  2

julia> label = label_components(A) # четыре несмежных компонента
5×5 Matrix{Int64}:
 1  1  1  1  1
 2  2  2  0  4
 2  0  3  4  4
 2  2  3  3  3
 2  0  3  3  3

julia> indices = component_indices(label)
5-element OffsetArray(::Vector{Vector{Int64}}, 0:4) with eltype Vector{Int64} with indices 0:4:
 [8, 10, 17]
 [1, 6, 11, 16, 21]
 [2, 3, 4, 5, 7, 9, 12]
 [13, 14, 15, 19, 20, 24, 25]
 [18, 22, 23]

julia> indices = component_indices(CartesianIndex, label)
5-element OffsetArray(::Vector{Vector{CartesianIndex{2}}}, 0:4) with eltype Vector{CartesianIndex{2}} with indices 0:4:
 [CartesianIndex(3, 2), CartesianIndex(5, 2), CartesianIndex(2, 4)]
 [CartesianIndex(1, 1), CartesianIndex(1, 2), CartesianIndex(1, 3), CartesianIndex(1, 4), CartesianIndex(1, 5)]
 [CartesianIndex(2, 1), CartesianIndex(3, 1), CartesianIndex(4, 1), CartesianIndex(5, 1), CartesianIndex(2, 2), CartesianIndex(4, 2), CartesianIndex(2, 3)]
 [CartesianIndex(3, 3), CartesianIndex(4, 3), CartesianIndex(5, 3), CartesianIndex(4, 4), CartesianIndex(5, 4), CartesianIndex(4, 5), CartesianIndex(5, 5)]
 [CartesianIndex(3, 4), CartesianIndex(2, 5), CartesianIndex(3, 5)]

Для изображений в оттенках серого метки можно вычислить с помощью функции label_components.

centroids = component_centroids(labeled_array)

Вычисляет центроид каждой метки во входном маркированном массиве. centroids смещается в вектор с индексацией с нуля так, что центроид фоновых пикселей равен centroids[0].

Центроид конечного множества X, также называемый геометрическим центром, вычисляется как sum(X)/length(X). Если дана метка i, для построения множества X используются все (декартовы) индексы пикселей с меткой i.

julia> A = [2 2 2 2 2; 1 1 1 0 1; 1 0 2 1 1; 1 1 2 2 2; 1 0 2 2 2]
5×5 Matrix{Int64}:
 2  2  2  2  2
 1  1  1  0  1
 1  0  2  1  1
 1  1  2  2  2
 1  0  2  2  2

julia> label = label_components(A) # четыре несмежных компонента
5×5 Matrix{Int64}:
 1  1  1  1  1
 2  2  2  0  4
 2  0  3  4  4
 2  2  3  3  3
 2  0  3  3  3

julia> component_centroids(label)
5-element OffsetArray(::Vector{Tuple{Float64, Float64}}, 0:4) with eltype Tuple{Float64, Float64} with indices 0:4:
 (3.3333333333333335, 2.6666666666666665)
 (1.0, 3.0)
 (3.142857142857143, 1.5714285714285714)
 (4.285714285714286, 3.857142857142857)
 (2.6666666666666665, 4.666666666666667)

Для изображений в оттенках серого метки можно вычислить с помощью функции label_components.

Дерево максимумов

Морфологическое представление изображения в виде дерева максимумов.

Подробные сведения

Рассмотрим операцию определения порога.

    mask = [val ≥ threshold for val in image]

В mask можно определить связанные компоненты (множества смежных значений true). При последовательном применении операции определения порога изображения для всех возможных порогов создается коллекция связанных компонентов, которые можно упорядочить в иерархическую структуру, называемую деревом компонентов. Рассмотрим одномерное изображение со значениями 1, 2 и 3.

       2233233312223322

Связанными будут следующие компоненты:

    1: AAAAAAAAAAAAAAAA
    2: BBBBBBBB.CCCCCCC
    3: ..DD.EEE....FF..

Здесь буквы — это метки итоговых связанных компонентов, а . указывает, что значение пикселя ниже порога. В данном примере соответствующее дерево компонентов имеет следующий вид.

      A
     ⭩ ⭨
    B   C
   ⭩ ⭨   ⭨
  D   E   F

Дерево максимумов — это эффективное представление дерева компонентов. Связанный компонент на пороговом уровне представлен одним базисным пикселем с этого уровня (image[r] == t). Он является родительским для всех остальных пикселей , а также для базисных пикселей связанных компонентов на более высоких пороговых уровнях, являющихся дочерними относительно . В нашем примере базисные пиксели (обозначенные буквой соответствующего компонента) будут следующими:

    1: ........A.......
    2: B........C......
    3: ..D..E......F...

То есть:

Компонент Базисный пиксель

A

9

B

1

C

10

D

3

E

6

F

13

Таким образом, все дерево максимумов можно закодировать как вектор индексов родительских пикселей:

9  1  1  3  1  1  6  6  9  9 10 10 10 13 10 10

Дерево максимумов является основой для многих морфологических операторов, а именно для связанных операторов. В отличие от морфологических открытий и закрытий, эти операторы не требуют фиксированного структурирующего элемента. Вместо этого они действуют как гибкий структурирующий элемент, отвечающий определенному критерию.

Дополнительные материалы

Справочные материалы

  1. Salembier, P., Oliveras, A., & Garrido, L. (1998). Antiextensive Connected Operators for Image and Sequence Processing. IEEE Transactions on Image Processing, 7(4), 555—​570.

  2. Berger, C., Geraud, T., Levillain, R., Widynski, N., Baillard, A., Bertin, E. (2007). Effective Component Tree Computation with Application to Pattern Recognition in Astronomical Imaging. In International Conference on Image Processing (ICIP), 41—​44.

  3. Najman, L., Couprie, M. (2006). Building the component tree in quasi-linear time. IEEE Transactions on Image Processing, 15(11), 3531—​3539.

  4. Carlinet, E., Geraud, T. (2014). A Comparative Review of Component Tree Computation Algorithms. IEEE Transactions on Image Processing, 23(9), 3885—​3895.

areas(maxtree::MaxTree) -> Array{Int}

Вычисляет площади всех компонентов maxtree.

Возвращаемые значения

Массив той же формы, что и у исходного изображения. i-й элемент — это площадь (в пикселях) компонента, представленного базисным пикселем с индексом i.

Дополнительные материалы

boundingboxes(maxtree::MaxTree) -> Array{NTuple{2, CartesianIndex}}

Вычисляет минимальные ограничивающие прямоугольники всех компонентов maxtree.

Возвращаемые значения

Массив той же формы, что и у исходного изображения. i-й элемент — это кортеж из минимального и максимального декартовых индексов ограничивающего прямоугольника для компонента, представленного базисным пикселем с индексом i.

Дополнительные материалы

diameters(maxtree::MaxTree) -> Array{Int}

Вычисляет «диаметры» всех компонентов maxtree.

«Диаметр» связанного компонента дерева максимумов — это длина самой широкой стороны ограничивающего прямоугольника компонента.

Возвращаемые значения

Массив той же формы, что и у исходного изображения. i-й элемент — это «диаметр» компонента, представленного базисным пикселем с индексом i.

Дополнительные материалы

area_opening(image, [maxtree]; min_area=64, connectivity=1) -> Array

Выполняет открытие по площади для image.

Операция открытия по площади заменяет все яркие компоненты изображения, имеющие площадь менее min_area, более темным значением из первого компонента-потомка (в представлении image в виде дерева максимумов) с площадью не менее min_area.

Подробные сведения

Открытие по площади сходно с морфологическим открытием (см. описание функции opening), но вместо использования фиксированного структурирующего элемента (например, диска) применяются небольшие (менее min_area) компоненты дерева максимумов. Следовательно, area_opening с min_area = 1 — тождественное преобразование.

В двоичном случае открытие по площади эквивалентно remove_small_objects; таким образом, этот оператор распространяется на полутоновые изображения.

Аргументы

  • image::GenericGrayImage: -мерное входное изображение.

  • min_area::Number=64: наименьший размер (в пикселях) компонента изображения, который должен оставаться без изменений.

  • connectivity::Integer=1: смежная связь. Максимальное количество ортогональных шагов для достижения смежного элемента пикселя. В двухмерном случае равно 1 для связи с 4 смежными элементами и 2 для связи с 8 смежными элементами.

  • maxtree::MaxTree: необязательное предварительно построенное дерево максимумов. Учтите, что необязательные параметры maxtree и connectivity являются взаимоисключающими.

Возвращаемые значения

Массив того же типа и той же формы, что и image.

Дополнительные материалы

Справочные материалы

  1. Vincent, L. (1993). Grayscale area openings and closings, their efficient implementation and applications, Proc. of EURASIP Workshop on Mathematical Morphology and its Applications to Signal Processing, Barcelona, Spain, 22—​27

  2. Soille, P. (2003). Chapter 6 Geodesic Metrics of Morphological Image Analysis: **Principles and Applications, 2nd edition, Springer.

  3. Salembier, P., Oliveras, A., & Garrido, L. (1998). Antiextensive Connected Operators for Image and Sequence Processing. IEEE Transactions on Image Processing, 7(4), 555—​570.

  4. Najman, L., Couprie, M. (2006). Building the component tree in quasi-linear time. IEEE Transactions on Image Processing, 15(11), 3531—​3539.

  5. Carlinet, E., Geraud, T. (2014). A Comparative Review of Component Tree Computation Algorithms. IEEE Transactions on Image Processing, 23(9), 3885—​3895.

Примеры

Создание тестового изображения f (квадратичная функция с максимумом в центре и еще 4 локальными максимумами):

julia> w = 12;

julia> f = [20 - 0.2*((x - w/2)^2 + (y-w/2)^2) for x in 0:w, y in 0:w];

julia> f[3:4, 2:6] .= 40; f[3:5, 10:12] .= 60; f[10:12, 3:5] .= 80;

julia> f[10:11, 10:12] .= 100; f[11, 11] = 100;

julia> f_aopen = area_opening(f, min_area=8, connectivity=1);

Пики с поверхностью менее 8 удалены.

area_opening!(output, image, [maxtree];
              min_area=64, connectivity=1) -> output

Выполняет открытие по площади для image на месте и сохраняет результат в output. Подробное описание метода см. в разделе, посвященном функции area_opening.

area_closing(image, [maxtree]; min_area=64, connectivity=1) -> Array

Выполняет закрытие по площади для image.

Операция закрытия по площади заменяет все темные компоненты изображения, имеющие площадь менее min_area, более ярким значением из первого компонента-потомка (в представлении image в виде дерева максимумов) с площадью не менее min_area.

Подробные сведения

Закрытие по площади является двойственной операцией по отношению к открытию по площади (см. описание функции area_opening). Оно сходно с морфологическим закрытием (см. описание функции closing), но вместо использования фиксированного структурирующего элемента (например, диска) применяются небольшие (менее min_area) компоненты дерева максимумов. Следовательно, area_closing с min_area = 1 — тождественное преобразование.

В двоичном случае закрытие по площади эквивалентно remove_small_holes; таким образом, этот оператор распространяется на полутоновые изображения.

Аргументы

  • image::GenericGrayImage: -мерное входное изображение.

  • min_area::Number=64: наименьший размер (в пикселях) компонента изображения, который должен оставаться без изменений.

  • connectivity::Integer=1: смежная связь. Максимальное количество ортогональных шагов для достижения смежного элемента пикселя. В двухмерном случае равно 1 для связи с 4 смежными элементами и 2 для связи с 8 смежными элементами.

  • maxtree::MaxTree: необязательное предварительно построенное дерево максимумов. Учтите, что необязательные параметры maxtree и connectivity являются взаимоисключающими.

Возвращаемые значения

Массив того же типа и той же формы, что и image.

Дополнительные материалы

Справочные материалы

  1. Vincent, L. (1993). Grayscale area openings and closings, their efficient implementation and applications, Proc. of EURASIP Workshop on Mathematical Morphology and its Applications to Signal Processing, Barcelona, Spain, 22—​27

  2. Soille, P. (2003). Chapter 6 Geodesic Metrics of Morphological Image Analysis: **Principles and Applications, 2nd edition, Springer.

  3. Salembier, P., Oliveras, A., & Garrido, L. (1998). Antiextensive Connected Operators for Image and Sequence Processing. IEEE Transactions on Image Processing, 7(4), 555—​570.

  4. Najman, L., Couprie, M. (2006). Building the component tree in quasi-linear time. IEEE Transactions on Image Processing, 15(11), 3531—​3539.

  5. Carlinet, E., Geraud, T. (2014). A Comparative Review of Component Tree Computation Algorithms. IEEE Transactions on Image Processing, 23(9), 3885—​3895.

Примеры

Создание тестового изображения f (квадратичная функция с минимумом в центре и еще 4 локальными минимумами):

julia> w = 12;

julia> f = [180 + 0.2*((x - w/2)^2 + (y-w/2)^2) for x in 0:w, y in 0:w];

julia> f[3:4, 2:6] .= 40; f[3:5, 10:12] .= 60; f[10:12, 3:5] .= 80;

julia> f[10:11, 10:12] .= 100; f[11, 11] = 100;

julia> f_aclose = area_closing(f, min_area=8, connectivity=1);

Все небольшие минимумы удаляются, так что остаются только минимумы размером не менее 8.

area_closing!(output, image, [maxtree];
              min_area=64, connectivity=1) -> output

Выполняет закрытие по площади для image на месте и сохраняет результат в output. Подробное описание метода см. в разделе, посвященном функции area_closing.

diameter_opening(image, [maxtree]; min_diameter=8, connectivity=1) -> Array

Выполняет открытие по диаметру для image.

Операция открытия по диаметру заменяет все яркие структуры изображения, имеющие диаметр (длину самой широкой стороны ограничивающего прямоугольника) менее min_diameter, более темным значением из первого компонента-потомка (в представлении image в виде дерева максимумов) с диаметром не менее min_diameter.

Этот оператор также называется открытием по ограничивающему прямоугольнику. На практике результат аналогичен морфологическому открытию, но длинные и узкие структуры не удаляются.

Аргументы

  • image::GenericGrayImage: -мерное входное изображение.

  • min_diameter::Number=8: минимальная длина (в пикселях) самой широкой стороны ограничивающего прямоугольника компонента изображения, который должен оставаться без изменений.

  • connectivity::Integer=1: смежная связь. Максимальное количество ортогональных шагов для достижения смежного элемента пикселя. В двухмерном случае равно 1 для связи с 4 смежными элементами и 2 для связи с 8 смежными элементами.

  • maxtree::MaxTree: необязательное предварительно построенное дерево максимумов. Учтите, что необязательные параметры maxtree и connectivity являются взаимоисключающими.

Возвращаемые значения

Массив того же типа и той же формы, что и image.

Дополнительные материалы

Справочные материалы

  1. Walter, T., Klein, J.-C. (2002). Automatic Detection of Microaneurysms in Color Fundus Images of the Human Retina by Means of the Bounding Box Closing. In A. Colosimo, P. Sirabella, A. Giuliani (Eds.), Medical Data Analysis. Lecture Notes in Computer Science, vol 2526, 210—​220. Springer Berlin Heidelberg.

  2. Carlinet, E., Geraud, T. (2014). A Comparative Review of Component Tree Computation Algorithms. IEEE Transactions on Image Processing, 23(9), 3885—​3895.

Примеры

Создание тестового изображения f (квадратичная функция с максимумом в центре и еще 4 локальными максимумами):

julia> w = 12;

julia> f = [20 - 0.2*((x - w/2)^2 + (y-w/2)^2) for x in 0:w, y in 0:w];

julia> f[3:4, 2:6] .= 40; f[3:5, 10:12] .= 60; f[10:12, 3:5] .= 80;

julia> f[10:11, 10:12] .= 100; f[11, 11] = 100;

julia> f_dopen = diameter_opening(f, min_diameter=3, connectivity=1);

Пики с максимальным диаметром 2 или менее удаляются. У оставшихся пиков самая широкая сторона ограничивающего прямоугольника имеет длину не менее 3.

diameter_opening!(output, image, [maxtree];
                  min_diameter=8, connectivity=1) -> output

Выполняет открытие по диаметру для image на месте и сохраняет результат в output. Подробное описание метода см. в разделе, посвященном функции diameter_opening.

diameter_closing(image, [maxtree]; min_diameter=8, connectivity=1) -> Array

Выполняет закрытие по диаметру для image.

Операция закрытия по диаметру заменяет все темные структуры изображения, имеющие диаметр (длину самой широкой стороны ограничивающего прямоугольника) менее min_diameter, более ярким значением из первого компонента-потомка (в представлении image в виде дерева максимумов) с диаметром не менее min_diameter.

Аргументы

  • image::GenericGrayImage: -мерное входное изображение.

  • min_diameter::Number=8: минимальная длина (в пикселях) самой широкой стороны ограничивающего прямоугольника компонента изображения, который должен оставаться без изменений.

  • connectivity::Integer=1: смежная связь. Максимальное количество ортогональных шагов для достижения смежного элемента пикселя. В двухмерном случае равно 1 для связи с 4 смежными элементами и 2 для связи с 8 смежными элементами.

  • maxtree::MaxTree: необязательное предварительно построенное дерево максимумов. Учтите, что необязательные параметры maxtree и connectivity являются взаимоисключающими.

Возвращаемые значения

Массив того же типа и той же формы, что и image.

Дополнительные материалы

Справочные материалы

  1. Walter, T., Klein, J.-C. (2002). Automatic Detection of Microaneurysms in Color Fundus Images of the Human Retina by Means of the Bounding Box Closing. In A. Colosimo, P. Sirabella, A. Giuliani (Eds.), Medical Data Analysis. Lecture Notes in Computer Science, vol 2526, 210—​220. Springer Berlin Heidelberg.

  2. Carlinet, E., Geraud, T. (2014). A Comparative Review of Component Tree Computation Algorithms. IEEE Transactions on Image Processing, 23(9), 3885—​3895.

Примеры

Создание тестового изображения f (квадратичная функция с минимумом в центре и еще 4 локальными минимумами):

julia> w = 12;

julia> f = [180 + 0.2*((x - w/2)^2 + (y-w/2)^2) for x in 0:w, y in 0:w];

julia> f[3:4, 2:6] .= 40; f[3:5, 10:12] .= 60; f[10:12, 3:5] .= 80;

julia> f[10:11, 10:12] .= 100; f[11, 11] = 100;

julia> f_dclose = diameter_closing(f, min_diameter=3, connectivity=1);

Все небольшие минимумы с диаметром 2 или менее удаляются. У оставшихся минимумов самая широкая сторона ограничивающего прямоугольника имеет длину не менее 3.

diameter_closing!(output, image, [maxtree];
                  min_diameter=8, connectivity=1) -> output

Выполняет закрытие по диаметру для image на месте и сохраняет результат в output. Подробное описание метода см. в разделе, посвященном функции diameter_closing.

local_maxima!(output, image, [maxtree]; connectivity=1) -> output

Находит локальные максимумы изображения image и сохраняет результат в output. Подробное описание метода см. в разделе, посвященном функции local_maxima.

local_maxima(image, [maxtree]; connectivity=1) -> Array

Находит и маркирует все локальные максимумы изображения image.

Подробные сведения

Локальный максимум определяется как связанное множество пикселей с одинаковым значением, большим чем значения всех пикселей, смежных со множеством.

Технически реализация основана на представлении изображения в виде дерева максимумов. Эта функция полезна, если дерево максимумов уже вычислено; в противном случае эффективнее будет функция ImageFiltering.findlocalmaxima.

Аргументы

  • image::GenericGrayImage: -мерное входное изображение.

  • connectivity::Integer=1: смежная связь. Максимальное количество ортогональных шагов для достижения смежного элемента пикселя. В двухмерном случае равно 1 для связи с 4 смежными элементами и 2 для связи с 8 смежными элементами.

  • maxtree::MaxTree: необязательное предварительно построенное дерево максимумов. Учтите, что необязательные параметры maxtree и connectivity являются взаимоисключающими.

Возвращаемые значения

Целочисленный массив той же формы, что и image. Пиксели, не являющиеся локальными максимумами, имеют значение 0. Пиксели, относящиеся к одному локальному максимуму, имеют одинаковое положительное значение (идентификатор локального максимума).

Дополнительные материалы

MaxTree, local_maxima!, local_minima, ImageFiltering.findlocalmaxima

Примеры

Создание f (квадратичная функция с максимумом в центре и еще 4 константными максимумами):

julia> w = 10;

julia> f = [20 - 0.2*((x - w/2)^2 + (y-w/2)^2) for x in 0:w, y in 0:w];

julia> f[3:5, 3:5] .= 40; f[3:5, 8:10] .= 60; f[8:10, 3:5] .= 80; f[8:10, 8:10] .= 100;

julia> f_maxima = local_maxima(f); # Получаем все локальные максимумы `f`

Итоговое изображение содержит 4 маркированных локальных максимума.

local_minima!(output, image, [maxtree]; connectivity=1) -> output

Находит локальные минимумы изображения image и сохраняет результат в output. Подробное описание метода см. в разделе, посвященном функции local_minima.

local_minima(image, [maxtree]; connectivity=1) -> Array

Находит и маркирует все локальные минимумы изображения image.

Подробные сведения

Локальный минимум определяется как связанное множество пикселей с одинаковым значением, меньшим чем значения всех пикселей, смежных со множеством.

Технически реализация основана на представлении изображения в виде дерева максимумов. Эта функция полезна, если дерево максимумов уже вычислено; в противном случае эффективнее будет функция ImageFiltering.findlocalminima.

Аргументы

  • image::GenericGrayImage: -мерное входное изображение.

  • connectivity::Integer=1: смежная связь. Максимальное количество ортогональных шагов для достижения смежного элемента пикселя. В двухмерном случае равно 1 для связи с 4 смежными элементами и 2 для связи с 8 смежными элементами.

  • maxtree::MaxTree: необязательное предварительно построенное дерево максимумов. Учтите, что необязательные параметры maxtree и connectivity являются взаимоисключающими.

Возвращаемые значения

Целочисленный массив той же формы, что и image. Пиксели, не являющиеся локальными минимумами, имеют значение 0. Пиксели, относящиеся к одному локальному минимуму, имеют одинаковое положительное значение (идентификатор локального минимума).

Дополнительные материалы

MaxTree, local_minima!, local_maxima, ImageFiltering.findlocalminima

Примеры

Создание f (квадратичная функция с минимумом в центре и еще 4 константными минимумами):

julia> w = 10;

julia> f = [180 + 0.2*((x - w/2)^2 + (y-w/2)^2) for x in 0:w, y in 0:w];

julia> f[3:5, 3:5] .= 40; f[3:5, 8:10] .= 60; f[8:10, 3:5] .= 80; f[8:10, 8:10] .= 100;

julia> f_minima = local_minima(f); # Вычисляем все локальные минимумы `f`

Итоговое изображение содержит маркированные локальные минимумы.

rebuild!(maxtree::MaxTree, image::GenericGrayImage,
         neighbors::AbstractVector{CartesianIndex}) -> maxtree

Повторно строит maxtree для image с использованием neighbors в качестве спецификации связи пикселей.

Подробные сведения

Пиксели в связанных компонентах, создаваемых методом, должны быть связаны друг с другом через соседние пиксели. Пиксели и являются соседними, если массив neighbors содержит такое, что .

Дополнительные материалы

filter_components!(output::GenericGrayImage, image::GenericGrayImage,
                   maxtree::MaxTree, attrs::AbstractVector,
                   min_attr, all_below_min) -> output

Фильтрует связанные компоненты image и сохраняет результат в output.

 — это копия за исключением связанных компонентов, значение атрибута которых менее min_attr. То есть пиксели исключаемого компонента сбрасываются в значение базисного пикселя его первого допустимого предка (связанного компонента со значением атрибута не менее min_attr).

Аргументы

  • maxtree::MaxTree: предварительно построенное представление image в виде дерева максимумов

  • attrs::AbstractVector: attrs[i] — это значение атрибута для -го компонента дерева (где  — линейный индекс его первого базисного пикселя)

  • all_below_min: значение, которым заполняется output, если все атрибуты всех компонентов (включая корневой) меньше min_attr

Подробные сведения

Эта функция является основой для area_opening, diameter_opening и аналогичных преобразований. Например, для area_opening атрибутом является площадь компонентов. В этом случае компоненты дерева максимумов output имеют площадь не менее min_attr пикселей.

Метод предполагает, что значения атрибута монотонны относительно иерархии компонентов, то есть <= attrs[maxtree.parentindices[i]]] для каждого i.

Преобразование признаков

feature_transform(img::AbstractArray{Bool, N};
                  weights=nothing, nthreads=Threads.nthreads()) -> F

Вычисляет преобразование признаков для двоичного изображения I, находя «ближайший» признак (позиции, где I имеет значение true) для каждой позиции в I. В частности, F[i] — это объект CartesianIndex, кодирующий ближайшую к i позицию, для которой I[F[i]] имеет значение true. В случае если два или более признаков в I находятся на одинаковом расстоянии от i, один из них выбирается произвольным образом. Если в I нет значений true, все позиции сопоставляются с индексом, каждая координата которого равна typemin(Int).

При необходимости можно указать вес w для каждой координаты. Например, если I соответствует изображению с анизотропными вокселами, весом w может быть интервал между вокселами по каждой координатной оси. Значение по умолчанию nothing эквивалентно w=(1,1,...).

См. также описание функции distance_transform.

Цитирование

  • [1] Maurer, Calvin R., Rensheng Qi, and Vijay Raghavan. A linear time algorithm for computing exact Euclidean distance transforms of binary images in arbitrary dimensions. IEEE Transactions on Pattern Analysis and Machine Intelligence 25.2 (2003): 265—​270.

distance_transform(F::AbstractArray{CartesianIndex}, [w=nothing]) -> D

Вычисляет преобразование расстояний для F, где каждый элемент F[i] представляет местоположение «цели» или «признака», присвоенное i. В частности, D[i] — это расстояние между i и F[i]. При необходимости можно указать вес w для каждой координаты. Значение по умолчанию nothing эквивалентно w=(1,1,...).

См. также описание функции feature_transform.

cleared_img = clearborder(img)
cleared_img = clearborder(img, width)
cleared_img = clearborder(img, width, background)

Возвращает копию исходного изображения после очистки объектов, связанных с его границей. Параметры:

  • img = входное изображение (двоичное или в оттенках серого)

  • width = ширина анализируемой границы (значение по умолчанию: 1)

  • background = значение, которое присваивается очищаемым пикселями или элементам (по умолчанию 0)

Прочее

chull = convexhull(img)

Вычисляет выпуклую оболочку двоичного изображения и возвращает ее вершины в виде массива элементов типа CartesianIndex.

isboundary(img::AbstractArray; background = 0, dims = coords_spatial(A), kwargs...)

Находит границы внутри каждого объекта. background — это скалярное значение фоновых пикселей, которые не будут помечаться как границы. Именованные аргументы передаются в extremefilt!, включая аргумент dims с измерениями, по которым должны определяться границы.

Существует также выполняемая на месте версия isboundary! и альтернативная версия, которая находит толстые границы, isboundary_thick.

Примеры

DocTestSetup = quote
    import ImageMorphology: isboundary
end
julia> A = zeros(Int64, 16, 16); A[4:8, 4:8] .= 5; A[4:8, 9:12] .= 6; A[10:12,13:15] .= 3; A[10:12,3:6] .= 9; A
16×16 Matrix{Int64}:
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  5  5  5  5  5  6  6  6  6  0  0  0  0
 0  0  0  5  5  5  5  5  6  6  6  6  0  0  0  0
 0  0  0  5  5  5  5  5  6  6  6  6  0  0  0  0
 0  0  0  5  5  5  5  5  6  6  6  6  0  0  0  0
 0  0  0  5  5  5  5  5  6  6  6  6  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  9  9  9  9  0  0  0  0  0  0  3  3  3  0
 0  0  9  9  9  9  0  0  0  0  0  0  3  3  3  0
 0  0  9  9  9  9  0  0  0  0  0  0  3  3  3  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

julia> isboundary(A)
16×16 Matrix{Int64}:
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  1  1  1  1  1  1  1  1  1  0  0  0  0
 0  0  0  1  0  0  0  1  1  0  0  1  0  0  0  0
 0  0  0  1  0  0  0  1  1  0  0  1  0  0  0  0
 0  0  0  1  0  0  0  1  1  0  0  1  0  0  0  0
 0  0  0  1  1  1  1  1  1  1  1  1  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  1  1  1  1  0  0  0  0  0  0  1  1  1  0
 0  0  1  0  0  1  0  0  0  0  0  0  1  0  1  0
 0  0  1  1  1  1  0  0  0  0  0  0  1  1  1  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

julia> isboundary(A .!= 0)
16×16 BitMatrix:
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  1  1  1  1  1  1  1  1  1  0  0  0  0
 0  0  0  1  0  0  0  0  0  0  0  1  0  0  0  0
 0  0  0  1  0  0  0  0  0  0  0  1  0  0  0  0
 0  0  0  1  0  0  0  0  0  0  0  1  0  0  0  0
 0  0  0  1  1  1  1  1  1  1  1  1  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  1  1  1  1  0  0  0  0  0  0  1  1  1  0
 0  0  1  0  0  1  0  0  0  0  0  0  1  0  1  0
 0  0  1  1  1  1  0  0  0  0  0  0  1  1  1  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

julia> isboundary(A .!= 0; dims = 1)
16×16 BitMatrix:
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  1  1  1  1  1  1  1  1  1  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  1  1  1  1  1  1  1  1  1  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  1  1  1  1  0  0  0  0  0  0  1  1  1  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  1  1  1  1  0  0  0  0  0  0  1  1  1  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

julia> isboundary(A .!= 0; dims = 2)
16×16 BitMatrix:
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  1  0  0  0  0  0  0  0  1  0  0  0  0
 0  0  0  1  0  0  0  0  0  0  0  1  0  0  0  0
 0  0  0  1  0  0  0  0  0  0  0  1  0  0  0  0
 0  0  0  1  0  0  0  0  0  0  0  1  0  0  0  0
 0  0  0  1  0  0  0  0  0  0  0  1  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  1  0  0  1  0  0  0  0  0  0  1  0  1  0
 0  0  1  0  0  1  0  0  0  0  0  0  1  0  1  0
 0  0  1  0  0  1  0  0  0  0  0  0  1  0  1  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
isboundary!(img::AbstractArray; background = 0, dims = coords_spatial(A), kwargs...)

Находит границы внутри каждого объекта и заменяет исходное изображение. background — это скалярное значение фоновых пикселей, которые не будут помечаться как границы. Именованные аргументы передаются в extreme_filter, включая аргумент dims с измерениями, по которым должны определяться границы.

Примеры см. в описании версии isboundary, выполняемой не на месте.

isboundary_thick(img::AbstractArray; dims = coords_spatial(img), kwargs...)

Находит толстые границы, которые находятся сразу вне объектов и внутри них. Результатом является объединение внутренних и внешних границ. Именованный аргумент dims указывает измерения, по которым ищутся границы. Этот аргумент dims и дополнительные именованные аргументы kwargs передаются в extreme_filter.

См. также описание функции isboundary, которая возвращает только внутренние границы.

Примеры

DocTestSetup = quote
    import ImageMorphology: isboundary_thick
end
julia> A = zeros(Int64, 16, 16); A[4:8, 4:8] .= 5; A[4:8, 9:12] .= 6; A[10:12,13:15] .= 3; A[10:12,3:6] .= 9; A
16×16 Matrix{Int64}:
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  5  5  5  5  5  6  6  6  6  0  0  0  0
 0  0  0  5  5  5  5  5  6  6  6  6  0  0  0  0
 0  0  0  5  5  5  5  5  6  6  6  6  0  0  0  0
 0  0  0  5  5  5  5  5  6  6  6  6  0  0  0  0
 0  0  0  5  5  5  5  5  6  6  6  6  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  9  9  9  9  0  0  0  0  0  0  3  3  3  0
 0  0  9  9  9  9  0  0  0  0  0  0  3  3  3  0
 0  0  9  9  9  9  0  0  0  0  0  0  3  3  3  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

julia> isboundary_thick(A)
16×16 BitMatrix:
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  1  1  1  1  1  1  1  1  1  1  1  0  0  0
 0  0  1  1  1  1  1  1  1  1  1  1  1  0  0  0
 0  0  1  1  0  0  0  1  1  0  0  1  1  0  0  0
 0  0  1  1  0  0  0  1  1  0  0  1  1  0  0  0
 0  0  1  1  0  0  0  1  1  0  0  1  1  0  0  0
 0  0  1  1  1  1  1  1  1  1  1  1  1  0  0  0
 0  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1
 0  1  1  1  1  1  1  0  0  0  0  1  1  1  1  1
 0  1  1  0  0  1  1  0  0  0  0  1  1  0  1  1
 0  1  1  1  1  1  1  0  0  0  0  1  1  1  1  1
 0  1  1  1  1  1  1  0  0  0  0  1  1  1  1  1
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

 julia> isboundary_thick(A) .& (A .!= 0)
16×16 BitMatrix:
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  1  1  1  1  1  1  1  1  1  0  0  0  0
 0  0  0  1  0  0  0  1  1  0  0  1  0  0  0  0
 0  0  0  1  0  0  0  1  1  0  0  1  0  0  0  0
 0  0  0  1  0  0  0  1  1  0  0  1  0  0  0  0
 0  0  0  1  1  1  1  1  1  1  1  1  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  1  1  1  1  0  0  0  0  0  0  1  1  1  0
 0  0  1  0  0  1  0  0  0  0  0  0  1  0  1  0
 0  0  1  1  1  1  0  0  0  0  0  0  1  1  1  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

 julia> isboundary_thick(A) == isboundary(A; background = -1)
true

julia> isboundary_thick(A) .& (A .!= 0) == isboundary(A) # внутренние границы
true

julia> isboundary_thick(A .!= 0) .& (A .== 0)  == isboundary(A .== 0) # внешние границы
true