Skip to content

Commit

Permalink
Get Generic recognizer tests working
Browse files Browse the repository at this point in the history
Issue #281: Get all Genericrecognizer tests passing correctly.
enable access to HANNING FFT window because it works better in some cases.
Whistle recognizer now works well on Plains Wanderer and Whip bird recordings.
  • Loading branch information
towsey committed Feb 7, 2020
1 parent 39e6a38 commit 1dfaa92
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,71 +10,58 @@ SegmentOverlap: 0

# Each of these profiles will be analyzed
Profiles:
FiveSecondwhistle: !WhistleParameters
FrameSize: 512
FrameStep: 512
Whistles: !WhistleParameters
FrameSize: 256
FrameStep: 256
WindowFunction: HANNING
BgNoiseThreshold: 6.0
# min and max of the freq band to search
MinHertz: 800
MaxHertz: 8000
MinHertz: 100
MaxHertz: 1000
BottomHertzBuffer: 0
TopHertzBuffer: 0
MinDuration: 0.15
MaxDuration: 0.8
DecibelThreshold: 9.0
ComponentName: Blob
SpeciesName: PteropusSp
HalfSecondwhistle: !WhistleParameters
FrameSize: 512
FrameStep: 512
BgNoiseThreshold: 6.0
# min and max of the freq band to search
MinHertz: 800
MaxHertz: 8000
BottomHertzBuffer: 0
TopHertzBuffer: 0
MinDuration: 0.15
MaxDuration: 0.8
DecibelThreshold: 9.0
ComponentName: Blob
SpeciesName: PteropusSp
LargeBlob: !WhistleParameters
MinDuration: 0.4
MaxDuration: 8.0
DecibelThreshold: 6.0
ComponentName: Whistle
SpeciesName: Artifical
LargeBlob: !BlobParameters
FrameSize: 512
FrameStep: 512
BgNoiseThreshold: 6.0
# min and max of the freq band to search
MinHertz: 800
MaxHertz: 8000
BottomHertzBuffer: 0
TopHertzBuffer: 0
MinDuration: 0.15
MaxDuration: 0.8
MinHertz: 4600
MaxHertz: 7000
BottomHertzBuffer: 1000
TopHertzBuffer: 1000
MinDuration: 1.0
MaxDuration: 3.0
DecibelThreshold: 9.0
ComponentName: Blob
SpeciesName: PteropusSp
Wingbeats: !OscillationParameters
SpeciesName: Artifical
GroupBlob: !OscillationParameters
FrameSize: 512
FrameStep: 512
MinHertz: 200
MinHertz: 600
MaxHertz: 2000
DecibelThreshold: 6.0
# duration of DCT in seconds
DctDuration: 0.5
DctDuration: 4.0
# minimum acceptable value of a DCT coefficient
DctThreshold: 0.5
# ignore oscillation rates below the min & above the max threshold
# A sample of four wingbeats yielded an average of 5.1 +/-0.5 beats/s.
# 95% confidence interval (2 SDs) is therefore approx 4-6 beats/s.
# OSCILLATIONS PER SECOND
MinOscillationFrequency: 4
MaxOscillationFrequency: 6
MinOscillationFrequency: 1
MaxOscillationFrequency: 2
# Minimum and maximum duration for the length of a true call.
MinDuration: 1.0
MinDuration: 5.0
MaxDuration: 10.0
# Event threshold - use this to determine FP / FN trade-off for events.
EventThreshold: 0.5
ComponentName: Oscillation
SpeciesName: PteropusSp
SpeciesName: Artificial


# Common settings
Expand Down
9 changes: 9 additions & 0 deletions src/AnalysisPrograms/Recognizers/Base/CommonParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// 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>

using TowseyLibrary;

namespace AnalysisPrograms.Recognizers.Base
{
/// <summary>
Expand Down Expand Up @@ -31,6 +33,13 @@ public abstract class CommonParameters
/// </summary>
public int? FrameStep { get; set; }

/// <summary>
/// Gets or sets the windowing funciton used in conjunction with the FFT when making spectrogram.
/// This can have quite an impact in some cases so it is worth giving user the option.
/// The default is a HAMMIN window.
/// </summary>
public string WindowFunction { get; set; } = WindowFunctions.HAMMING.ToString();

/// <summary>
/// Gets or sets the threshold in decibels which determines signal over
/// background noise.
Expand Down
2 changes: 1 addition & 1 deletion src/AnalysisPrograms/Recognizers/Base/DctParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,6 @@ public abstract class DctParameters : CommonParameters
/// <summary>
/// Gets or sets the Event threshold - use this to determine FP / FN trade-off for events.
/// </summary>
public double EventThreshold { get; set; }
public double EventThreshold { get; set; } = 0.3;
}
}
48 changes: 4 additions & 44 deletions src/AnalysisPrograms/Recognizers/Base/HarmonicParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ namespace AnalysisPrograms.Recognizers.Base
using TowseyLibrary;

/// <summary>
/// TODO TODO: THIS METHOD IS WORK IN PROGESS AND CURRENTLY DOES YIELD A SUCCESSFUL RESULT. To BE FURTHER WORKED ON!!.
/// Parameters needed from a config file to detect the stacked harmonic components of a soundscape.
/// This can also be used for recognizing the harmonics of non-biological sounds such as from turbines, motor-bikes, compressors and other hi-revving motors.
/// </summary>
[YamlTypeTag(typeof(HarmonicParameters))]
public class HarmonicParameters : CommonParameters
{
public static (List<AcousticEvent>, double[]) GetSyllablesWithHarmonics(
public static (List<AcousticEvent>, double[]) GetComponentsWithHarmonics(
SpectrogramStandard sonogram,
int minHz,
int maxHz,
Expand All @@ -36,7 +37,7 @@ public static (List<AcousticEvent>, double[]) GetSyllablesWithHarmonics(
int maxFormantGap = 450;

// Event threshold - Determines FP / FN trade-off for events.
double eventThreshold = 0.2;
//double eventThreshold = 0.2;

var sonogramData = sonogram.Data;
int frameCount = sonogramData.GetLength(0);
Expand Down Expand Up @@ -75,7 +76,6 @@ public static (List<AcousticEvent>, double[]) GetSyllablesWithHarmonics(
int minCallSpan = (int)Math.Round(minDuration * sonogram.FramesPerSecond);

//ii: DETECT HARMONICS
//#############################################################################################################################################
var results = CrossCorrelation.DetectHarmonicsInSonogramMatrix(subMatrix, decibelThreshold, minCallSpan);

double[] dBArray = results.Item1;
Expand Down Expand Up @@ -119,47 +119,7 @@ public static (List<AcousticEvent>, double[]) GetSyllablesWithHarmonics(
{
scoreArray[r] = intensity[r] * discount;
}

//transfer info to a hits matrix.
//var hits = new double[rowCount, colCount];
//double threshold = harmonicIntensityThreshold * 0.75; //reduced threshold for display of hits
//for (int r = 0; r < rowCount; r++)
//{
// if (scoreArray[r] < threshold)
// {
// continue;
// }

// double herzPeriod = periodicity[r] * freqBinWidth;
// for (int c = minBin; c < maxbin; c++)
// {
// //hits[r, c] = herzPeriod / (double)380; //divide by 380 to get a relativePeriod;
// hits[r, c] = (herzPeriod - minFormantgap) / maxFormantgap; //to get a relativePeriod;
// }
//}

//iii: CONVERT TO ACOUSTIC EVENTS
//double maxPossibleScore = 0.5;
//int halfCallSpan = minCallSpan / 2;
//var predictedEvents = new List<AcousticEvent>();
//for (int i = 0; i < rowCount; i++)
//{
// //assume one score position per crow call
// if (scoreArray[i] < 0.001)
// {
// continue;
// }

// double startTime = (i - halfCallSpan) / framesPerSecond;
// AcousticEvent ev = new AcousticEvent(segmentStartOffset, startTime, minDuration, minHz, maxHz);
// ev.SetTimeAndFreqScales(framesPerSecond, freqBinWidth);
// ev.Score = scoreArray[i];
// ev.ScoreNormalised = ev.Score / maxPossibleScore; // normalised to the user supplied threshold

// //ev.Score_MaxPossible = maxPossibleScore;
// predictedEvents.Add(ev);
//} //for loop
} // for all time frames
}

// smooth the decibel array to allow for brief gaps.
harmonicScores = DataTools.filterMovingAverageOdd(harmonicScores, 5);
Expand Down
2 changes: 1 addition & 1 deletion src/AnalysisPrograms/Recognizers/Base/WhistleParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public static (List<AcousticEvent>, double[]) GetWhistles(
var combinedIntensityArray = new double[frameCount];

// for all frequency bins except top and bottom
for (int bin = minBin + 1; bin < maxBin - 1; bin++)
for (int bin = minBin + 1; bin < maxBin; bin++)
{
// set up an intensity array for the frequency bin.
double[] intensity = new double[frameCount];
Expand Down
26 changes: 22 additions & 4 deletions src/AnalysisPrograms/Recognizers/GenericRecognizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public override AnalyzerConfig ParseConfig(FileInfo file)
break;
case HarmonicParameters _:
algorithmName = "Harmonics";
//throw new NotImplementedException("The harmonic algorithm has not been implemented yet");
throw new NotImplementedException("The harmonic algorithm has not been implemented yet");
break;
case Aed.AedConfiguration _:
algorithmName = "AED";
Expand Down Expand Up @@ -121,7 +121,6 @@ public override RecognizerResults Recognize(
var plots = new List<Plot>();
SpectrogramStandard sonogram;


Log.Debug($"Using the {profileName} algorithm... ");
if (profileConfig is CommonParameters parameters)
{
Expand Down Expand Up @@ -201,7 +200,7 @@ public override RecognizerResults Recognize(
{
//get the array of intensity values minus intensity in side/buffer bands.
double[] scoreArray;
(acousticEvents, scoreArray) = HarmonicParameters.GetSyllablesWithHarmonics(
(acousticEvents, scoreArray) = HarmonicParameters.GetComponentsWithHarmonics(
sonogram,
hp.MinHertz.Value,
hp.MaxHertz.Value,
Expand Down Expand Up @@ -259,6 +258,9 @@ public override RecognizerResults Recognize(
// effectively keeps only the *last* sonogram produced
allResults.Sonogram = sonogram;
Log.Debug($"{profileName} event count = {acousticEvents.Count}");

// DEBUG PURPOSES COMMENT NEXT LINE
//SaveDebugSpectrogram(allResults, genericConfig, outputDirectory, "name");
}

return allResults;
Expand All @@ -285,10 +287,12 @@ private static SonogramConfig ParametersToSonogramConfig(CommonParameters common
{
return new SonogramConfig()
{
//WindowOverlap = (WindowSize - WindowStep) / (double)WindowSize,
WindowSize = (int)common.FrameSize,
WindowStep = (int)common.FrameStep,

//WindowOverlap = (WindowSize - WindowStep) / (double)WindowSize,
// Default window is Hamming. Alternative is to use Hanning. Can sometimes be better.
WindowFunction = (string)common.WindowFunction, //WindowFunctions.HANNING.ToString(),
NoiseReductionType = NoiseReductionType.Standard,
NoiseReductionParameter = common.BgNoiseThreshold ?? 0.0,
};
Expand All @@ -311,11 +315,25 @@ private static Plot PreparePlot(double[] array, string title, double threshold)
return plot;
}

/// <summary>
/// THis method can be modified if want to do something non-standard with the output spectrogram.
/// </summary>
static void SaveDebugSpectrogram(RecognizerResults results, Config genericConfig, DirectoryInfo outputDirectory, string baseName)
{
//var image = sonogram.GetImageFullyAnnotated("Test");
var image = SpectrogramTools.GetSonogramPlusCharts(results.Sonogram, results.Events, results.Plots, null);

// image.Save(Path.Combine(outputDirectory.FullName, baseName + ".profile.png"));
image.Save(Path.Combine("C:\\temp\\test.profile.png"));
//sonogram.GetImageFullyAnnotated("test").Save("C:\\temp\\test.png");
}

/// <inheritdoc cref="RecognizerConfig"/> />
public class GenericRecognizerConfig : RecognizerConfig, INamedProfiles<object>
{
/// <inheritdoc />
public Dictionary<string, object> Profiles { get; set; }
}

}
}
Loading

0 comments on commit 1dfaa92

Please sign in to comment.