DirectConvolution

Documentation for DirectConvolution.

This package allows efficient computation of

\[\gamma[k] = \sum\limits_{i\in\Omega_\alpha}\alpha[i]\beta[k+\lambda i]\]

where $\alpha$ is a filter of support $\Omega_\alpha$ defined as follows (see filter of support):

\[\Omega_\alpha = \llbracket -\text{offset}(\alpha), -\text{offset}(\alpha) +\text{length}(\alpha)-1 \rrbracket\]

.

For $\lambda=-1$ you get a convolution, for $\lambda=+1$ a wiki:cross-correlation whereas using $\lambda=\pm 2^n$ is useful to implement the undecimated wavelet transform (the so called wiki:algorithme à trous).

Use cases

These demos use data stored in the data/ folder.

julia> dataDir"/juliateam/.julia/packages/DirectConvolution/tQ2NZ/src/../data"

There are one 1D signal and one 2D signal:

data_1D = readdlm(joinpath(dataDir,"signal_1.csv"),',');
data_1D_x = @view data_1D[:,1]
data_1D_y = @view data_1D[:,2]
plot(data_1D_x,data_1D_y,label="signal")
data_2D=readdlm(joinpath(dataDir,"surface.data"));
surface(data_2D,label="2D signal")

Savitzky-Golay filters

This example shows how to compute and use wiki: Savitzky-Golay filters.

Filter creation

Creates a set of Savitzky-Golay filters using polynomial of degree $3$ with a window width of $11=2\times 5+1$.

sg = SG_Filter(Float64,halfWidth=5,degree=3);
SG_Filter{Float64, 11}(DirectConvolution.LinearFilter_DefaultCentered{Float64, 11}[Filter(r=-5:5,c=[-0.08392, 0.02098, 0.1026, 0.1608, 0.1958, 0.2075, 0.1958, 0.1608, 0.1026, 0.02098, -0.08392])
, Filter(r=-5:5,c=[0.05828, -0.05711, -0.1033, -0.09771, -0.0575, -2.864e-18, 0.0575, 0.09771, 0.1033, 0.05711, -0.05828])
, Filter(r=-5:5,c=[0.03497, 0.01399, -0.002331, -0.01399, -0.02098, -0.02331, -0.02098, -0.01399, -0.002331, 0.01399, 0.03497])
, Filter(r=-5:5,c=[-0.03497, 0.006993, 0.02564, 0.02681, 0.01632, 2.404e-18, -0.01632, -0.02681, -0.02564, -0.006993, 0.03497])
])

This can be checked with

julia> length(sg)11
julia> polynomialOrder(sg)3

1D signal smoothing

Apply this filter on an unidimensional signal:

data_1D_y_smoothed = apply_SG_filter(data_1D_y,sg,derivativeOrder=0)

plot(data_1D_x,data_1D_y_smoothed,linewidth=3,label="smoothed signal")
plot!(data_1D_x,data_1D_y,label="signal")

2D signal smoothing

Create two filters, one for the I direction, the other for the J direction. Then, apply these filters on a two dimensional signal.

sg_I = SG_Filter(Float64,halfWidth=5,degree=3);
sg_J = SG_Filter(Float64,halfWidth=3,degree=3);

data_2D_smoothed = apply_SG_filter2D(data_2D,
                               sg_I,
                               sg_J,
                               derivativeOrder_I=0,
                               derivativeOrder_J=0)

surface(data_2D_smoothed,label="Smoothed 2D signal");

Wavelet transform

Choose a wavelet filter:

filter = UDWT_Filter_Starck2{Float64}()
UDWT_Filter_Starck2{Float64}(Filter(r=-2:2,c=[0.0625, 0.25, 0.375, 0.25, 0.0625])
, Filter(r=-2:2,c=[-0.0625, -0.25, 0.625, -0.25, -0.0625])
, Filter(r=0:0,c=[1.0])
, Filter(r=0:0,c=[1.0])
)

Perform a UDWT transform:

data_1D_udwt = udwt(data_1D_y,filter,scale=8)
DirectConvolution.UDWT{Float64}(UDWT_Filter_Starck2{Float64}(Filter(r=-2:2,c=[0.0625, 0.25, 0.375, 0.25, 0.0625])
, Filter(r=-2:2,c=[-0.0625, -0.25, 0.625, -0.25, -0.0625])
, Filter(r=0:0,c=[1.0])
, Filter(r=0:0,c=[1.0])
), [5.25 14.671875 … -2.9493609070777893 -14.86664617061615; -3.75 -4.14453125 … -2.9738472998142242 -15.00585688650608; … ; -1.8125 12.3046875 … -2.8953346759080887 -14.590529406210408; 23.5625 22.47265625 … -2.92319642752409 -14.72818864369765], [103.53602027893066, 103.67805175483227, 103.82173991226591, 103.96707606688142, 104.11405147449113, 104.26265731197782, 104.41288469638675, 104.56472466979176, 104.7181681978982, 104.87320622312836  …  102.20866686874069, 102.33367763482966, 102.46042445488274, 102.58890068694018, 102.71909916191362, 102.85101238754578, 102.98463259707205, 103.11995178461075, 103.2569617421832, 103.39565408462659])

Plot Results:

label=["W$i" for i in 1:scale(data_1D_udwt)];
plot(data_1D_udwt.W,label=reshape(label,1,scale(data_1D_udwt)))
plot!(data_1D_udwt.V,label="V$(scale(data_1D_udwt))");
plot!(data_1D_y,label="signal")

Inverse the transform (more precisely because of the coefficient redundancy, a pseudo-inverse is used):

data_1D_y_reconstructed = inverse_udwt(data_1D_udwt);
norm(data_1D_y-data_1D_y_reconstructed)
0.0

To smooth the signal a (very) rough solution would be to cancel the two finer scales:

data_1D_udwt.W[:,1] .= 0
data_1D_udwt.W[:,2] .= 0

data_1D_y_reconstructed = inverse_udwt(data_1D_udwt)

plot(data_1D_y_reconstructed,linewidth=3, label="smoothed");
plot!(data_1D_y,label="signal")

API

DirectConvolution.LinearFilter_DefaultType
struct LinearFilter_Default{T<:Number,N}

Default linear filter.

You can create a filter as follows

julia> linear_filter=LinearFilter([1,-2,1],1)
Filter(r=-1:1,c=[1.0, -2.0, 1.0])


julia> offset(linear_filter)
1


julia> range(linear_filter)
-1:1
DirectConvolution.SG_FilterType
function SG_Filter(T::DataType=Float64;halfWidth::Int=5,degree::Int=2)

Creates a SG_Filter structure used to store Savitzky-Golay filters.

  • filter length is 2*halfWidth+1
  • polynomial degree is degree, which defines maxDerivativeOrder

You can apply these filters using the

  • apply_SG_filter
  • apply_SG_filter2D

functions.

Example:

julia> sg = SG_Filter(halfWidth=5,degree=3);


julia> maxDerivativeOrder(sg)
3

julia> length(sg)
11

julia> filter(sg,derivativeOrder=2)
Filter(r=-5:5,c=[0.03497, 0.01399, -0.002331, -0.01399, -0.02098, -0.02331, -0.02098, -0.01399, -0.002331, 0.01399, 0.03497])
DirectConvolution.UDWT_FilterType
abstract type UDWT_Filter{T<:Number} <: UDWT_Filter_Biorthogonal{T} end

A specialization of UDWT_Filter_Biorthogonal for orthogonal filters.

For orthogonal filters we have: ϕ=tildeϕ, ψ=tildeψ

DirectConvolution.UDWT_Filter_BiorthogonalType
abstract type UDWT_Filter_Biorthogonal{T<:Number} end

Abstract type defining the ϕ, ψ, tildeϕ and tildeψ filters associated to an undecimated biorthogonal wavelet transform

Subtypes of this structure are:

Associated methods are:

  • ϕ_filter
  • ψ_filter
  • tildeϕ_filter
  • tildeψ_filter
Base.filterMethod
function filter(sg::SG_Filter{T,N};derivativeOrder::Int=0)

Returns the filter to be used to compute the smoothed derivatives of order derivativeOrder.

See: SG_Filter

Base.lengthMethod
length(c::LinearFilter)::Int

Returns filter length

Base.lengthMethod
function length(sg::SG_Filter{T,N})

Returns filter length, this is an odd number.

See: SG_Filter

Base.rangeMethod
range(c::LinearFilter)::UnitRange

Returns filter range Ω

Filter support of a filter α is defined by Ω = [ - offset(α), length(α) - offset(α) - 1 ]

DirectConvolution.apply_SG_filterMethod
function apply_SG_filter(signal::Array{T,1},
                         sg::SG_Filter{T};
                         derivativeOrder::Int=0,
                         left_BE::Type{<:BoundaryExtension}=ConstantBE,
                         right_BE::Type{<:BoundaryExtension}=ConstantBE)

Applies an 1D Savitzky-Golay and returns the smoothed signal.

DirectConvolution.apply_SG_filter2DMethod
function apply_SG_filter2D(signal::Array{T,2},
                           sg_I::SG_Filter{T},
                           sg_J::SG_Filter{T};
                           derivativeOrder_I::Int=0,
                           derivativeOrder_J::Int=0,
                           min_I_BE::Type{<:BoundaryExtension}=ConstantBE,
                           max_I_BE::Type{<:BoundaryExtension}=ConstantBE,
                           min_J_BE::Type{<:BoundaryExtension}=ConstantBE,
                           max_J_BE::Type{<:BoundaryExtension}=ConstantBE)

Applies an 2D Savitzky-Golay and returns the smoothed signal.

DirectConvolution.boundaryExtensionFunction
boundaryExtension(β::AbstractArray{T,1},
                  k::Int,
                  ::Type{BOUNDARY_EXT_TYPE})

Computes extended boundary value β[k] given boundary extension type BOUNDARY_EXT_TYPE

Available BOUNDARY_EXT_TYPE are:

  • ZeroPaddingBE: zero padding
  • ConstantBE: constant boundary extension padding
  • PeriodicBE: periodic boundary extension padding
  • MirrorBE: mirror symmetry boundary extension padding

The routine is robust in the sens that there is no restriction on k value.

julia> dom = [-5:10;];

julia> hcat(dom,map(x->boundaryExtension([1; 2; 3],x,ZeroPaddingBE),dom))'
2×16 adjoint(::Matrix{Int64}) with eltype Int64:
 -5  -4  -3  -2  -1  0  1  2  3  4  5  6  7  8  9  10
  0   0   0   0   0  0  1  2  3  0  0  0  0  0  0   0

julia> hcat(dom,map(x->boundaryExtension([1; 2; 3],x,ConstantBE),dom))'
2×16 adjoint(::Matrix{Int64}) with eltype Int64:
 -5  -4  -3  -2  -1  0  1  2  3  4  5  6  7  8  9  10
  1   1   1   1   1  1  1  2  3  3  3  3  3  3  3   3

julia> hcat(dom,map(x->boundaryExtension([1; 2; 3],x,PeriodicBE),dom))'
2×16 adjoint(::Matrix{Int64}) with eltype Int64:
 -5  -4  -3  -2  -1  0  1  2  3  4  5  6  7  8  9  10
  1   2   3   1   2  3  1  2  3  1  2  3  1  2  3   1

julia> hcat(dom,map(x->boundaryExtension([1; 2; 3],x,MirrorBE),dom))'
2×16 adjoint(::Matrix{Int64}) with eltype Int64:
 -5  -4  -3  -2  -1  0  1  2  3  4  5  6  7  8  9  10
  3   2   1   2   3  2  1  2  3  2  1  2  3  2  1   2
DirectConvolution.directConv!Method
directConv!

Computes a convolution.

\[\gamma[k]=\sum\limits_{i\in\Omega^\alpha}\alpha[i]\beta[k+\lambda i]\]

The following example shows how to apply inplace the [0 0 1] filter on γ[5:10]

julia> β=[1:15;];

julia> γ=ones(Int,15);

julia> α=LinearFilter([0,0,1],0);

julia> directConv!(α,1,β,γ,5:10)

julia> hcat([1:length(γ);],γ)
15×2 Matrix{Int64}:
  1   1
  2   1
  3   1
  4   1
  5   7
  6   8
  7   9
  8  10
  9  11
 10  12
 11   1
 12   1
 13   1
 14   1
 15   1
DirectConvolution.maxDerivativeOrderMethod
function maxDerivativeOrder(sg::SG_Filter{T,N})

Maximum order of the smoothed derivatives we can compute using sg filters.

See: SG_Filter

DirectConvolution.offsetMethod
offset(c::LinearFilter)::Int

Returns filter offset

Caveat: the first position is 0 (and not 1)

DirectConvolution.polynomialOrderMethod
function polynomialOrder(sg::SG_Filter{T,N})

Returns the degree of the polynomial used to construct the Savitzky-Golay filters. This is mainly a 'convenience' function, as it is equivalent to maxDerivativeOrder

See: SG_Filter

DirectConvolution.scaleMethod
scale(λ::Int,Ω::UnitRange{Int})

Range scaling.

Caveat:

We do not use Julia * operator as it returns a step range:

julia> r=6:8
6:8

julia> -2*r
-12:-2:-16

What we need is:

julia> r=6:8
6:8

julia> scale(-2,r)
-16:-12