diff --git a/src/AnalysisConfigFiles/RecognizerConfigFiles/Towsey.BotaurusPoiciloptilus.yml b/src/AnalysisConfigFiles/RecognizerConfigFiles/Towsey.BotaurusPoiciloptilus.yml index c0bbff784..2f0095f01 100644 --- a/src/AnalysisConfigFiles/RecognizerConfigFiles/Towsey.BotaurusPoiciloptilus.yml +++ b/src/AnalysisConfigFiles/RecognizerConfigFiles/Towsey.BotaurusPoiciloptilus.yml @@ -9,51 +9,63 @@ SegmentDuration: 60 SegmentOverlap: 0 # Each of these profiles will be analyzed +# This profile is required for the species-specific recogniser and must have the current name. Profiles: - AustBittern: !OnebinTrackParameters + BitternSyllable: !OnebinTrackParameters ComponentName: Whistle SpeciesName: BotaurusPoiciloptilus FrameSize: 512 FrameStep: 256 WindowFunction: HANNING - BgNoiseThreshold: 0.0 + #BgNoiseThreshold: 0.0 # min and max of the freq band to search MinHertz: 100 MaxHertz: 200 MinDuration: 0.3 MaxDuration: 1.0 - DecibelThreshold: 3.0 + DecibelThreshold: 6.0 -#Combine each pair of Boobook syllables as one event -#CombineProximalSimilarEvents: false +#################### POST-PROCESSING of EVENTS ################### + +# A: First post-processing steps are to combine overlapping/proximal/sequential events +# 1: Combine overlapping events +#CombineOverlappingEvents: false + +# 2: Combine each pair of Boobook syllables as one event +# Can also use this to "mop up" events in neighbourhood - these can be removed later. CombinePossibleSyllableSequence: true SyllableStartDifference: 3.0 -SyllableHertzGap: 100 +SyllableHertzGap: 35 -# Common settings -#Standard: &STANDARD -#EventThreshold: 0.2 -#BgNoiseThreshold: 3.0 +# B: Filter the events for excess activity in their upper and lower buffer zones +LowerHertzBuffer: 150 +UpperHertzBuffer: 400 -# This notation means the a profile has all of the settings that the Standard profile has, -# however, the DctDuration parameter has been overridden. -# <<: *STANDARD -# DctDuration: 0.3 - -################################################################################ -# Save intermediate file options (case-sensitive): [False/Never | True/Always | WhenEventsDetected] +# C: Options to save results files +# 4: Available options for saving data files (case-sensitive): [False/Never | True/Always | WhenEventsDetected] SaveIntermediateWavFiles: Never SaveIntermediateCsvFiles: false - -# Save spectrogram images: available options (case-sensitive): [False/Never | True/Always | WhenEventsDetected] +# Available options (case-sensitive): [False/Never | True/Always | WhenEventsDetected] # "True" is useful when debugging but "WhenEventsDetected" is required for operational use. + +# 5: Available options for saving #SaveSonogramImages: True SaveSonogramImages: WhenEventsDetected - # DisplayCsvImage is obsolete - ensure it remains set to: false DisplayCsvImage: false ## End section for AnalyzeLongRecording # Other config files to reference HighResolutionIndicesConfig: "../Towsey.Acoustic.HiResIndicesForRecognisers.yml" + +################################################################################ +# Common settings +#Standard: &STANDARD +#EventThreshold: 0.2 +#BgNoiseThreshold: 3.0 + +# This notation means the a profile has all of the settings that the Standard profile has, +# however, the DctDuration parameter has been overridden. +# <<: *STANDARD +# DctDuration: 0.3 ... \ No newline at end of file diff --git a/src/AnalysisPrograms/Recognizers/Birds/BotaurusPoiciloptilus.cs b/src/AnalysisPrograms/Recognizers/Birds/BotaurusPoiciloptilus.cs index e690a7cf2..c96e8342f 100644 --- a/src/AnalysisPrograms/Recognizers/Birds/BotaurusPoiciloptilus.cs +++ b/src/AnalysisPrograms/Recognizers/Birds/BotaurusPoiciloptilus.cs @@ -13,12 +13,9 @@ namespace AnalysisPrograms.Recognizers using Acoustics.Shared.ConfigFile; using AnalysisBase; using AnalysisPrograms.Recognizers.Base; - using AudioAnalysisTools; - using AudioAnalysisTools.DSP; using AudioAnalysisTools.Events; using AudioAnalysisTools.Events.Types; using AudioAnalysisTools.Indices; - using AudioAnalysisTools.StandardSpectrograms; using AudioAnalysisTools.WavTools; using log4net; using SixLabors.ImageSharp; @@ -29,7 +26,7 @@ namespace AnalysisPrograms.Recognizers /// /// A recognizer for the Australasian Bittern, Botaurus poiciloptilus, https://en.wikipedia.org/wiki/Australasian_bittern. /// The Australasian bittern, also known as the brown bittern or matuku hūrepo, is a large bird in the heron family Ardeidae. - /// A secretive bird with a distinctive booming call, it is more often heard than seen. + /// A secretive bird with a distinctive, low frequency booming call, it is more often heard than seen. /// Australasian bitterns are endangered in both Australia and New Zealand. /// internal class BotaurusPoiciloptilus : RecognizerBase @@ -91,42 +88,102 @@ public override RecognizerResults Recognize( outputDirectory, imageWidth); - // DO POST-PROCESSING of EVENTS - var events = combinedResults.NewEvents; - + // ################### POST-PROCESSING of EVENTS ################### // Following two commented lines are different ways of casting lists. //var newEvents = spectralEvents.Cast().ToList(); //var spectralEvents = events.Select(x => (SpectralEvent)x).ToList(); - List newEvents; + + // Uncomment the next line when want to obtain the event frequency profiles. + // WriteFrequencyProfiles(chirpEvents); + + //foreach (var ev in whistleEvents) + //{ + // // Calculate frequency profile score for event + // SetFrequencyProfileScore((WhistleEvent)ev); + //} + + if (combinedResults.NewEvents.Count == 0) + { + BitternLog.Debug($"Return zero events."); + return combinedResults; + } + + var newEvents = combinedResults.NewEvents; // NOTE: If the dB threshold is set low, may get lots of little events. if (genericConfig.CombinePossibleSyllableSequence) { // Convert events to spectral events for combining of possible sequences. - var spectralEvents = events.Cast().ToList(); + // Can also use this parameter to combine events that are in the upper or lower neighbourhood. + // Such combinations will increase bandwidth of the event and this property can be used later to weed out unlikely events.. + var spectralEvents = newEvents.Cast().ToList(); var startDiff = genericConfig.SyllableStartDifference; var hertzDiff = genericConfig.SyllableHertzGap; newEvents = CompositeEvent.CombineProximalEvents(spectralEvents, TimeSpan.FromSeconds(startDiff), (int)hertzDiff); + BitternLog.Debug($"Event count after combining proximals = {combinedResults.NewEvents.Count}"); + } + + // Get the Bittern syllable config. + const string profileName = "BitternSyllable"; + var configuration = (BotaurusPoiciloptilusConfig)genericConfig; + var whistleConfig = (OnebinTrackParameters)configuration.Profiles[profileName]; + + // 4: Filter events on the amount of acoustic activity in their upper and lower neighbourhoods - their buffer zone. + // The idea is that an unambiguous event should have some acoustic space above and below. + // The filter requires that the average acoustic activity in each frame and bin of the upper and lower buffer zones should not exceed the user specified decibel threshold. + // The bandwidth of these two neighbourhoods is determined by the following parameters. + // ########## These parameters could be specified by user in config.yml file. + var upperHertzBuffer = 400; + var lowerHertzBuffer = 150; + + // The decibel threshold is currently set 5/6ths of the user specified threshold. + // THIS IS TO BE WATCHED. IT MAY PROVE TO BE INAPPROPRIATE TO HARD-CODE. + // Want the activity in buffer zones to be "somewhat" less than the user-defined threshold. + var neighbourhoodDbThreshold = whistleConfig.DecibelThreshold.Value * 0.8333; + + if (upperHertzBuffer > 0 || lowerHertzBuffer > 0) + { + var spectralEvents2 = combinedResults.NewEvents.Cast().ToList(); + combinedResults.NewEvents = EventExtentions.FilterEventsOnNeighbourhood( + spectralEvents2, + combinedResults.Sonogram, + lowerHertzBuffer, + upperHertzBuffer, + segmentStartOffset, + neighbourhoodDbThreshold); + + BitternLog.Debug($"Event count after filtering on neighbourhood = {combinedResults.NewEvents.Count}"); } - else + + if (combinedResults.NewEvents.Count == 0) { - newEvents = events; + BitternLog.Debug($"Return zero events."); + return combinedResults; } - //filter the events for duration in seconds + // 5: Filter on COMPONENT COUNT in Composite events. + int maxComponentCount = 6; + combinedResults.NewEvents = EventExtentions.FilterEventsOnCompositeContent(combinedResults.NewEvents, maxComponentCount); + BitternLog.Debug($"Event count after filtering on component count = {combinedResults.NewEvents.Count}"); + + // 6: Filter the events for duration in seconds var minimumEventDuration = 0.5; var maximumEventDuration = 2.0; if (genericConfig.CombinePossibleSyllableSequence) { minimumEventDuration = 2.0; + maximumEventDuration = 10.0; } combinedResults.NewEvents = EventExtentions.FilterOnDuration(newEvents, minimumEventDuration, maximumEventDuration); + BitternLog.Debug($"Event count after filtering on duration = {combinedResults.NewEvents.Count}"); - double average = 365; - double sd = 22; + // 7: Filter the events for bandwidth in Hertz + double average = 100; + double sd = 15; double sigmaThreshold = 3.0; combinedResults.NewEvents = EventExtentions.FilterOnBandwidth(combinedResults.NewEvents, average, sd, sigmaThreshold); + BitternLog.Debug($"Event count after filtering on bandwidth = {combinedResults.NewEvents.Count}"); //UNCOMMENT following line if you want special debug spectrogram, i.e. with special plots. // NOTE: Standard spectrograms are produced by setting SaveSonogramImages: "True" or "WhenEventsDetected" in UserName.SpeciesName.yml config file. diff --git a/src/AnalysisPrograms/Recognizers/Birds/NinoxBoobook.cs b/src/AnalysisPrograms/Recognizers/Birds/NinoxBoobook.cs index f503610ea..ffe68fa6f 100644 --- a/src/AnalysisPrograms/Recognizers/Birds/NinoxBoobook.cs +++ b/src/AnalysisPrograms/Recognizers/Birds/NinoxBoobook.cs @@ -17,7 +17,6 @@ namespace AnalysisPrograms.Recognizers using AudioAnalysisTools.Events; using AudioAnalysisTools.Events.Types; using AudioAnalysisTools.Indices; - using AudioAnalysisTools.StandardSpectrograms; using AudioAnalysisTools.WavTools; using log4net; using SixLabors.ImageSharp; @@ -92,7 +91,7 @@ public override RecognizerResults Recognize( outputDirectory, imageWidth); - // ################### DO POST-PROCESSING of EVENTS ################### + // ################### POST-PROCESSING of EVENTS ################### // Following two commented lines are different ways of casting lists. //var newEvents = spectralEvents.Cast().ToList(); //var spectralEvents = events.Select(x => (SpectralEvent)x).ToList(); diff --git a/tests/Acoustics.Test/AnalysisPrograms/Recognizers/AustBitternTests.cs b/tests/Acoustics.Test/AnalysisPrograms/Recognizers/AustBitternTests.cs index 841d0f4c7..51d1da256 100644 --- a/tests/Acoustics.Test/AnalysisPrograms/Recognizers/AustBitternTests.cs +++ b/tests/Acoustics.Test/AnalysisPrograms/Recognizers/AustBitternTests.cs @@ -67,11 +67,11 @@ public void TestRecognizer() var onlyEvent = (CompositeEvent)events[0]; Assert.AreEqual(5.12, onlyEvent.EventStartSeconds); - Assert.AreEqual(12.26, onlyEvent.EventEndSeconds); + Assert.AreEqual(12.256, onlyEvent.EventEndSeconds); Assert.AreEqual(105, onlyEvent.LowFrequencyHertz); Assert.AreEqual(180, onlyEvent.HighFrequencyHertz); - Assert.AreEqual(21.7, onlyEvent.Score); - Assert.AreEqual(0.95, onlyEvent.ScoreNormalized); + Assert.AreEqual(21.72, onlyEvent.Score, 0.01); + Assert.AreEqual(0.947, onlyEvent.ScoreNormalized, 0.01); } } } \ No newline at end of file