Skip to content

Commit

Permalink
Change Track Algorithm classes to accept an array of decibel thresholds
Browse files Browse the repository at this point in the history
Issue #370 These changes allow track recognizers to be derived from an array of different decibel thresholds.
  • Loading branch information
towsey authored and atruskie committed Oct 14, 2020
1 parent 676725d commit 5fb425d
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 45 deletions.
56 changes: 31 additions & 25 deletions src/AnalysisPrograms/Recognizers/GenericRecognizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ public override RecognizerResults Recognize(

if (profileConfig is BlobParameters bp)
{
var thresholdArray = bp.DecibelThresholds;
var threshold = thresholdArray[0].Value;

//get the array of intensity values minus intensity in side/buffer bands.
//i.e. require silence in side-bands. Otherwise might simply be getting part of a broader band acoustic event.
var decibelArray = SNR.CalculateFreqBandAvIntensityMinusBufferIntensity(
Expand All @@ -178,7 +181,7 @@ public override RecognizerResults Recognize(
spectrogram.NyquistFrequency);

// prepare plot of resultant blob decibel array.
var plot = Plot.PreparePlot(decibelArray, $"{profileName} (Blob:db Intensity)", bp.DecibelThreshold.Value);
var plot = Plot.PreparePlot(decibelArray, $"{profileName} (Blob:db Intensity)", threshold);
plots.Add(plot);

// iii: CONVERT blob decibel SCORES TO ACOUSTIC EVENTS.
Expand All @@ -188,7 +191,7 @@ public override RecognizerResults Recognize(
segmentStartOffset,
bp.MinHertz.Value,
bp.MaxHertz.Value,
bp.DecibelThreshold.Value,
threshold,
TimeSpan.FromSeconds(bp.MinDuration.Value),
TimeSpan.FromSeconds(bp.MaxDuration.Value),
spectrogram.FramesPerSecond,
Expand All @@ -197,67 +200,70 @@ public override RecognizerResults Recognize(
}
else if (profileConfig is OnebinTrackParameters wp)
{
//get the array of intensity values minus intensity in side/buffer bands.
double[] decibelArray;
(spectralEvents, decibelArray) = OnebinTrackAlgorithm.GetOnebinTracks(
List<Plot> dbPlots;
(spectralEvents, dbPlots) = OnebinTrackAlgorithm.GetOnebinTracks(
spectrogram,
wp,
segmentStartOffset);
segmentStartOffset,
profileName);

var plot = Plot.PreparePlot(decibelArray, $"{profileName} (Whistle:dB Intensity)", wp.DecibelThreshold.Value);
plots.Add(plot);
plots.AddRange(dbPlots);
}
else if (profileConfig is ForwardTrackParameters tp)
{
double[] decibelArray;
(spectralEvents, decibelArray) = ForwardTrackAlgorithm.GetForwardTracks(
List<Plot> decibelPlots;
(spectralEvents, decibelPlots) = ForwardTrackAlgorithm.GetForwardTracks(
spectrogram,
tp,
segmentStartOffset);
segmentStartOffset,
profileName);

var plot = Plot.PreparePlot(decibelArray, $"{profileName} (Chirps:dB Intensity)", tp.DecibelThreshold.Value);
plots.Add(plot);
plots.AddRange(decibelPlots);
}
else if (profileConfig is OneframeTrackParameters cp)
{
double[] decibelArray;
(spectralEvents, decibelArray) = OneframeTrackAlgorithm.GetOneFrameTracks(
List<Plot> decibelPlots;
(spectralEvents, decibelPlots) = OneframeTrackAlgorithm.GetOneFrameTracks(
spectrogram,
cp,
segmentStartOffset);
segmentStartOffset,
profileName);

var plot = Plot.PreparePlot(decibelArray, $"{profileName} (Clicks:dB Intensity)", cp.DecibelThreshold.Value);
plots.Add(plot);
plots.AddRange(decibelPlots);
}
else if (profileConfig is UpwardTrackParameters vtp)
{
double[] decibelArray;
(spectralEvents, decibelArray) = UpwardTrackAlgorithm.GetUpwardTracks(
List<Plot> decibelPlots;
(spectralEvents, decibelPlots) = UpwardTrackAlgorithm.GetUpwardTracks(
spectrogram,
vtp,
segmentStartOffset);
segmentStartOffset,
profileName);

var plot = Plot.PreparePlot(decibelArray, $"{profileName} (VerticalTrack:dB Intensity)", vtp.DecibelThreshold.Value);
plots.Add(plot);
plots.AddRange(decibelPlots);
}
else if (profileConfig is HarmonicParameters hp)
{
var thresholdArray = hp.DecibelThresholds;
var threshold = thresholdArray[0];

double[] decibelMaxArray;
double[] harmonicIntensityScores;
(spectralEvents, decibelMaxArray, harmonicIntensityScores) = HarmonicParameters.GetComponentsWithHarmonics(
spectrogram,
hp.MinHertz.Value,
hp.MaxHertz.Value,
spectrogram.NyquistFrequency,
hp.DecibelThreshold.Value,
threshold.Value,
hp.DctThreshold.Value,
hp.MinDuration.Value,
hp.MaxDuration.Value,
hp.MinFormantGap.Value,
hp.MaxFormantGap.Value,
segmentStartOffset);

var plot = Plot.PreparePlot(harmonicIntensityScores, $"{profileName} (Harmonics:dct intensity)", hp.DctThreshold.Value);
// prepare plot of resultant Harmonics decibel array.
var plot = Plot.PreparePlot(decibelMaxArray, $"{profileName} (Harmonics:{threshold:d0}db)", threshold.Value);
plots.Add(plot);
}
else if (profileConfig is OscillationParameters op)
Expand Down
32 changes: 20 additions & 12 deletions src/AudioAnalysisTools/Tracks/ForwardTrackAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,27 @@ public static (List<EventCommon> Events, List<Plot> DecibelPlots) GetForwardTrac
TimeSpan segmentStartOffset,
string profileName)
{
var thresholds = parameters.DecibelThreshold;

double[] decibelArray;
List<EventCommon> spectralEvents;
var decibelThresholds = parameters.DecibelThresholds;
var spectralEvents = new List<EventCommon>();
var plots = new List<Plot>();

(spectralEvents, decibelArray) = ForwardTrackAlgorithm.GetForwardTracks(
spectrogram,
parameters,
segmentStartOffset);
foreach (var threshold in decibelThresholds)
{
double[] decibelArray;
List<EventCommon> events;

(events, decibelArray) = GetForwardTracks(
spectrogram,
parameters,
segmentStartOffset,
threshold.Value);

spectralEvents.AddRange(events);

var plot = Plot.PreparePlot(decibelArray, $"{profileName} (Chirps:{threshold.Value:d0}dB)", threshold.Value);
plots.Add(plot);
}

var plot = Plot.PreparePlot(decibelArray, $"{profileName} (Chirps:dB Intensity)", thresholds.Value);
plots.Add(plot);
return (spectralEvents, plots);
}

Expand All @@ -52,7 +60,8 @@ public static (List<EventCommon> Events, List<Plot> DecibelPlots) GetForwardTrac
public static (List<EventCommon> Events, double[] CombinedIntensity) GetForwardTracks(
SpectrogramStandard sonogram,
ForwardTrackParameters parameters,
TimeSpan segmentStartOffset)
TimeSpan segmentStartOffset,
double decibelThreshold)
{
var sonogramData = sonogram.Data;
int frameCount = sonogramData.GetLength(0);
Expand All @@ -63,7 +72,6 @@ public static (List<EventCommon> Events, double[] CombinedIntensity) GetForwardT
int maxBin = (int)Math.Round(parameters.MaxHertz.Value / binWidth);
double minDuration = parameters.MinDuration.Value;
double maxDuration = parameters.MaxDuration.Value;
double decibelThreshold = parameters.DecibelThreshold.Value;

var converter = new UnitConverters(
segmentStartOffset: segmentStartOffset.TotalSeconds,
Expand Down
35 changes: 33 additions & 2 deletions src/AudioAnalysisTools/Tracks/OnebinTrackAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,50 @@ namespace AudioAnalysisTools.Tracks
using AudioAnalysisTools.Events;
using AudioAnalysisTools.Events.Tracks;
using AudioAnalysisTools.StandardSpectrograms;
using TowseyLibrary;
using TrackType = AudioAnalysisTools.Events.Tracks.TrackType;

public static class OnebinTrackAlgorithm
{
public static (List<EventCommon> Events, List<Plot> DecibelPlots) GetOnebinTracks(
SpectrogramStandard spectrogram,
OnebinTrackParameters parameters,
TimeSpan segmentStartOffset,
string profileName)
{
var decibelThresholds = parameters.DecibelThresholds;
var spectralEvents = new List<EventCommon>();
var plots = new List<Plot>();

foreach (var threshold in decibelThresholds)
{
double[] decibelArray;
List<EventCommon> events;

(events, decibelArray) = GetOnebinTracks(
spectrogram,
parameters,
segmentStartOffset,
threshold.Value);

spectralEvents.AddRange(events);

var plot = Plot.PreparePlot(decibelArray, $"{profileName} (Whistle:{threshold.Value:d0}dB)", threshold.Value);
plots.Add(plot);
}

return (spectralEvents, plots);
}

/// <summary>
/// This method averages dB log values incorrectly but it is faster than doing many log conversions.
/// This method is used to find acoustic events and is accurate enough for the purpose.
/// </summary>
public static (List<EventCommon> ListOfevents, double[] CombinedIntensityArray) GetOnebinTracks(
SpectrogramStandard sonogram,
OnebinTrackParameters parameters,
TimeSpan segmentStartOffset)
TimeSpan segmentStartOffset,
double decibelThreshold)
{
var sonogramData = sonogram.Data;
int frameCount = sonogramData.GetLength(0);
Expand All @@ -32,7 +64,6 @@ public static (List<EventCommon> ListOfevents, double[] CombinedIntensityArray)
double binWidth = nyquist / (double)binCount;
int minBin = (int)Math.Round(parameters.MinHertz.Value / binWidth);
int maxBin = (int)Math.Round(parameters.MaxHertz.Value / binWidth);
double decibelThreshold = parameters.DecibelThreshold.Value;
double minDuration = parameters.MinDuration.Value;
double maxDuration = parameters.MaxDuration.Value;

Expand Down
35 changes: 33 additions & 2 deletions src/AudioAnalysisTools/Tracks/OneframeTrackAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,41 @@ namespace AudioAnalysisTools.Tracks
using AudioAnalysisTools.Events;
using AudioAnalysisTools.Events.Tracks;
using AudioAnalysisTools.StandardSpectrograms;
using TowseyLibrary;
using TrackType = AudioAnalysisTools.Events.Tracks.TrackType;

public static class OneframeTrackAlgorithm
{
public static (List<EventCommon> Events, List<Plot> DecibelPlots) GetOneFrameTracks(
SpectrogramStandard spectrogram,
OneframeTrackParameters parameters,
TimeSpan segmentStartOffset,
string profileName)
{
var decibelThresholds = parameters.DecibelThresholds;
var spectralEvents = new List<EventCommon>();
var plots = new List<Plot>();

foreach (var threshold in decibelThresholds)
{
double[] decibelArray;
List<EventCommon> events;

(events, decibelArray) = GetOneFrameTracks(
spectrogram,
parameters,
segmentStartOffset,
threshold.Value);

spectralEvents.AddRange(events);

var plot = Plot.PreparePlot(decibelArray, $"{profileName} (Clicks:{threshold.Value:d0}dB)", threshold.Value);
plots.Add(plot);
}

return (spectralEvents, plots);
}

/// <summary>
/// A one-frame track sounds like a click.
/// A click is a sharp onset broadband sound of brief duration. Geometrically it is similar to a vertical whistle.
Expand All @@ -23,7 +54,8 @@ public static class OneframeTrackAlgorithm
public static (List<EventCommon> Events, double[] Intensity) GetOneFrameTracks(
SpectrogramStandard sonogram,
OneframeTrackParameters parameters,
TimeSpan segmentStartOffset)
TimeSpan segmentStartOffset,
double decibelThreshold)
{
var sonogramData = sonogram.Data;
int frameCount = sonogramData.GetLength(0);
Expand All @@ -33,7 +65,6 @@ public static (List<EventCommon> Events, double[] Intensity) GetOneFrameTracks(
double binWidth = nyquist / (double)binCount;
int minBin = (int)Math.Round(parameters.MinHertz.Value / binWidth);
int maxBin = (int)Math.Round(parameters.MaxHertz.Value / binWidth);
var decibelThreshold = parameters.DecibelThreshold.Value;
var minBandwidthHertz = parameters.MinBandwidthHertz.Value;
var maxBandwidthHertz = parameters.MaxBandwidthHertz.Value;

Expand Down
41 changes: 37 additions & 4 deletions src/AudioAnalysisTools/Tracks/UpwardTrackAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,51 @@ namespace AudioAnalysisTools.Tracks
using System.Linq;
using System.Text;
using Acoustics.Shared;
using AnalysisPrograms.Recognizers.Base;
using AudioAnalysisTools.Events;
using AudioAnalysisTools.Events.Tracks;
using AudioAnalysisTools.Events.Types;
using AudioAnalysisTools.StandardSpectrograms;
using TowseyLibrary;
using TrackType = AudioAnalysisTools.Events.Tracks.TrackType;

/// <summary>
/// EXPANATION: A vertical track is a near click or rapidly frequency-modulated tone. A good example is the whip component of the whip-bird call.
/// They would typically be only a few time-frames duration.
/// </summary>
public static class UpwardTrackAlgorithm
{
public static (List<EventCommon> Events, List<Plot> DecibelPlots) GetUpwardTracks(
SpectrogramStandard spectrogram,
UpwardTrackParameters parameters,
TimeSpan segmentStartOffset,
string profileName)
{
var decibelThresholds = parameters.DecibelThresholds;
var spectralEvents = new List<EventCommon>();
var plots = new List<Plot>();

foreach (var threshold in decibelThresholds)
{
double[] decibelArray;
List<EventCommon> events;

(events, decibelArray) = GetUpwardTracks(
spectrogram,
parameters,
segmentStartOffset,
threshold.Value);

spectralEvents.AddRange(events);

var plot = Plot.PreparePlot(decibelArray, $"{profileName} (Clicks:{threshold.Value:d0}dB)", threshold.Value);
plots.Add(plot);
}

return (spectralEvents, plots);
}

/// <summary>
/// EXPANATION: A vertical track is a near click or rapidly frequency-modulated tone. A good example is the whip component of the whip-bird call.
/// They would typically be only a few time-frames duration.
/// THis method averages dB log values incorrectly but it is faster than doing many log conversions and is accurate enough for the purpose.
/// </summary>
/// <param name="sonogram">The spectrogram to be searched.</param>
Expand All @@ -30,7 +63,8 @@ public static class UpwardTrackAlgorithm
public static (List<EventCommon> Events, double[] CombinedIntensity) GetUpwardTracks(
SpectrogramStandard sonogram,
AnalysisPrograms.Recognizers.Base.UpwardTrackParameters parameters,
TimeSpan segmentStartOffset)
TimeSpan segmentStartOffset,
double decibelThreshold)
{
var sonogramData = sonogram.Data;
int frameCount = sonogramData.GetLength(0);
Expand All @@ -42,7 +76,6 @@ public static (List<EventCommon> Events, double[] CombinedIntensity) GetUpwardTr
int maxBin = (int)Math.Round(parameters.MaxHertz.Value / binWidth);
var minBandwidthHertz = parameters.MinBandwidthHertz.Value;
var maxBandwidthHertz = parameters.MaxBandwidthHertz.Value;
var decibelThreshold = parameters.DecibelThreshold.Value;

var converter = new UnitConverters(
segmentStartOffset: segmentStartOffset.TotalSeconds,
Expand Down

0 comments on commit 5fb425d

Please sign in to comment.