diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 3b60826..b4949df 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.6.7","generation_timestamp":"2024-11-15T16:12:37","documenter_version":"1.8.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.6.7","generation_timestamp":"2024-11-15T16:53:53","documenter_version":"1.8.0"}} \ No newline at end of file diff --git a/dev/generic/index.html b/dev/generic/index.html index 308f34f..7aaa3e0 100644 --- a/dev/generic/index.html +++ b/dev/generic/index.html @@ -22,4 +22,4 @@ update(v, a, _) = (; result = max(v.result, abs(a - v.center)), center=v.center) final(v) = v.result return localfilter!(similar(A), A, B, initial, update, final) -end

This example has been borrowed from the Geomorphometry package for the analysis of Digital Elevation Models (DEM).

+end

This example has been borrowed from the Geomorphometry package for the analysis of Digital Elevation Models (DEM).

diff --git a/dev/index.html b/dev/index.html index 33dbb58..d9a958b 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,2 +1,2 @@ -Introduction · LocalFilters.jl Package

Introduction

The LocalFilters package provides local linear and non-linear filters for Julia.

The source code of LocalFilters is available on GitHub.

Table of contents

+Introduction · LocalFilters.jl Package

Introduction

The LocalFilters package provides local linear and non-linear filters for Julia.

The source code of LocalFilters is available on GitHub.

Table of contents

diff --git a/dev/linear/index.html b/dev/linear/index.html index 50357b2..2c519fb 100644 --- a/dev/linear/index.html +++ b/dev/linear/index.html @@ -1,5 +1,5 @@ -Linear filters · LocalFilters.jl Package

Linear filters

LocalFilters provides a few linear filters: localmean or localmean! to compute the mean of values in a neighborhood, convolve or convolve! to compute the discrete convolution of an array by a kernel, and correlate or correlate! to compute the discrete correlation of an array by a kernel.

Local mean

The localmean method yields the local mean of an array A in a neighborhood B:

dst = localmean(A, B=3)

The result dst is an array similar to A. See Section Simple rules for specifying neighborhoods and kernels for the interpretation of B.

The in-place version localmean! may be used to avoid allocations:

localmean!(dst, A, B=3)

which overwrites dst with the local mean of A in the neighborhood defined by B and returns dst.

Discrete convolution

Call the convolve method as:

dst = convolve(A, B)

to compute the discrete convolution of array A by the kernel defined by B. The result dst is an array similar to A.

Using indices(A) to denote the set of valid indices for array A and assuming B is an array of values, the discrete convolution of A by B writes:

for i ∈ indices(A)
+Linear filters · LocalFilters.jl Package

Linear filters

LocalFilters provides a few linear filters: localmean or localmean! to compute the mean of values in a neighborhood, convolve or convolve! to compute the discrete convolution of an array by a kernel, and correlate or correlate! to compute the discrete correlation of an array by a kernel.

Local mean

The localmean method yields the local mean of an array A in a neighborhood B:

dst = localmean(A, B=3)

The result dst is an array similar to A. See Section Simple rules for specifying neighborhoods and kernels for the interpretation of B.

The in-place version localmean! may be used to avoid allocations:

localmean!(dst, A, B=3)

which overwrites dst with the local mean of A in the neighborhood defined by B and returns dst.

Discrete convolution

Call the convolve method as:

dst = convolve(A, B)

to compute the discrete convolution of array A by the kernel defined by B. The result dst is an array similar to A.

Using indices(A) to denote the set of valid indices for array A and assuming B is an array of values, the discrete convolution of A by B writes (see Section Discrete convolution and correlation):

for i ∈ indices(A)
     v = zero(T)
     @inbounds for k ∈ indices(B) ∩ (i - indices(A))
         v += A[i-k]*B[k]
@@ -19,16 +19,16 @@
         end
     end
     dst[i] = v
-end

which amounts to computing the local sum of the values of A in the neighborhood defined by the true entries of B.

The in-place version convolve! may be used to avoid allocations:

convolve!(dst, A, B)

which overwrites dst with the discrete convolution of A by the kernel B and returns dst.

Discrete correlation

Call the correlate method as:

dst = correlate(A, B)

to compute the discrete correlation of array A by the kernel defined by B. The result dst is an array similar to A.

Using indices(A) to denote the set of valid indices for array A and assuming B is an array of values, the discrete correlation of A by B writes:

for i ∈ indices(A)
+end

which amounts to computing the local sum of the values of A in the neighborhood defined by the true entries of B.

The in-place version convolve! may be used to avoid allocations:

convolve!(dst, A, B)

which overwrites dst with the discrete convolution of A by the kernel B and returns dst.

Discrete correlation

Call the correlate method as:

dst = correlate(A, B)

to compute the discrete correlation of array A by the kernel defined by B. The result dst is an array similar to A.

Using indices(A) to denote the set of valid indices for array A and assuming B is an array of values, the discrete correlation of A by B writes (see Section Discrete convolution and correlation):

for i ∈ indices(A)
     v = zero(T)
     @inbounds for k ∈ indices(B) ∩ (indices(A) - i)
-        v += A[i+k]*B[k]
+        v += A[i+k]*conj(B[k])
     end
     dst[i] = v
 end

with T a suitable element type for the result (see Section Element type of the result below) and where indices(B) ∩ (indices(A) - i) denotes the subset of indices k such that k ∈ indices(B) and i + k ∈ indices(A) and thus for which B[k] and A[i+k] are valid.

Following the conventions in localfilter!, the discrete correlation can also be expressed as:

for i ∈ indices(A)
     v = zero(T)
     @inbounds for j ∈ indices(A) ∩ (indices(B) + i)
-        v += A[j]*B[j-i]
+        v += A[j]*conj(B[j-i])
     end
     dst[i] = v
 end

If the kernel B is an array of Booleans, the discrete correlation is computed as:

for i ∈ indices(A)
@@ -37,8 +37,8 @@
         v += A[j]
     end
     dst[i] = v
-end

which amounts to computing the local sum of the values of A in the neighborhood defined by the true entries of B.

The in-place version correlate! may be used to avoid allocations:

correlate!(dst, A, B)

which overwrites dst with the discrete correlation of A by the kernel B and returns dst.

SInce accessing the indices of A and B in the same order is generally faster (e.g. it is easier to optimize via loop vectorization), the discrete convolution convolve(A,B) of A by B may be computed by:

correlate(A, reverse_kernel(B))

Element type of the result

Choosing a suitable element type for the result may be tricky if the entries of the source array A and of the kernel B have different types or have units.

For example, a suitable element type T for the result of the convolution or correlation of A by B is given by:

T = let a = oneunit(eltype(A)), b = oneunit(eltype(B)), c = a*b
+end

which amounts to computing the local sum of the values of A in the neighborhood defined by the true entries of B.

The in-place version correlate! may be used to avoid allocations:

correlate!(dst, A, B)

which overwrites dst with the discrete correlation of A by the kernel B and returns dst.

SInce accessing the indices of A and B in the same order is generally faster (e.g. it is easier to optimize via loop vectorization), the discrete convolution convolve(A,B) of A by B may be computed by:

correlate(A, reverse_kernel(B))

provided the entries of B are reals, not complexes.

Element type of the result

Choosing a suitable element type for the result may be tricky if the entries of the source array A and of the kernel B have different types or have units.

For example, a suitable element type T for the result of the convolution or correlation of A by B is given by:

T = let a = oneunit(eltype(A)), b = oneunit(eltype(B)), c = a*b
     typeof(c + c)
 end

which is the type of the sum of the element-wise product of the entries of A and B.

For the local mean, a similar reasoning yields:

T = let a = oneunit(eltype(A)), b = oneunit(eltype(B)), c = a*b
     typeof((c + c)/(b + b))
-end

which is the type of the sum of the element-wise product of the entries of A and B divided by the sum of the entries in B (the so-called weights).

These rules are the ones used by the out-of-place versions of the linear filters of LocalFilter when the destination is not provided.

+end

which is the type of the sum of the element-wise product of the entries of A and B divided by the sum of the entries in B (the so-called weights).

These rules are the ones used by the out-of-place versions of the linear filters of LocalFilter when the destination is not provided.

diff --git a/dev/math/index.html b/dev/math/index.html new file mode 100644 index 0000000..e1c6435 --- /dev/null +++ b/dev/math/index.html @@ -0,0 +1,20 @@ + +Convolution, correlation, and Fourier transform · LocalFilters.jl Package

Convolution, correlation, and Fourier transform

Fourier transform

The continuous Fourier transform of $a(x)$ is defined by:

\[\hat{a}(u) = \int a(x)\,\mathrm{e}^{-\mathrm{i}\,2\,\pi\,u\,x}\,\mathrm{d}x.\]

The inverse Fourier transform of $\hat{a}(u)$ then writes:

\[a(x) = \int \hat{a}(u)\,\mathrm{e}^{+\mathrm{i}\,2\,\pi\,u\,x}\,\mathrm{d}u.\]

Convolution product

The convolution product of $a(x)$ by $b(x)$ is defined by:

\[c(x) = \mathrm{Conv}(a,b)(x) += \int a(y)\,b(x - y)\,\mathrm{d}y += \int b(z)\,a(x - z)\,\mathrm{d}z,\]

with $z = x - y$. This also shows that the convolution product is symmetrical:

\[\mathrm{Conv}(b,a) = \mathrm{Conv}(a,b).\]

Taking $z = x - y$, the Fourier transform of the convolution product can be expanded as follows:

\[\begin{align*} +\hat{c}(u) &= \int c(x)\,\mathrm{e}^{-\mathrm{i}\,2\,\pi\,u\,x}\,\mathrm{d}x\\ +&= \iint a(y)\,b(x - y)\,\mathrm{e}^{-\mathrm{i}\,2\,\pi\,u\,x}\,\mathrm{d}x\,\mathrm{d}y\\ +&= \iint a(y)\,b(z)\,\mathrm{e}^{-\mathrm{i}\,2\,\pi\,u\,(y + z)}\,\mathrm{d}y\,\mathrm{d}z\\ +&= \int a(y)\,\mathrm{e}^{-\mathrm{i}\,2\,\pi\,u\,y}\,\mathrm{d}y + \int b(z)\,\mathrm{e}^{-\mathrm{i}\,2\,\pi\,u\,z}\,\mathrm{d}z\\ +&= \hat{a}(u)\,\hat{b}(u). +\end{align*}\]

Correlation product

The correlation product of $a(x)$ by $b(x)$ is defined by:

\[r(x) = \mathrm{Corr}(a,b)(x) += \int a(x + y)\,{b}^\star(y)\,\mathrm{d}y += \int {b}^\star(z - x)\,a(z)\,\mathrm{d}z,\]

where ${b}^\star(y)$ denotes the complex conjugate of $b(y)$ and with $z = x + y$. From this follows that:

\[\mathrm{Corr}(b,a)(x) = {\mathrm{Corr}(a,b)}^\star(-x).\]

Taking $z = x + y$, the Fourier transform of the correlation product can be expanded as follows:

\[\begin{align*} +\hat{r}(u) &= \int r(x)\,\mathrm{e}^{-\mathrm{i}\,2\,\pi\,u\,x}\,\mathrm{d}x\\ +&= \iint a(x + y)\,{b}^\star(y)\,\mathrm{e}^{-\mathrm{i}\,2\,\pi\,u\,x}\,\mathrm{d}x\,\mathrm{d}y\\ +&= \iint a(z)\,b^\star(y)\,\mathrm{e}^{-\mathrm{i}\,2\,\pi\,u\,(z - y)}\,\mathrm{d}y\,\mathrm{d}z\\ +&= \int a(z)\,\mathrm{e}^{-\mathrm{i}\,2\,\pi\,u\,z}\,\mathrm{d}z + \left(\int b(y)\,\mathrm{e}^{-\mathrm{i}\,2\,\pi\,u\,y}\,\mathrm{d}y\right)^\star\\ +&= \hat{a}(u)\,{\hat{b}}^\star(u). +\end{align*}\]

Discrete convolution and correlation

Following the continuous definition, the discrete convolution of $a$ by $b$ is given by:

\[c[i] = \sum_j a[j]\,b[i - j] = \sum_k b[k]\,a[i - k],\]

with $k = i - j$ and where the sums are taken for all possible valid indices.

Similarly, following the continuous definition, the discrete correlation of $a$ by $b$ is given by:

\[r[i] = \sum_k a[i + k]\,{b}^\star[k] = \sum_k {b}^\star[j - i]\,a[j],\]

with $j = i + k$ and where the sums are taken for all possible valid indices.

diff --git a/dev/morphology/index.html b/dev/morphology/index.html index aafa6e5..646b8ea 100644 --- a/dev/morphology/index.html +++ b/dev/morphology/index.html @@ -7,4 +7,4 @@ opening(A, R=3)

respectively perform a closing or an opening of array A by the structuring element R. If R is not specified, a default hyper-rectangular moving window of size 3 in every dimension of A is used. A closing is a dilation followed by an erosion, whereas an opening is an erosion followed by a dilation.

The in-place versions are:

closing!(dst, wrk, A, R=3) -> dst
 opening!(dst, wrk, A, R=3) -> dst

which perform the operation on the source A and store the result in destination dst using wrk as a workspace array. The 3 arguments dst, wrk, and A must be similar arrays; dst and A may be identical, but wrk must not be the same array as A or dst. The destination dst is returned.

Top-hat and bottom-hat filters

Methods top_hat and bottom_hat perform a summit/valley detection by applying a top-hat filter to an array. They are called as:

top_hat(A, R[, S]) -> dst
 bottom_hat(A, R[, S]) -> dst

to yield the result of the filter applied to array A. Argument R defines the structuring element for the feature detection. Optional argument S specifies the structuring element for smoothing A prior to the top-/bottom-hat filter. If R and S are specified as the radii of the structuring elements, then S should be smaller than R. For instance:

top_hat(bitmap, 3, 1)

may be used to detect text or lines in a bitmap image.

Methods LocalFilters.top_hat! and LocalFilters.bottom_hat! implement the in-place versions of these filters:

top_hat!(dst, wrk, A, R[, S]) -> dst
-bottom_hat!(dst, wrk, A, R[, S]) -> dst

apply the top-/bottom-hat filter on the source A and store the result in the destination dst using wrk as a workspace array. The 3 arguments dst, wrk, and A must be similar but different arrays. The destination dst is returned.

+bottom_hat!(dst, wrk, A, R[, S]) -> dst

apply the top-/bottom-hat filter on the source A and store the result in the destination dst using wrk as a workspace array. The 3 arguments dst, wrk, and A must be similar but different arrays. The destination dst is returned.

diff --git a/dev/neighborhoods/index.html b/dev/neighborhoods/index.html index 020af31..67c44a4 100644 --- a/dev/neighborhoods/index.html +++ b/dev/neighborhoods/index.html @@ -17,4 +17,4 @@ 1 1 1 1 1 1 1 0 1 1 1 1 1 0 0 0 1 1 1 0 0 - + diff --git a/dev/nonlinear/index.html b/dev/nonlinear/index.html index e33f248..390b7c3 100644 --- a/dev/nonlinear/index.html +++ b/dev/nonlinear/index.html @@ -1,2 +1,2 @@ -Non-linear filters · LocalFilters.jl Package

Non-linear filters

LocalFilters provides a number of non-linear filters such as the bilateral filter and mathematical morphology filters. The latter are described in the Section Non-linear morphological filters.

The bilateral filter

Applying the bilateral filter on array A writes:

bilateralfilter([T=float(eltype(A)),] A, F, G, ...)

Argument F specifies how to smooth the differences in values. It may be function which takes two values from A as arguments and returns a nonnegative weight. It may be a real which is assumed to be the standard deviation of a Gaussian.

Arguments G, ... specify the settings of the distance filter for smoothing differences in coordinates. There are several possibilities:

  • G, ... can be a single argument specifying a kernel (see Section Simple rules for specifying neighborhoods and kernels).

  • Argument G may be a function taking as argument the Cartesian index of the coordinate differences and returning a nonnegative weight. Argument G may also be a real specifying the standard deviation of the Gaussian used to compute weights. Subsequent arguments ... are to specify the neighborhood where to apply the distance filter function, they can be anything that may defined a neighborhood (again see Section Simple rules for specifying neighborhoods and kernels). If a standard deviation σ is specified for G with no subsequent arguments, a default window of radius is assumed.

Optional argument T can be used to force the element type used for (most) computations. This argument is needed if the element type of A is not a real.

See bilateralfilter! for an in-place version of this function.

bilateralfilter!([T=float(eltype(A)),] dst, A, F, G, ...) -> dst

overwrites dst with the result of applying the bilateral filter on array A and returns dst.

See bilateralfilter for a description of the other arguments than dst.

See wikipedia for a description of the bilateral filter.

+Non-linear filters · LocalFilters.jl Package

Non-linear filters

LocalFilters provides a number of non-linear filters such as the bilateral filter and mathematical morphology filters. The latter are described in the Section Non-linear morphological filters.

The bilateral filter

Applying the bilateral filter on array A writes:

bilateralfilter([T=float(eltype(A)),] A, F, G, ...)

Argument F specifies how to smooth the differences in values. It may be function which takes two values from A as arguments and returns a nonnegative weight. It may be a real which is assumed to be the standard deviation of a Gaussian.

Arguments G, ... specify the settings of the distance filter for smoothing differences in coordinates. There are several possibilities:

  • G, ... can be a single argument specifying a kernel (see Section Simple rules for specifying neighborhoods and kernels).

  • Argument G may be a function taking as argument the Cartesian index of the coordinate differences and returning a nonnegative weight. Argument G may also be a real specifying the standard deviation of the Gaussian used to compute weights. Subsequent arguments ... are to specify the neighborhood where to apply the distance filter function, they can be anything that may defined a neighborhood (again see Section Simple rules for specifying neighborhoods and kernels). If a standard deviation σ is specified for G with no subsequent arguments, a default window of radius is assumed.

Optional argument T can be used to force the element type used for (most) computations. This argument is needed if the element type of A is not a real.

See bilateralfilter! for an in-place version of this function.

bilateralfilter!([T=float(eltype(A)),] dst, A, F, G, ...) -> dst

overwrites dst with the result of applying the bilateral filter on array A and returns dst.

See bilateralfilter for a description of the other arguments than dst.

See wikipedia for a description of the bilateral filter.

diff --git a/dev/objects.inv b/dev/objects.inv index b463d5c..0437c3a 100644 Binary files a/dev/objects.inv and b/dev/objects.inv differ diff --git a/dev/reference/index.html b/dev/reference/index.html index f160db7..19e4965 100644 --- a/dev/reference/index.html +++ b/dev/reference/index.html @@ -7,15 +7,15 @@ v += A[j]*B[j-i] end dst[i] = v -end

with T the type of the product of elements of A and B, and where Sup(A) ∩ (i - Sup(A)) denotes the subset of indices k such that k ∈ Sup(B) and i - k ∈ Sup(A) and thus for which B[k] and A[i-k] are valid.

See also correlate! and convolve.

source
LocalFilters.correlate!Function
correlate!(dst, A, B) -> dst

overwrites dst with the discrete convolution of A by the kernel B and returns dst.

See also correlate and localfilter!.

source
LocalFilters.convolveFunction
convolve(A, B)

yields the discrete convolution of array A by the kernel defined by B. The result dst is an array similar to A.

Using Sup(A) to denote the set of valid indices for array A and assuming B is an array of values, the discrete convolution of A by B writes:

T = let x = oneunit(eltype(A))*oneunit(eltype(B)); typeof(x + x); end
+end

with T the type of the product of elements of A and B, and where Sup(A) ∩ (i - Sup(A)) denotes the subset of indices k such that k ∈ Sup(B) and i - k ∈ Sup(A) and thus for which B[k] and A[i-k] are valid.

See also correlate! and convolve.

source
LocalFilters.correlate!Function
correlate!(dst, A, B) -> dst

overwrites dst with the discrete convolution of A by the kernel B and returns dst.

See also correlate and localfilter!.

source
LocalFilters.convolveFunction
convolve(A, B)

yields the discrete convolution of array A by the kernel defined by B. The result dst is an array similar to A.

Using Sup(A) to denote the set of valid indices for array A and assuming B is an array of values, the discrete convolution of A by B writes:

T = let x = oneunit(eltype(A))*oneunit(eltype(B)); typeof(x + x); end
 for i ∈ Sup(A)
     v = zero(T)
     @inbounds for j ∈ Sup(B) ∩ (i - Sup(A))
         v += A[i-j]*B[j]
     end
     dst[i] = v
-end

with T the type of the product of elements of A and B, and where Sup(B) ∩ (i - Sup(A)) denotes the subset of indices k such that k ∈ Sup(B) and i - k ∈ Sup(A) and thus for which B[k] and A[i-k] are valid.

See also convolve! and localfilter!.

source
LocalFilters.convolve!Function
convolve!(dst, A, B) -> dst

overwrites dst with the discrete convolution of A by the kernel B and returns dst.

See also convolve and localfilter!.

source
LocalFilters.localmeanFunction
localmean(A, [ord=FORWARD_FILTER,] B=3; null=zero(eltype(A)))

yields the local mean of A in a neighborhood defined by B. The result is an array similar to A. If B is not specified, the neighborhood is a hyper-rectangular sliding window of size 3 in every dimension. Otherwise, B may be specified as a Cartesian box, or as an array of booleans of same number of dimensions as A. If B is a single odd integer (as it is by default), the neighborhood is assumed to be a hyper-rectangular sliding window of size B in every dimension.

Keyword null may be used to specify the value of the result where the sum of the weights in a local neighborhood is zero.

See also localmean! and localfilter!.

source
LocalFilters.localmean!Function
localmean!(dst, A, [ord=FORWARD_FILTER,] B=3; null=zero(eltype(dst))) -> dst

overwrites dst with the local mean of A in a neighborhood defined by B and returns dst.

Keyword null may be used to specify the value of the result where the sum of the weights in the a neighborhood is zero.

See also localmean and localfilter!.

source

Mathematical morphology

LocalFilters.erodeFunction
erode(A, [ord=FORWARD_FILTER,] B=3) -> Amin

yields the erosion of A by the structuring element defined by B. The erosion is the array of local minima of A. The returned result Amin is similar to A (same size and type).

If B is not a kernel (that is, if B is not an array or is an instance of CartesianIndices), kernel(Dims{N},B) is called to build a kernel with N the number of dimensions of A.

If the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used.

An erosion is one of the most basic operations of mathematical morphology. See erode! for an in-place version of the method, dilate for retrieving the local maxima, and localextrema for performing an erosion and a dilation in a single pass.

source
LocalFilters.erode!Function
erode!(Amin, A, [ord=FORWARD_FILTER,] B=3) -> Amin

overwrites Amin with the erosion of the array A by the structuring element defined by B and returns Amin.

If the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used and the operation can be done in-place. That is, A and Amin can be the same arrays. In that case, the following syntax is allowed:

erode!(A, [ord=FORWARD_FILTER,] B=3) -> A

See erode for an out-of-place version and for more information.

source
LocalFilters.dilateFunction
dilate(A, [ord=FORWARD_FILTER,] B=3) -> Amax

yields the dilation of A by the structuring element defined by B. The dilation is the array of local maxima of A. The returned result Amax is similar to A (same size and type).

If B is not a kernel (that is, if B is not an array or is an instance of CartesianIndices), kernel(Dims{N},B) is called to build a kernel with N the number of dimensions of A.

If the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used.

A dilation is one of the most basic operations of mathematical morphology. See dilate! for an in-place version of the method, erode for retrieving the local minima, and localextrema for performing an erosion and a dilation in a single pass.

source
LocalFilters.dilate!Function
dilate!(Amax, A, [ord=FORWARD_FILTER,] B=3) -> Amax

overwrites Amax with a dilation of the array A by the structuring element defined by B and returns Amax.

If the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used and the operation can be done in-place. That is, A and Amin can be the same arrays. In that case, the following syntax is allowed:

dilate!(A, [ord=FORWARD_FILTER,] B=3) -> A

See dilate for an out-of-place version and for more information.

source
LocalFilters.localextremaFunction
localextrema(A, [ord=FORWARD_FILTER,] B=3) -> Amin, Amax

yields the results of performing an erosion and a dilation of A by the structuring element defined by B in a single pass. Calling this method is usually almost twice as fast as calling erode and dilate.

See localextrema! for an in-place version of the method, and erode or dilate for a description of these operations.

source
LocalFilters.localextrema!Function
localextrema!(Amin, Amax, A, [ord=FORWARD_FILTER,] B=3) -> Amin, Amax

overwrites Amin and Amax with, respectively, an erosion and a dilation of the array A by the structuring element defined by B in a single pass.

See localextrema for an out-of-place version for more information.

source
LocalFilters.closingFunction
closing(A, [ord=FORWARD_FILTER,] B=3) -> dst

yields a closing of array A by the structuring element defined by B. A closing is a dilation followed by an erosion. The result dst is an array similar to A.

See closing! for an in-place version of the method, opening for a related filter, and erode or dilate for a description of these operations.

source
LocalFilters.closing!Function
closing!(dst, wrk, A, B=3) -> dst

overwrites dst with the result of a closing of A by the structuring element defined by B using wrk as a workspace array. The arguments dst, wrk, and A must be similar arrays, dst and A may be identical, but wrk must not be the same array as A or dst. The destination dst is returned.

See closing for a description of this kind of filter and for the meaning of the arguments.

source
LocalFilters.openingFunction
opening(A, B=3) -> dst

yields an opening of array A by the structuring element defined by B. An opening is an erosion followed by a dilation. The result dst is an array similar to A.

See opening! for an in-place version of the method, closing for a related filter, and erode or dilate for a description of these operations.

source
LocalFilters.opening!Function
opening!(dst, wrk, A, B=3) -> dst

overwrites dst with the result of an opening of A by the structuring element defined by B using wrk as a workspace array. The arguments dst, wrk, and A must be similar arrays, dst and A may be identical, but wrk must not be the same array as A or dst. The destination dst is returned.

See opening for a description of this kind of filter and for the meaning of the arguments.

source
LocalFilters.top_hatFunction
top_hat(A, B=3 [, C]) -> dst

performs a summit detection by applying a top-hat filter to array A using the structuring element defined by B for the feature detection. Top-hat filtering is equivalent to:

dst = A .- opening(A, B)

Optional argument C specifies the structuring element for smoothing A prior to top-hat filtering. If B and C are specified as the radii of the structuring elements, then C should be smaller than B. For instance:

top_hat(bitmap, 3, 1)

may be used to detect text or lines in a bitmap image.

See bottom_hat for a related operation, LocalFilters.top_hat! for an in-place version.

source
LocalFilters.top_hat!Function
LocalFilters.top_hat!(dst, wrk, A, [ord=FORWARD_FILTER,] B=3) -> dst

overwrites dst with the result of a top-hat filter applied to A with structuring element B, and using wrk as a workspace whose contents is not preserved. The arguments A, dst, and wrk must be similar but different arrays. The destination dst is returned.

See also top_hat for more details.

source
LocalFilters.bottom_hatFunction
bottom_hat(A, B=3 [, C]) -> dst

performs a valley detection by applying a bottom-hat filter to array A using the structuring element defined by B for the feature detection. Bottom-hat filtering is equivalent to:

dst = closing(A, B) .- A

Optional argument C specifies the structuring element for smoothing A prior to bottom-hat filtering. If B and C are specified as the radii of the structuring elements, then C should be smaller than B.

See top_hat for a related operation, LocalFilters.bottom_hat! for an in-place version.

source
LocalFilters.bottom_hat!Function
LocalFilters.bottom_hat!(dst, wrk, A, B=3) -> dst

overwrites dst with the result of a bottom-hat filter applied to A with structuring element B and optional smoothing element C. Argument wrk is a workspace array whose contents is not preserved. The arguments A, dst, and wrk must be similar but different arrays. The destination dst is returned.

See also bottom_hat for more details.

source

Other non-linear filters

LocalFilters.bilateralfilterFunction
bilateralfilter([T=float(eltype(A)),] A, F, [ord=FORWARD_FILTER,] G...=3)

yields the result of applying the bilateral filter on array A.

Argument F specifies how to smooth the differences in values. It can be:

  • a function, say f, which is called as f(A[i],A[j]) to yield a nonnegative weight for i the central index and j the index in a nearby position;

  • a positive real, say σ, which is assumed to be the standard deviation of a Gaussian.

Arguments G, ... specify the settings of the distance filter for smoothing differences in coordinates. There are several possibilities:

  • G... = wgt an array of nonnegative weights or of booleans. The axes of wgt must have offsets so that the zero index is part of the indices of wgt.

  • G... = f, w with f a function and w any kind of argument that can be used to build a window win specifying the extension of the neighborhood. The value of the distance filter will be max(f(i),0) for all Cartesian index i of win such that win[i] is true. See kernel for the different ways to specify a window.

  • G... = σ or , G... = σ, w with σ a positive real assumed to be the standard deviation of a Gaussian function and w any kind of argument that can be used to build a window win specifying the extension of the neighborhood. If w is not specified, a default window of size ±3σ is assumed.

Optional argument T can be used to force the element type of the result. This argument is needed if the element type of A is not a real.

See bilateralfilter! for an in-place version of this function and see Wikipedia for a description of the bilateral filter.

source
LocalFilters.bilateralfilter!Function
bilateralfilter!(dst, A, F, [ord=FORWARD_FILTER,] G...) -> dst

overwrites dst with the result of applying the bilateral filter on array A and returns dst.

See bilateralfilter for a description of the other arguments than dst and see Wikipedia for a description of the bilateral filter.

source

Methods to build local filters

LocalFilters.localfilterFunction
localfilter([T=eltype(A),] A, dims, op, [ord=FORWARD_FILTER,]
-            rngs[, wrk]) -> dst

yields the result of applying van Herk-Gil-Werman algorithm to filter array A along dimension(s) dims with (associative) binary operation op and contiguous structuring element(s) defined by the interval(s) rngs. Optional argument wrk is a workspace array with elements of type T which is automatically allocated if not provided; otherwise, it must be a vector with the same element type as A and it is resized as needed (by calling the resize! method). The optional argument T allows to specify another type of element than eltype(A) for the result.

Argument dims specifies along which dimension(s) of A the filter is to be applied, it can be a single integer, a tuple of integers, or a colon : to apply the operation to all dimensions. Dimensions are processed in the order given by dims (the same dimension may appear several times) and there must be a matching interval in rngs to specify the structuring element (except that if rngs is a single interval, it is used for every dimension in dims). An interval is either an integer or an integer valued unit range in the form kmin:kmax. An interval specified as a single integer yields an approximately centered range og this length.

Assuming a mono-dimensional array A, the single filtering pass:

dst = localfilter(A, :, op, rng)

amounts to computing (assuming forward ordering):

dst[j] =  A[i+kmin] ⋄ A[i+kmin+1] ⋄ ... ⋄ A[i+kmax-1] ⋄ A[i+kmax]

for all j ∈ axes(dst,1), with x ⋄ y = op(x, y), kmin = first(rng) and kmax = last(rng). Note that if kmin = kmax = k, the result of the filter is to operate a simple shift by k along the corresponding dimension and has no effects if k = 0. This can be exploited to not filter some dimension(s).

Flat boundary conditions are assumed for A[i+k] in the above formula.

Examples

The morphological erosion (local minimum) of the array A on a centered structuring element of width 7 in every dimension can be obtained by:

localfilter(A, :, min, -3:3)

Index interval 0:0 may be specified to do nothing along the corresponding dimension. For instance, assuming A is a three-dimensional array:

localfilter(A, :, max, (-3:3, 0:0, -4:4))

yields the morphological dilation (i.e. local maximum) of A in a centered local neighborhood of size 7×1×9 (nothing is done along the second dimension). The same result may be obtained with:

localfilter(A, (1,3), max, (-3:3, -4:4))

where the second dimension is omitted from the list of dimensions.

The local average of the two-dimensional array A on a centered moving window of size 11×11 can be computed as:

localfilter(A, :, +, (-5:5, -5:5))*(1/11)

See localfilter! for an in-place version of the method.

source
localfilter(A, args...; kwds...) -> dst

out of place version of localfilter! which is equivqlent to:

localfilter!(similar(A), A, args...; kwds...)
source
LocalFilters.localfilter!Function
localfilter!([dst = A,] A, dims, op, [ord=FORWARD_FILTER,] rngs[, wrk])

overwrites the contents of dst with the result of applying van Herk-Gil-Werman algorithm to filter array A along dimension(s) dims with (associative) binary operation op and contiguous structuring element(s) defined by the interval(s) rngs and using optional argument wrk as a workspace array. The destination dst must have the same indices as the source A (that is, axes(dst) == axes(A)). Operation may be done in-place and dst and A can be the same; this is the default behavior if dst is not specified.

See localfilter for a full description of the method.

The in-place morphological erosion (local minimum) of the array A on a centered structuring element of width 7 in every dimension can be obtained by:

localfilter!(A, :, min, -3:3)
source
localfilter!(dst, A, [ord = FORWARD_FILTER,] B, initial,
+end

with T the type of the product of elements of A and B, and where Sup(B) ∩ (i - Sup(A)) denotes the subset of indices k such that k ∈ Sup(B) and i - k ∈ Sup(A) and thus for which B[k] and A[i-k] are valid.

See also convolve! and localfilter!.

source
LocalFilters.convolve!Function
convolve!(dst, A, B) -> dst

overwrites dst with the discrete convolution of A by the kernel B and returns dst.

See also convolve and localfilter!.

source
LocalFilters.localmeanFunction
localmean(A, [ord=FORWARD_FILTER,] B=3; null=zero(eltype(A)))

yields the local mean of A in a neighborhood defined by B. The result is an array similar to A. If B is not specified, the neighborhood is a hyper-rectangular sliding window of size 3 in every dimension. Otherwise, B may be specified as a Cartesian box, or as an array of booleans of same number of dimensions as A. If B is a single odd integer (as it is by default), the neighborhood is assumed to be a hyper-rectangular sliding window of size B in every dimension.

Keyword null may be used to specify the value of the result where the sum of the weights in a local neighborhood is zero.

See also localmean! and localfilter!.

source
LocalFilters.localmean!Function
localmean!(dst, A, [ord=FORWARD_FILTER,] B=3; null=zero(eltype(dst))) -> dst

overwrites dst with the local mean of A in a neighborhood defined by B and returns dst.

Keyword null may be used to specify the value of the result where the sum of the weights in the a neighborhood is zero.

See also localmean and localfilter!.

source

Mathematical morphology

LocalFilters.erodeFunction
erode(A, [ord=FORWARD_FILTER,] B=3) -> Amin

yields the erosion of A by the structuring element defined by B. The erosion is the array of local minima of A. The returned result Amin is similar to A (same size and type).

If B is not a kernel (that is, if B is not an array or is an instance of CartesianIndices), kernel(Dims{N},B) is called to build a kernel with N the number of dimensions of A.

If the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used.

An erosion is one of the most basic operations of mathematical morphology. See erode! for an in-place version of the method, dilate for retrieving the local maxima, and localextrema for performing an erosion and a dilation in a single pass.

source
LocalFilters.erode!Function
erode!(Amin, A, [ord=FORWARD_FILTER,] B=3) -> Amin

overwrites Amin with the erosion of the array A by the structuring element defined by B and returns Amin.

If the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used and the operation can be done in-place. That is, A and Amin can be the same arrays. In that case, the following syntax is allowed:

erode!(A, [ord=FORWARD_FILTER,] B=3) -> A

See erode for an out-of-place version and for more information.

source
LocalFilters.dilateFunction
dilate(A, [ord=FORWARD_FILTER,] B=3) -> Amax

yields the dilation of A by the structuring element defined by B. The dilation is the array of local maxima of A. The returned result Amax is similar to A (same size and type).

If B is not a kernel (that is, if B is not an array or is an instance of CartesianIndices), kernel(Dims{N},B) is called to build a kernel with N the number of dimensions of A.

If the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used.

A dilation is one of the most basic operations of mathematical morphology. See dilate! for an in-place version of the method, erode for retrieving the local minima, and localextrema for performing an erosion and a dilation in a single pass.

source
LocalFilters.dilate!Function
dilate!(Amax, A, [ord=FORWARD_FILTER,] B=3) -> Amax

overwrites Amax with a dilation of the array A by the structuring element defined by B and returns Amax.

If the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used and the operation can be done in-place. That is, A and Amin can be the same arrays. In that case, the following syntax is allowed:

dilate!(A, [ord=FORWARD_FILTER,] B=3) -> A

See dilate for an out-of-place version and for more information.

source
LocalFilters.localextremaFunction
localextrema(A, [ord=FORWARD_FILTER,] B=3) -> Amin, Amax

yields the results of performing an erosion and a dilation of A by the structuring element defined by B in a single pass. Calling this method is usually almost twice as fast as calling erode and dilate.

See localextrema! for an in-place version of the method, and erode or dilate for a description of these operations.

source
LocalFilters.localextrema!Function
localextrema!(Amin, Amax, A, [ord=FORWARD_FILTER,] B=3) -> Amin, Amax

overwrites Amin and Amax with, respectively, an erosion and a dilation of the array A by the structuring element defined by B in a single pass.

See localextrema for an out-of-place version for more information.

source
LocalFilters.closingFunction
closing(A, [ord=FORWARD_FILTER,] B=3) -> dst

yields a closing of array A by the structuring element defined by B. A closing is a dilation followed by an erosion. The result dst is an array similar to A.

See closing! for an in-place version of the method, opening for a related filter, and erode or dilate for a description of these operations.

source
LocalFilters.closing!Function
closing!(dst, wrk, A, B=3) -> dst

overwrites dst with the result of a closing of A by the structuring element defined by B using wrk as a workspace array. The arguments dst, wrk, and A must be similar arrays, dst and A may be identical, but wrk must not be the same array as A or dst. The destination dst is returned.

See closing for a description of this kind of filter and for the meaning of the arguments.

source
LocalFilters.openingFunction
opening(A, B=3) -> dst

yields an opening of array A by the structuring element defined by B. An opening is an erosion followed by a dilation. The result dst is an array similar to A.

See opening! for an in-place version of the method, closing for a related filter, and erode or dilate for a description of these operations.

source
LocalFilters.opening!Function
opening!(dst, wrk, A, B=3) -> dst

overwrites dst with the result of an opening of A by the structuring element defined by B using wrk as a workspace array. The arguments dst, wrk, and A must be similar arrays, dst and A may be identical, but wrk must not be the same array as A or dst. The destination dst is returned.

See opening for a description of this kind of filter and for the meaning of the arguments.

source
LocalFilters.top_hatFunction
top_hat(A, B=3 [, C]) -> dst

performs a summit detection by applying a top-hat filter to array A using the structuring element defined by B for the feature detection. Top-hat filtering is equivalent to:

dst = A .- opening(A, B)

Optional argument C specifies the structuring element for smoothing A prior to top-hat filtering. If B and C are specified as the radii of the structuring elements, then C should be smaller than B. For instance:

top_hat(bitmap, 3, 1)

may be used to detect text or lines in a bitmap image.

See bottom_hat for a related operation, LocalFilters.top_hat! for an in-place version.

source
LocalFilters.top_hat!Function
LocalFilters.top_hat!(dst, wrk, A, [ord=FORWARD_FILTER,] B=3) -> dst

overwrites dst with the result of a top-hat filter applied to A with structuring element B, and using wrk as a workspace whose contents is not preserved. The arguments A, dst, and wrk must be similar but different arrays. The destination dst is returned.

See also top_hat for more details.

source
LocalFilters.bottom_hatFunction
bottom_hat(A, B=3 [, C]) -> dst

performs a valley detection by applying a bottom-hat filter to array A using the structuring element defined by B for the feature detection. Bottom-hat filtering is equivalent to:

dst = closing(A, B) .- A

Optional argument C specifies the structuring element for smoothing A prior to bottom-hat filtering. If B and C are specified as the radii of the structuring elements, then C should be smaller than B.

See top_hat for a related operation, LocalFilters.bottom_hat! for an in-place version.

source
LocalFilters.bottom_hat!Function
LocalFilters.bottom_hat!(dst, wrk, A, B=3) -> dst

overwrites dst with the result of a bottom-hat filter applied to A with structuring element B and optional smoothing element C. Argument wrk is a workspace array whose contents is not preserved. The arguments A, dst, and wrk must be similar but different arrays. The destination dst is returned.

See also bottom_hat for more details.

source

Other non-linear filters

LocalFilters.bilateralfilterFunction
bilateralfilter([T=float(eltype(A)),] A, F, [ord=FORWARD_FILTER,] G...=3)

yields the result of applying the bilateral filter on array A.

Argument F specifies how to smooth the differences in values. It can be:

  • a function, say f, which is called as f(A[i],A[j]) to yield a nonnegative weight for i the central index and j the index in a nearby position;

  • a positive real, say σ, which is assumed to be the standard deviation of a Gaussian.

Arguments G, ... specify the settings of the distance filter for smoothing differences in coordinates. There are several possibilities:

  • G... = wgt an array of nonnegative weights or of booleans. The axes of wgt must have offsets so that the zero index is part of the indices of wgt.

  • G... = f, w with f a function and w any kind of argument that can be used to build a window win specifying the extension of the neighborhood. The value of the distance filter will be max(f(i),0) for all Cartesian index i of win such that win[i] is true. See kernel for the different ways to specify a window.

  • G... = σ or , G... = σ, w with σ a positive real assumed to be the standard deviation of a Gaussian function and w any kind of argument that can be used to build a window win specifying the extension of the neighborhood. If w is not specified, a default window of size ±3σ is assumed.

Optional argument T can be used to force the element type of the result. This argument is needed if the element type of A is not a real.

See bilateralfilter! for an in-place version of this function and see Wikipedia for a description of the bilateral filter.

source
LocalFilters.bilateralfilter!Function
bilateralfilter!(dst, A, F, [ord=FORWARD_FILTER,] G...) -> dst

overwrites dst with the result of applying the bilateral filter on array A and returns dst.

See bilateralfilter for a description of the other arguments than dst and see Wikipedia for a description of the bilateral filter.

source

Methods to build local filters

LocalFilters.localfilterFunction
localfilter([T=eltype(A),] A, dims, op, [ord=FORWARD_FILTER,]
+            rngs[, wrk]) -> dst

yields the result of applying van Herk-Gil-Werman algorithm to filter array A along dimension(s) dims with (associative) binary operation op and contiguous structuring element(s) defined by the interval(s) rngs. Optional argument wrk is a workspace array with elements of type T which is automatically allocated if not provided; otherwise, it must be a vector with the same element type as A and it is resized as needed (by calling the resize! method). The optional argument T allows to specify another type of element than eltype(A) for the result.

Argument dims specifies along which dimension(s) of A the filter is to be applied, it can be a single integer, a tuple of integers, or a colon : to apply the operation to all dimensions. Dimensions are processed in the order given by dims (the same dimension may appear several times) and there must be a matching interval in rngs to specify the structuring element (except that if rngs is a single interval, it is used for every dimension in dims). An interval is either an integer or an integer valued unit range in the form kmin:kmax. An interval specified as a single integer yields an approximately centered range og this length.

Assuming a mono-dimensional array A, the single filtering pass:

dst = localfilter(A, :, op, rng)

amounts to computing (assuming forward ordering):

dst[j] =  A[i+kmin] ⋄ A[i+kmin+1] ⋄ ... ⋄ A[i+kmax-1] ⋄ A[i+kmax]

for all j ∈ axes(dst,1), with x ⋄ y = op(x, y), kmin = first(rng) and kmax = last(rng). Note that if kmin = kmax = k, the result of the filter is to operate a simple shift by k along the corresponding dimension and has no effects if k = 0. This can be exploited to not filter some dimension(s).

Flat boundary conditions are assumed for A[i+k] in the above formula.

Examples

The morphological erosion (local minimum) of the array A on a centered structuring element of width 7 in every dimension can be obtained by:

localfilter(A, :, min, -3:3)

Index interval 0:0 may be specified to do nothing along the corresponding dimension. For instance, assuming A is a three-dimensional array:

localfilter(A, :, max, (-3:3, 0:0, -4:4))

yields the morphological dilation (i.e. local maximum) of A in a centered local neighborhood of size 7×1×9 (nothing is done along the second dimension). The same result may be obtained with:

localfilter(A, (1,3), max, (-3:3, -4:4))

where the second dimension is omitted from the list of dimensions.

The local average of the two-dimensional array A on a centered moving window of size 11×11 can be computed as:

localfilter(A, :, +, (-5:5, -5:5))*(1/11)

See localfilter! for an in-place version of the method.

source
localfilter(A, args...; kwds...) -> dst

out of place version of localfilter! which is equivqlent to:

localfilter!(similar(A), A, args...; kwds...)
source
LocalFilters.localfilter!Function
localfilter!([dst = A,] A, dims, op, [ord=FORWARD_FILTER,] rngs[, wrk])

overwrites the contents of dst with the result of applying van Herk-Gil-Werman algorithm to filter array A along dimension(s) dims with (associative) binary operation op and contiguous structuring element(s) defined by the interval(s) rngs and using optional argument wrk as a workspace array. The destination dst must have the same indices as the source A (that is, axes(dst) == axes(A)). Operation may be done in-place and dst and A can be the same; this is the default behavior if dst is not specified.

See localfilter for a full description of the method.

The in-place morphological erosion (local minimum) of the array A on a centered structuring element of width 7 in every dimension can be obtained by:

localfilter!(A, :, min, -3:3)
source
localfilter!(dst, A, [ord = FORWARD_FILTER,] B, initial,
              update::Function, final::Function = identity) -> dst

overwrites the destination dst with the result of a local filter applied to the source A, on a relative neighborhood defined by B, and implemented by initial, update, and final. The initial argument may be a function or not. The purpose of these latter arguments is explained by the following pseudo-codes implementing the local filtering. If ord = FORWARD_FILTER:

@inbounds for i ∈ indices(dst)
     v = initial isa Function ? initial(A[i]) : initial
     for j ∈ indices(A) ∩ (indices(B) + i)
@@ -30,9 +30,9 @@
     dst[i] = final(v)
 end

where indices(A) denotes the range of indices of any array A while indices(B) + i and i - indices(B) respectively denote the set of indices j such that j - i ∈ indices(B) and i - j ∈ indices(B). In other words, j ∈ indices(A) ∩ (i - indices(B)) means all indices j such that j ∈ indices(A) and i - j ∈ indices(B) so that A[j] and B[i-j] are in-bounds.

If initial is a function, the initial value of the state variable v in the above pseudo-codes is given by v = initial(A[i]) with i the current index in dst. Hence, in that case, the destination array dst and the source array src must have the same axes.

For example, implementing a local minimum filter (that is, an erosion), is as simple as:

localfilter!(dst, A, ord, B, typemax(eltype(A)),
              (v,a,b) -> ifelse(b, min(v,a), v))

As another example, implementing a convolution by B writes:

T = promote_type(eltype(A), eltype(B))
-localfilter!(dst, A, ord, B, zero(T), (v,a,b) -> v + a*b)
source

Constants

LocalFilters.FORWARD_FILTERConstant
FORWARD_FILTER

is an exported constant object used to indicate forward ordering of indices in local filter operations. It can be called as:

FORWARD_FILTER(i, j) -> j - i

to yield the index in the filter kernel. See also REVERSE_FILTER for reverse ordering and LocalFilters.localindices for building a range of valid indices j.

source
LocalFilters.REVERSE_FILTERConstant
REVERSE_FILTER

is an exported constant object used to indicate reverse ordering of indices in local filter operations. It can be called as:

REVERSE_FILTER(i, j) -> i - j

to yield the index in the filter kernel. See also FORWARD_FILTER for forward ordering and LocalFilters.localindices for building a range of valid indices j.

source

Neighborhoods and kernels

LocalFilters.kernelFunction
kernel([Dims{N},] args...)

yields an N-dimensional abstract array built from args... and which can be used as a kernel in local filtering operations.

  • If args... is composed of N integers and/or ranges or if it is an N-tuple of integers and/or ranges, a uniformly true abstract array is returned whose axes are specified by args.... Each integer argument is converted in a centered unit range of this length (see LocalFilters.kernel_range).

  • If Dims{N} is provided and args... is a single integer or range, it is interpreted as being the same for all dimensions of an N-dimensional kernel. For example, kernel(Dims{3},5) yields a 3-dimensional uniformly true array with index range -2:2 in every dimension.

  • If args... is 2 Cartesian indices or a 2-tuple of Cartesian indices, say I_first and I_last, a uniformly true abstract array is returned whose first and last indices are I_first and I_last.

  • If args... is a Cartesian range, say R::CartesianIndices{N}, a uniformly true abstract array is returned whose axes are given by R.

  • If args... is an abstract array of any other type than an instance of CartesianIndices, it is returned unchanged.

Optional leading argument Dims{N} can be specified to assert the number of dimensions of the result or to provide the number of dimensions when it cannot be inferred from the arguments. For example, when args... is a single integer length or range which should be interpreted as being the same for all dimensions.

source
LocalFilters.reverse_kernelFunction
reverse_kernel(A) -> B

yields an array B such that B[i] = A[-i] holds for all indices i such that -i is a valid index in A.

source
LocalFilters.strelFunction
strel(T, A)

yields a structuring element suitable for mathematical morphology operations. The result is an array whose elements have type T (which can be Bool or a floating-point type). Argument A can be a hyper-rectangular Cartesian sliding window or an array with boolean elements.

If T is a floating-point type, then the result is a so-called flat structuring element whose coefficients are zero(T) inside the shape defined by A and -T(Inf) elsewhere.

source
LocalFilters.ballFunction
LocalFilters.ball(Dims{N}, r)

yields a boolean mask which is a N-dimensional array with all dimensions odd and equal and set to true where position is inside a N-dimensional ball of radius r.

To have a mask with centered index ranges, call:

LocalFilters.centered(LocalFilters.ball(Dims{N}, r))
source
LocalFilters.centeredFunction

LocalFilters.centered(A) -> B

yields an abstract array B sharing the entries of array A but with offsets on indices so that the axes of B are centered (for even dimension lengths, the same conventions as in fftshift are used).

This method is purposely not exported because it could introduce some confusions. For example OffsetArrays.centered is similar but has a slightly different semantic.

Argument A can also be an index range (linear or Cartesian), in which case a centered index range of same size is returned.

See LocalFilters.kernel_range.

source

Utilities

Below are described non-exported types and methods that may be useful in the context of building local filters.

LocalFilters.IndicesType
LocalFilters.Indices(A...) -> indices

yields a callable object that can be used to produce ranges of indices for each of the arrays A.... These ranges will all be of the same type: linear index ranges, if all arrays A... are vectors implementing fast linear indexing, Cartesian index ranges otherwise.

The returned object is similar to the eachindex method but specialized for a style of indexing, it can be used as indices(B...) to yield a suitable index range to access all the entries of array(s) B... which are any number of the A... specified when building the indices object. If B... consists in several arrays, they must have the same axes.

Call:

LocalFilters.Indices{S}()

with S = IndexLinear or S = IndexCartesian to specifically choose the indexing style.

source
LocalFilters.YieldsType
f = LocalFilters.Yields(value)
-f = LocalFilters.Yields{V}(value)

build a callable object f such that f(args...; kwds...) yields value whatever the arguments args... and the keywords kwds.... If type V is supplied, value is converted to that type.

source
LocalFilters.localindicesFunction
LocalFilters.localindices(A_inds, ord, B_inds, i) -> J

yields the subset J of all indices j such that:

  • A[j] and B[ord(i,j)] = B[j-i] are in-bounds if ord = FORWARD_FILTER;

  • A[j] and B[ord(i,j)] = B[i-j] are in-bounds if ord = REVERSE_FILTER;

with A and B arrays whose index ranges are given by A_inds and B_inds. To make the code agnostic to the ordering, use A[i] and B[ord(i,j)] to retrieve the values in A and B.

Index ranges A_inds and B_inds and index i must be of the same kind:

  • linear index ranges for A_inds and B_inds and linear index for i;

  • Cartesian index ranges for A_inds and B_inds and Cartesian index for i of same number of dimensions.

Constructor LocalFilters.Indices may by used to build a callable object that retrieves the index ranges of A and B in a consistent way:

indices = LocalFilters.Indices(A, B)
+localfilter!(dst, A, ord, B, zero(T), (v,a,b) -> v + a*b)
source

Constants

LocalFilters.FORWARD_FILTERConstant
FORWARD_FILTER

is an exported constant object used to indicate forward ordering of indices in local filter operations. It can be called as:

FORWARD_FILTER(i, j) -> j - i

to yield the index in the filter kernel. See also REVERSE_FILTER for reverse ordering and LocalFilters.localindices for building a range of valid indices j.

source
LocalFilters.REVERSE_FILTERConstant
REVERSE_FILTER

is an exported constant object used to indicate reverse ordering of indices in local filter operations. It can be called as:

REVERSE_FILTER(i, j) -> i - j

to yield the index in the filter kernel. See also FORWARD_FILTER for forward ordering and LocalFilters.localindices for building a range of valid indices j.

source

Neighborhoods and kernels

LocalFilters.kernelFunction
kernel([Dims{N},] args...)

yields an N-dimensional abstract array built from args... and which can be used as a kernel in local filtering operations.

  • If args... is composed of N integers and/or ranges or if it is an N-tuple of integers and/or ranges, a uniformly true abstract array is returned whose axes are specified by args.... Each integer argument is converted in a centered unit range of this length (see LocalFilters.kernel_range).

  • If Dims{N} is provided and args... is a single integer or range, it is interpreted as being the same for all dimensions of an N-dimensional kernel. For example, kernel(Dims{3},5) yields a 3-dimensional uniformly true array with index range -2:2 in every dimension.

  • If args... is 2 Cartesian indices or a 2-tuple of Cartesian indices, say I_first and I_last, a uniformly true abstract array is returned whose first and last indices are I_first and I_last.

  • If args... is a Cartesian range, say R::CartesianIndices{N}, a uniformly true abstract array is returned whose axes are given by R.

  • If args... is an abstract array of any other type than an instance of CartesianIndices, it is returned unchanged.

Optional leading argument Dims{N} can be specified to assert the number of dimensions of the result or to provide the number of dimensions when it cannot be inferred from the arguments. For example, when args... is a single integer length or range which should be interpreted as being the same for all dimensions.

source
LocalFilters.reverse_kernelFunction
reverse_kernel(A) -> B

yields an array B such that B[i] = A[-i] holds for all indices i such that -i is a valid index in A.

source
LocalFilters.strelFunction
strel(T, A)

yields a structuring element suitable for mathematical morphology operations. The result is an array whose elements have type T (which can be Bool or a floating-point type). Argument A can be a hyper-rectangular Cartesian sliding window or an array with boolean elements.

If T is a floating-point type, then the result is a so-called flat structuring element whose coefficients are zero(T) inside the shape defined by A and -T(Inf) elsewhere.

source
LocalFilters.ballFunction
LocalFilters.ball(Dims{N}, r)

yields a boolean mask which is a N-dimensional array with all dimensions odd and equal and set to true where position is inside a N-dimensional ball of radius r.

To have a mask with centered index ranges, call:

LocalFilters.centered(LocalFilters.ball(Dims{N}, r))
source
LocalFilters.centeredFunction

LocalFilters.centered(A) -> B

yields an abstract array B sharing the entries of array A but with offsets on indices so that the axes of B are centered (for even dimension lengths, the same conventions as in fftshift are used).

This method is purposely not exported because it could introduce some confusions. For example OffsetArrays.centered is similar but has a slightly different semantic.

Argument A can also be an index range (linear or Cartesian), in which case a centered index range of same size is returned.

See LocalFilters.kernel_range.

source

Utilities

Below are described non-exported types and methods that may be useful in the context of building local filters.

LocalFilters.IndicesType
LocalFilters.Indices(A...) -> indices

yields a callable object that can be used to produce ranges of indices for each of the arrays A.... These ranges will all be of the same type: linear index ranges, if all arrays A... are vectors implementing fast linear indexing, Cartesian index ranges otherwise.

The returned object is similar to the eachindex method but specialized for a style of indexing, it can be used as indices(B...) to yield a suitable index range to access all the entries of array(s) B... which are any number of the A... specified when building the indices object. If B... consists in several arrays, they must have the same axes.

Call:

LocalFilters.Indices{S}()

with S = IndexLinear or S = IndexCartesian to specifically choose the indexing style.

source
LocalFilters.YieldsType
f = LocalFilters.Yields(value)
+f = LocalFilters.Yields{V}(value)

build a callable object f such that f(args...; kwds...) yields value whatever the arguments args... and the keywords kwds.... If type V is supplied, value is converted to that type.

source
LocalFilters.localindicesFunction
LocalFilters.localindices(A_inds, ord, B_inds, i) -> J

yields the subset J of all indices j such that:

  • A[j] and B[ord(i,j)] = B[j-i] are in-bounds if ord = FORWARD_FILTER;

  • A[j] and B[ord(i,j)] = B[i-j] are in-bounds if ord = REVERSE_FILTER;

with A and B arrays whose index ranges are given by A_inds and B_inds. To make the code agnostic to the ordering, use A[i] and B[ord(i,j)] to retrieve the values in A and B.

Index ranges A_inds and B_inds and index i must be of the same kind:

  • linear index ranges for A_inds and B_inds and linear index for i;

  • Cartesian index ranges for A_inds and B_inds and Cartesian index for i of same number of dimensions.

Constructor LocalFilters.Indices may by used to build a callable object that retrieves the index ranges of A and B in a consistent way:

indices = LocalFilters.Indices(A, B)
 A_inds = indices(A)
-B_inds = indices(B)
source
LocalFilters.nearestFunction
LocalFilters.nearest(T, x)

converts value x to the nearest value of type T. By default, the result is given by convert(T,x) unless T is floating-point type and x is integer in which case the result is given by round(T,x).

This method may be extended for foreign types to implement other conversions.

source
LocalFilters.replicateFunction
LocalFilters.replicate(NTuple{N}, val)

yields the N-tuple (val, val, ...).

LocalFilters.replicate(NTuple{N,T}, val)

yields the N-tuple (x, x,...) where x is val converted to type T.

See LocalFilters.Yields.

source
LocalFilters.kernel_rangeFunction
LocalFilters.kernel_range(start, stop)
+B_inds = indices(B)
source
LocalFilters.nearestFunction
LocalFilters.nearest(T, x)

converts value x to the nearest value of type T. By default, the result is given by convert(T,x) unless T is floating-point type and x is integer in which case the result is given by round(T,x).

This method may be extended for foreign types to implement other conversions.

source
LocalFilters.replicateFunction
LocalFilters.replicate(NTuple{N}, val)

yields the N-tuple (val, val, ...).

LocalFilters.replicate(NTuple{N,T}, val)

yields the N-tuple (x, x,...) where x is val converted to type T.

See LocalFilters.Yields.

source
LocalFilters.kernel_rangeFunction
LocalFilters.kernel_range(start, stop)
 LocalFilters.kernel_range(rng)
-LocalFilters.kernel_range(dim)

yield an Int-valued unit range based on first and last indices start and stop, unit range rng, or dimension length dim. In the case of a given dimension length, a centered range of this length is returned (for even lengths, the same conventions as in fftshift are used).

See LocalFilters.kernel and LocalFilters.centered.

source
+LocalFilters.kernel_range(dim)

yield an Int-valued unit range based on first and last indices start and stop, unit range rng, or dimension length dim. In the case of a given dimension length, a centered range of this length is returned (for even lengths, the same conventions as in fftshift are used).

See LocalFilters.kernel and LocalFilters.centered.

source diff --git a/dev/search_index.js b/dev/search_index.js index ef55459..1dc16d2 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"neighborhoods/#Neighborhoods,-structuring-elements,-and-kernels","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"","category":"section"},{"location":"neighborhoods/#Definitions","page":"Neighborhoods, structuring elements, and kernels","title":"Definitions","text":"","category":"section"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"Neighborhoods (a.k.a. structuring elements in mathematical morphology) and kernels are central concepts in LocalFilters. The neighborhood defines which values are involved in a local operation for each output value of the filter. Neighborhoods are assumed to be shift invariant but may have any support shape and may have embedded weights (e.g., to implement local convolution). In this latter case, they are called kernels.","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"In LocalFilters, a filtering operation, say","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"dst = filter(A, B)","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"involves, at each index i of a source array A, the values A[i±k] of A for all indices k of B and where:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"i ± k = i + k for operations like correlations where A and B can both be accessed in forward order of their indices (which is generally faster);\ni ± k = i - k for operations like convolutions where one of A or B must be accessed in reverse order of its indices (which is generally slower).","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"In LocalFilters, the following terminology is used for B:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"B is called a neighborhood or a structuring element for mathematical morphology operations (see Section Non-linear morphological filters) if its purpose is to define the indices in the source relatively to a given index in the destination. Such neighborhoods are represented by arrays with Boolean entries (true where entries are part of the neighborhood) and with as many dimensions as the source array A.\nB is called an hyper-rectangular box if it is a sliding window whose edges are aligned with the Cartesian axes of the array. Such simple neighborhoods are like the previous ones but with all entries equal to true, they are most efficiently represented by fast uniform arrays like FastUniformArray{Bool,N} instances from the StructuredArrays package. Another advantage of hyper-rectangular boxes is that they define a separable structuring element which may be exploited for very fast morphological operations by the van Herk-Gil-Werman algorithm whose numerical complexity does not depend on the size of the neighborhood (see localfilter!).\nB is called a kernel when its values are combined by the filter with those of the source. This is typically the case of discrete convolutions and correlations. Kernels are represented by AbstractArray{T,N} instances, with T the numerical type of the weights and N the number of dimensions.","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"From the user point of view, B whether it is a neighborhood, a structuring element, an hyper-rectangular box, a sliding window, or a kernel is thus just an array (usually of small size) with the same number of dimensions as the source array A","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"In addition, B may have arbitrary index offsets, to make it centered for example. For hyper-rectangular neighborhoods, FastUniformArray instances (from the StructuredArrays package) may directly have custom index offsets. For other neighborhoods and kernels, offset arrays (from the OffsetArrays package) can be used to implement such offsets.","category":"page"},{"location":"neighborhoods/#Simple-rules-for-specifying-neighborhoods-and-kernels","page":"Neighborhoods, structuring elements, and kernels","title":"Simple rules for specifying neighborhoods and kernels","text":"","category":"section"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"To facilitate specifying N-dimensional neighborhoods and kernels in the filters provided by LocalFilters, the following rules are applied to convert argument B to a suitable neighborhood or kernel:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"If B is an N-dimensional array, it is used unchanged. Hyper-rectangular boxes or symmetries may however be automatically detected to choose the fastest filtering method.\nIf B is a 2-tuple ofN-dimensional Cartesian indices, say, (I_first,I_last) each of type CartesianIndex{N}, then the corresponding neighborhood is an hyper-rectangular box whose first and last indices are I_first and I_last.\nIf B is a N-dimensional Cartesian range of type of type CartesianIndices{N}, then the corresponding neighborhood is an hyper-rectangular box whose first and last indices are given by this Cartesian range.\nIf B is an integer or an integer-valued unit range, it is interpreted as the length or the range of indices of the neighborhood along each dimension. In the case of a simple length, say, len, the index range of the neighborhood will be approximately centered using the same conventions as for fftshift: -i:i for odd lengths and -i:i-1 for even ones and with i = len ÷ 2.\nOtherwise B may be specified as a N-tuple of lengths or ranges (the two can be mixed), one for each dimension and where lengths are interpreted as in the previous case.","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"Note that all these cases except the first one correspond to hyper-rectangular boxes. The default neighborhood is B = 3 which corresponds to a centered hyper-rectangular sliding window of width 3 in each of its dimensions.","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"This assumed conversion may be explicitly performed by calling the kernel method:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"kernel(Dims{N}, B)","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"yields the N-dimensional neighborhood or kernel corresponding to B. If the number N of dimensions can be inferred from B, argument Dims{N} can be omitted.","category":"page"},{"location":"neighborhoods/#Simple-operations-on-kernels","page":"Neighborhoods, structuring elements, and kernels","title":"Simple operations on kernels","text":"","category":"section"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"If B is an N-dimensional array representing a neighborhood or a kernel, its indices may be centered by calling the LocalFilters.centered method:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"C = LocalFilters.centered(B)","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"which yields an array C with the same dimensions and values as B but with offset axes. This even work for arrays already centered or with offset axes that are not centered. Note that this method follows the same conventions as for fftshift (explained above) and has thus a slightly different semantic than the OffsetArrays.centered method which assumes that the centered range of an even dimension 1-i:i instead of -i:i-1.","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"It may be convenient to reverse a kernel or a neighborhood, this is done by calling the reverse_kernel method:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"R = reverse_kernel(B)","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"which yields an array R with the same dimensions and values as B but such that R[i] = B[-i] for any index i such that -i is in-bounds for B. This may be useful to perform a discrete convolution by the kernel B by a discrete correlation by R which is usually faster. Note that the reverse_kernel method reverses the order of the values of B as the base reverse method but also negates the axis ranges.","category":"page"},{"location":"neighborhoods/#Hyper-balls","page":"Neighborhoods, structuring elements, and kernels","title":"Hyper-balls","text":"","category":"section"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"To build a neighborhood, or a structuring element that is a N-dimensional hyper-ball of radius r call:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"LocalFilters.ball(Dims{N}, r)","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"For instance:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"julia> B = LocalFilters.ball(Dims{2}, 3)\n7×7 Matrix{Bool}:\n 0 0 1 1 1 0 0\n 0 1 1 1 1 1 0\n 1 1 1 1 1 1 1\n 1 1 1 1 1 1 1\n 1 1 1 1 1 1 1\n 0 1 1 1 1 1 0\n 0 0 1 1 1 0 0\n","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"This neighborhood is however not geometrically centered (its first index is at Cartesian index (1,1)), to make it centered use LocalFilters.centered:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"julia> B = LocalFilters.centered(LocalFilters.ball(Dims{2}, 3))\n7×7 OffsetArray(::Matrix{Bool}, -3:3, -3:3) with eltype Bool with indices -3:3×-3:3:\n 0 0 1 1 1 0 0\n 0 1 1 1 1 1 0\n 1 1 1 1 1 1 1\n 1 1 1 1 1 1 1\n 1 1 1 1 1 1 1\n 0 1 1 1 1 1 0\n 0 0 1 1 1 0 0\n","category":"page"},{"location":"separable/#Efficient-separable-filters-for-associative-operations","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"","category":"section"},{"location":"separable/#Out-of-place-version","page":"Efficient separable filters for associative operations","title":"Out-of-place version","text":"","category":"section"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"The localfilter method may be called as:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"dst = localfilter([T=eltype(A),] A, dims, op, rngs [, w])","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"to apply the van Herk-Gil-Werman algorithm to filter array A along dimension(s) dims with (associative) binary operation op and contiguous structuring element(s) defined by the interval(s) rngs. Optional argument T is the element type of the result dst (by default T = eltype(A)). Optional argument w is a workspace array which is automatically allocated if not provided; otherwise, it must be a vector with the same element type as A (w is resized as needed by calling resize!).","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"Argument dims specifies along which dimension(s) of A the filter is to be applied, it can be a single integer, several integers or a colon : to specify all dimensions. Dimensions are processed in the order given by dims (the same dimension may appear several times) and there must be a matching interval in rngs to specify the structuring element (except that if rngs is a single interval, it is used for every dimension in dims). An interval is either an integer or an integer valued unit range in the form kmin:kmax (an interval specified as a single integer, say k, is the same as specifying k:k).","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"Assuming a mono-dimensional array A, the single filtering pass:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"localfilter!(dst, A, :, op, rng)","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"amounts to computing:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"dst[j] = A[j-kmax] ⋄ A[j-kmax+1] ⋄ A[j-kmax+2] ⋄ ... ⋄ A[j-kmin]","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"for all j ∈ [firstindex(A):lastindex(A)], with x ⋄ y = op(x, y), kmin = first(rng) and kmax = last(rng). Note that if rng = k:k or rng = k with k an integer, the result of the filter is to operate a simple shift by k along the corresponding dimension and has no effects if k = 0. This can be exploited to not filter some dimension(s).","category":"page"},{"location":"separable/#In-place-version","page":"Efficient separable filters for associative operations","title":"In-place version","text":"","category":"section"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"The localfilter! method implements the in-place version of the van Herk-Gil-Werman algorithm:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"localfilter!([dst = A,] A, dims, op, rngs [, w]) -> dst","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"overwrites the contents of dst with the result of the filter and returns dst. The destination array dst must have the same indices as the source A (that is, axes(dst) == axes(A) must hold). If dst is not specified or if dst is A, the operation is performed in-place.","category":"page"},{"location":"separable/#Examples","page":"Efficient separable filters for associative operations","title":"Examples","text":"","category":"section"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"The in-place morphological erosion (local minimum) of the array A on a centered structuring element of width 7 in every dimension can be done by:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"localfilter!(A, :, min, -3:3)","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"As said before, index interval 0 may be specified to do nothing along the corresponding dimension. For instance, assuming A is a three-dimensional array:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"localfilter!(A, :, max, (-3:3, 0, -4:4))","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"overwrites A by its morphological dilation (i.e. local maximum) in a centered local neighborhood of size 7×1×9 (nothing is done along the second dimension). The same result may be obtained with:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"localfilter!(A, (1,3), max, (-3:3, -4:4))","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"where the second dimension is omitted from the list of dimensions.","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"The local average of the two-dimensional array A on a centered moving window of size 11×11 can be computed as:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"localfilter(A, :, +, (-5:5, -5:5))*(1/11^2)","category":"page"},{"location":"separable/#Efficiency-and-restrictions","page":"Efficient separable filters for associative operations","title":"Efficiency and restrictions","text":"","category":"section"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"The van Herk-Gil-Werman algorithm is very fast for rectangular structuring elements. It takes at most 3 operations to filter an element along a given dimension whatever the width p of the considered local neighborhood. For N-dimensional arrays, the algorithm requires only 3N operations per element instead of p^N - 1 operations for a naive implementation. This however requires to make a pass along each dimension so memory page faults may reduce the performances. This is somewhat attenuated by the fact that the algorithm can be applied in-place. For efficient multi-dimensional out-of-place filtering, it is recommended to make the first pass with a fresh destination array and then all other passes in-place on the destination array.","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"To apply the van Herk-Gil-Werman algorithm, the structuring element must be separable along the dimensions and its components must be contiguous. In other words, the algorithm is only applicable for N-dimensional rectangular neighborhoods, so-called hyperrectangles. The structuring element may however be off-centered by arbitrary offsets along each dimension.","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"To take into account boundary conditions (for now, only nearest neighbor is implemented) and allow for in-place operation, the algorithm allocates a workspace array.","category":"page"},{"location":"separable/#References","page":"Efficient separable filters for associative operations","title":"References","text":"","category":"section"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"Marcel van Herk, \"A fast algorithm for local minimum and maximum filters on rectangular and octagonal kernels\" in Pattern Recognition Letters 13, 517-521 (1992).\nJoseph Gil and Michael Werman, \"Computing 2-D Min, Median, and Max Filters\" in IEEE Transactions on Pattern Analysis and Machine Intelligence 15, 504-507 (1993).","category":"page"},{"location":"linear/#Linear-filters","page":"Linear filters","title":"Linear filters","text":"","category":"section"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"LocalFilters provides a few linear filters: localmean or localmean! to compute the mean of values in a neighborhood, convolve or convolve! to compute the discrete convolution of an array by a kernel, and correlate or correlate! to compute the discrete correlation of an array by a kernel.","category":"page"},{"location":"linear/#Local-mean","page":"Linear filters","title":"Local mean","text":"","category":"section"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"The localmean method yields the local mean of an array A in a neighborhood B:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"dst = localmean(A, B=3)","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"The result dst is an array similar to A. See Section Simple rules for specifying neighborhoods and kernels for the interpretation of B.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"The in-place version localmean! may be used to avoid allocations:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"localmean!(dst, A, B=3)","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which overwrites dst with the local mean of A in the neighborhood defined by B and returns dst.","category":"page"},{"location":"linear/#Discrete-convolution","page":"Linear filters","title":"Discrete convolution","text":"","category":"section"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Call the convolve method as:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"dst = convolve(A, B)","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"to compute the discrete convolution of array A by the kernel defined by B. The result dst is an array similar to A.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Using indices(A) to denote the set of valid indices for array A and assuming B is an array of values, the discrete convolution of A by B writes:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"for i ∈ indices(A)\n v = zero(T)\n @inbounds for k ∈ indices(B) ∩ (i - indices(A))\n v += A[i-k]*B[k]\n end\n dst[i] = v\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"with T a suitable element type for the result (see Section Element type of the result below) and where indices(B) ∩ (i - indices(A)) denotes the subset of indices k such that k ∈ indices(B) and i - k ∈ indices(A) and thus for which B[k] and A[i-k] are valid.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Following the conventions in localfilter!, the discrete convolution can also be expressed as:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"for i ∈ indices(A)\n v = zero(T)\n @inbounds for j ∈ indices(A) ∩ (i - indices(B))\n v += A[j]*B[i-j]\n end\n dst[i] = v\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"If the kernel B is an array of Booleans, the discrete convolution is computed as:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"for i ∈ indices(A)\n v = zero(T)\n @inbounds for j ∈ indices(A) ∩ (i - indices(B))\n if B[i-j]\n v += A[j]\n end\n end\n dst[i] = v\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which amounts to computing the local sum of the values of A in the neighborhood defined by the true entries of B.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"The in-place version convolve! may be used to avoid allocations:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"convolve!(dst, A, B)","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which overwrites dst with the discrete convolution of A by the kernel B and returns dst.","category":"page"},{"location":"linear/#Discrete-correlation","page":"Linear filters","title":"Discrete correlation","text":"","category":"section"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Call the correlate method as:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"dst = correlate(A, B)","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"to compute the discrete correlation of array A by the kernel defined by B. The result dst is an array similar to A.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Using indices(A) to denote the set of valid indices for array A and assuming B is an array of values, the discrete correlation of A by B writes:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"for i ∈ indices(A)\n v = zero(T)\n @inbounds for k ∈ indices(B) ∩ (indices(A) - i)\n v += A[i+k]*B[k]\n end\n dst[i] = v\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"with T a suitable element type for the result (see Section Element type of the result below) and where indices(B) ∩ (indices(A) - i) denotes the subset of indices k such that k ∈ indices(B) and i + k ∈ indices(A) and thus for which B[k] and A[i+k] are valid.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Following the conventions in localfilter!, the discrete correlation can also be expressed as:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"for i ∈ indices(A)\n v = zero(T)\n @inbounds for j ∈ indices(A) ∩ (indices(B) + i)\n v += A[j]*B[j-i]\n end\n dst[i] = v\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"If the kernel B is an array of Booleans, the discrete correlation is computed as:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"for i ∈ indices(A)\n v = zero(T)\n @inbounds for j ∈ indices(A) ∩ (indices(B) + i)\n v += A[j]\n end\n dst[i] = v\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which amounts to computing the local sum of the values of A in the neighborhood defined by the true entries of B.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"The in-place version correlate! may be used to avoid allocations:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"correlate!(dst, A, B)","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which overwrites dst with the discrete correlation of A by the kernel B and returns dst.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"SInce accessing the indices of A and B in the same order is generally faster (e.g. it is easier to optimize via loop vectorization), the discrete convolution convolve(A,B) of A by B may be computed by:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"correlate(A, reverse_kernel(B))","category":"page"},{"location":"linear/#Element-type-of-the-result","page":"Linear filters","title":"Element type of the result","text":"","category":"section"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Choosing a suitable element type for the result may be tricky if the entries of the source array A and of the kernel B have different types or have units.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"For example, a suitable element type T for the result of the convolution or correlation of A by B is given by:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"T = let a = oneunit(eltype(A)), b = oneunit(eltype(B)), c = a*b\n typeof(c + c)\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which is the type of the sum of the element-wise product of the entries of A and B.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"For the local mean, a similar reasoning yields:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"T = let a = oneunit(eltype(A)), b = oneunit(eltype(B)), c = a*b\n typeof((c + c)/(b + b))\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which is the type of the sum of the element-wise product of the entries of A and B divided by the sum of the entries in B (the so-called weights).","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"These rules are the ones used by the out-of-place versions of the linear filters of LocalFilter when the destination is not provided.","category":"page"},{"location":"nonlinear/#Non-linear-filters","page":"Non-linear filters","title":"Non-linear filters","text":"","category":"section"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"LocalFilters provides a number of non-linear filters such as the bilateral filter and mathematical morphology filters. The latter are described in the Section Non-linear morphological filters.","category":"page"},{"location":"nonlinear/#The-bilateral-filter","page":"Non-linear filters","title":"The bilateral filter","text":"","category":"section"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"Applying the bilateral filter on array A writes:","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"bilateralfilter([T=float(eltype(A)),] A, F, G, ...)","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"Argument F specifies how to smooth the differences in values. It may be function which takes two values from A as arguments and returns a nonnegative weight. It may be a real which is assumed to be the standard deviation of a Gaussian.","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"Arguments G, ... specify the settings of the distance filter for smoothing differences in coordinates. There are several possibilities:","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"G, ... can be a single argument specifying a kernel (see Section Simple rules for specifying neighborhoods and kernels).\nArgument G may be a function taking as argument the Cartesian index of the coordinate differences and returning a nonnegative weight. Argument G may also be a real specifying the standard deviation of the Gaussian used to compute weights. Subsequent arguments ... are to specify the neighborhood where to apply the distance filter function, they can be anything that may defined a neighborhood (again see Section Simple rules for specifying neighborhoods and kernels). If a standard deviation σ is specified for G with no subsequent arguments, a default window of radius 3σ is assumed.","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"Optional argument T can be used to force the element type used for (most) computations. This argument is needed if the element type of A is not a real.","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"See bilateralfilter! for an in-place version of this function.","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"bilateralfilter!([T=float(eltype(A)),] dst, A, F, G, ...) -> dst","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"overwrites dst with the result of applying the bilateral filter on array A and returns dst.","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"See bilateralfilter for a description of the other arguments than dst.","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"See wikipedia for a description of the bilateral filter.","category":"page"},{"location":"morphology/#Non-linear-morphological-filters","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"","category":"section"},{"location":"morphology/#Basic-morphological-operations","page":"Non-linear morphological filters","title":"Basic morphological operations","text":"","category":"section"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"Erosion and dilation are the basic operations of mathematical morphology, they are implemented by methods erode and dilate:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"erode(A, R=3) -> Amin\ndilate(A, R=3) -> Amax","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"which respectively return the local minima Amin and the local maxima Amax of argument A in a structuring element defined by R. The notion of structuring element in mathematical morphology is equivalent to that of neighborhood in LocalFilters. The returned result is similar to A (same size and type). If R is not specified, a default hyper-rectangular moving window 3 samples wide in every dimension of A is used. If the structuring element R is a simple hyper-rectangular moving window, the much faster van Herk-Gil-Werman algorithm is used","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"The localextrema method combines these two operations in one call:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"localextrema(A, R=3) -> Amin, Amax","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"Calling localextrema is usually almost twice as fast as calling erode and dilate.","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"To avoid allocating new arrays, the methods erode!, dilate!, and localextrema! provide in-place versions which apply the operation to A with structuring element R and store the result in the provided arrays Amin and/or Amax:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"erode!(Amin, A, R=3) -> Amin\ndilate!(Amax, A, R=3) -> Amax\nlocalextrema!(Amin, Amax, A, R=3) -> Amin, Amax","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"If the structuring element R is a simple hyper-rectangular moving window, the much faster van Herk-Gil-Werman algorithm is used and the operation can be done in-place. That is, A and Amin can be the same arrays. In that case, the following syntax is allowed:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"erode!(A, R=3) -> A\ndilate!(A, R=3) -> A","category":"page"},{"location":"morphology/#Opening-and-closing-filters","page":"Non-linear morphological filters","title":"Opening and closing filters","text":"","category":"section"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"closing(A, R=3)\nopening(A, R=3)","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"respectively perform a closing or an opening of array A by the structuring element R. If R is not specified, a default hyper-rectangular moving window of size 3 in every dimension of A is used. A closing is a dilation followed by an erosion, whereas an opening is an erosion followed by a dilation.","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"The in-place versions are:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"closing!(dst, wrk, A, R=3) -> dst\nopening!(dst, wrk, A, R=3) -> dst","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"which perform the operation on the source A and store the result in destination dst using wrk as a workspace array. The 3 arguments dst, wrk, and A must be similar arrays; dst and A may be identical, but wrk must not be the same array as A or dst. The destination dst is returned.","category":"page"},{"location":"morphology/#Top-hat-and-bottom-hat-filters","page":"Non-linear morphological filters","title":"Top-hat and bottom-hat filters","text":"","category":"section"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"Methods top_hat and bottom_hat perform a summit/valley detection by applying a top-hat filter to an array. They are called as:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"top_hat(A, R[, S]) -> dst\nbottom_hat(A, R[, S]) -> dst","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"to yield the result of the filter applied to array A. Argument R defines the structuring element for the feature detection. Optional argument S specifies the structuring element for smoothing A prior to the top-/bottom-hat filter. If R and S are specified as the radii of the structuring elements, then S should be smaller than R. For instance:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"top_hat(bitmap, 3, 1)","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"may be used to detect text or lines in a bitmap image.","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"Methods LocalFilters.top_hat! and LocalFilters.bottom_hat! implement the in-place versions of these filters:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"top_hat!(dst, wrk, A, R[, S]) -> dst\nbottom_hat!(dst, wrk, A, R[, S]) -> dst","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"apply the top-/bottom-hat filter on the source A and store the result in the destination dst using wrk as a workspace array. The 3 arguments dst, wrk, and A must be similar but different arrays. The destination dst is returned.","category":"page"},{"location":"reference/#Reference","page":"Reference","title":"Reference","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"The following summarizes the documentation of types and methods provided by the LocalFilters package. This information is also available from the REPL by typing ? followed by the name of a method or a type.","category":"page"},{"location":"reference/#Index","page":"Reference","title":"Index","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"","category":"page"},{"location":"reference/#Simple-linear-filters","page":"Reference","title":"Simple linear filters","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"LocalFilters provides a number of shift-invariant linear filters.","category":"page"},{"location":"reference/","page":"Reference","title":"Reference","text":"correlate\ncorrelate!\nconvolve\nconvolve!\nlocalmean\nlocalmean!","category":"page"},{"location":"reference/#LocalFilters.correlate","page":"Reference","title":"LocalFilters.correlate","text":"correlate(A, B) -> dst\n\nyields the discrete correlation of the array A by the kernel defined by B. The result dst is an array similar to A.\n\nUsing Sup(A) to denote the set of valid indices for array A and assuming B is an array of numerical values, the discrete convolution of A by B writes:\n\nT = let x = oneunit(eltype(A))*oneunit(eltype(B)); typeof(x + x); end\ndst = similar(A, T)\nfor i ∈ Sup(A)\n v = zero(T)\n @inbounds for j ∈ Sup(A) ∩ (Sup(B) - i)\n v += A[j]*B[j-i]\n end\n dst[i] = v\nend\n\nwith T the type of the product of elements of A and B, and where Sup(A) ∩ (i - Sup(A)) denotes the subset of indices k such that k ∈ Sup(B) and i - k ∈ Sup(A) and thus for which B[k] and A[i-k] are valid.\n\nSee also correlate! and convolve.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.correlate!","page":"Reference","title":"LocalFilters.correlate!","text":"correlate!(dst, A, B) -> dst\n\noverwrites dst with the discrete convolution of A by the kernel B and returns dst.\n\nSee also correlate and localfilter!.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.convolve","page":"Reference","title":"LocalFilters.convolve","text":"convolve(A, B)\n\nyields the discrete convolution of array A by the kernel defined by B. The result dst is an array similar to A.\n\nUsing Sup(A) to denote the set of valid indices for array A and assuming B is an array of values, the discrete convolution of A by B writes:\n\nT = let x = oneunit(eltype(A))*oneunit(eltype(B)); typeof(x + x); end\nfor i ∈ Sup(A)\n v = zero(T)\n @inbounds for j ∈ Sup(B) ∩ (i - Sup(A))\n v += A[i-j]*B[j]\n end\n dst[i] = v\nend\n\nwith T the type of the product of elements of A and B, and where Sup(B) ∩ (i - Sup(A)) denotes the subset of indices k such that k ∈ Sup(B) and i - k ∈ Sup(A) and thus for which B[k] and A[i-k] are valid.\n\nSee also convolve! and localfilter!.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.convolve!","page":"Reference","title":"LocalFilters.convolve!","text":"convolve!(dst, A, B) -> dst\n\noverwrites dst with the discrete convolution of A by the kernel B and returns dst.\n\nSee also convolve and localfilter!.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.localmean","page":"Reference","title":"LocalFilters.localmean","text":"localmean(A, [ord=FORWARD_FILTER,] B=3; null=zero(eltype(A)))\n\nyields the local mean of A in a neighborhood defined by B. The result is an array similar to A. If B is not specified, the neighborhood is a hyper-rectangular sliding window of size 3 in every dimension. Otherwise, B may be specified as a Cartesian box, or as an array of booleans of same number of dimensions as A. If B is a single odd integer (as it is by default), the neighborhood is assumed to be a hyper-rectangular sliding window of size B in every dimension.\n\nKeyword null may be used to specify the value of the result where the sum of the weights in a local neighborhood is zero.\n\nSee also localmean! and localfilter!.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.localmean!","page":"Reference","title":"LocalFilters.localmean!","text":"localmean!(dst, A, [ord=FORWARD_FILTER,] B=3; null=zero(eltype(dst))) -> dst\n\noverwrites dst with the local mean of A in a neighborhood defined by B and returns dst.\n\nKeyword null may be used to specify the value of the result where the sum of the weights in the a neighborhood is zero.\n\nSee also localmean and localfilter!.\n\n\n\n\n\n","category":"function"},{"location":"reference/#Mathematical-morphology","page":"Reference","title":"Mathematical morphology","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"erode\nerode!\ndilate\ndilate!\nlocalextrema\nlocalextrema!\nclosing\nclosing!\nopening\nopening!\ntop_hat\nLocalFilters.top_hat!\nbottom_hat\nLocalFilters.bottom_hat!","category":"page"},{"location":"reference/#LocalFilters.erode","page":"Reference","title":"LocalFilters.erode","text":"erode(A, [ord=FORWARD_FILTER,] B=3) -> Amin\n\nyields the erosion of A by the structuring element defined by B. The erosion is the array of local minima of A. The returned result Amin is similar to A (same size and type).\n\nIf B is not a kernel (that is, if B is not an array or is an instance of CartesianIndices), kernel(Dims{N},B) is called to build a kernel with N the number of dimensions of A.\n\nIf the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used.\n\nAn erosion is one of the most basic operations of mathematical morphology. See erode! for an in-place version of the method, dilate for retrieving the local maxima, and localextrema for performing an erosion and a dilation in a single pass.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.erode!","page":"Reference","title":"LocalFilters.erode!","text":"erode!(Amin, A, [ord=FORWARD_FILTER,] B=3) -> Amin\n\noverwrites Amin with the erosion of the array A by the structuring element defined by B and returns Amin.\n\nIf the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used and the operation can be done in-place. That is, A and Amin can be the same arrays. In that case, the following syntax is allowed:\n\nerode!(A, [ord=FORWARD_FILTER,] B=3) -> A\n\nSee erode for an out-of-place version and for more information.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.dilate","page":"Reference","title":"LocalFilters.dilate","text":"dilate(A, [ord=FORWARD_FILTER,] B=3) -> Amax\n\nyields the dilation of A by the structuring element defined by B. The dilation is the array of local maxima of A. The returned result Amax is similar to A (same size and type).\n\nIf B is not a kernel (that is, if B is not an array or is an instance of CartesianIndices), kernel(Dims{N},B) is called to build a kernel with N the number of dimensions of A.\n\nIf the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used.\n\nA dilation is one of the most basic operations of mathematical morphology. See dilate! for an in-place version of the method, erode for retrieving the local minima, and localextrema for performing an erosion and a dilation in a single pass.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.dilate!","page":"Reference","title":"LocalFilters.dilate!","text":"dilate!(Amax, A, [ord=FORWARD_FILTER,] B=3) -> Amax\n\noverwrites Amax with a dilation of the array A by the structuring element defined by B and returns Amax.\n\nIf the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used and the operation can be done in-place. That is, A and Amin can be the same arrays. In that case, the following syntax is allowed:\n\ndilate!(A, [ord=FORWARD_FILTER,] B=3) -> A\n\nSee dilate for an out-of-place version and for more information.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.localextrema","page":"Reference","title":"LocalFilters.localextrema","text":"localextrema(A, [ord=FORWARD_FILTER,] B=3) -> Amin, Amax\n\nyields the results of performing an erosion and a dilation of A by the structuring element defined by B in a single pass. Calling this method is usually almost twice as fast as calling erode and dilate.\n\nSee localextrema! for an in-place version of the method, and erode or dilate for a description of these operations.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.localextrema!","page":"Reference","title":"LocalFilters.localextrema!","text":"localextrema!(Amin, Amax, A, [ord=FORWARD_FILTER,] B=3) -> Amin, Amax\n\noverwrites Amin and Amax with, respectively, an erosion and a dilation of the array A by the structuring element defined by B in a single pass.\n\nSee localextrema for an out-of-place version for more information.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.closing","page":"Reference","title":"LocalFilters.closing","text":"closing(A, [ord=FORWARD_FILTER,] B=3) -> dst\n\nyields a closing of array A by the structuring element defined by B. A closing is a dilation followed by an erosion. The result dst is an array similar to A.\n\nSee closing! for an in-place version of the method, opening for a related filter, and erode or dilate for a description of these operations.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.closing!","page":"Reference","title":"LocalFilters.closing!","text":"closing!(dst, wrk, A, B=3) -> dst\n\noverwrites dst with the result of a closing of A by the structuring element defined by B using wrk as a workspace array. The arguments dst, wrk, and A must be similar arrays, dst and A may be identical, but wrk must not be the same array as A or dst. The destination dst is returned.\n\nSee closing for a description of this kind of filter and for the meaning of the arguments.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.opening","page":"Reference","title":"LocalFilters.opening","text":"opening(A, B=3) -> dst\n\nyields an opening of array A by the structuring element defined by B. An opening is an erosion followed by a dilation. The result dst is an array similar to A.\n\nSee opening! for an in-place version of the method, closing for a related filter, and erode or dilate for a description of these operations.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.opening!","page":"Reference","title":"LocalFilters.opening!","text":"opening!(dst, wrk, A, B=3) -> dst\n\noverwrites dst with the result of an opening of A by the structuring element defined by B using wrk as a workspace array. The arguments dst, wrk, and A must be similar arrays, dst and A may be identical, but wrk must not be the same array as A or dst. The destination dst is returned.\n\nSee opening for a description of this kind of filter and for the meaning of the arguments.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.top_hat","page":"Reference","title":"LocalFilters.top_hat","text":"top_hat(A, B=3 [, C]) -> dst\n\nperforms a summit detection by applying a top-hat filter to array A using the structuring element defined by B for the feature detection. Top-hat filtering is equivalent to:\n\ndst = A .- opening(A, B)\n\nOptional argument C specifies the structuring element for smoothing A prior to top-hat filtering. If B and C are specified as the radii of the structuring elements, then C should be smaller than B. For instance:\n\ntop_hat(bitmap, 3, 1)\n\nmay be used to detect text or lines in a bitmap image.\n\nSee bottom_hat for a related operation, LocalFilters.top_hat! for an in-place version.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.top_hat!","page":"Reference","title":"LocalFilters.top_hat!","text":"LocalFilters.top_hat!(dst, wrk, A, [ord=FORWARD_FILTER,] B=3) -> dst\n\noverwrites dst with the result of a top-hat filter applied to A with structuring element B, and using wrk as a workspace whose contents is not preserved. The arguments A, dst, and wrk must be similar but different arrays. The destination dst is returned.\n\nSee also top_hat for more details.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.bottom_hat","page":"Reference","title":"LocalFilters.bottom_hat","text":"bottom_hat(A, B=3 [, C]) -> dst\n\nperforms a valley detection by applying a bottom-hat filter to array A using the structuring element defined by B for the feature detection. Bottom-hat filtering is equivalent to:\n\ndst = closing(A, B) .- A\n\nOptional argument C specifies the structuring element for smoothing A prior to bottom-hat filtering. If B and C are specified as the radii of the structuring elements, then C should be smaller than B.\n\nSee top_hat for a related operation, LocalFilters.bottom_hat! for an in-place version.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.bottom_hat!","page":"Reference","title":"LocalFilters.bottom_hat!","text":"LocalFilters.bottom_hat!(dst, wrk, A, B=3) -> dst\n\noverwrites dst with the result of a bottom-hat filter applied to A with structuring element B and optional smoothing element C. Argument wrk is a workspace array whose contents is not preserved. The arguments A, dst, and wrk must be similar but different arrays. The destination dst is returned.\n\nSee also bottom_hat for more details.\n\n\n\n\n\n","category":"function"},{"location":"reference/#Other-non-linear-filters","page":"Reference","title":"Other non-linear filters","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"bilateralfilter\nbilateralfilter!","category":"page"},{"location":"reference/#LocalFilters.bilateralfilter","page":"Reference","title":"LocalFilters.bilateralfilter","text":"bilateralfilter([T=float(eltype(A)),] A, F, [ord=FORWARD_FILTER,] G...=3)\n\nyields the result of applying the bilateral filter on array A.\n\nArgument F specifies how to smooth the differences in values. It can be:\n\na function, say f, which is called as f(A[i],A[j]) to yield a nonnegative weight for i the central index and j the index in a nearby position;\na positive real, say σ, which is assumed to be the standard deviation of a Gaussian.\n\nArguments G, ... specify the settings of the distance filter for smoothing differences in coordinates. There are several possibilities:\n\nG... = wgt an array of nonnegative weights or of booleans. The axes of wgt must have offsets so that the zero index is part of the indices of wgt.\nG... = f, w with f a function and w any kind of argument that can be used to build a window win specifying the extension of the neighborhood. The value of the distance filter will be max(f(i),0) for all Cartesian index i of win such that win[i] is true. See kernel for the different ways to specify a window.\nG... = σ or , G... = σ, w with σ a positive real assumed to be the standard deviation of a Gaussian function and w any kind of argument that can be used to build a window win specifying the extension of the neighborhood. If w is not specified, a default window of size ±3σ is assumed.\n\nOptional argument T can be used to force the element type of the result. This argument is needed if the element type of A is not a real.\n\nSee bilateralfilter! for an in-place version of this function and see Wikipedia for a description of the bilateral filter.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.bilateralfilter!","page":"Reference","title":"LocalFilters.bilateralfilter!","text":"bilateralfilter!(dst, A, F, [ord=FORWARD_FILTER,] G...) -> dst\n\noverwrites dst with the result of applying the bilateral filter on array A and returns dst.\n\nSee bilateralfilter for a description of the other arguments than dst and see Wikipedia for a description of the bilateral filter.\n\n\n\n\n\n","category":"function"},{"location":"reference/#Methods-to-build-local-filters","page":"Reference","title":"Methods to build local filters","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"localfilter\nlocalfilter!","category":"page"},{"location":"reference/#LocalFilters.localfilter","page":"Reference","title":"LocalFilters.localfilter","text":"localfilter([T=eltype(A),] A, dims, op, [ord=FORWARD_FILTER,]\n rngs[, wrk]) -> dst\n\nyields the result of applying van Herk-Gil-Werman algorithm to filter array A along dimension(s) dims with (associative) binary operation op and contiguous structuring element(s) defined by the interval(s) rngs. Optional argument wrk is a workspace array with elements of type T which is automatically allocated if not provided; otherwise, it must be a vector with the same element type as A and it is resized as needed (by calling the resize! method). The optional argument T allows to specify another type of element than eltype(A) for the result.\n\nArgument dims specifies along which dimension(s) of A the filter is to be applied, it can be a single integer, a tuple of integers, or a colon : to apply the operation to all dimensions. Dimensions are processed in the order given by dims (the same dimension may appear several times) and there must be a matching interval in rngs to specify the structuring element (except that if rngs is a single interval, it is used for every dimension in dims). An interval is either an integer or an integer valued unit range in the form kmin:kmax. An interval specified as a single integer yields an approximately centered range og this length.\n\nAssuming a mono-dimensional array A, the single filtering pass:\n\ndst = localfilter(A, :, op, rng)\n\namounts to computing (assuming forward ordering):\n\ndst[j] = A[i+kmin] ⋄ A[i+kmin+1] ⋄ ... ⋄ A[i+kmax-1] ⋄ A[i+kmax]\n\nfor all j ∈ axes(dst,1), with x ⋄ y = op(x, y), kmin = first(rng) and kmax = last(rng). Note that if kmin = kmax = k, the result of the filter is to operate a simple shift by k along the corresponding dimension and has no effects if k = 0. This can be exploited to not filter some dimension(s).\n\nFlat boundary conditions are assumed for A[i+k] in the above formula.\n\nExamples\n\nThe morphological erosion (local minimum) of the array A on a centered structuring element of width 7 in every dimension can be obtained by:\n\nlocalfilter(A, :, min, -3:3)\n\nIndex interval 0:0 may be specified to do nothing along the corresponding dimension. For instance, assuming A is a three-dimensional array:\n\nlocalfilter(A, :, max, (-3:3, 0:0, -4:4))\n\nyields the morphological dilation (i.e. local maximum) of A in a centered local neighborhood of size 7×1×9 (nothing is done along the second dimension). The same result may be obtained with:\n\nlocalfilter(A, (1,3), max, (-3:3, -4:4))\n\nwhere the second dimension is omitted from the list of dimensions.\n\nThe local average of the two-dimensional array A on a centered moving window of size 11×11 can be computed as:\n\nlocalfilter(A, :, +, (-5:5, -5:5))*(1/11)\n\nSee localfilter! for an in-place version of the method.\n\n\n\n\n\nlocalfilter(A, args...; kwds...) -> dst\n\nout of place version of localfilter! which is equivqlent to:\n\nlocalfilter!(similar(A), A, args...; kwds...)\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.localfilter!","page":"Reference","title":"LocalFilters.localfilter!","text":"localfilter!([dst = A,] A, dims, op, [ord=FORWARD_FILTER,] rngs[, wrk])\n\noverwrites the contents of dst with the result of applying van Herk-Gil-Werman algorithm to filter array A along dimension(s) dims with (associative) binary operation op and contiguous structuring element(s) defined by the interval(s) rngs and using optional argument wrk as a workspace array. The destination dst must have the same indices as the source A (that is, axes(dst) == axes(A)). Operation may be done in-place and dst and A can be the same; this is the default behavior if dst is not specified.\n\nSee localfilter for a full description of the method.\n\nThe in-place morphological erosion (local minimum) of the array A on a centered structuring element of width 7 in every dimension can be obtained by:\n\nlocalfilter!(A, :, min, -3:3)\n\n\n\n\n\nlocalfilter!(dst, A, [ord = FORWARD_FILTER,] B, initial,\n update::Function, final::Function = identity) -> dst\n\noverwrites the destination dst with the result of a local filter applied to the source A, on a relative neighborhood defined by B, and implemented by initial, update, and final. The initial argument may be a function or not. The purpose of these latter arguments is explained by the following pseudo-codes implementing the local filtering. If ord = FORWARD_FILTER:\n\n@inbounds for i ∈ indices(dst)\n v = initial isa Function ? initial(A[i]) : initial\n for j ∈ indices(A) ∩ (indices(B) + i)\n v = update(v, A[j], B[j-i])\n end\n dst[i] = final(v)\nend\n\nelse if ord = REVERSE_FILTER:\n\n@inbounds for i ∈ indices(dst)\n v = initial isa Function ? initial(A[i]) : initial\n for j ∈ indices(A) ∩ (i - indices(B))\n v = update(v, A[j], B[i-j])\n end\n dst[i] = final(v)\nend\n\nwhere indices(A) denotes the range of indices of any array A while indices(B) + i and i - indices(B) respectively denote the set of indices j such that j - i ∈ indices(B) and i - j ∈ indices(B). In other words, j ∈ indices(A) ∩ (i - indices(B)) means all indices j such that j ∈ indices(A) and i - j ∈ indices(B) so that A[j] and B[i-j] are in-bounds.\n\nIf initial is a function, the initial value of the state variable v in the above pseudo-codes is given by v = initial(A[i]) with i the current index in dst. Hence, in that case, the destination array dst and the source array src must have the same axes.\n\nFor example, implementing a local minimum filter (that is, an erosion), is as simple as:\n\nlocalfilter!(dst, A, ord, B, typemax(eltype(A)),\n (v,a,b) -> ifelse(b, min(v,a), v))\n\nAs another example, implementing a convolution by B writes:\n\nT = promote_type(eltype(A), eltype(B))\nlocalfilter!(dst, A, ord, B, zero(T), (v,a,b) -> v + a*b)\n\n\n\n\n\n","category":"function"},{"location":"reference/#Constants","page":"Reference","title":"Constants","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"FORWARD_FILTER\nREVERSE_FILTER","category":"page"},{"location":"reference/#LocalFilters.FORWARD_FILTER","page":"Reference","title":"LocalFilters.FORWARD_FILTER","text":"FORWARD_FILTER\n\nis an exported constant object used to indicate forward ordering of indices in local filter operations. It can be called as:\n\nFORWARD_FILTER(i, j) -> j - i\n\nto yield the index in the filter kernel. See also REVERSE_FILTER for reverse ordering and LocalFilters.localindices for building a range of valid indices j.\n\n\n\n\n\n","category":"constant"},{"location":"reference/#LocalFilters.REVERSE_FILTER","page":"Reference","title":"LocalFilters.REVERSE_FILTER","text":"REVERSE_FILTER\n\nis an exported constant object used to indicate reverse ordering of indices in local filter operations. It can be called as:\n\nREVERSE_FILTER(i, j) -> i - j\n\nto yield the index in the filter kernel. See also FORWARD_FILTER for forward ordering and LocalFilters.localindices for building a range of valid indices j.\n\n\n\n\n\n","category":"constant"},{"location":"reference/#Neighborhoods-and-kernels","page":"Reference","title":"Neighborhoods and kernels","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"kernel\nreverse_kernel\nstrel\nLocalFilters.ball\nLocalFilters.centered","category":"page"},{"location":"reference/#LocalFilters.kernel","page":"Reference","title":"LocalFilters.kernel","text":"kernel([Dims{N},] args...)\n\nyields an N-dimensional abstract array built from args... and which can be used as a kernel in local filtering operations.\n\nIf args... is composed of N integers and/or ranges or if it is an N-tuple of integers and/or ranges, a uniformly true abstract array is returned whose axes are specified by args.... Each integer argument is converted in a centered unit range of this length (see LocalFilters.kernel_range).\nIf Dims{N} is provided and args... is a single integer or range, it is interpreted as being the same for all dimensions of an N-dimensional kernel. For example, kernel(Dims{3},5) yields a 3-dimensional uniformly true array with index range -2:2 in every dimension.\nIf args... is 2 Cartesian indices or a 2-tuple of Cartesian indices, say I_first and I_last, a uniformly true abstract array is returned whose first and last indices are I_first and I_last.\nIf args... is a Cartesian range, say R::CartesianIndices{N}, a uniformly true abstract array is returned whose axes are given by R.\nIf args... is an abstract array of any other type than an instance of CartesianIndices, it is returned unchanged.\n\nOptional leading argument Dims{N} can be specified to assert the number of dimensions of the result or to provide the number of dimensions when it cannot be inferred from the arguments. For example, when args... is a single integer length or range which should be interpreted as being the same for all dimensions.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.reverse_kernel","page":"Reference","title":"LocalFilters.reverse_kernel","text":"reverse_kernel(A) -> B\n\nyields an array B such that B[i] = A[-i] holds for all indices i such that -i is a valid index in A.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.strel","page":"Reference","title":"LocalFilters.strel","text":"strel(T, A)\n\nyields a structuring element suitable for mathematical morphology operations. The result is an array whose elements have type T (which can be Bool or a floating-point type). Argument A can be a hyper-rectangular Cartesian sliding window or an array with boolean elements.\n\nIf T is a floating-point type, then the result is a so-called flat structuring element whose coefficients are zero(T) inside the shape defined by A and -T(Inf) elsewhere.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.ball","page":"Reference","title":"LocalFilters.ball","text":"LocalFilters.ball(Dims{N}, r)\n\nyields a boolean mask which is a N-dimensional array with all dimensions odd and equal and set to true where position is inside a N-dimensional ball of radius r.\n\nTo have a mask with centered index ranges, call:\n\nLocalFilters.centered(LocalFilters.ball(Dims{N}, r))\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.centered","page":"Reference","title":"LocalFilters.centered","text":"LocalFilters.centered(A) -> B\n\nyields an abstract array B sharing the entries of array A but with offsets on indices so that the axes of B are centered (for even dimension lengths, the same conventions as in fftshift are used).\n\nThis method is purposely not exported because it could introduce some confusions. For example OffsetArrays.centered is similar but has a slightly different semantic.\n\nArgument A can also be an index range (linear or Cartesian), in which case a centered index range of same size is returned.\n\nSee LocalFilters.kernel_range.\n\n\n\n\n\n","category":"function"},{"location":"reference/#Utilities","page":"Reference","title":"Utilities","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"Below are described non-exported types and methods that may be useful in the context of building local filters.","category":"page"},{"location":"reference/","page":"Reference","title":"Reference","text":"LocalFilters.Indices\nLocalFilters.Yields\nLocalFilters.localindices\nLocalFilters.nearest\nLocalFilters.replicate\nLocalFilters.kernel_range","category":"page"},{"location":"reference/#LocalFilters.Indices","page":"Reference","title":"LocalFilters.Indices","text":"LocalFilters.Indices(A...) -> indices\n\nyields a callable object that can be used to produce ranges of indices for each of the arrays A.... These ranges will all be of the same type: linear index ranges, if all arrays A... are vectors implementing fast linear indexing, Cartesian index ranges otherwise.\n\nThe returned object is similar to the eachindex method but specialized for a style of indexing, it can be used as indices(B...) to yield a suitable index range to access all the entries of array(s) B... which are any number of the A... specified when building the indices object. If B... consists in several arrays, they must have the same axes.\n\nCall:\n\nLocalFilters.Indices{S}()\n\nwith S = IndexLinear or S = IndexCartesian to specifically choose the indexing style.\n\n\n\n\n\n","category":"type"},{"location":"reference/#LocalFilters.Yields","page":"Reference","title":"LocalFilters.Yields","text":"f = LocalFilters.Yields(value)\nf = LocalFilters.Yields{V}(value)\n\nbuild a callable object f such that f(args...; kwds...) yields value whatever the arguments args... and the keywords kwds.... If type V is supplied, value is converted to that type.\n\n\n\n\n\n","category":"type"},{"location":"reference/#LocalFilters.localindices","page":"Reference","title":"LocalFilters.localindices","text":"LocalFilters.localindices(A_inds, ord, B_inds, i) -> J\n\nyields the subset J of all indices j such that:\n\nA[j] and B[ord(i,j)] = B[j-i] are in-bounds if ord = FORWARD_FILTER;\nA[j] and B[ord(i,j)] = B[i-j] are in-bounds if ord = REVERSE_FILTER;\n\nwith A and B arrays whose index ranges are given by A_inds and B_inds. To make the code agnostic to the ordering, use A[i] and B[ord(i,j)] to retrieve the values in A and B.\n\nIndex ranges A_inds and B_inds and index i must be of the same kind:\n\nlinear index ranges for A_inds and B_inds and linear index for i;\nCartesian index ranges for A_inds and B_inds and Cartesian index for i of same number of dimensions.\n\nConstructor LocalFilters.Indices may by used to build a callable object that retrieves the index ranges of A and B in a consistent way:\n\nindices = LocalFilters.Indices(A, B)\nA_inds = indices(A)\nB_inds = indices(B)\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.nearest","page":"Reference","title":"LocalFilters.nearest","text":"LocalFilters.nearest(T, x)\n\nconverts value x to the nearest value of type T. By default, the result is given by convert(T,x) unless T is floating-point type and x is integer in which case the result is given by round(T,x).\n\nThis method may be extended for foreign types to implement other conversions.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.replicate","page":"Reference","title":"LocalFilters.replicate","text":"LocalFilters.replicate(NTuple{N}, val)\n\nyields the N-tuple (val, val, ...).\n\nLocalFilters.replicate(NTuple{N,T}, val)\n\nyields the N-tuple (x, x,...) where x is val converted to type T.\n\nSee LocalFilters.Yields.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.kernel_range","page":"Reference","title":"LocalFilters.kernel_range","text":"LocalFilters.kernel_range(start, stop)\nLocalFilters.kernel_range(rng)\nLocalFilters.kernel_range(dim)\n\nyield an Int-valued unit range based on first and last indices start and stop, unit range rng, or dimension length dim. In the case of a given dimension length, a centered range of this length is returned (for even lengths, the same conventions as in fftshift are used).\n\nSee LocalFilters.kernel and LocalFilters.centered.\n\n\n\n\n\n","category":"function"},{"location":"#Introduction","page":"Introduction","title":"Introduction","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"The LocalFilters package provides local linear and non-linear filters for Julia.","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"The source code of LocalFilters is available on GitHub.","category":"page"},{"location":"#Table-of-contents","page":"Introduction","title":"Table of contents","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Pages = [\"generic.md\", \"neighborhoods.md\", \"linear.md\", \"nonlinear.md\",\n \"morphology.md\", \"separable.md\", \"reference.md\"]","category":"page"},{"location":"generic/#Generic-local-filters","page":"Generic local filters","title":"Generic local filters","text":"","category":"section"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"Most filters provided by the LocalFilters package are implemented by the generic localfilter! method.","category":"page"},{"location":"generic/#The-localfilter!-method","page":"Generic local filters","title":"The localfilter! method","text":"","category":"section"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"A local filtering operation can be performed by calling the localfilter! method:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"localfilter!(dst, A, B, initial, update, final)","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"where dst is the destination, A is the source, B defines the neighborhood, initial is a function or the initial value of the state variable, update is a function to update the state variable for each entry of the neighborhood, and final is a function to yield the local result of the filter given the final value of the state variable. The purposes of these parameters are explained by the following pseudo-code implementing the local filtering:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"@inbounds for i ∈ indices(dst)\n v = initial isa Function ? initial(A[i]) : initial\n for j ∈ indices(A) ∩ (indices(B) + i)\n v = update(v, A[j], B[j-i])\n end\n dst[i] = final(v)\nend","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"where indices(A) denotes the set of indices of A while indices(B) + i denotes the set of indices j such that j - i ∈ indices(B). In other words, j ∈ indices(A) ∩ (indices(B) + i) means all indices j such that j ∈ indices(A) and j - i ∈ indices(B), hence A[j] and B[j-i] are both in-bounds. In LocalFilters, indices i and j are Cartesian indices for multi-dimensional arrays, thus indices(A) is the analogous of CartesianIndices(A) in Julia in that case. For vectors, indices i and j are linear indices.","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"The behavior of the filter is fully determined by the neighborhood B (see Section Neighborhoods, structuring elements, and kernels) and by the other arguments to deal with the state variable v:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"initial may be a function, in which case the state variable is initially given by v = initial(A[i]); otherwise, initial is assumed to be the initial value of the state variable. If initial is a function, then dst and A must have the same axes.\nupdate(v, a, b) yields the updated state variable v given v, a = A[j], and b = B[j-i].\nfinal(v) yields the result of the filter given the state variable v at the end of the loop on the neighborhood. If not specified, final = identity is assumed.","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"The localfilter! method takes another optional argument ord::FilterOrdering to specify the ordering of the filter:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"localfilter!(dst, A, ord, B, initial, update, final)","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"By default, ord = FORWARD_FILTER which is implemented by the above pseudo-code and which corresponds to a correlation for a shift-invariant linear filter. The other possibility is ord = REVERSE_FILTER which corresponds to a convolution for a shift-invariant linear filter and which amounts to:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"@inbounds for i ∈ indices(dst)\n v = initial isa Function ? initial(A[i]) : initial\n for j ∈ indices(A) ∩ (i - indices(B))\n v = update(v, A[j], B[i-j])\n end\n dst[i] = final(v)\nend","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"If B is symmetric, in the sense that B[-j] = B[j] for any in-bounds j, both orderings yield the same result but FORWARD_FILTER is generally faster which is why it is used by default.","category":"page"},{"location":"generic/#Examples","page":"Generic local filters","title":"Examples","text":"","category":"section"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"Implementing a local minimum filter (that is, an erosion) with localfilter! is as simple as:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"dst = localfilter!(similar(A), A, B,\n #= initial =# typemax(eltype(A)),\n #= update =# (v,a,b) -> min(v,a))","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"This is typically how Basic morphological operations are implemented in LocalFilters. Note that localfilter! returns the destination. Also note that B is only used to define the neighborhood, it is usually called a structuring element in this context.","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"As another example, implementing a convolution of A by B writes:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"dst = localfilter!(similar(A), A, REVERSE_FILTER, B,\n #= initial =# zero(eltype(A)),\n #= update =# (v,a,b) -> v + a*b)","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"while:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"dst = localfilter!(similar(A), A, FORWARD_FILTER, B,\n #= initial =# zero(eltype(A)),\n #= update =# (v,a,b) -> v + a*b)","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"computes a correlation of A by B. The only difference is the ord argument which may be omitted in the latter case as FORWARD_FILTER is the default. In the case of convolutions and correlations, B defines the neighborhood but also the weights, it is usually called a kernel in this context.","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"Apart from specializations to account for the type of neighborhood defined by B, it is essentially the way the correlate and convolve! methods (described in the Section Linear filters) are implemented in LocalFilters.","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"In the above examples, the initial value of the state variable is always the same and directly provided while the default final = identity is assumed. Below is a more involved example to compute the roughness defined by Wilson et al. (in Marine Geodesy 30:3-35, 2007) as the maximum absolute difference between a central cell and surrounding cells:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"function roughness(A::AbstractArray{<:Real}, B=3)\n initial(a) = (; result = zero(a), center = a)\n update(v, a, _) = (; result = max(v.result, abs(a - v.center)), center=v.center)\n final(v) = v.result\n return localfilter!(similar(A), A, B, initial, update, final)\nend","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"This example has been borrowed from the Geomorphometry package for the analysis of Digital Elevation Models (DEM).","category":"page"}] +[{"location":"neighborhoods/#Neighborhoods,-structuring-elements,-and-kernels","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"","category":"section"},{"location":"neighborhoods/#Definitions","page":"Neighborhoods, structuring elements, and kernels","title":"Definitions","text":"","category":"section"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"Neighborhoods (a.k.a. structuring elements in mathematical morphology) and kernels are central concepts in LocalFilters. The neighborhood defines which values are involved in a local operation for each output value of the filter. Neighborhoods are assumed to be shift invariant but may have any support shape and may have embedded weights (e.g., to implement local convolution). In this latter case, they are called kernels.","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"In LocalFilters, a filtering operation, say","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"dst = filter(A, B)","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"involves, at each index i of a source array A, the values A[i±k] of A for all indices k of B and where:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"i ± k = i + k for operations like correlations where A and B can both be accessed in forward order of their indices (which is generally faster);\ni ± k = i - k for operations like convolutions where one of A or B must be accessed in reverse order of its indices (which is generally slower).","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"In LocalFilters, the following terminology is used for B:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"B is called a neighborhood or a structuring element for mathematical morphology operations (see Section Non-linear morphological filters) if its purpose is to define the indices in the source relatively to a given index in the destination. Such neighborhoods are represented by arrays with Boolean entries (true where entries are part of the neighborhood) and with as many dimensions as the source array A.\nB is called an hyper-rectangular box if it is a sliding window whose edges are aligned with the Cartesian axes of the array. Such simple neighborhoods are like the previous ones but with all entries equal to true, they are most efficiently represented by fast uniform arrays like FastUniformArray{Bool,N} instances from the StructuredArrays package. Another advantage of hyper-rectangular boxes is that they define a separable structuring element which may be exploited for very fast morphological operations by the van Herk-Gil-Werman algorithm whose numerical complexity does not depend on the size of the neighborhood (see localfilter!).\nB is called a kernel when its values are combined by the filter with those of the source. This is typically the case of discrete convolutions and correlations. Kernels are represented by AbstractArray{T,N} instances, with T the numerical type of the weights and N the number of dimensions.","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"From the user point of view, B whether it is a neighborhood, a structuring element, an hyper-rectangular box, a sliding window, or a kernel is thus just an array (usually of small size) with the same number of dimensions as the source array A","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"In addition, B may have arbitrary index offsets, to make it centered for example. For hyper-rectangular neighborhoods, FastUniformArray instances (from the StructuredArrays package) may directly have custom index offsets. For other neighborhoods and kernels, offset arrays (from the OffsetArrays package) can be used to implement such offsets.","category":"page"},{"location":"neighborhoods/#Simple-rules-for-specifying-neighborhoods-and-kernels","page":"Neighborhoods, structuring elements, and kernels","title":"Simple rules for specifying neighborhoods and kernels","text":"","category":"section"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"To facilitate specifying N-dimensional neighborhoods and kernels in the filters provided by LocalFilters, the following rules are applied to convert argument B to a suitable neighborhood or kernel:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"If B is an N-dimensional array, it is used unchanged. Hyper-rectangular boxes or symmetries may however be automatically detected to choose the fastest filtering method.\nIf B is a 2-tuple ofN-dimensional Cartesian indices, say, (I_first,I_last) each of type CartesianIndex{N}, then the corresponding neighborhood is an hyper-rectangular box whose first and last indices are I_first and I_last.\nIf B is a N-dimensional Cartesian range of type of type CartesianIndices{N}, then the corresponding neighborhood is an hyper-rectangular box whose first and last indices are given by this Cartesian range.\nIf B is an integer or an integer-valued unit range, it is interpreted as the length or the range of indices of the neighborhood along each dimension. In the case of a simple length, say, len, the index range of the neighborhood will be approximately centered using the same conventions as for fftshift: -i:i for odd lengths and -i:i-1 for even ones and with i = len ÷ 2.\nOtherwise B may be specified as a N-tuple of lengths or ranges (the two can be mixed), one for each dimension and where lengths are interpreted as in the previous case.","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"Note that all these cases except the first one correspond to hyper-rectangular boxes. The default neighborhood is B = 3 which corresponds to a centered hyper-rectangular sliding window of width 3 in each of its dimensions.","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"This assumed conversion may be explicitly performed by calling the kernel method:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"kernel(Dims{N}, B)","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"yields the N-dimensional neighborhood or kernel corresponding to B. If the number N of dimensions can be inferred from B, argument Dims{N} can be omitted.","category":"page"},{"location":"neighborhoods/#Simple-operations-on-kernels","page":"Neighborhoods, structuring elements, and kernels","title":"Simple operations on kernels","text":"","category":"section"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"If B is an N-dimensional array representing a neighborhood or a kernel, its indices may be centered by calling the LocalFilters.centered method:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"C = LocalFilters.centered(B)","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"which yields an array C with the same dimensions and values as B but with offset axes. This even work for arrays already centered or with offset axes that are not centered. Note that this method follows the same conventions as for fftshift (explained above) and has thus a slightly different semantic than the OffsetArrays.centered method which assumes that the centered range of an even dimension 1-i:i instead of -i:i-1.","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"It may be convenient to reverse a kernel or a neighborhood, this is done by calling the reverse_kernel method:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"R = reverse_kernel(B)","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"which yields an array R with the same dimensions and values as B but such that R[i] = B[-i] for any index i such that -i is in-bounds for B. This may be useful to perform a discrete convolution by the kernel B by a discrete correlation by R which is usually faster. Note that the reverse_kernel method reverses the order of the values of B as the base reverse method but also negates the axis ranges.","category":"page"},{"location":"neighborhoods/#Hyper-balls","page":"Neighborhoods, structuring elements, and kernels","title":"Hyper-balls","text":"","category":"section"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"To build a neighborhood, or a structuring element that is a N-dimensional hyper-ball of radius r call:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"LocalFilters.ball(Dims{N}, r)","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"For instance:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"julia> B = LocalFilters.ball(Dims{2}, 3)\n7×7 Matrix{Bool}:\n 0 0 1 1 1 0 0\n 0 1 1 1 1 1 0\n 1 1 1 1 1 1 1\n 1 1 1 1 1 1 1\n 1 1 1 1 1 1 1\n 0 1 1 1 1 1 0\n 0 0 1 1 1 0 0\n","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"This neighborhood is however not geometrically centered (its first index is at Cartesian index (1,1)), to make it centered use LocalFilters.centered:","category":"page"},{"location":"neighborhoods/","page":"Neighborhoods, structuring elements, and kernels","title":"Neighborhoods, structuring elements, and kernels","text":"julia> B = LocalFilters.centered(LocalFilters.ball(Dims{2}, 3))\n7×7 OffsetArray(::Matrix{Bool}, -3:3, -3:3) with eltype Bool with indices -3:3×-3:3:\n 0 0 1 1 1 0 0\n 0 1 1 1 1 1 0\n 1 1 1 1 1 1 1\n 1 1 1 1 1 1 1\n 1 1 1 1 1 1 1\n 0 1 1 1 1 1 0\n 0 0 1 1 1 0 0\n","category":"page"},{"location":"separable/#Efficient-separable-filters-for-associative-operations","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"","category":"section"},{"location":"separable/#Out-of-place-version","page":"Efficient separable filters for associative operations","title":"Out-of-place version","text":"","category":"section"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"The localfilter method may be called as:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"dst = localfilter([T=eltype(A),] A, dims, op, rngs [, w])","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"to apply the van Herk-Gil-Werman algorithm to filter array A along dimension(s) dims with (associative) binary operation op and contiguous structuring element(s) defined by the interval(s) rngs. Optional argument T is the element type of the result dst (by default T = eltype(A)). Optional argument w is a workspace array which is automatically allocated if not provided; otherwise, it must be a vector with the same element type as A (w is resized as needed by calling resize!).","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"Argument dims specifies along which dimension(s) of A the filter is to be applied, it can be a single integer, several integers or a colon : to specify all dimensions. Dimensions are processed in the order given by dims (the same dimension may appear several times) and there must be a matching interval in rngs to specify the structuring element (except that if rngs is a single interval, it is used for every dimension in dims). An interval is either an integer or an integer valued unit range in the form kmin:kmax (an interval specified as a single integer, say k, is the same as specifying k:k).","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"Assuming a mono-dimensional array A, the single filtering pass:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"localfilter!(dst, A, :, op, rng)","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"amounts to computing:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"dst[j] = A[j-kmax] ⋄ A[j-kmax+1] ⋄ A[j-kmax+2] ⋄ ... ⋄ A[j-kmin]","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"for all j ∈ [firstindex(A):lastindex(A)], with x ⋄ y = op(x, y), kmin = first(rng) and kmax = last(rng). Note that if rng = k:k or rng = k with k an integer, the result of the filter is to operate a simple shift by k along the corresponding dimension and has no effects if k = 0. This can be exploited to not filter some dimension(s).","category":"page"},{"location":"separable/#In-place-version","page":"Efficient separable filters for associative operations","title":"In-place version","text":"","category":"section"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"The localfilter! method implements the in-place version of the van Herk-Gil-Werman algorithm:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"localfilter!([dst = A,] A, dims, op, rngs [, w]) -> dst","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"overwrites the contents of dst with the result of the filter and returns dst. The destination array dst must have the same indices as the source A (that is, axes(dst) == axes(A) must hold). If dst is not specified or if dst is A, the operation is performed in-place.","category":"page"},{"location":"separable/#Examples","page":"Efficient separable filters for associative operations","title":"Examples","text":"","category":"section"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"The in-place morphological erosion (local minimum) of the array A on a centered structuring element of width 7 in every dimension can be done by:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"localfilter!(A, :, min, -3:3)","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"As said before, index interval 0 may be specified to do nothing along the corresponding dimension. For instance, assuming A is a three-dimensional array:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"localfilter!(A, :, max, (-3:3, 0, -4:4))","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"overwrites A by its morphological dilation (i.e. local maximum) in a centered local neighborhood of size 7×1×9 (nothing is done along the second dimension). The same result may be obtained with:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"localfilter!(A, (1,3), max, (-3:3, -4:4))","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"where the second dimension is omitted from the list of dimensions.","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"The local average of the two-dimensional array A on a centered moving window of size 11×11 can be computed as:","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"localfilter(A, :, +, (-5:5, -5:5))*(1/11^2)","category":"page"},{"location":"separable/#Efficiency-and-restrictions","page":"Efficient separable filters for associative operations","title":"Efficiency and restrictions","text":"","category":"section"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"The van Herk-Gil-Werman algorithm is very fast for rectangular structuring elements. It takes at most 3 operations to filter an element along a given dimension whatever the width p of the considered local neighborhood. For N-dimensional arrays, the algorithm requires only 3N operations per element instead of p^N - 1 operations for a naive implementation. This however requires to make a pass along each dimension so memory page faults may reduce the performances. This is somewhat attenuated by the fact that the algorithm can be applied in-place. For efficient multi-dimensional out-of-place filtering, it is recommended to make the first pass with a fresh destination array and then all other passes in-place on the destination array.","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"To apply the van Herk-Gil-Werman algorithm, the structuring element must be separable along the dimensions and its components must be contiguous. In other words, the algorithm is only applicable for N-dimensional rectangular neighborhoods, so-called hyperrectangles. The structuring element may however be off-centered by arbitrary offsets along each dimension.","category":"page"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"To take into account boundary conditions (for now, only nearest neighbor is implemented) and allow for in-place operation, the algorithm allocates a workspace array.","category":"page"},{"location":"separable/#References","page":"Efficient separable filters for associative operations","title":"References","text":"","category":"section"},{"location":"separable/","page":"Efficient separable filters for associative operations","title":"Efficient separable filters for associative operations","text":"Marcel van Herk, \"A fast algorithm for local minimum and maximum filters on rectangular and octagonal kernels\" in Pattern Recognition Letters 13, 517-521 (1992).\nJoseph Gil and Michael Werman, \"Computing 2-D Min, Median, and Max Filters\" in IEEE Transactions on Pattern Analysis and Machine Intelligence 15, 504-507 (1993).","category":"page"},{"location":"linear/#Linear-filters","page":"Linear filters","title":"Linear filters","text":"","category":"section"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"LocalFilters provides a few linear filters: localmean or localmean! to compute the mean of values in a neighborhood, convolve or convolve! to compute the discrete convolution of an array by a kernel, and correlate or correlate! to compute the discrete correlation of an array by a kernel.","category":"page"},{"location":"linear/#Local-mean","page":"Linear filters","title":"Local mean","text":"","category":"section"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"The localmean method yields the local mean of an array A in a neighborhood B:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"dst = localmean(A, B=3)","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"The result dst is an array similar to A. See Section Simple rules for specifying neighborhoods and kernels for the interpretation of B.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"The in-place version localmean! may be used to avoid allocations:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"localmean!(dst, A, B=3)","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which overwrites dst with the local mean of A in the neighborhood defined by B and returns dst.","category":"page"},{"location":"linear/#Discrete-convolution","page":"Linear filters","title":"Discrete convolution","text":"","category":"section"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Call the convolve method as:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"dst = convolve(A, B)","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"to compute the discrete convolution of array A by the kernel defined by B. The result dst is an array similar to A.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Using indices(A) to denote the set of valid indices for array A and assuming B is an array of values, the discrete convolution of A by B writes (see Section Discrete convolution and correlation):","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"for i ∈ indices(A)\n v = zero(T)\n @inbounds for k ∈ indices(B) ∩ (i - indices(A))\n v += A[i-k]*B[k]\n end\n dst[i] = v\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"with T a suitable element type for the result (see Section Element type of the result below) and where indices(B) ∩ (i - indices(A)) denotes the subset of indices k such that k ∈ indices(B) and i - k ∈ indices(A) and thus for which B[k] and A[i-k] are valid.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Following the conventions in localfilter!, the discrete convolution can also be expressed as:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"for i ∈ indices(A)\n v = zero(T)\n @inbounds for j ∈ indices(A) ∩ (i - indices(B))\n v += A[j]*B[i-j]\n end\n dst[i] = v\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"If the kernel B is an array of Booleans, the discrete convolution is computed as:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"for i ∈ indices(A)\n v = zero(T)\n @inbounds for j ∈ indices(A) ∩ (i - indices(B))\n if B[i-j]\n v += A[j]\n end\n end\n dst[i] = v\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which amounts to computing the local sum of the values of A in the neighborhood defined by the true entries of B.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"The in-place version convolve! may be used to avoid allocations:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"convolve!(dst, A, B)","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which overwrites dst with the discrete convolution of A by the kernel B and returns dst.","category":"page"},{"location":"linear/#Discrete-correlation","page":"Linear filters","title":"Discrete correlation","text":"","category":"section"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Call the correlate method as:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"dst = correlate(A, B)","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"to compute the discrete correlation of array A by the kernel defined by B. The result dst is an array similar to A.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Using indices(A) to denote the set of valid indices for array A and assuming B is an array of values, the discrete correlation of A by B writes (see Section Discrete convolution and correlation):","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"for i ∈ indices(A)\n v = zero(T)\n @inbounds for k ∈ indices(B) ∩ (indices(A) - i)\n v += A[i+k]*conj(B[k])\n end\n dst[i] = v\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"with T a suitable element type for the result (see Section Element type of the result below) and where indices(B) ∩ (indices(A) - i) denotes the subset of indices k such that k ∈ indices(B) and i + k ∈ indices(A) and thus for which B[k] and A[i+k] are valid.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Following the conventions in localfilter!, the discrete correlation can also be expressed as:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"for i ∈ indices(A)\n v = zero(T)\n @inbounds for j ∈ indices(A) ∩ (indices(B) + i)\n v += A[j]*conj(B[j-i])\n end\n dst[i] = v\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"If the kernel B is an array of Booleans, the discrete correlation is computed as:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"for i ∈ indices(A)\n v = zero(T)\n @inbounds for j ∈ indices(A) ∩ (indices(B) + i)\n v += A[j]\n end\n dst[i] = v\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which amounts to computing the local sum of the values of A in the neighborhood defined by the true entries of B.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"The in-place version correlate! may be used to avoid allocations:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"correlate!(dst, A, B)","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which overwrites dst with the discrete correlation of A by the kernel B and returns dst.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"SInce accessing the indices of A and B in the same order is generally faster (e.g. it is easier to optimize via loop vectorization), the discrete convolution convolve(A,B) of A by B may be computed by:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"correlate(A, reverse_kernel(B))","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"provided the entries of B are reals, not complexes.","category":"page"},{"location":"linear/#Element-type-of-the-result","page":"Linear filters","title":"Element type of the result","text":"","category":"section"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"Choosing a suitable element type for the result may be tricky if the entries of the source array A and of the kernel B have different types or have units.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"For example, a suitable element type T for the result of the convolution or correlation of A by B is given by:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"T = let a = oneunit(eltype(A)), b = oneunit(eltype(B)), c = a*b\n typeof(c + c)\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which is the type of the sum of the element-wise product of the entries of A and B.","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"For the local mean, a similar reasoning yields:","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"T = let a = oneunit(eltype(A)), b = oneunit(eltype(B)), c = a*b\n typeof((c + c)/(b + b))\nend","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"which is the type of the sum of the element-wise product of the entries of A and B divided by the sum of the entries in B (the so-called weights).","category":"page"},{"location":"linear/","page":"Linear filters","title":"Linear filters","text":"These rules are the ones used by the out-of-place versions of the linear filters of LocalFilter when the destination is not provided.","category":"page"},{"location":"nonlinear/#Non-linear-filters","page":"Non-linear filters","title":"Non-linear filters","text":"","category":"section"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"LocalFilters provides a number of non-linear filters such as the bilateral filter and mathematical morphology filters. The latter are described in the Section Non-linear morphological filters.","category":"page"},{"location":"nonlinear/#The-bilateral-filter","page":"Non-linear filters","title":"The bilateral filter","text":"","category":"section"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"Applying the bilateral filter on array A writes:","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"bilateralfilter([T=float(eltype(A)),] A, F, G, ...)","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"Argument F specifies how to smooth the differences in values. It may be function which takes two values from A as arguments and returns a nonnegative weight. It may be a real which is assumed to be the standard deviation of a Gaussian.","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"Arguments G, ... specify the settings of the distance filter for smoothing differences in coordinates. There are several possibilities:","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"G, ... can be a single argument specifying a kernel (see Section Simple rules for specifying neighborhoods and kernels).\nArgument G may be a function taking as argument the Cartesian index of the coordinate differences and returning a nonnegative weight. Argument G may also be a real specifying the standard deviation of the Gaussian used to compute weights. Subsequent arguments ... are to specify the neighborhood where to apply the distance filter function, they can be anything that may defined a neighborhood (again see Section Simple rules for specifying neighborhoods and kernels). If a standard deviation σ is specified for G with no subsequent arguments, a default window of radius 3σ is assumed.","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"Optional argument T can be used to force the element type used for (most) computations. This argument is needed if the element type of A is not a real.","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"See bilateralfilter! for an in-place version of this function.","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"bilateralfilter!([T=float(eltype(A)),] dst, A, F, G, ...) -> dst","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"overwrites dst with the result of applying the bilateral filter on array A and returns dst.","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"See bilateralfilter for a description of the other arguments than dst.","category":"page"},{"location":"nonlinear/","page":"Non-linear filters","title":"Non-linear filters","text":"See wikipedia for a description of the bilateral filter.","category":"page"},{"location":"morphology/#Non-linear-morphological-filters","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"","category":"section"},{"location":"morphology/#Basic-morphological-operations","page":"Non-linear morphological filters","title":"Basic morphological operations","text":"","category":"section"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"Erosion and dilation are the basic operations of mathematical morphology, they are implemented by methods erode and dilate:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"erode(A, R=3) -> Amin\ndilate(A, R=3) -> Amax","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"which respectively return the local minima Amin and the local maxima Amax of argument A in a structuring element defined by R. The notion of structuring element in mathematical morphology is equivalent to that of neighborhood in LocalFilters. The returned result is similar to A (same size and type). If R is not specified, a default hyper-rectangular moving window 3 samples wide in every dimension of A is used. If the structuring element R is a simple hyper-rectangular moving window, the much faster van Herk-Gil-Werman algorithm is used","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"The localextrema method combines these two operations in one call:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"localextrema(A, R=3) -> Amin, Amax","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"Calling localextrema is usually almost twice as fast as calling erode and dilate.","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"To avoid allocating new arrays, the methods erode!, dilate!, and localextrema! provide in-place versions which apply the operation to A with structuring element R and store the result in the provided arrays Amin and/or Amax:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"erode!(Amin, A, R=3) -> Amin\ndilate!(Amax, A, R=3) -> Amax\nlocalextrema!(Amin, Amax, A, R=3) -> Amin, Amax","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"If the structuring element R is a simple hyper-rectangular moving window, the much faster van Herk-Gil-Werman algorithm is used and the operation can be done in-place. That is, A and Amin can be the same arrays. In that case, the following syntax is allowed:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"erode!(A, R=3) -> A\ndilate!(A, R=3) -> A","category":"page"},{"location":"morphology/#Opening-and-closing-filters","page":"Non-linear morphological filters","title":"Opening and closing filters","text":"","category":"section"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"closing(A, R=3)\nopening(A, R=3)","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"respectively perform a closing or an opening of array A by the structuring element R. If R is not specified, a default hyper-rectangular moving window of size 3 in every dimension of A is used. A closing is a dilation followed by an erosion, whereas an opening is an erosion followed by a dilation.","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"The in-place versions are:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"closing!(dst, wrk, A, R=3) -> dst\nopening!(dst, wrk, A, R=3) -> dst","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"which perform the operation on the source A and store the result in destination dst using wrk as a workspace array. The 3 arguments dst, wrk, and A must be similar arrays; dst and A may be identical, but wrk must not be the same array as A or dst. The destination dst is returned.","category":"page"},{"location":"morphology/#Top-hat-and-bottom-hat-filters","page":"Non-linear morphological filters","title":"Top-hat and bottom-hat filters","text":"","category":"section"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"Methods top_hat and bottom_hat perform a summit/valley detection by applying a top-hat filter to an array. They are called as:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"top_hat(A, R[, S]) -> dst\nbottom_hat(A, R[, S]) -> dst","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"to yield the result of the filter applied to array A. Argument R defines the structuring element for the feature detection. Optional argument S specifies the structuring element for smoothing A prior to the top-/bottom-hat filter. If R and S are specified as the radii of the structuring elements, then S should be smaller than R. For instance:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"top_hat(bitmap, 3, 1)","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"may be used to detect text or lines in a bitmap image.","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"Methods LocalFilters.top_hat! and LocalFilters.bottom_hat! implement the in-place versions of these filters:","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"top_hat!(dst, wrk, A, R[, S]) -> dst\nbottom_hat!(dst, wrk, A, R[, S]) -> dst","category":"page"},{"location":"morphology/","page":"Non-linear morphological filters","title":"Non-linear morphological filters","text":"apply the top-/bottom-hat filter on the source A and store the result in the destination dst using wrk as a workspace array. The 3 arguments dst, wrk, and A must be similar but different arrays. The destination dst is returned.","category":"page"},{"location":"reference/#Reference","page":"Reference","title":"Reference","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"The following summarizes the documentation of types and methods provided by the LocalFilters package. This information is also available from the REPL by typing ? followed by the name of a method or a type.","category":"page"},{"location":"reference/#Index","page":"Reference","title":"Index","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"","category":"page"},{"location":"reference/#Simple-linear-filters","page":"Reference","title":"Simple linear filters","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"LocalFilters provides a number of shift-invariant linear filters.","category":"page"},{"location":"reference/","page":"Reference","title":"Reference","text":"correlate\ncorrelate!\nconvolve\nconvolve!\nlocalmean\nlocalmean!","category":"page"},{"location":"reference/#LocalFilters.correlate","page":"Reference","title":"LocalFilters.correlate","text":"correlate(A, B) -> dst\n\nyields the discrete correlation of the array A by the kernel defined by B. The result dst is an array similar to A.\n\nUsing Sup(A) to denote the set of valid indices for array A and assuming B is an array of numerical values, the discrete convolution of A by B writes:\n\nT = let x = oneunit(eltype(A))*oneunit(eltype(B)); typeof(x + x); end\ndst = similar(A, T)\nfor i ∈ Sup(A)\n v = zero(T)\n @inbounds for j ∈ Sup(A) ∩ (Sup(B) - i)\n v += A[j]*B[j-i]\n end\n dst[i] = v\nend\n\nwith T the type of the product of elements of A and B, and where Sup(A) ∩ (i - Sup(A)) denotes the subset of indices k such that k ∈ Sup(B) and i - k ∈ Sup(A) and thus for which B[k] and A[i-k] are valid.\n\nSee also correlate! and convolve.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.correlate!","page":"Reference","title":"LocalFilters.correlate!","text":"correlate!(dst, A, B) -> dst\n\noverwrites dst with the discrete convolution of A by the kernel B and returns dst.\n\nSee also correlate and localfilter!.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.convolve","page":"Reference","title":"LocalFilters.convolve","text":"convolve(A, B)\n\nyields the discrete convolution of array A by the kernel defined by B. The result dst is an array similar to A.\n\nUsing Sup(A) to denote the set of valid indices for array A and assuming B is an array of values, the discrete convolution of A by B writes:\n\nT = let x = oneunit(eltype(A))*oneunit(eltype(B)); typeof(x + x); end\nfor i ∈ Sup(A)\n v = zero(T)\n @inbounds for j ∈ Sup(B) ∩ (i - Sup(A))\n v += A[i-j]*B[j]\n end\n dst[i] = v\nend\n\nwith T the type of the product of elements of A and B, and where Sup(B) ∩ (i - Sup(A)) denotes the subset of indices k such that k ∈ Sup(B) and i - k ∈ Sup(A) and thus for which B[k] and A[i-k] are valid.\n\nSee also convolve! and localfilter!.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.convolve!","page":"Reference","title":"LocalFilters.convolve!","text":"convolve!(dst, A, B) -> dst\n\noverwrites dst with the discrete convolution of A by the kernel B and returns dst.\n\nSee also convolve and localfilter!.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.localmean","page":"Reference","title":"LocalFilters.localmean","text":"localmean(A, [ord=FORWARD_FILTER,] B=3; null=zero(eltype(A)))\n\nyields the local mean of A in a neighborhood defined by B. The result is an array similar to A. If B is not specified, the neighborhood is a hyper-rectangular sliding window of size 3 in every dimension. Otherwise, B may be specified as a Cartesian box, or as an array of booleans of same number of dimensions as A. If B is a single odd integer (as it is by default), the neighborhood is assumed to be a hyper-rectangular sliding window of size B in every dimension.\n\nKeyword null may be used to specify the value of the result where the sum of the weights in a local neighborhood is zero.\n\nSee also localmean! and localfilter!.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.localmean!","page":"Reference","title":"LocalFilters.localmean!","text":"localmean!(dst, A, [ord=FORWARD_FILTER,] B=3; null=zero(eltype(dst))) -> dst\n\noverwrites dst with the local mean of A in a neighborhood defined by B and returns dst.\n\nKeyword null may be used to specify the value of the result where the sum of the weights in the a neighborhood is zero.\n\nSee also localmean and localfilter!.\n\n\n\n\n\n","category":"function"},{"location":"reference/#Mathematical-morphology","page":"Reference","title":"Mathematical morphology","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"erode\nerode!\ndilate\ndilate!\nlocalextrema\nlocalextrema!\nclosing\nclosing!\nopening\nopening!\ntop_hat\nLocalFilters.top_hat!\nbottom_hat\nLocalFilters.bottom_hat!","category":"page"},{"location":"reference/#LocalFilters.erode","page":"Reference","title":"LocalFilters.erode","text":"erode(A, [ord=FORWARD_FILTER,] B=3) -> Amin\n\nyields the erosion of A by the structuring element defined by B. The erosion is the array of local minima of A. The returned result Amin is similar to A (same size and type).\n\nIf B is not a kernel (that is, if B is not an array or is an instance of CartesianIndices), kernel(Dims{N},B) is called to build a kernel with N the number of dimensions of A.\n\nIf the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used.\n\nAn erosion is one of the most basic operations of mathematical morphology. See erode! for an in-place version of the method, dilate for retrieving the local maxima, and localextrema for performing an erosion and a dilation in a single pass.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.erode!","page":"Reference","title":"LocalFilters.erode!","text":"erode!(Amin, A, [ord=FORWARD_FILTER,] B=3) -> Amin\n\noverwrites Amin with the erosion of the array A by the structuring element defined by B and returns Amin.\n\nIf the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used and the operation can be done in-place. That is, A and Amin can be the same arrays. In that case, the following syntax is allowed:\n\nerode!(A, [ord=FORWARD_FILTER,] B=3) -> A\n\nSee erode for an out-of-place version and for more information.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.dilate","page":"Reference","title":"LocalFilters.dilate","text":"dilate(A, [ord=FORWARD_FILTER,] B=3) -> Amax\n\nyields the dilation of A by the structuring element defined by B. The dilation is the array of local maxima of A. The returned result Amax is similar to A (same size and type).\n\nIf B is not a kernel (that is, if B is not an array or is an instance of CartesianIndices), kernel(Dims{N},B) is called to build a kernel with N the number of dimensions of A.\n\nIf the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used.\n\nA dilation is one of the most basic operations of mathematical morphology. See dilate! for an in-place version of the method, erode for retrieving the local minima, and localextrema for performing an erosion and a dilation in a single pass.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.dilate!","page":"Reference","title":"LocalFilters.dilate!","text":"dilate!(Amax, A, [ord=FORWARD_FILTER,] B=3) -> Amax\n\noverwrites Amax with a dilation of the array A by the structuring element defined by B and returns Amax.\n\nIf the structuring element B is equivalent to a simple hyper-rectangular sliding window (which is the case by default), the much faster van Herk-Gil-Werman algorithm is used and the operation can be done in-place. That is, A and Amin can be the same arrays. In that case, the following syntax is allowed:\n\ndilate!(A, [ord=FORWARD_FILTER,] B=3) -> A\n\nSee dilate for an out-of-place version and for more information.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.localextrema","page":"Reference","title":"LocalFilters.localextrema","text":"localextrema(A, [ord=FORWARD_FILTER,] B=3) -> Amin, Amax\n\nyields the results of performing an erosion and a dilation of A by the structuring element defined by B in a single pass. Calling this method is usually almost twice as fast as calling erode and dilate.\n\nSee localextrema! for an in-place version of the method, and erode or dilate for a description of these operations.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.localextrema!","page":"Reference","title":"LocalFilters.localextrema!","text":"localextrema!(Amin, Amax, A, [ord=FORWARD_FILTER,] B=3) -> Amin, Amax\n\noverwrites Amin and Amax with, respectively, an erosion and a dilation of the array A by the structuring element defined by B in a single pass.\n\nSee localextrema for an out-of-place version for more information.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.closing","page":"Reference","title":"LocalFilters.closing","text":"closing(A, [ord=FORWARD_FILTER,] B=3) -> dst\n\nyields a closing of array A by the structuring element defined by B. A closing is a dilation followed by an erosion. The result dst is an array similar to A.\n\nSee closing! for an in-place version of the method, opening for a related filter, and erode or dilate for a description of these operations.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.closing!","page":"Reference","title":"LocalFilters.closing!","text":"closing!(dst, wrk, A, B=3) -> dst\n\noverwrites dst with the result of a closing of A by the structuring element defined by B using wrk as a workspace array. The arguments dst, wrk, and A must be similar arrays, dst and A may be identical, but wrk must not be the same array as A or dst. The destination dst is returned.\n\nSee closing for a description of this kind of filter and for the meaning of the arguments.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.opening","page":"Reference","title":"LocalFilters.opening","text":"opening(A, B=3) -> dst\n\nyields an opening of array A by the structuring element defined by B. An opening is an erosion followed by a dilation. The result dst is an array similar to A.\n\nSee opening! for an in-place version of the method, closing for a related filter, and erode or dilate for a description of these operations.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.opening!","page":"Reference","title":"LocalFilters.opening!","text":"opening!(dst, wrk, A, B=3) -> dst\n\noverwrites dst with the result of an opening of A by the structuring element defined by B using wrk as a workspace array. The arguments dst, wrk, and A must be similar arrays, dst and A may be identical, but wrk must not be the same array as A or dst. The destination dst is returned.\n\nSee opening for a description of this kind of filter and for the meaning of the arguments.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.top_hat","page":"Reference","title":"LocalFilters.top_hat","text":"top_hat(A, B=3 [, C]) -> dst\n\nperforms a summit detection by applying a top-hat filter to array A using the structuring element defined by B for the feature detection. Top-hat filtering is equivalent to:\n\ndst = A .- opening(A, B)\n\nOptional argument C specifies the structuring element for smoothing A prior to top-hat filtering. If B and C are specified as the radii of the structuring elements, then C should be smaller than B. For instance:\n\ntop_hat(bitmap, 3, 1)\n\nmay be used to detect text or lines in a bitmap image.\n\nSee bottom_hat for a related operation, LocalFilters.top_hat! for an in-place version.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.top_hat!","page":"Reference","title":"LocalFilters.top_hat!","text":"LocalFilters.top_hat!(dst, wrk, A, [ord=FORWARD_FILTER,] B=3) -> dst\n\noverwrites dst with the result of a top-hat filter applied to A with structuring element B, and using wrk as a workspace whose contents is not preserved. The arguments A, dst, and wrk must be similar but different arrays. The destination dst is returned.\n\nSee also top_hat for more details.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.bottom_hat","page":"Reference","title":"LocalFilters.bottom_hat","text":"bottom_hat(A, B=3 [, C]) -> dst\n\nperforms a valley detection by applying a bottom-hat filter to array A using the structuring element defined by B for the feature detection. Bottom-hat filtering is equivalent to:\n\ndst = closing(A, B) .- A\n\nOptional argument C specifies the structuring element for smoothing A prior to bottom-hat filtering. If B and C are specified as the radii of the structuring elements, then C should be smaller than B.\n\nSee top_hat for a related operation, LocalFilters.bottom_hat! for an in-place version.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.bottom_hat!","page":"Reference","title":"LocalFilters.bottom_hat!","text":"LocalFilters.bottom_hat!(dst, wrk, A, B=3) -> dst\n\noverwrites dst with the result of a bottom-hat filter applied to A with structuring element B and optional smoothing element C. Argument wrk is a workspace array whose contents is not preserved. The arguments A, dst, and wrk must be similar but different arrays. The destination dst is returned.\n\nSee also bottom_hat for more details.\n\n\n\n\n\n","category":"function"},{"location":"reference/#Other-non-linear-filters","page":"Reference","title":"Other non-linear filters","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"bilateralfilter\nbilateralfilter!","category":"page"},{"location":"reference/#LocalFilters.bilateralfilter","page":"Reference","title":"LocalFilters.bilateralfilter","text":"bilateralfilter([T=float(eltype(A)),] A, F, [ord=FORWARD_FILTER,] G...=3)\n\nyields the result of applying the bilateral filter on array A.\n\nArgument F specifies how to smooth the differences in values. It can be:\n\na function, say f, which is called as f(A[i],A[j]) to yield a nonnegative weight for i the central index and j the index in a nearby position;\na positive real, say σ, which is assumed to be the standard deviation of a Gaussian.\n\nArguments G, ... specify the settings of the distance filter for smoothing differences in coordinates. There are several possibilities:\n\nG... = wgt an array of nonnegative weights or of booleans. The axes of wgt must have offsets so that the zero index is part of the indices of wgt.\nG... = f, w with f a function and w any kind of argument that can be used to build a window win specifying the extension of the neighborhood. The value of the distance filter will be max(f(i),0) for all Cartesian index i of win such that win[i] is true. See kernel for the different ways to specify a window.\nG... = σ or , G... = σ, w with σ a positive real assumed to be the standard deviation of a Gaussian function and w any kind of argument that can be used to build a window win specifying the extension of the neighborhood. If w is not specified, a default window of size ±3σ is assumed.\n\nOptional argument T can be used to force the element type of the result. This argument is needed if the element type of A is not a real.\n\nSee bilateralfilter! for an in-place version of this function and see Wikipedia for a description of the bilateral filter.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.bilateralfilter!","page":"Reference","title":"LocalFilters.bilateralfilter!","text":"bilateralfilter!(dst, A, F, [ord=FORWARD_FILTER,] G...) -> dst\n\noverwrites dst with the result of applying the bilateral filter on array A and returns dst.\n\nSee bilateralfilter for a description of the other arguments than dst and see Wikipedia for a description of the bilateral filter.\n\n\n\n\n\n","category":"function"},{"location":"reference/#Methods-to-build-local-filters","page":"Reference","title":"Methods to build local filters","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"localfilter\nlocalfilter!","category":"page"},{"location":"reference/#LocalFilters.localfilter","page":"Reference","title":"LocalFilters.localfilter","text":"localfilter([T=eltype(A),] A, dims, op, [ord=FORWARD_FILTER,]\n rngs[, wrk]) -> dst\n\nyields the result of applying van Herk-Gil-Werman algorithm to filter array A along dimension(s) dims with (associative) binary operation op and contiguous structuring element(s) defined by the interval(s) rngs. Optional argument wrk is a workspace array with elements of type T which is automatically allocated if not provided; otherwise, it must be a vector with the same element type as A and it is resized as needed (by calling the resize! method). The optional argument T allows to specify another type of element than eltype(A) for the result.\n\nArgument dims specifies along which dimension(s) of A the filter is to be applied, it can be a single integer, a tuple of integers, or a colon : to apply the operation to all dimensions. Dimensions are processed in the order given by dims (the same dimension may appear several times) and there must be a matching interval in rngs to specify the structuring element (except that if rngs is a single interval, it is used for every dimension in dims). An interval is either an integer or an integer valued unit range in the form kmin:kmax. An interval specified as a single integer yields an approximately centered range og this length.\n\nAssuming a mono-dimensional array A, the single filtering pass:\n\ndst = localfilter(A, :, op, rng)\n\namounts to computing (assuming forward ordering):\n\ndst[j] = A[i+kmin] ⋄ A[i+kmin+1] ⋄ ... ⋄ A[i+kmax-1] ⋄ A[i+kmax]\n\nfor all j ∈ axes(dst,1), with x ⋄ y = op(x, y), kmin = first(rng) and kmax = last(rng). Note that if kmin = kmax = k, the result of the filter is to operate a simple shift by k along the corresponding dimension and has no effects if k = 0. This can be exploited to not filter some dimension(s).\n\nFlat boundary conditions are assumed for A[i+k] in the above formula.\n\nExamples\n\nThe morphological erosion (local minimum) of the array A on a centered structuring element of width 7 in every dimension can be obtained by:\n\nlocalfilter(A, :, min, -3:3)\n\nIndex interval 0:0 may be specified to do nothing along the corresponding dimension. For instance, assuming A is a three-dimensional array:\n\nlocalfilter(A, :, max, (-3:3, 0:0, -4:4))\n\nyields the morphological dilation (i.e. local maximum) of A in a centered local neighborhood of size 7×1×9 (nothing is done along the second dimension). The same result may be obtained with:\n\nlocalfilter(A, (1,3), max, (-3:3, -4:4))\n\nwhere the second dimension is omitted from the list of dimensions.\n\nThe local average of the two-dimensional array A on a centered moving window of size 11×11 can be computed as:\n\nlocalfilter(A, :, +, (-5:5, -5:5))*(1/11)\n\nSee localfilter! for an in-place version of the method.\n\n\n\n\n\nlocalfilter(A, args...; kwds...) -> dst\n\nout of place version of localfilter! which is equivqlent to:\n\nlocalfilter!(similar(A), A, args...; kwds...)\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.localfilter!","page":"Reference","title":"LocalFilters.localfilter!","text":"localfilter!([dst = A,] A, dims, op, [ord=FORWARD_FILTER,] rngs[, wrk])\n\noverwrites the contents of dst with the result of applying van Herk-Gil-Werman algorithm to filter array A along dimension(s) dims with (associative) binary operation op and contiguous structuring element(s) defined by the interval(s) rngs and using optional argument wrk as a workspace array. The destination dst must have the same indices as the source A (that is, axes(dst) == axes(A)). Operation may be done in-place and dst and A can be the same; this is the default behavior if dst is not specified.\n\nSee localfilter for a full description of the method.\n\nThe in-place morphological erosion (local minimum) of the array A on a centered structuring element of width 7 in every dimension can be obtained by:\n\nlocalfilter!(A, :, min, -3:3)\n\n\n\n\n\nlocalfilter!(dst, A, [ord = FORWARD_FILTER,] B, initial,\n update::Function, final::Function = identity) -> dst\n\noverwrites the destination dst with the result of a local filter applied to the source A, on a relative neighborhood defined by B, and implemented by initial, update, and final. The initial argument may be a function or not. The purpose of these latter arguments is explained by the following pseudo-codes implementing the local filtering. If ord = FORWARD_FILTER:\n\n@inbounds for i ∈ indices(dst)\n v = initial isa Function ? initial(A[i]) : initial\n for j ∈ indices(A) ∩ (indices(B) + i)\n v = update(v, A[j], B[j-i])\n end\n dst[i] = final(v)\nend\n\nelse if ord = REVERSE_FILTER:\n\n@inbounds for i ∈ indices(dst)\n v = initial isa Function ? initial(A[i]) : initial\n for j ∈ indices(A) ∩ (i - indices(B))\n v = update(v, A[j], B[i-j])\n end\n dst[i] = final(v)\nend\n\nwhere indices(A) denotes the range of indices of any array A while indices(B) + i and i - indices(B) respectively denote the set of indices j such that j - i ∈ indices(B) and i - j ∈ indices(B). In other words, j ∈ indices(A) ∩ (i - indices(B)) means all indices j such that j ∈ indices(A) and i - j ∈ indices(B) so that A[j] and B[i-j] are in-bounds.\n\nIf initial is a function, the initial value of the state variable v in the above pseudo-codes is given by v = initial(A[i]) with i the current index in dst. Hence, in that case, the destination array dst and the source array src must have the same axes.\n\nFor example, implementing a local minimum filter (that is, an erosion), is as simple as:\n\nlocalfilter!(dst, A, ord, B, typemax(eltype(A)),\n (v,a,b) -> ifelse(b, min(v,a), v))\n\nAs another example, implementing a convolution by B writes:\n\nT = promote_type(eltype(A), eltype(B))\nlocalfilter!(dst, A, ord, B, zero(T), (v,a,b) -> v + a*b)\n\n\n\n\n\n","category":"function"},{"location":"reference/#Constants","page":"Reference","title":"Constants","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"FORWARD_FILTER\nREVERSE_FILTER","category":"page"},{"location":"reference/#LocalFilters.FORWARD_FILTER","page":"Reference","title":"LocalFilters.FORWARD_FILTER","text":"FORWARD_FILTER\n\nis an exported constant object used to indicate forward ordering of indices in local filter operations. It can be called as:\n\nFORWARD_FILTER(i, j) -> j - i\n\nto yield the index in the filter kernel. See also REVERSE_FILTER for reverse ordering and LocalFilters.localindices for building a range of valid indices j.\n\n\n\n\n\n","category":"constant"},{"location":"reference/#LocalFilters.REVERSE_FILTER","page":"Reference","title":"LocalFilters.REVERSE_FILTER","text":"REVERSE_FILTER\n\nis an exported constant object used to indicate reverse ordering of indices in local filter operations. It can be called as:\n\nREVERSE_FILTER(i, j) -> i - j\n\nto yield the index in the filter kernel. See also FORWARD_FILTER for forward ordering and LocalFilters.localindices for building a range of valid indices j.\n\n\n\n\n\n","category":"constant"},{"location":"reference/#Neighborhoods-and-kernels","page":"Reference","title":"Neighborhoods and kernels","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"kernel\nreverse_kernel\nstrel\nLocalFilters.ball\nLocalFilters.centered","category":"page"},{"location":"reference/#LocalFilters.kernel","page":"Reference","title":"LocalFilters.kernel","text":"kernel([Dims{N},] args...)\n\nyields an N-dimensional abstract array built from args... and which can be used as a kernel in local filtering operations.\n\nIf args... is composed of N integers and/or ranges or if it is an N-tuple of integers and/or ranges, a uniformly true abstract array is returned whose axes are specified by args.... Each integer argument is converted in a centered unit range of this length (see LocalFilters.kernel_range).\nIf Dims{N} is provided and args... is a single integer or range, it is interpreted as being the same for all dimensions of an N-dimensional kernel. For example, kernel(Dims{3},5) yields a 3-dimensional uniformly true array with index range -2:2 in every dimension.\nIf args... is 2 Cartesian indices or a 2-tuple of Cartesian indices, say I_first and I_last, a uniformly true abstract array is returned whose first and last indices are I_first and I_last.\nIf args... is a Cartesian range, say R::CartesianIndices{N}, a uniformly true abstract array is returned whose axes are given by R.\nIf args... is an abstract array of any other type than an instance of CartesianIndices, it is returned unchanged.\n\nOptional leading argument Dims{N} can be specified to assert the number of dimensions of the result or to provide the number of dimensions when it cannot be inferred from the arguments. For example, when args... is a single integer length or range which should be interpreted as being the same for all dimensions.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.reverse_kernel","page":"Reference","title":"LocalFilters.reverse_kernel","text":"reverse_kernel(A) -> B\n\nyields an array B such that B[i] = A[-i] holds for all indices i such that -i is a valid index in A.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.strel","page":"Reference","title":"LocalFilters.strel","text":"strel(T, A)\n\nyields a structuring element suitable for mathematical morphology operations. The result is an array whose elements have type T (which can be Bool or a floating-point type). Argument A can be a hyper-rectangular Cartesian sliding window or an array with boolean elements.\n\nIf T is a floating-point type, then the result is a so-called flat structuring element whose coefficients are zero(T) inside the shape defined by A and -T(Inf) elsewhere.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.ball","page":"Reference","title":"LocalFilters.ball","text":"LocalFilters.ball(Dims{N}, r)\n\nyields a boolean mask which is a N-dimensional array with all dimensions odd and equal and set to true where position is inside a N-dimensional ball of radius r.\n\nTo have a mask with centered index ranges, call:\n\nLocalFilters.centered(LocalFilters.ball(Dims{N}, r))\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.centered","page":"Reference","title":"LocalFilters.centered","text":"LocalFilters.centered(A) -> B\n\nyields an abstract array B sharing the entries of array A but with offsets on indices so that the axes of B are centered (for even dimension lengths, the same conventions as in fftshift are used).\n\nThis method is purposely not exported because it could introduce some confusions. For example OffsetArrays.centered is similar but has a slightly different semantic.\n\nArgument A can also be an index range (linear or Cartesian), in which case a centered index range of same size is returned.\n\nSee LocalFilters.kernel_range.\n\n\n\n\n\n","category":"function"},{"location":"reference/#Utilities","page":"Reference","title":"Utilities","text":"","category":"section"},{"location":"reference/","page":"Reference","title":"Reference","text":"Below are described non-exported types and methods that may be useful in the context of building local filters.","category":"page"},{"location":"reference/","page":"Reference","title":"Reference","text":"LocalFilters.Indices\nLocalFilters.Yields\nLocalFilters.localindices\nLocalFilters.nearest\nLocalFilters.replicate\nLocalFilters.kernel_range","category":"page"},{"location":"reference/#LocalFilters.Indices","page":"Reference","title":"LocalFilters.Indices","text":"LocalFilters.Indices(A...) -> indices\n\nyields a callable object that can be used to produce ranges of indices for each of the arrays A.... These ranges will all be of the same type: linear index ranges, if all arrays A... are vectors implementing fast linear indexing, Cartesian index ranges otherwise.\n\nThe returned object is similar to the eachindex method but specialized for a style of indexing, it can be used as indices(B...) to yield a suitable index range to access all the entries of array(s) B... which are any number of the A... specified when building the indices object. If B... consists in several arrays, they must have the same axes.\n\nCall:\n\nLocalFilters.Indices{S}()\n\nwith S = IndexLinear or S = IndexCartesian to specifically choose the indexing style.\n\n\n\n\n\n","category":"type"},{"location":"reference/#LocalFilters.Yields","page":"Reference","title":"LocalFilters.Yields","text":"f = LocalFilters.Yields(value)\nf = LocalFilters.Yields{V}(value)\n\nbuild a callable object f such that f(args...; kwds...) yields value whatever the arguments args... and the keywords kwds.... If type V is supplied, value is converted to that type.\n\n\n\n\n\n","category":"type"},{"location":"reference/#LocalFilters.localindices","page":"Reference","title":"LocalFilters.localindices","text":"LocalFilters.localindices(A_inds, ord, B_inds, i) -> J\n\nyields the subset J of all indices j such that:\n\nA[j] and B[ord(i,j)] = B[j-i] are in-bounds if ord = FORWARD_FILTER;\nA[j] and B[ord(i,j)] = B[i-j] are in-bounds if ord = REVERSE_FILTER;\n\nwith A and B arrays whose index ranges are given by A_inds and B_inds. To make the code agnostic to the ordering, use A[i] and B[ord(i,j)] to retrieve the values in A and B.\n\nIndex ranges A_inds and B_inds and index i must be of the same kind:\n\nlinear index ranges for A_inds and B_inds and linear index for i;\nCartesian index ranges for A_inds and B_inds and Cartesian index for i of same number of dimensions.\n\nConstructor LocalFilters.Indices may by used to build a callable object that retrieves the index ranges of A and B in a consistent way:\n\nindices = LocalFilters.Indices(A, B)\nA_inds = indices(A)\nB_inds = indices(B)\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.nearest","page":"Reference","title":"LocalFilters.nearest","text":"LocalFilters.nearest(T, x)\n\nconverts value x to the nearest value of type T. By default, the result is given by convert(T,x) unless T is floating-point type and x is integer in which case the result is given by round(T,x).\n\nThis method may be extended for foreign types to implement other conversions.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.replicate","page":"Reference","title":"LocalFilters.replicate","text":"LocalFilters.replicate(NTuple{N}, val)\n\nyields the N-tuple (val, val, ...).\n\nLocalFilters.replicate(NTuple{N,T}, val)\n\nyields the N-tuple (x, x,...) where x is val converted to type T.\n\nSee LocalFilters.Yields.\n\n\n\n\n\n","category":"function"},{"location":"reference/#LocalFilters.kernel_range","page":"Reference","title":"LocalFilters.kernel_range","text":"LocalFilters.kernel_range(start, stop)\nLocalFilters.kernel_range(rng)\nLocalFilters.kernel_range(dim)\n\nyield an Int-valued unit range based on first and last indices start and stop, unit range rng, or dimension length dim. In the case of a given dimension length, a centered range of this length is returned (for even lengths, the same conventions as in fftshift are used).\n\nSee LocalFilters.kernel and LocalFilters.centered.\n\n\n\n\n\n","category":"function"},{"location":"math/#Convolution,-correlation,-and-Fourier-transform","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"","category":"section"},{"location":"math/#Fourier-transform","page":"Convolution, correlation, and Fourier transform","title":"Fourier transform","text":"","category":"section"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"The continuous Fourier transform of a(x) is defined by:","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"hata(u) = int a(x)mathrme^-mathrmi2piuxmathrmdx","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"The inverse Fourier transform of hata(u) then writes:","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"a(x) = int hata(u)mathrme^+mathrmi2piuxmathrmdu","category":"page"},{"location":"math/#Convolution-product","page":"Convolution, correlation, and Fourier transform","title":"Convolution product","text":"","category":"section"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"The convolution product of a(x) by b(x) is defined by:","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"c(x) = mathrmConv(ab)(x)\n= int a(y)b(x - y)mathrmdy\n= int b(z)a(x - z)mathrmdz","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"with z = x - y. This also shows that the convolution product is symmetrical:","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"mathrmConv(ba) = mathrmConv(ab)","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"Taking z = x - y, the Fourier transform of the convolution product can be expanded as follows:","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"beginalign*\nhatc(u) = int c(x)mathrme^-mathrmi2piuxmathrmdx\n= iint a(y)b(x - y)mathrme^-mathrmi2piuxmathrmdxmathrmdy\n= iint a(y)b(z)mathrme^-mathrmi2piu(y + z)mathrmdymathrmdz\n= int a(y)mathrme^-mathrmi2piuymathrmdy\n int b(z)mathrme^-mathrmi2piuzmathrmdz\n= hata(u)hatb(u)\nendalign*","category":"page"},{"location":"math/#Correlation-product","page":"Convolution, correlation, and Fourier transform","title":"Correlation product","text":"","category":"section"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"The correlation product of a(x) by b(x) is defined by:","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"r(x) = mathrmCorr(ab)(x)\n= int a(x + y)b^star(y)mathrmdy\n= int b^star(z - x)a(z)mathrmdz","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"where b^star(y) denotes the complex conjugate of b(y) and with z = x + y. From this follows that:","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"mathrmCorr(ba)(x) = mathrmCorr(ab)^star(-x)","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"Taking z = x + y, the Fourier transform of the correlation product can be expanded as follows:","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"beginalign*\nhatr(u) = int r(x)mathrme^-mathrmi2piuxmathrmdx\n= iint a(x + y)b^star(y)mathrme^-mathrmi2piuxmathrmdxmathrmdy\n= iint a(z)b^star(y)mathrme^-mathrmi2piu(z - y)mathrmdymathrmdz\n= int a(z)mathrme^-mathrmi2piuzmathrmdz\n left(int b(y)mathrme^-mathrmi2piuymathrmdyright)^star\n= hata(u)hatb^star(u)\nendalign*","category":"page"},{"location":"math/#Discrete-convolution-and-correlation","page":"Convolution, correlation, and Fourier transform","title":"Discrete convolution and correlation","text":"","category":"section"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"Following the continuous definition, the discrete convolution of a by b is given by:","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"ci = sum_j ajbi - j = sum_k bkai - k","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"with k = i - j and where the sums are taken for all possible valid indices.","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"Similarly, following the continuous definition, the discrete correlation of a by b is given by:","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"ri = sum_k ai + kb^stark = sum_k b^starj - iaj","category":"page"},{"location":"math/","page":"Convolution, correlation, and Fourier transform","title":"Convolution, correlation, and Fourier transform","text":"with j = i + k and where the sums are taken for all possible valid indices.","category":"page"},{"location":"#Introduction","page":"Introduction","title":"Introduction","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"The LocalFilters package provides local linear and non-linear filters for Julia.","category":"page"},{"location":"","page":"Introduction","title":"Introduction","text":"The source code of LocalFilters is available on GitHub.","category":"page"},{"location":"#Table-of-contents","page":"Introduction","title":"Table of contents","text":"","category":"section"},{"location":"","page":"Introduction","title":"Introduction","text":"Pages = [\"generic.md\", \"neighborhoods.md\", \"linear.md\", \"nonlinear.md\",\n \"morphology.md\", \"separable.md\", \"math.md\", \"reference.md\"]","category":"page"},{"location":"generic/#Generic-local-filters","page":"Generic local filters","title":"Generic local filters","text":"","category":"section"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"Most filters provided by the LocalFilters package are implemented by the generic localfilter! method.","category":"page"},{"location":"generic/#The-localfilter!-method","page":"Generic local filters","title":"The localfilter! method","text":"","category":"section"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"A local filtering operation can be performed by calling the localfilter! method:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"localfilter!(dst, A, B, initial, update, final)","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"where dst is the destination, A is the source, B defines the neighborhood, initial is a function or the initial value of the state variable, update is a function to update the state variable for each entry of the neighborhood, and final is a function to yield the local result of the filter given the final value of the state variable. The purposes of these parameters are explained by the following pseudo-code implementing the local filtering:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"@inbounds for i ∈ indices(dst)\n v = initial isa Function ? initial(A[i]) : initial\n for j ∈ indices(A) ∩ (indices(B) + i)\n v = update(v, A[j], B[j-i])\n end\n dst[i] = final(v)\nend","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"where indices(A) denotes the set of indices of A while indices(B) + i denotes the set of indices j such that j - i ∈ indices(B). In other words, j ∈ indices(A) ∩ (indices(B) + i) means all indices j such that j ∈ indices(A) and j - i ∈ indices(B), hence A[j] and B[j-i] are both in-bounds. In LocalFilters, indices i and j are Cartesian indices for multi-dimensional arrays, thus indices(A) is the analogous of CartesianIndices(A) in Julia in that case. For vectors, indices i and j are linear indices.","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"The behavior of the filter is fully determined by the neighborhood B (see Section Neighborhoods, structuring elements, and kernels) and by the other arguments to deal with the state variable v:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"initial may be a function, in which case the state variable is initially given by v = initial(A[i]); otherwise, initial is assumed to be the initial value of the state variable. If initial is a function, then dst and A must have the same axes.\nupdate(v, a, b) yields the updated state variable v given v, a = A[j], and b = B[j-i].\nfinal(v) yields the result of the filter given the state variable v at the end of the loop on the neighborhood. If not specified, final = identity is assumed.","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"The localfilter! method takes another optional argument ord::FilterOrdering to specify the ordering of the filter:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"localfilter!(dst, A, ord, B, initial, update, final)","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"By default, ord = FORWARD_FILTER which is implemented by the above pseudo-code and which corresponds to a correlation for a shift-invariant linear filter. The other possibility is ord = REVERSE_FILTER which corresponds to a convolution for a shift-invariant linear filter and which amounts to:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"@inbounds for i ∈ indices(dst)\n v = initial isa Function ? initial(A[i]) : initial\n for j ∈ indices(A) ∩ (i - indices(B))\n v = update(v, A[j], B[i-j])\n end\n dst[i] = final(v)\nend","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"If B is symmetric, in the sense that B[-j] = B[j] for any in-bounds j, both orderings yield the same result but FORWARD_FILTER is generally faster which is why it is used by default.","category":"page"},{"location":"generic/#Examples","page":"Generic local filters","title":"Examples","text":"","category":"section"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"Implementing a local minimum filter (that is, an erosion) with localfilter! is as simple as:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"dst = localfilter!(similar(A), A, B,\n #= initial =# typemax(eltype(A)),\n #= update =# (v,a,b) -> min(v,a))","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"This is typically how Basic morphological operations are implemented in LocalFilters. Note that localfilter! returns the destination. Also note that B is only used to define the neighborhood, it is usually called a structuring element in this context.","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"As another example, implementing a convolution of A by B writes:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"dst = localfilter!(similar(A), A, REVERSE_FILTER, B,\n #= initial =# zero(eltype(A)),\n #= update =# (v,a,b) -> v + a*b)","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"while:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"dst = localfilter!(similar(A), A, FORWARD_FILTER, B,\n #= initial =# zero(eltype(A)),\n #= update =# (v,a,b) -> v + a*b)","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"computes a correlation of A by B. The only difference is the ord argument which may be omitted in the latter case as FORWARD_FILTER is the default. In the case of convolutions and correlations, B defines the neighborhood but also the weights, it is usually called a kernel in this context.","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"Apart from specializations to account for the type of neighborhood defined by B, it is essentially the way the correlate and convolve! methods (described in the Section Linear filters) are implemented in LocalFilters.","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"In the above examples, the initial value of the state variable is always the same and directly provided while the default final = identity is assumed. Below is a more involved example to compute the roughness defined by Wilson et al. (in Marine Geodesy 30:3-35, 2007) as the maximum absolute difference between a central cell and surrounding cells:","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"function roughness(A::AbstractArray{<:Real}, B=3)\n initial(a) = (; result = zero(a), center = a)\n update(v, a, _) = (; result = max(v.result, abs(a - v.center)), center=v.center)\n final(v) = v.result\n return localfilter!(similar(A), A, B, initial, update, final)\nend","category":"page"},{"location":"generic/","page":"Generic local filters","title":"Generic local filters","text":"This example has been borrowed from the Geomorphometry package for the analysis of Digital Elevation Models (DEM).","category":"page"}] } diff --git a/dev/separable/index.html b/dev/separable/index.html index 9a88b7c..82d5ebc 100644 --- a/dev/separable/index.html +++ b/dev/separable/index.html @@ -1,2 +1,2 @@ -Efficient separable filters for associative operations · LocalFilters.jl Package

Efficient separable filters for associative operations

Out-of-place version

The localfilter method may be called as:

dst = localfilter([T=eltype(A),] A, dims, op, rngs [, w])

to apply the van Herk-Gil-Werman algorithm to filter array A along dimension(s) dims with (associative) binary operation op and contiguous structuring element(s) defined by the interval(s) rngs. Optional argument T is the element type of the result dst (by default T = eltype(A)). Optional argument w is a workspace array which is automatically allocated if not provided; otherwise, it must be a vector with the same element type as A (w is resized as needed by calling resize!).

Argument dims specifies along which dimension(s) of A the filter is to be applied, it can be a single integer, several integers or a colon : to specify all dimensions. Dimensions are processed in the order given by dims (the same dimension may appear several times) and there must be a matching interval in rngs to specify the structuring element (except that if rngs is a single interval, it is used for every dimension in dims). An interval is either an integer or an integer valued unit range in the form kmin:kmax (an interval specified as a single integer, say k, is the same as specifying k:k).

Assuming a mono-dimensional array A, the single filtering pass:

localfilter!(dst, A, :, op, rng)

amounts to computing:

dst[j] = A[j-kmax] ⋄ A[j-kmax+1] ⋄ A[j-kmax+2] ⋄ ... ⋄ A[j-kmin]

for all j ∈ [firstindex(A):lastindex(A)], with x ⋄ y = op(x, y), kmin = first(rng) and kmax = last(rng). Note that if rng = k:k or rng = k with k an integer, the result of the filter is to operate a simple shift by k along the corresponding dimension and has no effects if k = 0. This can be exploited to not filter some dimension(s).

In-place version

The localfilter! method implements the in-place version of the van Herk-Gil-Werman algorithm:

localfilter!([dst = A,] A, dims, op, rngs [, w]) -> dst

overwrites the contents of dst with the result of the filter and returns dst. The destination array dst must have the same indices as the source A (that is, axes(dst) == axes(A) must hold). If dst is not specified or if dst is A, the operation is performed in-place.

Examples

The in-place morphological erosion (local minimum) of the array A on a centered structuring element of width 7 in every dimension can be done by:

localfilter!(A, :, min, -3:3)

As said before, index interval 0 may be specified to do nothing along the corresponding dimension. For instance, assuming A is a three-dimensional array:

localfilter!(A, :, max, (-3:3, 0, -4:4))

overwrites A by its morphological dilation (i.e. local maximum) in a centered local neighborhood of size 7×1×9 (nothing is done along the second dimension). The same result may be obtained with:

localfilter!(A, (1,3), max, (-3:3, -4:4))

where the second dimension is omitted from the list of dimensions.

The local average of the two-dimensional array A on a centered moving window of size 11×11 can be computed as:

localfilter(A, :, +, (-5:5, -5:5))*(1/11^2)

Efficiency and restrictions

The van Herk-Gil-Werman algorithm is very fast for rectangular structuring elements. It takes at most 3 operations to filter an element along a given dimension whatever the width p of the considered local neighborhood. For N-dimensional arrays, the algorithm requires only 3N operations per element instead of p^N - 1 operations for a naive implementation. This however requires to make a pass along each dimension so memory page faults may reduce the performances. This is somewhat attenuated by the fact that the algorithm can be applied in-place. For efficient multi-dimensional out-of-place filtering, it is recommended to make the first pass with a fresh destination array and then all other passes in-place on the destination array.

To apply the van Herk-Gil-Werman algorithm, the structuring element must be separable along the dimensions and its components must be contiguous. In other words, the algorithm is only applicable for N-dimensional rectangular neighborhoods, so-called hyperrectangles. The structuring element may however be off-centered by arbitrary offsets along each dimension.

To take into account boundary conditions (for now, only nearest neighbor is implemented) and allow for in-place operation, the algorithm allocates a workspace array.

References

  • Marcel van Herk, "A fast algorithm for local minimum and maximum filters on rectangular and octagonal kernels" in Pattern Recognition Letters 13, 517-521 (1992).

  • Joseph Gil and Michael Werman, "Computing 2-D Min, Median, and Max Filters" in IEEE Transactions on Pattern Analysis and Machine Intelligence 15, 504-507 (1993).

+Efficient separable filters for associative operations · LocalFilters.jl Package

Efficient separable filters for associative operations

Out-of-place version

The localfilter method may be called as:

dst = localfilter([T=eltype(A),] A, dims, op, rngs [, w])

to apply the van Herk-Gil-Werman algorithm to filter array A along dimension(s) dims with (associative) binary operation op and contiguous structuring element(s) defined by the interval(s) rngs. Optional argument T is the element type of the result dst (by default T = eltype(A)). Optional argument w is a workspace array which is automatically allocated if not provided; otherwise, it must be a vector with the same element type as A (w is resized as needed by calling resize!).

Argument dims specifies along which dimension(s) of A the filter is to be applied, it can be a single integer, several integers or a colon : to specify all dimensions. Dimensions are processed in the order given by dims (the same dimension may appear several times) and there must be a matching interval in rngs to specify the structuring element (except that if rngs is a single interval, it is used for every dimension in dims). An interval is either an integer or an integer valued unit range in the form kmin:kmax (an interval specified as a single integer, say k, is the same as specifying k:k).

Assuming a mono-dimensional array A, the single filtering pass:

localfilter!(dst, A, :, op, rng)

amounts to computing:

dst[j] = A[j-kmax] ⋄ A[j-kmax+1] ⋄ A[j-kmax+2] ⋄ ... ⋄ A[j-kmin]

for all j ∈ [firstindex(A):lastindex(A)], with x ⋄ y = op(x, y), kmin = first(rng) and kmax = last(rng). Note that if rng = k:k or rng = k with k an integer, the result of the filter is to operate a simple shift by k along the corresponding dimension and has no effects if k = 0. This can be exploited to not filter some dimension(s).

In-place version

The localfilter! method implements the in-place version of the van Herk-Gil-Werman algorithm:

localfilter!([dst = A,] A, dims, op, rngs [, w]) -> dst

overwrites the contents of dst with the result of the filter and returns dst. The destination array dst must have the same indices as the source A (that is, axes(dst) == axes(A) must hold). If dst is not specified or if dst is A, the operation is performed in-place.

Examples

The in-place morphological erosion (local minimum) of the array A on a centered structuring element of width 7 in every dimension can be done by:

localfilter!(A, :, min, -3:3)

As said before, index interval 0 may be specified to do nothing along the corresponding dimension. For instance, assuming A is a three-dimensional array:

localfilter!(A, :, max, (-3:3, 0, -4:4))

overwrites A by its morphological dilation (i.e. local maximum) in a centered local neighborhood of size 7×1×9 (nothing is done along the second dimension). The same result may be obtained with:

localfilter!(A, (1,3), max, (-3:3, -4:4))

where the second dimension is omitted from the list of dimensions.

The local average of the two-dimensional array A on a centered moving window of size 11×11 can be computed as:

localfilter(A, :, +, (-5:5, -5:5))*(1/11^2)

Efficiency and restrictions

The van Herk-Gil-Werman algorithm is very fast for rectangular structuring elements. It takes at most 3 operations to filter an element along a given dimension whatever the width p of the considered local neighborhood. For N-dimensional arrays, the algorithm requires only 3N operations per element instead of p^N - 1 operations for a naive implementation. This however requires to make a pass along each dimension so memory page faults may reduce the performances. This is somewhat attenuated by the fact that the algorithm can be applied in-place. For efficient multi-dimensional out-of-place filtering, it is recommended to make the first pass with a fresh destination array and then all other passes in-place on the destination array.

To apply the van Herk-Gil-Werman algorithm, the structuring element must be separable along the dimensions and its components must be contiguous. In other words, the algorithm is only applicable for N-dimensional rectangular neighborhoods, so-called hyperrectangles. The structuring element may however be off-centered by arbitrary offsets along each dimension.

To take into account boundary conditions (for now, only nearest neighbor is implemented) and allow for in-place operation, the algorithm allocates a workspace array.

References

  • Marcel van Herk, "A fast algorithm for local minimum and maximum filters on rectangular and octagonal kernels" in Pattern Recognition Letters 13, 517-521 (1992).

  • Joseph Gil and Michael Werman, "Computing 2-D Min, Median, and Max Filters" in IEEE Transactions on Pattern Analysis and Machine Intelligence 15, 504-507 (1993).