diff --git a/src/AnalysisPrograms/Recognizers/PteropusSpecies.cs b/src/AnalysisPrograms/Recognizers/PteropusSpecies.cs
index 20a608ca5..c85823e38 100644
--- a/src/AnalysisPrograms/Recognizers/PteropusSpecies.cs
+++ b/src/AnalysisPrograms/Recognizers/PteropusSpecies.cs
@@ -27,10 +27,8 @@ namespace AnalysisPrograms.Recognizers
using System.IO;
using System.Linq;
using System.Reflection;
- using System.Text;
using Acoustics.Shared;
using Acoustics.Shared.ConfigFile;
- using Acoustics.Tools.Wav;
using AnalysisBase;
using AnalysisBase.ResultBases;
using AnalysisPrograms.Recognizers.Base;
@@ -100,20 +98,20 @@ internal static RecognizerResults Gruntwork(AudioRecording audioRecording, Confi
// get the common properties
string speciesName = configuration[AnalysisKeys.SpeciesName] ?? "Pteropus species";
string abbreviatedSpeciesName = configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? "Pteropus";
+
+ // The following parameters worked well on a ten minute recording containing 14-16 calls.
+ // Note: if you lower the dB threshold, you need to increase maxDurationSeconds
int minHz = configuration.GetIntOrNull(AnalysisKeys.MinHz) ?? 800;
int maxHz = configuration.GetIntOrNull(AnalysisKeys.MaxHz) ?? 8000;
-
- //var samples = audioRecording.WavReader.Samples;
- double minDurationSeconds = configuration.GetIntOrNull(AnalysisKeys.MinDuration) ?? 0.2;
+ double minDurationSeconds = configuration.GetIntOrNull(AnalysisKeys.MinDuration) ?? 0.15;
double maxDurationSeconds = configuration.GetIntOrNull(AnalysisKeys.MaxDuration) ?? 0.5;
var minTimeSpan = TimeSpan.FromSeconds(minDurationSeconds);
var maxTimeSpan = TimeSpan.FromSeconds(maxDurationSeconds);
-
double decibelThreshold = configuration.GetDoubleOrNull(AnalysisKeys.NoiseBgThreshold) ?? 9.0;
//######################
- //2.Convert each segment to a spectrogram.
- //double noiseReductionParameter = configuration.GetDoubleOrNull(AnalysisKeys.NoiseBgThreshold) ?? 0.1;
+ //2.Convert each segment to a spectrogram. Don't use samples in this recogniser.
+ //var samples = audioRecording.WavReader.Samples;
// make a spectrogram
var sonoConfig = new SonogramConfig
@@ -158,17 +156,18 @@ internal static RecognizerResults Gruntwork(AudioRecording audioRecording, Confi
ae.Name = abbreviatedSpeciesName;
});
- acousticEvents = FilterEventsForSpectralProfile(acousticEvents, sonogram.Data);
-
- //var sonoImage = sonogram.GetImageFullyAnnotated("Test");
- //var sonoImage = SpectrogramTools.GetSonogramPlusCharts(sonogram, acousticEvents, plots, null);
+ acousticEvents = FilterEventsForSpectralProfile(acousticEvents, sonogram);
- //var opPath =
- // outputDirectory.Combine(
- // FilenameHelpers.AnalysisResultName(
- // Path.GetFileNameWithoutExtension(recording.BaseName), speciesName, "png", "DebugSpectrogram"));
- //string imageFilename = audioRecording.BaseName + ".png";
- //sonoImage.Save(Path.Combine(outputDirectory.FullName, imageFilename));
+ //Set following true if you want special debug spectrogram, i.e. with special plots
+ //In addition, standard spectrograms are produced when you set true in the config file, Towsey.PteropusSpecies.yml.
+ if (false)
+ {
+ //var image = sonogram.GetImageFullyAnnotated("Test");
+ var image = SpectrogramTools.GetSonogramPlusCharts(sonogram, acousticEvents, plots, null);
+ var opPath = outputDirectory.Combine(FilenameHelpers.AnalysisResultName(Path.GetFileNameWithoutExtension(audioRecording.BaseName), speciesName, "png", "DebugSpectrogram"));
+ string imageFilename = audioRecording.BaseName + ".png";
+ image.Save(Path.Combine(outputDirectory.FullName, imageFilename));
+ }
return new RecognizerResults()
{
@@ -184,61 +183,78 @@ internal static RecognizerResults Gruntwork(AudioRecording audioRecording, Confi
/// Remove events whose acoustic profile does not match that of a flying fox.
///
/// unfiltered acoustic events.
- /// matrix of spectrogram values
+ /// includes matrix of spectrogram values.
/// filtered acoustic events.
- private static List FilterEventsForSpectralProfile(List events, double[,] spectrogramData)
+ private static List FilterEventsForSpectralProfile(List events, BaseSonogram sonogram)
{
+ double[,] spectrogramData = sonogram.Data;
int colCount = spectrogramData.GetLength(1);
var filteredEvents = new List();
foreach (AcousticEvent ae in events)
{
int startFrame = ae.Oblong.RowTop;
int endFrame = ae.Oblong.RowBottom;
- var subMatrix = DataTools.Submatrix(spectrogramData, startFrame, 0, endFrame, colCount - 1);
+
+ int maxBin = (int)Math.Round(8000 / sonogram.FBinWidth);
+
+ // get all the frames of the acoustic event
+ //var subMatrix = DataTools.Submatrix(spectrogramData, startFrame, 0, endFrame, colCount - 1);
+
+ // get only the frames from centre of the acoustic event
+ var subMatrix = DataTools.Submatrix(spectrogramData, startFrame + 1, 0, startFrame + 4, maxBin);
+
var spectrum = MatrixTools.GetColumnAverages(subMatrix);
+ var normalisedSpectrum = DataTools.normalise(spectrum);
+ normalisedSpectrum = DataTools.filterMovingAverageOLD(normalisedSpectrum, 11);
+ var maxId = DataTools.GetMaxIndex(normalisedSpectrum);
+ var maxHz = (int)Math.Ceiling(maxId * sonogram.FBinWidth);
- // do test to determine if event has spectrum matching a Flying fox.
- // TODO write method to determine similarity of spectrum to a true flying fox spectrum.
- // There should be little energy in 0-600 Hz band.
- // There should three peaks at around 1.5 kHz, 3 kHz and 6 kHz.
- bool goodMatch = true;
- if (goodMatch)
- {
- filteredEvents.Add(ae);
- }
- }
+ // Do TESTS to determine if event has spectrum matching a Flying fox.
- return filteredEvents;
- }
+ // Test 1: Spectral maximum should be below 4 kHz.
+ int fourkHzBin = (int)Math.Round(4000 / sonogram.FBinWidth);
+ bool passTest1 = maxId < fourkHzBin;
- /*
- // example method
- private static List RunFemaleProfile(configuration, rest of arguments)
- {
- const string femaleProfile = "Female";
- Config currentProfile = ConfigFile.GetProfile(configuration, femaleProfile);
- Log.Info($"Analyzing profile: {femaleProfile}");
+ // Test 2: There should be little energy in 0-1 kHz band.
+ int onekHzBin = (int)Math.Round(1000 / sonogram.FBinWidth);
+ var subband1Khz = DataTools.Subarray(normalisedSpectrum, 0, onekHzBin);
+ double bandArea1 = subband1Khz.Sum();
+ double energyRatio1 = bandArea1 / normalisedSpectrum.Sum();
- // extract parameters
- int minHz = (int)configuration[AnalysisKeys.MinHz];
+ // 0.125 = 1/8. i.e. test requires that energy in 0-1kHz band is less than average in all 8 kHz bands
+ // 0.0938 = 3/32. i.e. test requires that energy in 0-1kHz band is less than 3/4 average in all 8 kHz bands
+ // 0.0625 = 1/16. i.e. test requires that energy in 0-1kHz band is less than half average in all 8 kHz bands
+ bool passTest2 = !(energyRatio1 > 0.0938);
- // ...
+ // Test 3: There should be little energy in 4-5 kHz band.
+ var subband4Khz = DataTools.Subarray(normalisedSpectrum, fourkHzBin, onekHzBin);
+ double bandArea2 = subband4Khz.Sum();
+ double energyRatio2 = bandArea2 / normalisedSpectrum.Sum();
+ bool passTest3 = !(energyRatio2 > 0.125);
- // run the algorithm
- List acousticEvents;
- Oscillations2012.Execute(All the correct parameters, minHz);
+ // TODO write method to determine similarity of spectrum to a true flying fox spectrum.
+ // Problem: it is not certain how variable the FF spectra are.
+ // In ten minutes of recording used so far, which include 14-15 obvious calls, there appear to be two spectral types.
+ // One type has three peaks at around 1.5 kHz, 3 kHz and 6 kHz.
+ // The other type have two peaks around 2.5 and 5.5 kHz.
- // augment the returned events
- acousticEvents.ForEach(ae =>
+ //if (passTest1)
+ //if (true)
+ if (passTest1 && passTest2 && passTest3)
{
- ae.SpeciesName = speciesName;
- ae.Profile = femaleProfile;
- ae.AnalysisIdealSegmentDuration = recordingDuration;
- ae.Name = abbreviatedSpeciesName;
- });
+ filteredEvents.Add(ae);
- return acousticEvents;
+ // draw DEBUG IMAGES
+ if (true)
+ {
+ string name = "FF spectrum " + ae.SegmentStartSeconds + "s Frame" + startFrame + " maxHz" + maxHz;
+ var bmp2 = GraphsAndCharts.DrawGraph(name, normalisedSpectrum, 100);
+ bmp2.Save(Path.Combine(@"C:\Ecoacoustics\Output\BradLaw\FlyingFox\Towsey.PteropusSpecies", name + ".png"));
+ }
+ }
}
- */
+
+ return filteredEvents;
+ }
}
}
diff --git a/src/AudioAnalysisTools/AcousticEvent.cs b/src/AudioAnalysisTools/AcousticEvent.cs
index 1c9c15252..02e51195f 100644
--- a/src/AudioAnalysisTools/AcousticEvent.cs
+++ b/src/AudioAnalysisTools/AcousticEvent.cs
@@ -1172,10 +1172,15 @@ public static List GetEventsAroundMaxima(
// find start frame of current event
while (values[i] > thresholdValue)
{
+ if (i <= 0)
+ {
+ break;
+ }
+
i--;
}
- startFrame = i;
+ startFrame = i + 1;
// find end frame of current event
i = maxFrame;
@@ -1186,7 +1191,7 @@ public static List GetEventsAroundMaxima(
endFrame = i;
- int frameDuration = endFrame - startFrame; // +1 ?????????????????
+ int frameDuration = endFrame - startFrame + 1;
if (frameDuration >= minFrames && frameDuration <= maxFrames)
{
double startTime = startFrame * frameOffset; // time in seconds