Skip to content

Commit

Permalink
Set up classes
Browse files Browse the repository at this point in the history
Issue #281 Set up classes for generic syllable recognition.
  • Loading branch information
towsey committed Jan 25, 2020
1 parent 2bfa889 commit 0d255d4
Show file tree
Hide file tree
Showing 8 changed files with 486 additions and 405 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
SpeciesName: Test Species.
SpeciesName: GenericRecognizers
CommonName: Generic Test
AbbreviatedSpeciesName: TestSp

Expand All @@ -10,32 +10,20 @@ SegmentDuration: 60
# SegmentOverlap: units=seconds;
SegmentOverlap: 0

#Proposed Approach:
#1. Assume that a long recording has been broken into one-minute segments.
#2. Convert each one-minute segment to a spectrogram.
#3. Obtain a noise profile for each segment. This is to be used later to remove insect chorusing.
#4. Scan the one-minute waveform and select "spike maxima" whose amplitude exceeds a decibel threshold, D.
#5. Extract a single frame (say 512 samples) centred on each spike and convert to a spike spectrum.
#6. Subtract the noise profile from the spike spectrum.
#7. Smooth the remaining spectrum.
#8. Look for evenly spaced harmonics in the smoothed spectrum.
# Typically the lowest harmonic will lie between 1200 Hz and 3000 Hz and the higher ones evenly spaced.
# This is the tricky bit due to variability but may work to use spectrum auto-correlation.

# Each of these profiles will be analyzed
Profiles:
#Standard: &STANDARD
#EventThreshold: 0.2
# Common settings
#BgNoiseThreshold: 3.0
Territorial:
# min and max of the freq band to search
# MinHz: 800
# MaxHz: 8000
# MinDuration: 0.15
# MaxDuration: 0.8
# DecibelThreshold: 9.0
Wingbeats:
Territorial:
Algorithm: BlobRecognizer
Parameters:
# min and max of the freq band to search
MinHz: 800
MaxHz: 8000
MinDuration: 0.15
MaxDuration: 0.8
DecibelThreshold: 9.0
Wingbeats:
Algorithm: OscillationRecognizer
Parameters:
# MinHz: 200
# MaxHz: 2000
# DecibelThreshold: 6.0
Expand All @@ -60,6 +48,14 @@ Profiles:
# <<: *STANDARD
# DctDuration: 0.3


#Standard: &STANDARD
#EventThreshold: 0.2
# Common settings
#BgNoiseThreshold: 3.0



# Available options (case-sensitive): [False/Never | True/Always | WhenEventsDetected]
SaveIntermediateWavFiles: Never
SaveIntermediateCsvFiles: false
Expand All @@ -73,5 +69,5 @@ DisplayCsvImage: false

# Other config files to reference

#HighResolutionIndicesConfig: "../Towsey.Acoustic.HiResIndicesForRecognisers.yml"
HighResolutionIndicesConfig: "../Towsey.Acoustic.HiResIndicesForRecognisers.yml"
...
3 changes: 3 additions & 0 deletions src/AnalysisPrograms/AnalysisPrograms.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,9 @@
<Compile Include="Recognizers\Base\RecognizerEntry.cs" />
<Compile Include="Recognizers\Base\RecognizerResults.cs" />
<Compile Include="Recognizers\CriniaTinnula_OBSOLETE.cs" />
<Compile Include="Recognizers\GenericBlobRecognizer.cs" />
<Compile Include="Recognizers\GenericOscillationRecognizer.cs" />
<Compile Include="Recognizers\GenericWhistleRecognizer.cs" />
<Compile Include="Recognizers\IctalurusFurcatus.cs" />
<Compile Include="Recognizers\LewiniaPectoralis.cs" />
<Compile Include="Recognizers\LitoriaNasuta.cs" />
Expand Down
94 changes: 94 additions & 0 deletions src/AnalysisPrograms/Recognizers/GenericBlobRecognizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// <copyright file="GenericBlobRecognizer.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>

namespace Recognizers
{
using System;
using System.Collections.Generic;
using Acoustics.Shared.ConfigFile;
using AnalysisPrograms.Recognizers.Base;
using AudioAnalysisTools;
using AudioAnalysisTools.DSP;
using AudioAnalysisTools.WavTools;
using TowseyLibrary;

public class GenericBlobRecognizer
{
/// <summary>
/// THis method does the work.
/// </summary>
/// <param name="audioRecording">the recording.</param>
/// <param name="configuration">the config file.</param>
/// <param name="profileName">name of the call/event type.</param>
/// <param name="segmentStartOffset">where one segment is located in the total recording.</param>
/// <returns>a list of events.</returns>
public static RecognizerResults BlobRecognizer(AudioRecording audioRecording, Config configuration, string profileName, TimeSpan segmentStartOffset)
{
ConfigFile.TryGetProfile(configuration, profileName, out var profile);

// 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 = profile.GetIntOrNull(AnalysisKeys.MinHz) ?? 800;
int maxHz = profile.GetIntOrNull(AnalysisKeys.MaxHz) ?? 8000;
double minDurationSeconds = profile.GetDoubleOrNull(AnalysisKeys.MinDuration) ?? 0.15;
double maxDurationSeconds = profile.GetDoubleOrNull(AnalysisKeys.MaxDuration) ?? 0.5;
double decibelThreshold = profile.GetDoubleOrNull(AnalysisKeys.DecibelThreshold) ?? 9.0;

var minTimeSpan = TimeSpan.FromSeconds(minDurationSeconds);
var maxTimeSpan = TimeSpan.FromSeconds(maxDurationSeconds);

//######################
//2. Convert each segment to a spectrogram.
using (AudioAnalysisTools.StandardSpectrograms.BaseSonogram sonogram = RecognizerTools.GetSonogram(configuration, audioRecording: audioRecording))
{
var decibelArray = SNR.CalculateFreqBandAvIntensity(sonogram.Data, minHz, maxHz, sonogram.NyquistFrequency);

// prepare plots
double intensityNormalisationMax = 3 * decibelThreshold;
var eventThreshold = decibelThreshold / intensityNormalisationMax;
var normalisedIntensityArray = DataTools.NormaliseInZeroOne(decibelArray, 0, intensityNormalisationMax);
var plot = new Plot(speciesName + "Blob", normalisedIntensityArray, eventThreshold);
var plots = new List<Plot> { plot };

//iii: CONVERT decibel SCORES TO ACOUSTIC EVENTS
var acousticEvents = AcousticEvent.GetEventsAroundMaxima(
decibelArray,
segmentStartOffset,
minHz,
maxHz,
decibelThreshold,
minTimeSpan,
maxTimeSpan,
sonogram.FramesPerSecond,
sonogram.FBinWidth);

//iV add additional info to the acoustic events
acousticEvents.ForEach(ae =>
{
ae.FileName = audioRecording.BaseName;
ae.SpeciesName = speciesName;
ae.Name = abbreviatedSpeciesName + profileName;
ae.Profile = profileName;
ae.SegmentDurationSeconds = audioRecording.Duration.TotalSeconds;
ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds;
});

acousticEvents = RecognizerTools.FilterEventsForSpectralProfile(acousticEvents, sonogram);

return new RecognizerResults()
{
Events = acousticEvents,
Hits = null,
ScoreTrack = null,
Plots = plots,
Sonogram = sonogram,
};
}
}
}
}
151 changes: 151 additions & 0 deletions src/AnalysisPrograms/Recognizers/GenericOscillationRecognizer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// <copyright file="GenericOscillationRecognizer.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>

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

public class GenericOscillationRecognizer
{
/// <summary>
/// THis method does the work.
/// </summary>
/// <param name="audioRecording">the recording.</param>
/// <param name="configuration">the config file.</param>
/// <param name="profileName">name of call/event type to be found.</param>
/// <param name="segmentStartOffset">where one segment is located in the total recording.</param>
/// <returns>a list of events.</returns>
public static RecognizerResults OscillationRecognizer(AudioRecording audioRecording, Config configuration, string profileName, TimeSpan segmentStartOffset)
{
ConfigFile.TryGetProfile(configuration, profileName, out var profile);

// 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 = profile.GetIntOrNull(AnalysisKeys.MinHz) ?? 100;
int maxHz = profile.GetIntOrNull(AnalysisKeys.MaxHz) ?? 3000;
double minDurationSeconds = profile.GetDoubleOrNull(AnalysisKeys.MinDuration) ?? 1.0;
double maxDurationSeconds = profile.GetDoubleOrNull(AnalysisKeys.MaxDuration) ?? 10.0;
double decibelThreshold = profile.GetDoubleOrNull("DecibelThreshold") ?? 6.0;
double dctDuration = profile.GetDoubleOrNull("DctDuration") ?? 1.0;
double dctThreshold = profile.GetDoubleOrNull("DctThreshold") ?? 0.5;
double minOscFreq = profile.GetDoubleOrNull("MinOscilFreq") ?? 4.0;
double maxOscFreq = profile.GetDoubleOrNull("MaxOscilFreq") ?? 6.0;
double eventThreshold = profile.GetDoubleOrNull("EventThreshold") ?? 0.3;

//######################

//2. Don't use samples in this recognizer.
//var samples = audioRecording.WavReader.Samples;
//Instead, convert each segment to a spectrogram.
var sonogram = RecognizerTools.GetSonogram(configuration, audioRecording);
var decibelArray = SNR.CalculateFreqBandAvIntensity(sonogram.Data, minHz, maxHz, sonogram.NyquistFrequency);

// Call oscillation detector
/*
int scoreSmoothingWindow = 11; // sets a default that was good for Cane toad
Oscillations2019.Execute(
(SpectrogramStandard)sonogram,
minHz,
maxHz,
decibelThreshold,
dctDuration,
(int)Math.Floor(minOscFreq),
(int)Math.Floor(maxOscFreq),
dctThreshold,
eventThreshold,
minDurationSeconds,
maxDurationSeconds,
scoreSmoothingWindow,
out var scores,
out var acousticEvents,
//out var hits,
segmentStartOffset);
*/
Oscillations2012.Execute(
(SpectrogramStandard)sonogram,
minHz,
maxHz,
//decibelThreshold,
dctDuration,
(int)Math.Floor(minOscFreq),
(int)Math.Floor(maxOscFreq),
dctThreshold,
eventThreshold,
minDurationSeconds,
maxDurationSeconds,
out var scores,
out var acousticEvents,
out var hits,
segmentStartOffset);

// prepare plots
double intensityNormalisationMax = 3 * decibelThreshold;
var normThreshold = decibelThreshold / intensityNormalisationMax;
var normalisedIntensityArray = DataTools.NormaliseInZeroOne(decibelArray, 0, intensityNormalisationMax);
var plot1 = new Plot(speciesName + " Wing-beat band", normalisedIntensityArray, normThreshold);
var plot2 = new Plot(speciesName + " Wing-beat Osc Score", scores, eventThreshold);
var plots = new List<Plot> { plot1, plot2 };

// ######################################################################

// add additional information about the recording and sonogram properties from which the event is derived.
acousticEvents.ForEach(ae =>
{
ae.FileName = audioRecording.BaseName;
ae.SpeciesName = speciesName;
ae.Name = abbreviatedSpeciesName + profileName;
ae.Profile = profileName;
ae.SegmentDurationSeconds = audioRecording.Duration.TotalSeconds;
ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds;
var frameOffset = sonogram.FrameStep;
var frameDuration = sonogram.FrameDuration;
ae.SetTimeAndFreqScales(frameOffset, frameDuration, sonogram.FBinWidth);

//UNCOMMENT following lines to get spectral profiles of the Wingbeat events.
/* double[,] spectrogramData = sonogram.Data;
int maxBin = (int)Math.Round(8000 / sonogram.FBinWidth);
double startSecond = ae.EventStartSeconds - ae.SegmentStartSeconds;
int startFrame = (int)Math.Round(startSecond / sonogram.FrameStep);
int frameLength = (int)Math.Round(ae.EventDurationSeconds / sonogram.FrameStep);
int endFrame = startFrame + frameLength;
// get only the frames from centre of the acoustic event
var subMatrix = DataTools.Submatrix(spectrogramData, startFrame + 10, 0, endFrame - 10, maxBin);
var spectrum = MatrixTools.GetColumnAverages(subMatrix);
var normalisedSpectrum = DataTools.normalise(spectrum);
normalisedSpectrum = DataTools.filterMovingAverageOdd(normalisedSpectrum, 11);
var maxId = DataTools.GetMaxIndex(normalisedSpectrum);
var hzMax = (int)Math.Ceiling(maxId * sonogram.FBinWidth);
string name = "BeatSpectrum " + (ae.SegmentStartSeconds / 60) + "m" + (int)Math.Floor(startSecond) + "s hzMax" + hzMax;
var bmp2 = GraphsAndCharts.DrawGraph(name, normalisedSpectrum, 100);
//Set required path
bmp2.Save(Path.Combine(@"C:\PATH", name + ".png"));
*/
});

return new RecognizerResults()
{
Events = acousticEvents,
Hits = null,
ScoreTrack = null,
Plots = plots,
Sonogram = sonogram,
};
}

}
}
Loading

0 comments on commit 0d255d4

Please sign in to comment.