From 946464b3d940bb67361746391a150b8d463dbeff Mon Sep 17 00:00:00 2001 From: "Longhao.Chen" Date: Mon, 22 Feb 2021 10:55:43 +0800 Subject: [PATCH 1/6] feat:add modes to conv function (#399) --- src/dspbase.jl | 58 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/src/dspbase.jl b/src/dspbase.jl index fbcb5a7d7..af44cf7f0 100644 --- a/src/dspbase.jl +++ b/src/dspbase.jl @@ -679,13 +679,31 @@ end # May switch argument order """ - conv(u,v) + conv(u,v; mode = :full) Convolution of two arrays. Uses either FFT convolution or overlap-save, depending on the size of the input. `u` and `v` can be N-dimensional arrays, with arbitrary indexing offsets, but their axes must be a `UnitRange`. + +:full — Return the full 2-D convolution. + +:same — Return the central part of the convolution, which is the same size as u. + +:valid — Return only parts of the convolution that are computed without zero-padded edges. """ -function conv(u::AbstractArray{T, N}, +function conv(u, v; mode::Symbol = :full) + if mode == :full + conv_full(u, v) + elseif mode == :same + conv_same(u, v) + elseif mode == :valid + conv_valid(u, v) + else + throw(ArgumentError("mode keyword argument must be either :full or :same or :valid")) + end +end + +function conv_full(u::AbstractArray{T, N}, v::AbstractArray{T, N}) where {T<:BLAS.BlasFloat, N} su = size(u) sv = size(v) @@ -696,38 +714,52 @@ function conv(u::AbstractArray{T, N}, end end -function conv(u::AbstractArray{<:BLAS.BlasFloat, N}, +function conv_full(u::AbstractArray{<:BLAS.BlasFloat, N}, v::AbstractArray{<:BLAS.BlasFloat, N}) where N fu, fv = promote(u, v) - conv(fu, fv) + conv_full(fu, fv) end -conv(u::AbstractArray{<:Integer, N}, v::AbstractArray{<:Integer, N}) where {N} = +conv_full(u::AbstractArray{<:Integer, N}, v::AbstractArray{<:Integer, N}) where {N} = round.(Int, conv(float(u), float(v))) -conv(u::AbstractArray{<:Number, N}, v::AbstractArray{<:Number, N}) where {N} = +conv_full(u::AbstractArray{<:Number, N}, v::AbstractArray{<:Number, N}) where {N} = conv(float(u), float(v)) -function conv(u::AbstractArray{<:Number, N}, +function conv_full(u::AbstractArray{<:Number, N}, v::AbstractArray{<:BLAS.BlasFloat, N}) where N - conv(float(u), v) + conv_full(float(u), v) end -function conv(u::AbstractArray{<:BLAS.BlasFloat, N}, +function conv_full(u::AbstractArray{<:BLAS.BlasFloat, N}, v::AbstractArray{<:Number, N}) where N - conv(u, float(v)) + conv_full(u, float(v)) end -function conv(A::AbstractArray{<:Number, M}, +function conv_full(A::AbstractArray{<:Number, M}, B::AbstractArray{<:Number, N}) where {M, N} if (M < N) - conv(cat(A, dims=N)::AbstractArray{eltype(A), N}, B) + conv_full(cat(A, dims=N)::AbstractArray{eltype(A), N}, B) else @assert M > N - conv(A, cat(B, dims=M)::AbstractArray{eltype(B), M}) + conv_full(A, cat(B, dims=M)::AbstractArray{eltype(B), M}) end end +function conv_same(u::AbstractArray{T, N}, v::AbstractArray{T, N}) where {T, N} + su = size(u) + sv = size(v) + conv_res = conv_full(u, v) + conv_res[Int(floor.(sv[1]/2 + 1)):Int(floor.(sv[1]/2) + su[1]), Int(floor.(sv[2]/2 + 1)):Int(floor.(sv[2]/2) + su[2]), axes(conv_res)[3:end]...] +end + +function conv_valid(u::AbstractArray{T}, v::AbstractArray{T})::AbstractArray{T} where {T <: Number} + su = size(u) + sv = size(v) + conv_res = conv_full(u, v) + conv_res[sv[1]:su[1], sv[2]:su[2], axes(conv_res)[3:end]...] +end + """ conv(u,v,A) From cc81ca3b30ced37bc4f35a81b5dd96534bff0d3b Mon Sep 17 00:00:00 2001 From: "Longhao.Chen" Date: Mon, 22 Feb 2021 12:14:08 +0800 Subject: [PATCH 2/6] feat:Add :same and :valid support for Vector. --- src/dspbase.jl | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/dspbase.jl b/src/dspbase.jl index af44cf7f0..aede594b2 100644 --- a/src/dspbase.jl +++ b/src/dspbase.jl @@ -685,7 +685,7 @@ Convolution of two arrays. Uses either FFT convolution or overlap-save, depending on the size of the input. `u` and `v` can be N-dimensional arrays, with arbitrary indexing offsets, but their axes must be a `UnitRange`. -:full — Return the full 2-D convolution. +:full — Return the full convolution. :same — Return the central part of the convolution, which is the same size as u. @@ -699,7 +699,7 @@ function conv(u, v; mode::Symbol = :full) elseif mode == :valid conv_valid(u, v) else - throw(ArgumentError("mode keyword argument must be either :full or :same or :valid")) + throw(ArgumentError("mode keyword argument must be :full or :same or :valid")) end end @@ -721,10 +721,10 @@ function conv_full(u::AbstractArray{<:BLAS.BlasFloat, N}, end conv_full(u::AbstractArray{<:Integer, N}, v::AbstractArray{<:Integer, N}) where {N} = - round.(Int, conv(float(u), float(v))) + round.(Int, conv_full(float(u), float(v))) conv_full(u::AbstractArray{<:Number, N}, v::AbstractArray{<:Number, N}) where {N} = - conv(float(u), float(v)) + conv_full(float(u), float(v)) function conv_full(u::AbstractArray{<:Number, N}, v::AbstractArray{<:BLAS.BlasFloat, N}) where N @@ -747,17 +747,25 @@ function conv_full(A::AbstractArray{<:Number, M}, end function conv_same(u::AbstractArray{T, N}, v::AbstractArray{T, N}) where {T, N} - su = size(u) - sv = size(v) + su = size(u) + sv = size(v) conv_res = conv_full(u, v) - conv_res[Int(floor.(sv[1]/2 + 1)):Int(floor.(sv[1]/2) + su[1]), Int(floor.(sv[2]/2 + 1)):Int(floor.(sv[2]/2) + su[2]), axes(conv_res)[3:end]...] + if N != 1 + conv_res[Int(floor.(sv[1]/2 + 1)):Int(floor.(sv[1]/2) + su[1]), Int(floor.(sv[2]/2 + 1)):Int(floor.(sv[2]/2) + su[2]), axes(conv_res)[3:end]...] + else + conv_res[Int(floor.(sv[1]/2 + 1)):Int(floor.(sv[1]/2) + su[1])] + end end -function conv_valid(u::AbstractArray{T}, v::AbstractArray{T})::AbstractArray{T} where {T <: Number} - su = size(u) - sv = size(v) - conv_res = conv_full(u, v) - conv_res[sv[1]:su[1], sv[2]:su[2], axes(conv_res)[3:end]...] +function conv_valid(u::AbstractArray{T, N}, v::AbstractArray{T, N}) where {T, N} + su = size(u) + sv = size(v) + conv_res = conv_full(u, v) + if N != 1 + conv_res[sv[1]:su[1], sv[2]:su[2], axes(conv_res)[3:end]...] + else + conv_res[sv[1]:su[1]] + end end """ From 0584fbde6ec0e9a87601a27bc67e5cd454432411 Mon Sep 17 00:00:00 2001 From: "Longhao.Chen" Date: Mon, 22 Feb 2021 12:23:15 +0800 Subject: [PATCH 3/6] test:Add tests for :same and :valid modes of conv --- test/dsp.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/dsp.jl b/test/dsp.jl index 91dc942d7..1366ef17b 100644 --- a/test/dsp.jl +++ b/test/dsp.jl @@ -46,6 +46,8 @@ end im_expectation = [1, 3, 6, 6, 5, 3] a32 = convert(Array{Int32}, a) @test conv(a, b) == expectation + @test conv(a, b, mode=:same) == expectation[2:5] + @test conv(a, b, mode=:valid) == expectation[3:4] @test conv(a32, b) == expectation fa = convert(Array{Float64}, a) f32a = convert(Array{Float32}, a) @@ -90,6 +92,8 @@ end # Real Integers a32 = convert(Array{Int32}, a) @test conv(a, b) == expectation + @test conv(a, b, mode=:same) == expectation[2:4, 2:4] + @test conv(a, b, mode=:valid) == expectation[2:3, 2:3] @test conv(a32, b) == expectation # Floats fa = convert(Array{Float64}, a) From 54f0529c7661d94a685c642310efcf232ebc694b Mon Sep 17 00:00:00 2001 From: "Longhao.Chen" Date: Wed, 28 Apr 2021 16:12:55 +0800 Subject: [PATCH 4/6] fix:Fix calculation errors. --- src/dspbase.jl | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/dspbase.jl b/src/dspbase.jl index aede594b2..ae03cde8f 100644 --- a/src/dspbase.jl +++ b/src/dspbase.jl @@ -750,22 +750,16 @@ function conv_same(u::AbstractArray{T, N}, v::AbstractArray{T, N}) where {T, N} su = size(u) sv = size(v) conv_res = conv_full(u, v) - if N != 1 - conv_res[Int(floor.(sv[1]/2 + 1)):Int(floor.(sv[1]/2) + su[1]), Int(floor.(sv[2]/2 + 1)):Int(floor.(sv[2]/2) + su[2]), axes(conv_res)[3:end]...] - else - conv_res[Int(floor.(sv[1]/2 + 1)):Int(floor.(sv[1]/2) + su[1])] - end + outsize = CartesianIndex(Int.(floor.(sv ./2 .+ 1))...):CartesianIndex(Int.(floor.(sv ./ 2) .+ su)...) + conv_res[outsize] end function conv_valid(u::AbstractArray{T, N}, v::AbstractArray{T, N}) where {T, N} su = size(u) sv = size(v) conv_res = conv_full(u, v) - if N != 1 - conv_res[sv[1]:su[1], sv[2]:su[2], axes(conv_res)[3:end]...] - else - conv_res[sv[1]:su[1]] - end + outsize = CartesianIndex(sv...):CartesianIndex(su...) + conv_res[outsize] end """ From 07e3b0110f39602dbba0914e2c23dc9b6024904e Mon Sep 17 00:00:00 2001 From: "Longhao.Chen" Date: Fri, 30 Apr 2021 17:43:57 +0800 Subject: [PATCH 5/6] test:Add tests for :same and :valid modes of conv-ND --- test/dsp.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/dsp.jl b/test/dsp.jl index 1366ef17b..6b62a136e 100644 --- a/test/dsp.jl +++ b/test/dsp.jl @@ -165,6 +165,8 @@ end 47, 96, 100, 51, 25, 51, 53, 27], (4, 4, 4))) @test conv(a, b) == exp + @test conv(a, b, mode=:same) == exp[2:4, 2:4, 2:4] + @test conv(a, b, mode=:valid) == exp[2:3, 2:3, 2:3] #6D, trivial, just to see if it works From 5bdc1c27f59fb67dcc9346455417c60ac857b08c Mon Sep 17 00:00:00 2001 From: "Longhao.Chen" Date: Fri, 30 Apr 2021 18:34:12 +0800 Subject: [PATCH 6/6] Add modes to conv function. add central part of the convolution add only parts of the convolution that are computed without zero-padded edges --- src/dspbase.jl | 87 ++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 48 deletions(-) diff --git a/src/dspbase.jl b/src/dspbase.jl index ae03cde8f..a487d62f5 100644 --- a/src/dspbase.jl +++ b/src/dspbase.jl @@ -690,78 +690,69 @@ with arbitrary indexing offsets, but their axes must be a `UnitRange`. :same — Return the central part of the convolution, which is the same size as u. :valid — Return only parts of the convolution that are computed without zero-padded edges. + +Warning: `:same` and `:valid` will result in an extra copy of the result. """ -function conv(u, v; mode::Symbol = :full) +function conv(u::AbstractArray{T, N}, + v::AbstractArray{T, N}; + mode::Symbol = :full) where {T<:BLAS.BlasFloat, N} + su = size(u) + sv = size(v) if mode == :full - conv_full(u, v) + if prod(su) >= prod(sv) + _conv(u, v, su, sv) + else + _conv(v, u, sv, su) + end elseif mode == :same - conv_same(u, v) + conv_res = conv(u, v) + outsize = CartesianIndex(Int.(floor.(sv ./2 .+ 1))...):CartesianIndex(Int.(floor.(sv ./ 2) .+ su)...) + conv_res[outsize] elseif mode == :valid - conv_valid(u, v) + conv_res = conv(u, v) + outsize = CartesianIndex(sv...):CartesianIndex(su...) + conv_res[outsize] else throw(ArgumentError("mode keyword argument must be :full or :same or :valid")) end end -function conv_full(u::AbstractArray{T, N}, - v::AbstractArray{T, N}) where {T<:BLAS.BlasFloat, N} - su = size(u) - sv = size(v) - if prod(su) >= prod(sv) - _conv(u, v, su, sv) - else - _conv(v, u, sv, su) - end -end - -function conv_full(u::AbstractArray{<:BLAS.BlasFloat, N}, - v::AbstractArray{<:BLAS.BlasFloat, N}) where N +function conv(u::AbstractArray{<:BLAS.BlasFloat, N}, + v::AbstractArray{<:BLAS.BlasFloat, N}; + mode::Symbol = :full) where N fu, fv = promote(u, v) - conv_full(fu, fv) + conv(fu, fv, mode = mode) end -conv_full(u::AbstractArray{<:Integer, N}, v::AbstractArray{<:Integer, N}) where {N} = - round.(Int, conv_full(float(u), float(v))) +conv(u::AbstractArray{<:Integer, N}, v::AbstractArray{<:Integer, N}; mode::Symbol = :full) where {N} = + round.(Int, conv(float(u), float(v), mode = mode)) -conv_full(u::AbstractArray{<:Number, N}, v::AbstractArray{<:Number, N}) where {N} = - conv_full(float(u), float(v)) +conv(u::AbstractArray{<:Number, N}, v::AbstractArray{<:Number, N}; mode::Symbol = :full) where {N} = + conv(float(u), float(v), mode = mode) -function conv_full(u::AbstractArray{<:Number, N}, - v::AbstractArray{<:BLAS.BlasFloat, N}) where N - conv_full(float(u), v) +function conv(u::AbstractArray{<:Number, N}, + v::AbstractArray{<:BLAS.BlasFloat, N}; + mode::Symbol = :full) where N + conv(float(u), v, mode = mode) end -function conv_full(u::AbstractArray{<:BLAS.BlasFloat, N}, - v::AbstractArray{<:Number, N}) where N - conv_full(u, float(v)) +function conv(u::AbstractArray{<:BLAS.BlasFloat, N}, + v::AbstractArray{<:Number, N}; + mode::Symbol = :full) where N + conv(u, float(v), mode = mode) end -function conv_full(A::AbstractArray{<:Number, M}, - B::AbstractArray{<:Number, N}) where {M, N} +function conv(A::AbstractArray{<:Number, M}, + B::AbstractArray{<:Number, N}; + mode::Symbol = :full) where {M, N} if (M < N) - conv_full(cat(A, dims=N)::AbstractArray{eltype(A), N}, B) + conv(cat(A, dims=N)::AbstractArray{eltype(A), N}, B, mode = mode) else @assert M > N - conv_full(A, cat(B, dims=M)::AbstractArray{eltype(B), M}) + conv(A, cat(B, dims=M)::AbstractArray{eltype(B), M}, mode = mode) end end -function conv_same(u::AbstractArray{T, N}, v::AbstractArray{T, N}) where {T, N} - su = size(u) - sv = size(v) - conv_res = conv_full(u, v) - outsize = CartesianIndex(Int.(floor.(sv ./2 .+ 1))...):CartesianIndex(Int.(floor.(sv ./ 2) .+ su)...) - conv_res[outsize] -end - -function conv_valid(u::AbstractArray{T, N}, v::AbstractArray{T, N}) where {T, N} - su = size(u) - sv = size(v) - conv_res = conv_full(u, v) - outsize = CartesianIndex(sv...):CartesianIndex(su...) - conv_res[outsize] -end - """ conv(u,v,A)