Skip to content

Commit

Permalink
New method to find events
Browse files Browse the repository at this point in the history
Issue-#238 Main work was to add a new method to AcousticEvents.cs in order to find events located at a score maximum. All such events have the same (user defined) time duration centred on the maximum.
  • Loading branch information
towsey authored and atruskie committed Aug 28, 2019
1 parent d123d30 commit c86caaa
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 24 deletions.
43 changes: 24 additions & 19 deletions src/AnalysisPrograms/Recognizers/PteropusSpecies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,15 @@ internal static RecognizerResults Gruntwork(AudioRecording audioRecording, Confi
string abbreviatedSpeciesName = configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? "<no.sp>";
int minHz = configuration.GetIntOrNull(AnalysisKeys.MinHz) ?? 500;
int maxHz = configuration.GetIntOrNull(AnalysisKeys.MaxHz) ?? 8000;
double minDuration = configuration.GetIntOrNull(AnalysisKeys.MinDuration) ?? 0.1;
double maxDuration = configuration.GetIntOrNull(AnalysisKeys.MaxDuration) ?? 0.5;

//double minDuration = configuration.GetIntOrNull(AnalysisKeys.MinDuration) ?? 0.1;
//double maxDuration = configuration.GetIntOrNull(AnalysisKeys.MaxDuration) ?? 0.5;
var neighbourhoodDuration = TimeSpan.FromSeconds(0.05);

double intensityNormalisationMax = 12.0; // decibels
double intensityThreshold = 9.0; // decibels
var eventThreshold = intensityThreshold / intensityNormalisationMax;


// Get a value from the config file - without a string accessor, as a double
//double someExampleSettingA = configuration.GetDoubleOrNull("SomeExampleSettingA") ?? 0.0;
Expand All @@ -129,7 +136,7 @@ internal static RecognizerResults Gruntwork(AudioRecording audioRecording, Confi

//######################
//2.Convert each segment to a spectrogram.
double noiseReductionParameter = configuration.GetDoubleOrNull(AnalysisKeys.NoiseBgThreshold) ?? 0.1;
//double noiseReductionParameter = configuration.GetDoubleOrNull(AnalysisKeys.NoiseBgThreshold) ?? 0.1;

// make a spectrogram
var sonoConfig = new SonogramConfig
Expand All @@ -143,26 +150,24 @@ internal static RecognizerResults Gruntwork(AudioRecording audioRecording, Confi
// now construct the standard decibel spectrogram WITH noise removal, and look for LimConvex
// get frame parameters for the analysis
var sonogram = (BaseSonogram)new SpectrogramStandard(sonoConfig, audioRecording.WavReader);
var intensityArray = SNR.CalculateFreqBandAvIntensity(sonogram.Data, minHz, maxHz, sonogram.NyquistFrequency);

var data = sonogram.Data;
var score = MatrixTools.GetRowAverages(data);
score = DataTools.NormaliseInZeroOne(score, 0, 12);
//var eventThreshold = 0.25; // equivalent to 3dB
var eventThreshold = 0.5; // equivalent to 6dB
var plot = new Plot(speciesName, score, eventThreshold);
//var data = sonogram.Data;
//var intensityArray = MatrixTools.GetRowAverages(data);
intensityArray = DataTools.NormaliseInZeroOne(intensityArray, 0, 12);
var plot = new Plot(speciesName, intensityArray, eventThreshold);
var plots = new List<Plot> { plot };

//iii: CONVERT decibel SCORES TO ACOUSTIC EVENTS
var acousticEvents = AcousticEvent.ConvertScoreArray2Events(
score,
var acousticEvents = AcousticEvent.GetEventsAroundMaxima(
intensityArray,
segmentStartOffset,
minHz,
maxHz,
sonogram.FramesPerSecond,
sonogram.FBinWidth,
eventThreshold,
minDuration,
maxDuration,
segmentStartOffset);
neighbourhoodDuration);

// ######################################################################
acousticEvents.ForEach(ae =>
Expand All @@ -182,11 +187,11 @@ internal static RecognizerResults Gruntwork(AudioRecording audioRecording, Confi
// Path.GetFileNameWithoutExtension(recording.BaseName), speciesName, "png", "DebugSpectrogram"));
//sonoImage.Save(opPath.FullName);

string imageFilename = "Test.png";
string imageFilename = audioRecording.BaseName + ".png";
sonoImage.Save(Path.Combine(outputDirectory.FullName, imageFilename));

// get samples
var samples = audioRecording.WavReader.Samples;
//var samples = audioRecording.WavReader.Samples;

// Profile example: running the same algorithm on every profile with different settings (regional variation)
/*
Expand Down Expand Up @@ -262,7 +267,7 @@ private static List<AcousticEvent> RunFemaleProfile(configuration, rest of argum


// get high resolution indices

/*
var foundEvents = new List<AcousticEvent>();
Expand All @@ -282,10 +287,10 @@ private static List<AcousticEvent> RunFemaleProfile(configuration, rest of argum
};
foundEvents.Add(anEvent);

*/
return new RecognizerResults()
{
Events = foundEvents,
Events = acousticEvents,
Hits = null,
ScoreTrack = null,

Expand Down
3 changes: 2 additions & 1 deletion src/AnalysisPrograms/Sandpit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ public static void Audio2CsvOverOneFile()
//audio2csv "C:\SensorNetworks\WavFiles\KoalaMale\SmallTestSet\DaguilarGoldCreek1_DM420157_0000m_00s__0059m_47s_49h.mp3" "C:\SensorNetworks\Software\AudioAnalysis\AnalysisConfigFiles\Towsey.MultiAnalyser.cfg" "C:\SensorNetworks\Output\Test1"

// FLYING FOX RECORDINGS
string recordingPath = @"C:\Ecoacoustics\WavFiles\BradLawData\FlyingFox\20190127_Bellingen_Feeding_SM4.wav";
//string recordingPath = @"C:\Ecoacoustics\WavFiles\BradLawData\FlyingFox\20190127_Bellingen_Feeding_SM4.wav";
string recordingPath = @"C:\Ecoacoustics\WavFiles\BradLawData\FlyingFox\20190115_Bellingen_Feeding.wav";
string configPath = @"C:\Work\GitHub\audio-analysis\src\AnalysisConfigFiles\RecognizerConfigFiles\Towsey.PteropusSpecies.yml";
string outputPath = @"C:\Ecoacoustics\Output\BradLaw\FlyingFox";

Expand Down
67 changes: 63 additions & 4 deletions src/AudioAnalysisTools/AcousticEvent.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// --------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="AcousticEvent.cs" company="QutEcoacoustics">
// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group).
// </copyright>
Expand All @@ -14,10 +14,12 @@ namespace AudioAnalysisTools
using System.Collections.ObjectModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Acoustics.Shared.Contracts;
using Acoustics.Shared.Csv;
using AForge.Imaging.Filters;
using AnalysisBase.ResultBases;
using CsvHelper.Configuration;
using DSP;
Expand Down Expand Up @@ -1097,9 +1099,9 @@ public static List<AcousticEvent> ConvertIntensityArray2Events(
//obtain average intensity score.
double av = 0.0;
for (int n = startFrame; n <= i; n++)
{
av += values[n];
}
{
av += values[n];
}

ev.Score = av / (i - startFrame + 1);
events.Add(ev);
Expand All @@ -1109,6 +1111,63 @@ public static List<AcousticEvent> ConvertIntensityArray2Events(
return events;
}

public static List<AcousticEvent> GetEventsAroundMaxima(
double[] values,
TimeSpan segmentStartOffset,
int minHz,
int maxHz,
double framesPerSec,
double freqBinWidth,
double thresholdValue,
TimeSpan nh)
{
int count = values.Length;
var events = new List<AcousticEvent>();
double frameOffset = 1 / framesPerSec; //frame offset in fractions of second

// every event will have duration of twice the buffer + 1
int frameBuffer = (int)Math.Ceiling(nh.TotalSeconds / frameOffset);
frameBuffer = Math.Max(frameBuffer, 1);
int eventLength = (2 * frameBuffer) + 1;

// every event has the same duration
double eventDuration = eventLength * frameOffset;

//int startFrame = 0;

// for all frames
for (int i = frameBuffer; i < count - frameBuffer; i++)
{
// skip if value below threshold
if (values[i] < thresholdValue)
{
continue;
}

// get the neighbourhood
var nhArray = DataTools.Subarray(values, i - frameBuffer, eventLength);
int maxId = DataTools.GetMaxIndex(nhArray);

// if middle frame is a local maximum then have an event
if (maxId == frameBuffer)
{
double startTime = (i - frameBuffer) * frameOffset; // time in seconds
AcousticEvent ev = new AcousticEvent(segmentStartOffset, startTime, eventDuration, minHz, maxHz)
{
Name = "Event", //default name
};

ev.SetTimeAndFreqScales(framesPerSec, freqBinWidth);

//obtain average intensity score.
ev.Score = nhArray.Average();
events.Add(ev);
}
}

return events;
}

/// <summary>
/// A general method to convert an array of score values to a list of AcousticEvents.
/// The method uses the passed scoreThreshold in order to calculate a normalised score.
Expand Down

0 comments on commit c86caaa

Please sign in to comment.