コード例 #1
0
        /// <summary>
        /// This method averages dB log values incorrectly but it is faster than doing many log conversions.
        /// This method is used to find acoustic events and is accurate enough for the purpose.
        /// </summary>
        public static (List <EventCommon> ListOfevents, double[] CombinedIntensityArray) GetOnebinTracks(
            SpectrogramStandard sonogram,
            OnebinTrackParameters parameters,
            TimeSpan segmentStartOffset)
        {
            var    sonogramData     = sonogram.Data;
            int    frameCount       = sonogramData.GetLength(0);
            int    binCount         = sonogramData.GetLength(1);
            int    nyquist          = sonogram.NyquistFrequency;
            double binWidth         = nyquist / (double)binCount;
            int    minBin           = (int)Math.Round(parameters.MinHertz.Value / binWidth);
            int    maxBin           = (int)Math.Round(parameters.MaxHertz.Value / binWidth);
            double decibelThreshold = parameters.DecibelThreshold.Value;
            double minDuration      = parameters.MinDuration.Value;
            double maxDuration      = parameters.MaxDuration.Value;

            var converter = new UnitConverters(
                segmentStartOffset: segmentStartOffset.TotalSeconds,
                sampleRate: sonogram.SampleRate,
                frameSize: sonogram.Configuration.WindowSize,
                frameOverlap: sonogram.Configuration.WindowOverlap);

            //Find all bin peaks and place in peaks matrix
            var peaks = new double[frameCount, binCount];

            for (int tf = 0; tf < frameCount; tf++)
            {
                for (int bin = minBin + 1; bin < maxBin - 1; bin++)
                {
                    if (sonogramData[tf, bin] < decibelThreshold)
                    {
                        continue;
                    }

                    // here we define the amplitude profile of a whistle. The buffer zone around whistle is five bins wide.
                    var bandIntensity        = ((sonogramData[tf, bin - 1] * 0.5) + sonogramData[tf, bin] + (sonogramData[tf, bin + 1] * 0.5)) / 2.0;
                    var topSidebandIntensity = (sonogramData[tf, bin + 3] + sonogramData[tf, bin + 4] + sonogramData[tf, bin + 5]) / 3.0;
                    var netAmplitude         = 0.0;
                    if (bin < 4)
                    {
                        netAmplitude = bandIntensity - topSidebandIntensity;
                    }
                    else
                    {
                        var bottomSideBandIntensity = (sonogramData[tf, bin - 3] + sonogramData[tf, bin - 4] + sonogramData[tf, bin - 5]) / 3.0;
                        netAmplitude = bandIntensity - ((topSidebandIntensity + bottomSideBandIntensity) / 2.0);
                    }

                    if (netAmplitude >= decibelThreshold)
                    {
                        peaks[tf, bin] = sonogramData[tf, bin];
                    }
                }
            }

            var tracks = GetOnebinTracks(peaks, minDuration, maxDuration, decibelThreshold, converter);

            // Initialise tracks as events and get the combined intensity array.
            var events = new List <WhistleEvent>();
            var combinedIntensityArray = new double[frameCount];
            var scoreRange             = new Interval <double>(0, decibelThreshold * 5);

            foreach (var track in tracks)
            {
                var ae = new WhistleEvent(track, scoreRange)
                {
                    SegmentStartSeconds    = segmentStartOffset.TotalSeconds,
                    SegmentDurationSeconds = frameCount * converter.SecondsPerFrameStep,
                    Name = "Whistle",
                };

                events.Add(ae);

                // fill the intensity array
                var startRow       = converter.FrameFromStartTime(track.StartTimeSeconds);
                var amplitudeTrack = track.GetAmplitudeOverTimeFrames();
                for (int i = 0; i < amplitudeTrack.Length; i++)
                {
                    combinedIntensityArray[startRow + i] = Math.Max(combinedIntensityArray[startRow + i], amplitudeTrack[i]);
                }
            }

            // This algorithm tends to produce temporally overlapped whistle events in adjacent channels.
            // Combine overlapping whistle events
            var hertzDifference = 4 * binWidth;
            var whistleEvents   = WhistleEvent.CombineAdjacentWhistleEvents(events, hertzDifference);

            // Finally filter the whistles for presense of excess noise in buffer band just above the whistle.
            // Excess noise would suggest this is not a whistle event.
            var bufferHertz    = 300;
            var bufferBins     = (int)Math.Round(bufferHertz / binWidth);
            var filteredEvents = new List <EventCommon>();

            foreach (var ev in whistleEvents)
            {
                var avNhAmplitude = GetAverageAmplitudeInNeighbourhood((SpectralEvent)ev, sonogramData, bufferBins, converter);
                Console.WriteLine($"###################################Buffer Average decibels = {avNhAmplitude}");

                if (avNhAmplitude < decibelThreshold)
                {
                    // There is little acoustic activity in the buffer zone above the whistle. It is likely to be a whistle.
                    filteredEvents.Add(ev);
                }
            }

            return(filteredEvents, combinedIntensityArray);
        }
コード例 #2
0
        public void TestOnebinTrackAlgorithm()
        {
            // Set up the recognizer parameters.
            var parameters = new OnebinTrackParameters()
            {
                MinHertz         = 500,
                MaxHertz         = 6000,
                MinDuration      = 0.2,
                MaxDuration      = 1.1,
                DecibelThreshold = 2.0,
                CombinePossibleSyllableSequence = false,
                //SyllableStartDifference = TimeSpan.FromSeconds(0.2),
                //SyllableHertzGap = 300,
            };

            //Set up the virtual recording.
            int    samplerate         = 22050;
            double signalDuration     = 13.0; //seconds
            var    segmentStartOffset = TimeSpan.FromSeconds(60.0);

            // set up the config for a virtual spectrogram.
            var sonoConfig = new SonogramConfig()
            {
                WindowSize              = 512,
                WindowStep              = 512,
                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 plots = new List <Plot>();

            var(spectralEvents, dBArray) = OnebinTrackAlgorithm.GetOnebinTracks(
                spectrogram,
                parameters,
                segmentStartOffset);

            // draw a plot of max decibels in each frame
            double decibelNormalizationMax = 5 * parameters.DecibelThreshold.Value;
            var    dBThreshold             = parameters.DecibelThreshold.Value / decibelNormalizationMax;
            var    normalisedDecibelArray  = DataTools.NormaliseInZeroOne(dBArray, 0, decibelNormalizationMax);
            var    plot1 = new Plot("decibel max", normalisedDecibelArray, dBThreshold);

            plots.Add(plot1);

            var allResults = new RecognizerResults()
            {
                NewEvents  = new List <EventCommon>(),
                Hits       = null,
                ScoreTrack = null,
                Plots      = new List <Plot>(),
                Sonogram   = null,
            };

            // combine the results i.e. add the events list of call events.
            allResults.NewEvents.AddRange(spectralEvents);
            allResults.Plots.AddRange(plots);
            allResults.Sonogram = spectrogram;

            // DEBUG PURPOSES
            this.SaveTestOutput(
                outputDirectory => GenericRecognizer.SaveDebugSpectrogram(allResults, null, outputDirectory, "WhistleTrack"));

            //NOTE: There are 16 whistles in the test spectrogram ...
            // but three of them are too weak to be detected at this threshold.
            Assert.AreEqual(13, allResults.NewEvents.Count);

            var @event = (SpectralEvent)allResults.NewEvents[0];

            Assert.AreEqual(60 + 0.0, @event.EventStartSeconds, 0.1);
            Assert.AreEqual(60 + 0.35, @event.EventEndSeconds, 0.1);
            Assert.AreEqual(2150, @event.LowFrequencyHertz);
            Assert.AreEqual(2193, @event.HighFrequencyHertz);

            @event = (SpectralEvent)allResults.NewEvents[4];
            Assert.AreEqual(60 + 5.0, @event.EventStartSeconds, 0.1);
            Assert.AreEqual(60 + 6.0, @event.EventEndSeconds, 0.1);
            Assert.AreEqual(989, @event.LowFrequencyHertz);
            Assert.AreEqual(1032, @event.HighFrequencyHertz);

            @event = (SpectralEvent)allResults.NewEvents[11];
            Assert.AreEqual(60 + 11.0, @event.EventStartSeconds, 0.1);
            Assert.AreEqual(60 + 12.0, @event.EventEndSeconds, 0.1);
            Assert.AreEqual(989, @event.LowFrequencyHertz);
            Assert.AreEqual(1032, @event.HighFrequencyHertz);
        }