Пример #1
0
        /// <summary>
        /// This method is called once per segment (typically one-minute segments).
        /// </summary>
        /// <param name="audioRecording">one minute of audio recording.</param>
        /// <param name="config">config file that contains parameters used by all profiles.</param>
        /// <param name="segmentStartOffset">when recording starts.</param>
        /// <param name="getSpectralIndexes">not sure what this is.</param>
        /// <param name="outputDirectory">where the recognizer results can be found.</param>
        /// <param name="imageWidth"> assuming ????.</param>
        /// <returns>recognizer results.</returns>
        public override RecognizerResults Recognize(
            AudioRecording audioRecording,
            Config config,
            TimeSpan segmentStartOffset,
            Lazy <IndexCalculateResult[]> getSpectralIndexes,
            DirectoryInfo outputDirectory,
            int?imageWidth)
        {
            //class NinoxBoobookConfig is defined at bottom of this file.
            var genericConfig = (NinoxBoobookConfig)config;
            var recognizer    = new GenericRecognizer();

            RecognizerResults combinedResults = recognizer.Recognize(
                audioRecording,
                genericConfig,
                segmentStartOffset,
                getSpectralIndexes,
                outputDirectory,
                imageWidth);

            // DO POST-PROCESSING of EVENTS

            // Filter out the chirp events for possible combining.
            var(chirpEvents, others) = combinedResults.NewEvents.FilterForEventType <ChirpEvent, EventCommon>();

            // Uncomment the next line when want to obtain the event frequency profiles.
            // WriteFrequencyProfiles(chirpEvents);

            // Calculate frequency profile score for each event
            foreach (var ev in chirpEvents)
            {
                SetFrequencyProfileScore((ChirpEvent)ev);
            }

            // Combine overlapping events. If the dB threshold is set low, may get lots of little events.
            var newEvents = CompositeEvent.CombineOverlappingEvents(chirpEvents.Cast <EventCommon>().ToList());

            if (genericConfig.CombinePossibleSyllableSequence)
            {
                // convert events to spectral events for possible combining.
                var(spectralEvents, _) = combinedResults.NewEvents.FilterForEventType <SpectralEvent, EventCommon>();

                var startDiff = genericConfig.SyllableStartDifference;
                var hertzDiff = genericConfig.SyllableHertzGap;
                newEvents = CompositeEvent.CombineSimilarProximalEvents(spectralEvents, TimeSpan.FromSeconds(startDiff), (int)hertzDiff);
            }

            combinedResults.NewEvents = 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 <Towsey.PteropusSpecies.yml> config file.
            //GenericRecognizer.SaveDebugSpectrogram(territorialResults, genericConfig, outputDirectory, audioRecording.BaseName);
            return(combinedResults);
        }
Пример #2
0
        /// <summary>
        /// EXPANATION: A vertical track is a near click or rapidly frequency-modulated tone. A good example is the whip component of the whip-bird call.
        /// They would typically be only a few time-frames duration.
        /// THis method averages dB log values incorrectly but it is faster than doing many log conversions and is accurate enough for the purpose.
        /// </summary>
        /// <param name="sonogram">The spectrogram to be searched.</param>
        /// <param name="parameters">parameters for the upwards track algorithm.</param>
        /// <param name="segmentStartOffset">The start time of the current recording segment under analysis.</param>
        /// <returns>A list of acoustic events containing foward tracks.</returns>
        public static (List <EventCommon> Events, double[] CombinedIntensity) GetUpwardTracks(
            SpectrogramStandard sonogram,
            AnalysisPrograms.Recognizers.Base.UpwardTrackParameters 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    minBandwidthHertz = parameters.MinBandwidthHertz.Value;
            var    maxBandwidthHertz = parameters.MaxBandwidthHertz.Value;
            var    decibelThreshold  = parameters.DecibelThreshold.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 (int row = 1; row < frameCount - 1; row++)
            {
                for (int col = minBin; col < maxBin; col++)
                {
                    if (sonogramData[row, col] < decibelThreshold)
                    {
                        continue;
                    }

                    // if given matrix element is greater than in frame either side
                    bool isPeak = (sonogramData[row, col] > sonogramData[row - 1, col]) && (sonogramData[row, col] > sonogramData[row + 1, col]);
                    if (isPeak)
                    {
                        peaks[row, col] = sonogramData[row, col];
                    }
                }
            }

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

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

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

                events.Add(ae);

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

            List <EventCommon> returnEvents = events.Cast <EventCommon>().ToList();

            // combine proximal events that occupy similar frequency band
            if (parameters.CombineProximalSimilarEvents)
            {
                returnEvents = CompositeEvent.CombineSimilarProximalEvents(events, parameters.SyllableStartDifference, parameters.SyllableHertzDifference);
            }

            return(returnEvents, temporalIntensityArray);
        }
Пример #3
0
        /// <summary>
        /// This method returns foward (spectral peak) tracks enclosed in spectral events.
        /// It averages dB log values incorrectly but it is faster than doing many log conversions.
        /// </summary>
        /// <param name="sonogram">The spectrogram to be searched.</param>
        /// <returns>A list of acoustic events containing foward tracks.</returns>
        public static (List <EventCommon> Events, double[] CombinedIntensity) GetForwardTracks(
            SpectrogramStandard sonogram,
            ForwardTrackParameters 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 minDuration      = parameters.MinDuration.Value;
            double maxDuration      = parameters.MaxDuration.Value;
            double decibelThreshold = parameters.DecibelThreshold.Value;

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

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

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

                    // if given matrix element is greater than in freq bin either side
                    bool isPeak = (sonogramData[row, col] > sonogramData[row, col - 1]) && (sonogramData[row, col] > sonogramData[row, col + 1]);
                    if (isPeak)
                    {
                        peaks[row, col] = sonogramData[row, col];
                    }
                }
            }

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

            // initialise tracks as events and get the combined intensity array.
            // list of accumulated acoustic events
            var events = new List <SpectralEvent>();
            var combinedIntensityArray = new double[frameCount];

            // The following lines are used only for debug purposes.
            //var options = new EventRenderingOptions(new UnitConverters(segmentStartOffset.TotalSeconds, sonogram.Duration.TotalSeconds, nyquist, frameCount, binCount));
            //var spectrogram = sonogram.GetImage(doHighlightSubband: false, add1KHzLines: true, doMelScale: false);

            // Initialise events with tracks.
            foreach (var track in tracks)
            {
                //Following line used only for debug purposes. Can save as image.
                //spectrogram.Mutate(x => track.Draw(x, options));
                var maxScore   = decibelThreshold * 5;
                var scoreRange = new Interval <double>(0, maxScore);
                var ae         = new ChirpEvent(track, scoreRange)
                {
                    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++)
                {
                    combinedIntensityArray[startRow + i] = Math.Max(combinedIntensityArray[startRow + i], amplitudeTrack[i]);
                }
            }

            List <EventCommon> returnEvents = events.Cast <EventCommon>().ToList();

            // Combine coincident events that are stacked one above other.
            // This will help in some cases to combine related events.
            if (parameters.CombinePossibleHarmonics)
            {
                returnEvents = CompositeEvent.CombinePotentialStackedTracks(events, parameters.HarmonicsStartDifference, parameters.HarmonicsHertzGap);
            }

            // Combine events that are temporally close and in the same frequency band.
            // This will help in some cases to combine related events.
            if (parameters.CombinePossibleSyllableSequence)
            {
                var timeDiff = TimeSpan.FromSeconds(parameters.SyllableStartDifference);
                returnEvents = CompositeEvent.CombineSimilarProximalEvents(events, timeDiff, parameters.SyllableHertzGap);
            }

            return(returnEvents, combinedIntensityArray);
        }
Пример #4
0
        /// <summary>
        /// This method is called once per segment (typically one-minute segments).
        /// </summary>
        /// <param name="audioRecording">one minute of audio recording.</param>
        /// <param name="config">config file that contains parameters used by all profiles.</param>
        /// <param name="segmentStartOffset">when recording starts.</param>
        /// <param name="getSpectralIndexes">not sure what this is.</param>
        /// <param name="outputDirectory">where the recognizer results can be found.</param>
        /// <param name="imageWidth"> assuming ????.</param>
        /// <returns>recognizer results.</returns>
        public override RecognizerResults Recognize(
            AudioRecording audioRecording,
            Config config,
            TimeSpan segmentStartOffset,
            Lazy <IndexCalculateResult[]> getSpectralIndexes,
            DirectoryInfo outputDirectory,
            int?imageWidth)
        {
            //class BotaurusPoiciloptilusConfig is define at bottom of this file.
            var genericConfig = (BotaurusPoiciloptilusConfig)config;
            var recognizer    = new GenericRecognizer();

            RecognizerResults combinedResults = recognizer.Recognize(
                audioRecording,
                genericConfig,
                segmentStartOffset,
                getSpectralIndexes,
                outputDirectory,
                imageWidth);

            // DO POST-PROCESSING of EVENTS
            var events = combinedResults.NewEvents;

            // Following two commented lines are different ways of casting lists.
            //var newEvents = spectralEvents.Cast<EventCommon>().ToList();
            //var spectralEvents = events.Select(x => (SpectralEvent)x).ToList();
            List <EventCommon> newEvents;

            // NOTE: 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.
                var spectralEvents = events.Cast <SpectralEvent>().ToList();
                var startDiff      = genericConfig.SyllableStartDifference;
                var hertzDiff      = genericConfig.SyllableHertzGap;
                newEvents = CompositeEvent.CombineSimilarProximalEvents(spectralEvents, TimeSpan.FromSeconds(startDiff), (int)hertzDiff);
            }
            else
            {
                newEvents = events;
            }

            //filter the events for duration in seconds
            var minimumEventDuration = 0.5;

            if (genericConfig.CombinePossibleSyllableSequence)
            {
                minimumEventDuration = 2.0;
            }

            var filteredEvents = new List <EventCommon>();

            foreach (var ev in newEvents)
            {
                var eventDuration = ((SpectralEvent)ev).EventDurationSeconds;
                if (eventDuration > minimumEventDuration && eventDuration < 11.0)
                {
                    filteredEvents.Add(ev);
                }
            }

            combinedResults.NewEvents = filteredEvents;

            //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);
            return(combinedResults);
        }