/// <inheritdoc/> public override RecognizerResults Recognize( AudioRecording audioRecording, Config genericConfig, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth) { var configuration = (GenericRecognizerConfig)genericConfig; if (configuration.Profiles.NotNull() && configuration.Profiles.Count == 0) { throw new ConfigFileException( "The generic recognizer needs at least one profile set. 0 were found."); } int count = configuration.Profiles.Count; var message = $"Found {count} analysis profile(s): " + configuration.Profiles.Keys.Join(", "); Log.Info(message); var allResults = new RecognizerResults() { Events = new List <AcousticEvent>(), Hits = null, ScoreTrack = null, Plots = new List <Plot>(), Sonogram = null, }; // Now process each of the profiles foreach (var(profileName, profileConfig) in configuration.Profiles) { Log.Info("Processing profile: " + profileName); List <AcousticEvent> acousticEvents; var plots = new List <Plot>(); SpectrogramStandard sonogram; Log.Debug($"Using the {profileName} algorithm... "); if (profileConfig is CommonParameters parameters) { if (profileConfig is BlobParameters || profileConfig is OscillationParameters || profileConfig is WhistleParameters || profileConfig is HarmonicParameters || profileConfig is SpectralPeakTrackParameters || profileConfig is VerticalTrackParameters || profileConfig is ClickParameters) { sonogram = new SpectrogramStandard(ParametersToSonogramConfig(parameters), audioRecording.WavReader); if (profileConfig is OscillationParameters op) { Oscillations2012.Execute( sonogram, op.MinHertz.Value, op.MaxHertz.Value, op.DctDuration, op.MinOscillationFrequency, op.MaxOscillationFrequency, op.DctThreshold, op.EventThreshold, op.MinDuration.Value, op.MaxDuration.Value, out var scores, out acousticEvents, out var hits, segmentStartOffset); //plots.Add(new Plot($"{profileName} (:OscillationScore)", scores, op.EventThreshold)); var plot = PreparePlot(scores, $"{profileName} (:OscillationScore)", op.EventThreshold); plots.Add(plot); } else if (profileConfig is BlobParameters bp) { //get the array of intensity values minus intensity in side/buffer bands. //i.e. require silence in side-bands. Otherwise might simply be getting part of a broader band acoustic event. var decibelArray = SNR.CalculateFreqBandAvIntensityMinusBufferIntensity( sonogram.Data, bp.MinHertz.Value, bp.MaxHertz.Value, bp.BottomHertzBuffer.Value, bp.TopHertzBuffer.Value, sonogram.NyquistFrequency); // prepare plot of resultant blob decibel array. var plot = PreparePlot(decibelArray, $"{profileName} (Blob:db Intensity)", bp.DecibelThreshold.Value); plots.Add(plot); // iii: CONVERT blob decibel SCORES TO ACOUSTIC EVENTS. // Note: This method does NOT do prior smoothing of the dB array. acousticEvents = AcousticEvent.GetEventsAroundMaxima( decibelArray, segmentStartOffset, bp.MinHertz.Value, bp.MaxHertz.Value, bp.DecibelThreshold.Value, TimeSpan.FromSeconds(bp.MinDuration.Value), TimeSpan.FromSeconds(bp.MaxDuration.Value), sonogram.FramesPerSecond, sonogram.FBinWidth); } else if (profileConfig is WhistleParameters wp) { //get the array of intensity values minus intensity in side/buffer bands. double[] decibelArray; (acousticEvents, decibelArray) = WhistleParameters.GetWhistles( sonogram, wp.MinHertz.Value, wp.MaxHertz.Value, sonogram.NyquistFrequency, wp.DecibelThreshold.Value, wp.MinDuration.Value, wp.MaxDuration.Value, segmentStartOffset); var plot = PreparePlot(decibelArray, $"{profileName} (Whistle:dB Intensity)", wp.DecibelThreshold.Value); plots.Add(plot); } else if (profileConfig is HarmonicParameters hp) { double[] decibelMaxArray; double[] harmonicIntensityScores; (acousticEvents, decibelMaxArray, harmonicIntensityScores) = HarmonicParameters.GetComponentsWithHarmonics( sonogram, hp.MinHertz.Value, hp.MaxHertz.Value, sonogram.NyquistFrequency, hp.DecibelThreshold.Value, hp.DctThreshold.Value, hp.MinDuration.Value, hp.MaxDuration.Value, hp.MinFormantGap.Value, hp.MaxFormantGap.Value, segmentStartOffset); var plot = PreparePlot(harmonicIntensityScores, $"{profileName} (Harmonics:dct intensity)", hp.DctThreshold.Value); plots.Add(plot); } else if (profileConfig is SpectralPeakTrackParameters tp) { double[] decibelArray; (acousticEvents, decibelArray) = SpectralPeakTrackParameters.GetSpectralPeakTracks( sonogram, tp.MinHertz.Value, tp.MaxHertz.Value, sonogram.NyquistFrequency, tp.DecibelThreshold.Value, tp.MinDuration.Value, tp.MaxDuration.Value, tp.CombinePossibleHarmonics, segmentStartOffset); var plot = PreparePlot(decibelArray, $"{profileName} (SpectralPeaks:dB Intensity)", tp.DecibelThreshold.Value); plots.Add(plot); } else if (profileConfig is ClickParameters cp) { double[] decibelArray; (acousticEvents, decibelArray) = ClickParameters.GetClicks( sonogram, cp.MinHertz.Value, cp.MaxHertz.Value, sonogram.NyquistFrequency, cp.DecibelThreshold.Value, cp.MinBandwidthHertz.Value, cp.MaxBandwidthHertz.Value, cp.CombineProximalSimilarEvents, segmentStartOffset); var plot = PreparePlot(decibelArray, $"{profileName} (Click:dB Intensity)", cp.DecibelThreshold.Value); plots.Add(plot); } else if (profileConfig is VerticalTrackParameters vtp) { double[] decibelArray; (acousticEvents, decibelArray) = VerticalTrackParameters.GetVerticalTracks( sonogram, vtp.MinHertz.Value, vtp.MaxHertz.Value, sonogram.NyquistFrequency, vtp.DecibelThreshold.Value, vtp.MinBandwidthHertz.Value, vtp.MaxBandwidthHertz.Value, vtp.CombineProximalSimilarEvents, segmentStartOffset); var plot = PreparePlot(decibelArray, $"{profileName} (VerticalTrack:dB Intensity)", vtp.DecibelThreshold.Value); plots.Add(plot); } else { throw new InvalidOperationException(); } } else { throw new InvalidOperationException(); } //iV add additional info to the acoustic events acousticEvents.ForEach(ae => { ae.FileName = audioRecording.BaseName; ae.SpeciesName = parameters.SpeciesName; ae.Name = parameters.ComponentName; ae.Profile = profileName; ae.SegmentDurationSeconds = audioRecording.Duration.TotalSeconds; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; ae.SetTimeAndFreqScales(sonogram.FrameStep, sonogram.FrameDuration, sonogram.FBinWidth); }); } else if (profileConfig is Aed.AedConfiguration ac) { var config = new SonogramConfig { NoiseReductionType = ac.NoiseReductionType, NoiseReductionParameter = ac.NoiseReductionParameter, }; sonogram = new SpectrogramStandard(config, audioRecording.WavReader); acousticEvents = Aed.CallAed(sonogram, ac, segmentStartOffset, audioRecording.Duration).ToList(); } else { throw new InvalidOperationException(); } // combine the results i.e. add the events list of call events. allResults.Events.AddRange(acousticEvents); allResults.Plots.AddRange(plots); // 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"); } // combine adjacent acoustic events if (this.combineOverlappedEvents) { allResults.Events = AcousticEvent.CombineOverlappingEvents(allResults.Events, segmentStartOffset); } return(allResults); }
public void TestClickAlgorithm() { // Set up the recognizer parameters. var windowSize = 512; var windowStep = 512; var minHertz = 6000; var maxHertz = 11000; var minBandwidthHertz = 100; var maxBandwidthHertz = 5000; var decibelThreshold = 2.0; var combineProximalSimilarEvents = true; //Set up the virtual recording. int samplerate = 22050; double signalDuration = 13.0; //seconds // set up the config for a virtual spectrogram. var sonoConfig = new SonogramConfig() { WindowSize = windowSize, WindowStep = windowStep, WindowOverlap = 0.0, // this must be set WindowFunction = WindowFunctions.HANNING.ToString(), NoiseReductionType = NoiseReductionType.Standard, NoiseReductionParameter = 0.0, Duration = TimeSpan.FromSeconds(signalDuration), SampleRate = samplerate, }; var spectrogram = this.CreateArtificialSpectrogramToTestTracksAndHarmonics(sonoConfig); //var image1 = SpectrogramTools.GetSonogramPlusCharts(spectrogram, null, null, null); //results.Sonogram.GetImage().Save(this.outputDirectory + "\\debug.png"); var segmentStartOffset = TimeSpan.Zero; var plots = new List <Plot>(); double[] dBArray; List <AcousticEvent> acousticEvents; (acousticEvents, dBArray) = ClickParameters.GetClicks( spectrogram, minHertz, maxHertz, spectrogram.NyquistFrequency, decibelThreshold, minBandwidthHertz, maxBandwidthHertz, combineProximalSimilarEvents, segmentStartOffset); // draw a plot of max decibels in each frame double decibelNormalizationMax = 3 * decibelThreshold; var dBThreshold = decibelThreshold / decibelNormalizationMax; var normalisedDecibelArray = DataTools.NormaliseInZeroOne(dBArray, 0, decibelNormalizationMax); var plot1 = new Plot("decibel max", normalisedDecibelArray, dBThreshold); plots.Add(plot1); var allResults = new RecognizerResults() { Events = new List <AcousticEvent>(), Hits = null, ScoreTrack = null, Plots = new List <Plot>(), Sonogram = null, }; // combine the results i.e. add the events list of call events. allResults.Events.AddRange(acousticEvents); allResults.Plots.AddRange(plots); // effectively keeps only the *last* sonogram produced allResults.Sonogram = spectrogram; // DEBUG PURPOSES COMMENT NEXT LINE var outputDirectory = new DirectoryInfo("C:\\temp"); GenericRecognizer.SaveDebugSpectrogram(allResults, null, outputDirectory, "Click"); Assert.AreEqual(2, allResults.Events.Count); var @event = allResults.Events[0]; Assert.AreEqual(10.0, @event.EventStartSeconds, 0.1); Assert.AreEqual(10.1, @event.EventEndSeconds, 0.1); Assert.AreEqual(6474, @event.LowFrequencyHertz); Assert.AreEqual(10781, @event.HighFrequencyHertz); @event = allResults.Events[1]; Assert.AreEqual(11.0, @event.EventStartSeconds, 0.1); Assert.AreEqual(11.24, @event.EventEndSeconds, 0.1); Assert.AreEqual(6474, @event.LowFrequencyHertz); Assert.AreEqual(7335, @event.HighFrequencyHertz); }