Skip to content

Commit

Permalink
Make two Oscillation algorithms available
Browse files Browse the repository at this point in the history
Issue #500 Make two Oscillation algorithms available for the GenericRecognizer. These are implemented by the Oscillations2012 and Oscillations2019 classes.
  • Loading branch information
towsey committed Jun 29, 2021
1 parent 53a723f commit 9521508
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 145 deletions.
7 changes: 3 additions & 4 deletions src/AnalysisPrograms/Recognizers/GenericRecognizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -406,16 +406,15 @@ public static RecognizerResults RunOneProfile(
}
else if (profileConfig is OscillationParameters op)
{
List<Plot> decibelPlots;
double[,] hits;
(spectralEvents, decibelPlots, hits) = Oscillations2012.GetComponentsWithOscillations(
List<Plot> oscPlots;
(spectralEvents, oscPlots) = OscillationParameters.GetOscillationEvents(
spectrogram,
op,
decibelThreshold,
segmentStartOffset,
profileName);

plots.AddRange(decibelPlots);
plots.AddRange(oscPlots);
}
else
{
Expand Down
55 changes: 55 additions & 0 deletions src/AudioAnalysisTools/Ocillations/OscillationParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,31 @@

namespace AnalysisPrograms.Recognizers.Base
{
using System;
using System.Collections.Generic;
using Acoustics.Shared;
using AudioAnalysisTools;
using AudioAnalysisTools.Events;
using AudioAnalysisTools.StandardSpectrograms;
using TowseyLibrary;

public enum OscillationAlgorithm
{
Standard,
Hits,
}

/// <summary>
/// Parameters needed from a config file to detect oscillation components.
/// </summary>
[YamlTypeTag(typeof(OscillationParameters))]
public class OscillationParameters : DctParameters
{
/// <summary>
/// Gets or sets he algorithm to be used to find oscillation events.
/// </summary>
public OscillationAlgorithm Algorithm { get; set; }

/// <summary>
/// Gets or sets the minimum OSCILLATIONS PER SECOND
/// Ignore oscillation rates below the min threshold.
Expand All @@ -25,5 +42,43 @@ public class OscillationParameters : DctParameters
/// </summary>
/// <value>The value in oscillations per second.</value>
public double? MaxOscillationFrequency { get; set; }

/// <summary>
/// Return oscillation events as determined by the user set parameters.
/// </summary>
public static (List<EventCommon> OscillEvents, List<Plot> Plots) GetOscillationEvents(
SpectrogramStandard spectrogram,
OscillationParameters op,
double? decibelThreshold,
TimeSpan segmentStartOffset,
string profileName)
{
var algorithm = op.Algorithm;

List<EventCommon> events;
List<Plot> plots;

if (algorithm == OscillationAlgorithm.Hits)
{
(events, plots) = Oscillations2012.GetComponentsWithOscillations(
spectrogram,
op,
decibelThreshold,
segmentStartOffset,
profileName);
}
else
{
// the standard algorithm is the default.
(events, plots) = Oscillations2019.GetComponentsWithOscillations(
spectrogram,
op,
decibelThreshold,
segmentStartOffset,
profileName);
}

return (events, plots);
}
}
}
125 changes: 10 additions & 115 deletions src/AudioAnalysisTools/Ocillations/Oscillations2012.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace AudioAnalysisTools
using AudioAnalysisTools.DSP;
using AudioAnalysisTools.Events;
using AudioAnalysisTools.StandardSpectrograms;
using SixLabors.ImageSharp;
using TowseyLibrary;

/// <summary>
Expand All @@ -23,7 +24,7 @@ namespace AudioAnalysisTools
/// </summary>
public static class Oscillations2012
{
public static (List<EventCommon> SpectralEvents, List<Plot> DecibelPlots, double[,] Hits) GetComponentsWithOscillations(
public static (List<EventCommon> SpectralEvents, List<Plot> DecibelPlots) GetComponentsWithOscillations(
SpectrogramStandard spectrogram,
OscillationParameters op,
double? decibelThreshold,
Expand All @@ -47,7 +48,7 @@ public static (List<EventCommon> SpectralEvents, List<Plot> DecibelPlots, double
out var bandDecibels,
out var oscScores,
out var oscillationEvents,
out var hits,
out var hits, // keep this for debuggin purposes. See below.
segmentStartOffset);

oscEvents.AddRange(oscillationEvents);
Expand All @@ -58,7 +59,12 @@ public static (List<EventCommon> SpectralEvents, List<Plot> DecibelPlots, double
var plot2 = Plot.PreparePlot(oscScores, $"{profileName} (Oscillation Event Score:{op.EventThreshold:F2})", op.EventThreshold);
plots.Add(plot2);

return (oscEvents, plots, hits);
// save a debug image
//var image3 = SpectrogramTools.GetSonogramPlusCharts(spectrogram, oscEvents, plots, hits, profileName + " Oscillations");
//var path = "C:\\temp\\oscillationsImage.png";
//image3.Save(path);

return (oscEvents, plots);
}

public static void Execute(
Expand Down Expand Up @@ -140,7 +146,7 @@ public static void Execute(

// smooth the scores - window=11 has been the DEFAULT. Now letting user set this.
oscScores = DataTools.filterMovingAverage(oscScores, smoothingWindow);
events = ConvertOscillationScores2Events(
events = OscillationEvent.ConvertOscillationScores2Events(
sonogram,
minDuration,
maxDuration,
Expand Down Expand Up @@ -327,117 +333,6 @@ public static double[] GetOscillationScores(double[,] hits, int minHz, int maxHz
return scores;
}

/// <summary>
/// Converts the Oscillation Detector score array to a list of Oscillation Events.
/// </summary>
/// <param name="minDurationThreshold">min threshold.</param>
/// <param name="maxDurationThreshold">max threshold.</param>
/// <param name="minHz">lower freq bound of the acoustic event.</param>
/// <param name="maxHz">upper freq bound of the acoustic event.</param>
/// <param name="minOscilFrequency">the minimum oscillations per second.</param>
/// <param name="maxOscilFrequency">the maximum oscillations per second.</param>
/// <param name="oscilScores">the array of OD scores.</param>
/// <param name="scoreThreshold">threshold.</param>
/// <param name="segmentStartOffset">time offset.</param>
public static List<OscillationEvent> ConvertOscillationScores2Events(
SpectrogramStandard spectrogram,
double minDurationThreshold,
double maxDurationThreshold,
int minHz,
int maxHz,
double? minOscilFrequency,
double? maxOscilFrequency,
double[] oscilScores,
double scoreThreshold,
TimeSpan segmentStartOffset)
{
// The name of source file
string fileName = spectrogram.Configuration.SourceFName;
double framesPerSec = spectrogram.FramesPerSecond;
double freqBinWidth = spectrogram.FBinWidth;
int count = oscilScores.Length;

// get the bin bounds of the frequency band of interest.
int minBin = (int)(minHz / freqBinWidth);
int maxBin = (int)(maxHz / freqBinWidth);

// get the oeriodicity bounds for the frequency band of interest.
double? minPeriodicity = 1 / maxOscilFrequency;
double? maxPeriodicity = 1 / minOscilFrequency;

var events = new List<OscillationEvent>();
bool isHit = false;
double frameOffset = 1 / framesPerSec;
int startFrame = 0;

//pass over all frames
for (int i = 0; i < count; i++)
{
if (isHit == false && oscilScores[i] >= scoreThreshold)
{
//start of an event
isHit = true;
startFrame = i;
}
else //check for the end of an event
if (isHit && (oscilScores[i] < scoreThreshold || i == count - 1))
{
isHit = false;
double duration = (i - startFrame + 1) * frameOffset;
if (duration < minDurationThreshold || duration > maxDurationThreshold)
{
//skip events with duration outside the required bounds
continue;
}

// This is end of an event, so initialise it
// First trim the event because oscillation events spill over the edges of the true event due to use of the DCT.
(int trueStartFrame, int trueEndFrame, double framePeriodicity) = OscillationEvent.TrimEvent(spectrogram, startFrame, minBin, i, maxBin);
double trueStartTime = trueStartFrame * frameOffset;
double trueEndTime = trueEndFrame * frameOffset;
int trueFrameLength = trueEndFrame - trueStartFrame + 1;

// Determine if the periodicity is within the required bounds.
var periodicity = framePeriodicity * frameOffset;
if (periodicity < minPeriodicity || periodicity > maxPeriodicity)
{
//skip events with periodicity outside the required bounds
continue;
}

//obtain average score.
double sum = 0.0;
for (int n = trueStartFrame; n <= trueEndFrame; n++)
{
sum += oscilScores[n];
}

double score = sum / trueFrameLength;

var ev = new OscillationEvent()
{
Name = "Oscillation",
SegmentStartSeconds = segmentStartOffset.TotalSeconds,
ResultStartSeconds = segmentStartOffset.TotalSeconds + trueStartTime,
EventStartSeconds = segmentStartOffset.TotalSeconds + trueStartTime,
EventEndSeconds = segmentStartOffset.TotalSeconds + trueEndTime,
LowFrequencyHertz = minHz,
HighFrequencyHertz = maxHz,
Periodicity = framePeriodicity * frameOffset,
Score = score,
FileName = fileName,
};

//##########################################################################################
//ev.Score2 = av / (i - startFrame + 1);
//ev.Intensity = (int)ev.Score2; // store this info for later inclusion in csv file as Event Intensity
events.Add(ev);
}
}

return events;
}

/// <summary>
/// Calculates the optimal frame overlap for the given sample rate, frame width and max oscillation or pulse rate.
/// Pulse rate is determined using a DCT and efficient use of the DCT requires that the dominant pulse sit somewhere 3.4 along the array of coefficients.
Expand Down
52 changes: 26 additions & 26 deletions src/AudioAnalysisTools/Ocillations/Oscillations2019.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace AudioAnalysisTools
{
using System;
using System.Collections.Generic;
using AnalysisPrograms.Recognizers.Base;
using AudioAnalysisTools.DSP;
using AudioAnalysisTools.Events;
using AudioAnalysisTools.StandardSpectrograms;
Expand All @@ -19,54 +20,47 @@ namespace AudioAnalysisTools
/// </summary>
public static class Oscillations2019
{
public static void Execute(
public static (List<EventCommon> OscEvents, List<Plot> Plots) GetComponentsWithOscillations(
SpectrogramStandard sonogram,
int minHz,
int maxHz,
double decibelThreshold,
double dctDuration,
double minOscFreq,
double maxOscFreq,
double dctThreshold,
double scoreThreshold,
double minDuration,
double maxDuration,
int smoothingWindow,
out double[] dctScores,
out List<OscillationEvent> events,
TimeSpan segmentStartOffset)
OscillationParameters op,
double? decibelThreshold,
TimeSpan segmentStartOffset,
string profileName)
{
double minDuration = op.MinDuration.Value;
double maxDuration = op.MaxDuration.Value;
int minHz = op.MinHertz.Value;
int maxHz = op.MaxHertz.Value;
double minOscFreq = op.MinOscillationFrequency.Value;
double maxOscFreq = op.MaxOscillationFrequency.Value;
double dctDuration = op.DctDuration;
double dctThreshold = op.DctThreshold;
double scoreThreshold = op.EventThreshold;
int smoothingWindow = 5;

// smooth the frames to make oscillations more regular.
sonogram.Data = MatrixTools.SmoothRows(sonogram.Data, 5);

// extract array of decibel values, frame averaged over required frequency band
var decibelArray = SNR.CalculateFreqBandAvIntensity(sonogram.Data, minHz, maxHz, sonogram.NyquistFrequency);

// if first value is negative dB, this means noise removal was not done.
// Do noise removal now
//if (decibelArray[0] < 0.0)
//{
// NoiseRemovalModal.CalculateNoiseUsingLamelsAlgorithm(decibelArray, out double _, out double _, out double noiseMode, out double _);
// decibelArray = SNR.SubtractAndTruncate2Zero(decibelArray, noiseMode);
//}

//DETECT OSCILLATIONS
var framesPerSecond = sonogram.FramesPerSecond;
DetectOscillations(
decibelArray,
framesPerSecond,
decibelThreshold,
decibelThreshold.Value,
dctDuration,
minOscFreq,
maxOscFreq,
dctThreshold,
out dctScores,
out var dctScores,
out var oscFreq);

// smooth the scores - window=11 has been the DEFAULT. Now letting user set this.
dctScores = DataTools.filterMovingAverage(dctScores, smoothingWindow);

events = Oscillations2012.ConvertOscillationScores2Events(
var oscillationEvents = OscillationEvent.ConvertOscillationScores2Events(
sonogram,
minDuration,
maxDuration,
Expand All @@ -77,6 +71,12 @@ public static void Execute(
dctScores,
scoreThreshold,
segmentStartOffset);

var oscEvents = new List<EventCommon>();
oscEvents.AddRange(oscillationEvents);
var plots = new List<Plot>();

return (oscEvents, plots);
}

/// <summary>
Expand Down

0 comments on commit 9521508

Please sign in to comment.