Ejemplo n.º 1
0
        /// <summary>
        /// A one-frame track sounds like a click.
        /// A click is a sharp onset broadband sound of brief duration. Geometrically it is similar to a vertical whistle.
        /// 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> Events, double[] Intensity) GetOneFrameTracks(
            SpectrogramStandard sonogram,
            OneframeTrackParameters parameters,
            TimeSpan segmentStartOffset)
        {
            var    sonogramData      = sonogram.Data;
            int    frameCount        = sonogramData.GetLength(0);
            int    binCount          = sonogramData.GetLength(1);
            var    frameStep         = sonogram.FrameStep;
            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);
            var    decibelThreshold  = parameters.DecibelThreshold.Value;
            var    minBandwidthHertz = parameters.MinBandwidthHertz.Value;
            var    maxBandwidthHertz = parameters.MaxBandwidthHertz.Value;

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

            // Find all frame peaks and place in peaks matrix
            // avoid row edge effects.
            var peaks = new double[frameCount, binCount];

            // for all time frames except 1st and last allowing for edge effects.
            for (int t = 1; t < frameCount - 1; t++)
            {
                // buffer zone around click is one frame wide.
                // for all frequency bins except top and bottom in this time frame
                for (int bin = minBin; bin < maxBin; bin++)
                {
                    if (sonogramData[t, bin] < decibelThreshold)
                    {
                        continue;
                    }

                    // THis is where the profile of a click is defined
                    // A click requires sudden onset, with maximum amplitude followed by decay.
                    bool isClickPeak = sonogramData[t - 1, bin] < decibelThreshold && sonogramData[t, bin] > sonogramData[t + 1, bin];
                    if (isClickPeak)
                    {
                        peaks[t, bin] = sonogramData[t, bin];
                    }
                }
            }

            //NOTE: the Peaks matrix is same size as the sonogram.
            var tracks = GetOneFrameTracks(peaks, minBin, maxBin, minBandwidthHertz, maxBandwidthHertz, decibelThreshold, converter);

            // initialise tracks as events and get the combined intensity array.
            var events = new List <EventCommon>();
            var temporalIntensityArray = new double[frameCount];
            var maxScore = decibelThreshold * 5;

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

                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++)
                {
                    temporalIntensityArray[startRow + i] += amplitudeTrack[i];
                }
            }

            // MAY NOT WANT TO Do THIS FOR ONE-FRAME tracks
            // combine proximal events that occupy similar frequency band
            //if (combineProximalSimilarEvents)
            //{
            //    TimeSpan startDifference = TimeSpan.FromSeconds(0.5);
            //    int hertzDifference = 500;
            //    //######################################################################## TODO TODO TODOD
            //    //events = AcousticEvent.CombineSimilarProximalEvents(events, startDifference, hertzDifference);
            //}

            return(events, temporalIntensityArray);
        }
Ejemplo n.º 2
0
        public void TestOneframeTrackAlgorithm()
        {
            // Set up the recognizer parameters.
            var parameters = new OneframeTrackParameters()
            {
                MinHertz          = 6000,
                MaxHertz          = 11000,
                MinBandwidthHertz = 100,
                MaxBandwidthHertz = 5000,
                DecibelThreshold  = 2.0,
            };

            //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              = 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 segmentStartOffset = TimeSpan.Zero;
            var plots = new List <Plot>();

            var(spectralEvents, dBArray) = OneframeTrackAlgorithm.GetOneFrameTracks(
                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);

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

            // DEBUG PURPOSES COMMENT NEXT LINE
            this.SaveTestOutput(
                outputDirectory => GenericRecognizer.SaveDebugSpectrogram(allResults, null, outputDirectory, "ClickTrack"));

            Assert.AreEqual(6, allResults.NewEvents.Count);

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

            Assert.AreEqual(10.0, @event.EventStartSeconds, 0.1);
            Assert.AreEqual(10.1, @event.EventEndSeconds, 0.1);
            Assert.AreEqual(6450, @event.LowFrequencyHertz);
            Assert.AreEqual(10750, @event.HighFrequencyHertz);

            @event = (SpectralEvent)allResults.NewEvents[2];
            Assert.AreEqual(11.05, @event.EventStartSeconds, 0.05);
            Assert.AreEqual(11.07, @event.EventEndSeconds, 0.05);
            Assert.AreEqual(6450, @event.LowFrequencyHertz);
            Assert.AreEqual(7310, @event.HighFrequencyHertz);
        }