diff --git a/src/AnalysisConfigFiles/RecognizerConfigFiles/Towsey.CisticolaExilis.yml b/src/AnalysisConfigFiles/RecognizerConfigFiles/Towsey.CisticolaExilis.yml index a2dcd592e..fe74bfd92 100644 --- a/src/AnalysisConfigFiles/RecognizerConfigFiles/Towsey.CisticolaExilis.yml +++ b/src/AnalysisConfigFiles/RecognizerConfigFiles/Towsey.CisticolaExilis.yml @@ -2,7 +2,7 @@ # Golden-headed cisticola = Cisticola exilis # Resample rate must be 2 X the desired Nyquist -ResampleRate: 8000 +ResampleRate: 16000 # SegmentDuration: units=seconds; SegmentDuration: 60 # SegmentOverlap: units=seconds; @@ -15,43 +15,47 @@ Profiles: ComponentName: Whip SpeciesName: CisticolaExilis FrameSize: 512 - FrameStep: 256 + FrameStep: 128 WindowFunction: HANNING #BgNoiseThreshold: 0.0 # min and max of the freq band to search - MinHertz: 100 - MaxHertz: 200 - MinDuration: 0.3 - MaxDuration: 1.0 + MinHertz: 2100 + MaxHertz: 3300 + MinBandwidthHertz: 200 + MaxBandwidthHertz: 900 + #MinDuration: 0.03 + #MaxDuration: 0.06 DecibelThreshold: 6.0 #################### POST-PROCESSING of EVENTS ################### # A: First post-processing steps are to combine overlapping/proximal/sequential events # 1: Combine overlapping events -#CombineOverlappingEvents: false +CombineOverlappingEvents: true # 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: 35 +SyllableStartDifference: 0.15 +SyllableHertzGap: 500 # B: Filter the events for excess activity in their upper and lower buffer zones -LowerHertzBuffer: 150 -UpperHertzBuffer: 400 +NeighbourhoodLowerHertzBuffer: 100 +NeighbourhoodUpperHertzBuffer: 200 +NeighbourhoodDbThreshold: 4.0 + # C: Options to save results files -# 4: Available options for saving data files (case-sensitive): [False/Never | True/Always | WhenEventsDetected] -SaveIntermediateWavFiles: Never -SaveIntermediateCsvFiles: false -# Available options (case-sensitive): [False/Never | True/Always | WhenEventsDetected] +# 4: Available options for saving spectrograms (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 + +# 5: Available options for saving data files (case-sensitive): [False/Never | True/Always | WhenEventsDetected] +SaveIntermediateWavFiles: Never +SaveIntermediateCsvFiles: false + +# 6: DisplayCsvImage is obsolete - ensure it remains set to: false DisplayCsvImage: false ## End section for AnalyzeLongRecording diff --git a/src/AnalysisPrograms/Recognizers/Birds/CisticolaExilis.cs b/src/AnalysisPrograms/Recognizers/Birds/CisticolaExilis.cs index f92e48767..858ef05f9 100644 --- a/src/AnalysisPrograms/Recognizers/Birds/CisticolaExilis.cs +++ b/src/AnalysisPrograms/Recognizers/Birds/CisticolaExilis.cs @@ -52,12 +52,12 @@ public override AnalyzerConfig ParseConfig(FileInfo file) // This call sets a restriction so that only one generic algorithm is used. // CHANGE this to accept multiple generic algorithms as required. //if (result.Profiles.SingleOrDefault() is ForwardTrackParameters) - if (config.Profiles?.Count == 1 && config.Profiles.First().Value is ForwardTrackParameters) + if (config.Profiles?.Count == 1 && config.Profiles.First().Value is UpwardTrackParameters) { return config; } - throw new ConfigFileException("CisticolaExilis expects one and only one ForwardTrack algorithm.", file); + throw new ConfigFileException("CisticolaExilis expects one and only one UpwardTrack algorithm.", file); } /// @@ -95,94 +95,32 @@ public override RecognizerResults Recognize( //var newEvents = spectralEvents.Cast().ToList(); //var spectralEvents = events.Select(x => (SpectralEvent)x).ToList(); - // 1: Pull out the chirp events and calculate their frequency profiles. - var (chirpEvents, others) = combinedResults.NewEvents.FilterForEventType(); - - if (combinedResults.NewEvents.Count == 0) - { - CisticolaLog.Debug($"Return zero events."); - return combinedResults; - } - - // 2: Combine overlapping events. If the dB threshold is set low, may get lots of little events. - combinedResults.NewEvents = CompositeEvent.CombineOverlappingEvents(chirpEvents.Cast().ToList()); - CisticolaLog.Debug($"Event count after combining overlaps = {combinedResults.NewEvents.Count}"); - - // 3: Combine proximal events. 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. - // 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 spectralEvents1 = combinedResults.NewEvents.Cast().ToList(); - var startDiff = genericConfig.SyllableStartDifference; - var hertzDiff = genericConfig.SyllableHertzGap; - combinedResults.NewEvents = CompositeEvent.CombineProximalEvents(spectralEvents1, TimeSpan.FromSeconds(startDiff), (int)hertzDiff); - CisticolaLog.Debug($"Event count after combining proximals = {combinedResults.NewEvents.Count}"); - } - - // Get the CisticolaSyllable config. - const string profileName = "CisticolaSyllable"; - var configuration = (CisticolaExilisConfig)genericConfig; - var chirpConfig = (ForwardTrackParameters)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 = chirpConfig.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); - - CisticolaLog.Debug($"Event count after filtering on neighbourhood = {combinedResults.NewEvents.Count}"); - } - if (combinedResults.NewEvents.Count == 0) { CisticolaLog.Debug($"Return zero events."); return combinedResults; } - // 5: Filter on COMPONENT COUNT in Composite events. - int maxComponentCount = 2; - combinedResults.NewEvents = EventExtentions.FilterEventsOnCompositeContent(combinedResults.NewEvents, maxComponentCount); - CisticolaLog.Debug($"Event count after filtering on component count = {combinedResults.NewEvents.Count}"); - - // 6: Filter the events for duration in seconds - var minimumEventDuration = chirpConfig.MinDuration; - var maximumEventDuration = chirpConfig.MaxDuration; - if (genericConfig.CombinePossibleSyllableSequence) - { - minimumEventDuration *= 2.0; - maximumEventDuration *= 1.5; - } - - combinedResults.NewEvents = EventExtentions.FilterOnDuration(combinedResults.NewEvents, minimumEventDuration.Value, maximumEventDuration.Value); + // 1: Filter the events for duration in seconds + var minimumEventDuration = 0.1; + var maximumEventDuration = 0.25; + combinedResults.NewEvents = EventExtentions.FilterOnDuration(combinedResults.NewEvents, minimumEventDuration, maximumEventDuration); CisticolaLog.Debug($"Event count after filtering on duration = {combinedResults.NewEvents.Count}"); - // 7: Filter the events for bandwidth in Hertz - double average = 280; - double sd = 40; + // 2: Filter the events for bandwidth in Hertz + double average = 600; + double sd = 150; double sigmaThreshold = 3.0; combinedResults.NewEvents = EventExtentions.FilterOnBandwidth(combinedResults.NewEvents, average, sd, sigmaThreshold); CisticolaLog.Debug($"Event count after filtering on bandwidth = {combinedResults.NewEvents.Count}"); + // 3: Filter on COMPONENT COUNT in Composite events. + //int maxComponentCount = 2; + //combinedResults.NewEvents = EventExtentions.FilterEventsOnCompositeContent(combinedResults.NewEvents, maxComponentCount); + //CisticolaLog.Debug($"Event count after filtering on component count = {combinedResults.NewEvents.Count}"); + + //combinedResults.NewEvents = FilterEventsOnFrequencyProfile(combinedResults.NewEvents); + //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. //GenericRecognizer.SaveDebugSpectrogram(territorialResults, genericConfig, outputDirectory, audioRecording.BaseName); @@ -209,11 +147,6 @@ public override void SummariseResults( /// /> public class CisticolaExilisConfig : GenericRecognizerConfig, INamedProfiles { - public bool CombinePossibleSyllableSequence { get; set; } = false; - - public double SyllableStartDifference { get; set; } = 0.5; - - public double SyllableHertzGap { get; set; } = 200; } } } diff --git a/tests/Acoustics.Test/AnalysisPrograms/Recognizers/CisticolaTests.cs b/tests/Acoustics.Test/AnalysisPrograms/Recognizers/CisticolaTests.cs index b956a8444..c952d594e 100644 --- a/tests/Acoustics.Test/AnalysisPrograms/Recognizers/CisticolaTests.cs +++ b/tests/Acoustics.Test/AnalysisPrograms/Recognizers/CisticolaTests.cs @@ -18,7 +18,8 @@ namespace Acoustics.Test.AnalysisPrograms.Recognizers using Microsoft.VisualStudio.TestTools.UnitTesting; /// - /// Species name = Golden-headed cisticola = Cisticola exilis. + /// The canonical recording used for this recognizer is a 30 second recording containing five Pipit calls and a number of Cisticola and other bird calls. + /// It was recorded in Narrabri region and forms part of the Cotton Project data set. /// [TestClass] public class CisticolaTests : OutputDirectoryTest @@ -26,18 +27,23 @@ public class CisticolaTests : OutputDirectoryTest /// /// The canonical recording used for this recognizer is a 31 second recording . /// - private static readonly FileInfo TestAsset = PathHelper.ResolveAsset("Recordings", "gympie_np_1192_331618_20150818_054959_31_0.wav"); + //private static readonly FileInfo TestAsset = PathHelper.ResolveAsset("Recordings", "ms1_2559_630118_20170402_075841_30_0.wav"); + private static readonly FileInfo TestAsset = new FileInfo(@"C:\Ecoacoustics\WavFiles\TestNoiseRecordings\Cisticola\ms1_2559_628362_20170408_074841_30_0.wav"); + private static readonly FileInfo ConfigFile = PathHelper.ResolveConfigFile("RecognizerConfigFiles", "Towsey.CisticolaExilis.yml"); - private static readonly AudioRecording Recording = new AudioRecording(TestAsset); private static readonly CisticolaExilis Recognizer = new CisticolaExilis(); [TestMethod] public void TestRecognizer() { var config = Recognizer.ParseConfig(ConfigFile); + int resampleRate = config.ResampleRate.Value; + string opDir = this.TestOutputDirectory.FullName; + string opFileName = "tempFile.wav"; + var recording = AudioRecording.GetAudioRecording(TestAsset, resampleRate, opDir, opFileName); var results = Recognizer.Recognize( - audioRecording: Recording, + audioRecording: recording, config: config, segmentStartOffset: TimeSpan.Zero, getSpectralIndexes: null,