diff --git a/Project.toml b/Project.toml index e9c3c7d..f5d6b9a 100644 --- a/Project.toml +++ b/Project.toml @@ -47,7 +47,7 @@ DSP = "0.6" Distances = "0.8, 0.9" DocStringExtensions = "0.8" DoubleFloats = "1.1" -DynamicAxisWarping = "0.1, 0.2" +DynamicAxisWarping = "0.1, 0.2, 0.3" FillArrays = "0.8" Hungarian = "0.6" Lazy = "0.14, 0.15" diff --git a/src/SpectralDistances.jl b/src/SpectralDistances.jl index ca0a8ed..bda62b4 100644 --- a/src/SpectralDistances.jl +++ b/src/SpectralDistances.jl @@ -93,6 +93,7 @@ DiscretizedRationalDistance, WelchOptimalTransportDistance, WelchLPDistance, ConvOptimalTransportDistance, +SlidingConvOptimalTransportDistance, RationalOptimalTransportDistance, OptimalTransportHistogramDistance, DiscreteGridTransportDistance, diff --git a/src/sinkhorn.jl b/src/sinkhorn.jl index 40f0d16..4a78024 100644 --- a/src/sinkhorn.jl +++ b/src/sinkhorn.jl @@ -630,6 +630,8 @@ It's important to tune the two parameters below, see the docstring for [`sinkhor - `β = 0.001` - `dynamic_floor = -10.0` - `invariant_axis::Int = 0` If this is set to 1 or 2, the distance will be approximately invariant to translations along the invariant axis. As an example, to be invariant to a spectrogram being shifted slightly in time, set `invariant_axis = 2`. + +See also [`SlidingConvOptimalTransportDistance`](@ref) """ ConvOptimalTransportDistance Base.@kwdef mutable struct ConvOptimalTransportDistance{T} <: AbstractDistance @@ -664,6 +666,52 @@ function evaluate(d::ConvOptimalTransportDistance, A::AbstractMatrix, B::Abstrac return c end +""" + SlidingConvOptimalTransportDistance + +Similar to [`ConvOptimalTransportDistance`](@ref) but lets the shorter signal slide across the longer signal and returns the minimum distance. +Equivalent to calling +```julia +minimum(distance_profile(d::ConvOptimalTransportDistance, a, b; kwargs...)) +``` +""" +struct SlidingConvOptimalTransportDistance{D} <: AbstractDistance + d::D +end + +function SlidingConvOptimalTransportDistance(;β = 0.001, + dynamic_floor = NaN, + invariant_axis::Int = 0, + workspace = nothing) + + SlidingConvOptimalTransportDistance( + ConvOptimalTransportDistance(β, dynamic_floor, invariant_axis, workspace) + ) +end + +function Distances.evaluate(d::SlidingConvOptimalTransportDistance, a, b; kwargs...) + if size(a,2) > size(b,2) + a,b = b,a + end + minimum(distance_profile(d.d, a, b; kwargs...)) +end + +function Distances.evaluate( + d::SlidingConvOptimalTransportDistance, + a::DSP.Periodograms.TFR, + b::DSP.Periodograms.TFR; + kwargs..., +) + evaluate( + d, + normalize_spectrogram(a, d.d.dynamic_floor), + normalize_spectrogram(b, d.d.dynamic_floor); + kwargs..., + ) +end + + + """ normalize_spectrogram(S, dynamic_floor = default_dynamic_floor(S)) """ diff --git a/test/test_convolutional.jl b/test/test_convolutional.jl index 9902a75..d77d27c 100644 --- a/test/test_convolutional.jl +++ b/test/test_convolutional.jl @@ -153,6 +153,10 @@ end isinteractive() && plot(D) @test 40 <= argmin(D) <= 70 + dslide = SlidingConvOptimalTransportDistance(d) + @test dslide(A1, A3; tol=1e-6, stride=15) ≈ minimum(D) + @test SlidingConvOptimalTransportDistance(β=0.05, dynamic_floor=-3.0).d.β == SlidingConvOptimalTransportDistance(ConvOptimalTransportDistance(β=0.05, dynamic_floor=-3.0)).d.β +