/// <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);
        }
Пример #2
0
        public void TestVerticalTrackAlgorithm()
        {
            // 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) = VerticalTrackParameters.GetVerticalTracks(
                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 ONLY - COMMENT NEXT LINE
            var outputDirectory = new DirectoryInfo("C:\\temp");

            GenericRecognizer.SaveDebugSpectrogram(allResults, null, outputDirectory, "VerticalTracks1");

            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(6460, @event.LowFrequencyHertz);
            Assert.AreEqual(10724, @event.HighFrequencyHertz);

            @event = allResults.Events[1];
            Assert.AreEqual(11.0, @event.EventStartSeconds, 0.1);
            Assert.AreEqual(11.24, @event.EventEndSeconds, 0.1);
            Assert.AreEqual(6460, @event.LowFrequencyHertz);
            Assert.AreEqual(7278, @event.HighFrequencyHertz);

            // do a SECOND TEST of the vertical tracks
            minHertz                  = 500;
            maxHertz                  = 6000;
            minBandwidthHertz         = 200;
            maxBandwidthHertz         = 5000;
            (acousticEvents, dBArray) = VerticalTrackParameters.GetVerticalTracks(
                spectrogram,
                minHertz,
                maxHertz,
                spectrogram.NyquistFrequency,
                decibelThreshold,
                minBandwidthHertz,
                maxBandwidthHertz,
                combineProximalSimilarEvents,
                segmentStartOffset);

            // draw a plot of max decibels in each frame
            normalisedDecibelArray = DataTools.NormaliseInZeroOne(dBArray, 0, decibelNormalizationMax);
            var plot2 = new Plot("decibel max", normalisedDecibelArray, dBThreshold);

            plots.Add(plot2);

            var allResults2 = 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.
            allResults2.Events.AddRange(acousticEvents);
            allResults2.Plots.AddRange(plots);

            // effectively keeps only the *last* sonogram produced
            allResults2.Sonogram = spectrogram;

            // DEBUG PURPOSES ONLY - COMMENT NEXT LINE
            GenericRecognizer.SaveDebugSpectrogram(allResults2, null, outputDirectory, "VerticalTracks2");

            Assert.AreEqual(5, allResults2.Events.Count);
        }