diff --git a/src/AnalysisPrograms/Recognizers/PteropusSpecies.cs b/src/AnalysisPrograms/Recognizers/PteropusSpecies.cs index b8471a843..eedf8de1a 100644 --- a/src/AnalysisPrograms/Recognizers/PteropusSpecies.cs +++ b/src/AnalysisPrograms/Recognizers/PteropusSpecies.cs @@ -102,8 +102,15 @@ internal static RecognizerResults Gruntwork(AudioRecording audioRecording, Confi string abbreviatedSpeciesName = configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? ""; 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; @@ -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 @@ -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 }; //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 => @@ -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) /* @@ -262,7 +267,7 @@ private static List RunFemaleProfile(configuration, rest of argum // get high resolution indices - +/* var foundEvents = new List(); @@ -282,10 +287,10 @@ private static List RunFemaleProfile(configuration, rest of argum }; foundEvents.Add(anEvent); - +*/ return new RecognizerResults() { - Events = foundEvents, + Events = acousticEvents, Hits = null, ScoreTrack = null, diff --git a/src/AnalysisPrograms/Sandpit.cs b/src/AnalysisPrograms/Sandpit.cs index f59165e38..de5f4bd06 100644 --- a/src/AnalysisPrograms/Sandpit.cs +++ b/src/AnalysisPrograms/Sandpit.cs @@ -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"; diff --git a/src/AudioAnalysisTools/AcousticEvent.cs b/src/AudioAnalysisTools/AcousticEvent.cs index ff56f47c6..293987b75 100644 --- a/src/AudioAnalysisTools/AcousticEvent.cs +++ b/src/AudioAnalysisTools/AcousticEvent.cs @@ -1,4 +1,4 @@ -// -------------------------------------------------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- // // 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). // @@ -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; @@ -1097,9 +1099,9 @@ public static List 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); @@ -1109,6 +1111,63 @@ public static List ConvertIntensityArray2Events( return events; } + public static List 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(); + 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; + } + /// /// 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.