Skip to content

Commit fdfccd7

Browse files
committed
Merge pull request #7560 from mbauman/filtarray
Filtering (filt) across columns of arrays and method signature tweaks
2 parents 3f0adc6 + 06803c9 commit fdfccd7

File tree

3 files changed

+51
-36
lines changed

3 files changed

+51
-36
lines changed

base/dsp.jl

+36-29
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module DSP
22

33
importall Base.FFTW
44
import Base.FFTW.normalization
5+
import Base.trailingsize
56

67
export FFTW, filt, filt!, deconv, conv, conv2, xcorr, fftshift, ifftshift,
78
dct, idct, dct!, idct!, plan_dct, plan_idct, plan_dct!, plan_idct!,
@@ -10,19 +11,17 @@ export FFTW, filt, filt!, deconv, conv, conv2, xcorr, fftshift, ifftshift,
1011
plan_fft, plan_bfft, plan_ifft, plan_rfft, plan_brfft, plan_irfft,
1112
fft!, bfft!, ifft!, plan_fft!, plan_bfft!, plan_ifft!
1213

13-
function filt{T<:Number}(b::Union(AbstractVector{T}, T), a::Union(AbstractVector{T}, T),
14-
x::AbstractVector{T}; si::AbstractVector{T}=zeros(T, max(length(a), length(b))-1))
15-
filt!(Array(T, size(x)), b, a, x, si)
14+
_zerosi(b,a,T) = zeros(promote_type(eltype(b), eltype(a), T), max(length(a), length(b))-1)
15+
16+
function filt{T,S}(b::Union(AbstractVector, Number), a::Union(AbstractVector, Number),
17+
x::AbstractArray{T}, si::AbstractArray{S}=_zerosi(b,a,T))
18+
filt!(Array(promote_type(eltype(b), eltype(a), T, S), size(x)), b, a, x, si)
1619
end
1720

1821
# in-place filtering: returns results in the out argument, which may shadow x
1922
# (and does so by default)
20-
function filt!{T<:Number}(b::Union(AbstractVector{T}, T), a::Union(AbstractVector{T}, T), x::AbstractVector{T};
21-
si::AbstractVector{T}=zeros(T, max(length(a), length(b))-1), out::AbstractVector{T}=x)
22-
filt!(out, b, a, x, si)
23-
end
24-
function filt!{T<:Number}(out::AbstractVector{T}, b::Union(AbstractVector{T}, T), a::Union(AbstractVector{T}, T),
25-
x::AbstractVector{T}, si::AbstractVector{T})
23+
function filt!{T,S,N}(out::AbstractArray, b::Union(AbstractVector, Number), a::Union(AbstractVector, Number),
24+
x::AbstractArray{T}, si::AbstractArray{S,N}=_zerosi(b,a,T))
2625
isempty(b) && error("b must be non-empty")
2726
isempty(a) && error("a must be non-empty")
2827
a[1] == 0 && error("a[1] must be nonzero")
@@ -33,6 +32,10 @@ function filt!{T<:Number}(out::AbstractVector{T}, b::Union(AbstractVector{T}, T)
3332
sz = max(as, bs)
3433
silen = sz - 1
3534
xs = size(x,1)
35+
ncols = trailingsize(x,2)
36+
37+
size(si, 1) != silen && error("si must have max(length(a),length(b))-1 rows")
38+
N > 1 && trailingsize(si,2) != ncols && error("si must either be a vector or have the same number of columns as x")
3639

3740
xs == 0 && return out
3841
sz == 1 && return scale!(out, x, b[1]/a[1]) # Simple scaling without memory
@@ -44,29 +47,33 @@ function filt!{T<:Number}(out::AbstractVector{T}, b::Union(AbstractVector{T}, T)
4447
b ./= norml
4548
end
4649
# Pad the coefficients with zeros if needed
47-
bs<sz && (b = copy!(zeros(T,sz), b))
48-
1<as<sz && (a = copy!(zeros(T,sz), a))
50+
bs<sz && (b = copy!(zeros(eltype(b), sz), b))
51+
1<as<sz && (a = copy!(zeros(eltype(a), sz), a))
4952

50-
si = copy!(Array(T, silen), si)
51-
if as > 1
52-
@inbounds for i=1:xs
53-
xi = x[i]
54-
val = si[1] + b[1]*xi
55-
for j=1:(silen-1)
56-
si[j] = si[j+1] + b[j+1]*xi - a[j+1]*val
53+
initial_si = si
54+
for col = 1:ncols
55+
# Reset the filter state
56+
si = initial_si[:, N > 1 ? col : 1]
57+
if as > 1
58+
@inbounds for i=1:xs
59+
xi = x[i,col]
60+
val = si[1] + b[1]*xi
61+
for j=1:(silen-1)
62+
si[j] = si[j+1] + b[j+1]*xi - a[j+1]*val
63+
end
64+
si[silen] = b[silen+1]*xi - a[silen+1]*val
65+
out[i,col] = val
5766
end
58-
si[silen] = b[silen+1]*xi - a[silen+1]*val
59-
out[i] = val
60-
end
61-
else
62-
@inbounds for i=1:xs
63-
xi = x[i]
64-
val = si[1] + b[1]*xi
65-
for j=1:(silen-1)
66-
si[j] = si[j+1] + b[j+1]*xi
67+
else
68+
@inbounds for i=1:xs
69+
xi = x[i,col]
70+
val = si[1] + b[1]*xi
71+
for j=1:(silen-1)
72+
si[j] = si[j+1] + b[j+1]*xi
73+
end
74+
si[silen] = b[silen+1]*xi
75+
out[i,col] = val
6776
end
68-
si[silen] = b[silen+1]*xi
69-
out[i] = val
7077
end
7178
end
7279
return out

doc/stdlib/base.rst

+5-5
Original file line numberDiff line numberDiff line change
@@ -4664,15 +4664,15 @@ calling functions from `FFTW <http://www.fftw.org>`_.
46644664

46654665
Undoes the effect of ``fftshift``.
46664666

4667-
.. function:: filt(b, a, x; si=zeros(max(length(a),length(b))-1))
4667+
.. function:: filt(b, a, x, [si])
46684668

46694669
Apply filter described by vectors ``a`` and ``b`` to vector ``x``, with an
4670-
optional initial filter state keyword argument ``si`` (defaults to zeros).
4670+
optional initial filter state vector ``si`` (defaults to zeros).
46714671

4672-
.. function:: filt!(b, a, x; si=zeros(max(length(a),length(b))-1), out=x)
4672+
.. function:: filt!(out, b, a, x, [si])
46734673

4674-
Same as :func:`filt`, but stores the result in the ``out`` keyword argument,
4675-
which may alias the input ``x`` to modify it in-place (it does so by default).
4674+
Same as :func:`filt` but writes the result into the ``out`` argument,
4675+
which may alias the input ``x`` to modify it in-place.
46764676

46774677
.. function:: deconv(b,a)
46784678

test/dsp.jl

+10-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@ x = [1., 1., 0., 1., 1., 0., 0., 0.]
66
# With ranges
77
@test filt(b, 1., 1.0:10.0) == [1., 4., 10., 20., 30., 40., 50., 60., 70., 80.]
88
@test filt(1.:4., 1., 1.0:10.0) == [1., 4., 10., 20., 30., 40., 50., 60., 70., 80.]
9+
# Across an array is the same as channel-by-channel
10+
@test filt(b, 1., [x 1.0:8.0]) == [filt(b, 1., x) filt(b, 1., 1.0:8.0)]
11+
@test filt(b, [1., -0.5], [x 1.0:8.0]) == [filt(b, [1., -0.5], x) filt(b, [1., -0.5], 1.0:8.0)]
12+
si = zeros(3)
13+
@test filt(b, 1., [x 1.0:8.0], si) == [filt(b, 1., x, si) filt(b, 1., 1.0:8.0, si)]
14+
@test si == zeros(3) # Will likely fail if/when arrayviews are implemented
15+
si = [zeros(3) ones(3)]
16+
@test filt(b, 1., [x 1.0:8.0], si) == [filt(b, 1., x, zeros(3)) filt(b, 1., 1.0:8.0, ones(3))]
917
# With initial conditions: a lowpass 5-pole butterworth filter with W_n = 0.25,
1018
# and a stable initial filter condition matched to the initial value
1119
b = [0.003279216306360201,0.016396081531801006,0.03279216306360201,0.03279216306360201,0.016396081531801006,0.003279216306360201]
1220
a = [1.0,-2.4744161749781606,2.8110063119115782,-1.703772240915465,0.5444326948885326,-0.07231566910295834]
13-
init = [0.9967207836936347,-1.4940914728163142,1.2841226760316475,-0.4524417279474106,0.07559488540931815]
14-
@test_approx_eq filt(b, a, ones(10), si=init) ones(10) # Shouldn't affect DC offset
21+
si = [0.9967207836936347,-1.4940914728163142,1.2841226760316475,-0.4524417279474106,0.07559488540931815]
22+
@test_approx_eq filt(b, a, ones(10), si) ones(10) # Shouldn't affect DC offset
1523

1624
# Convolution
1725
a = [1., 2., 1., 2.]

0 commit comments

Comments
 (0)