Пример #1
0
        public static void DrawEventLabel(this SpectralEvent @event, IImageProcessingContext graphics, EventRenderingOptions options)
        {
            if (!options.DrawLabel)
            {
                return;
            }

            var text = @event.Name;

            if (string.IsNullOrWhiteSpace(text))
            {
                return;
            }

            var bounds  = TextMeasurer.MeasureBounds(text, new RendererOptions(Roboto6));
            var topLeft = options.Converters.GetPoint(@event);

            topLeft.Offset(0, -bounds.Height);

            graphics.DrawTextSafe(
                @event.Name,
                Roboto6,
                options.Label,
                topLeft);
        }
Пример #2
0
        public void DrawTest()
        {
            // arrange
            string specification = @"
⬇10
E10R80E10
78×E10RE78RE10
E10R80E10
⬇10
";

            this.ExpectedImage = TestImage.Create(100, 100, Color.Black, specification);

            var @event = new SpectralEvent()
            {
                EventEndSeconds    = 9,
                EventStartSeconds  = 1,
                HighFrequencyHertz = 900,
                LowFrequencyHertz  = 100,
            };
            var options = new EventRenderingOptions(new UnitConverters(0, 10, 1000, 100, 100));

            // act

            this.ActualImage.Mutate(x => @event.Draw(x, options));

            // assert
            this.AssertImagesEqual();
        }
Пример #3
0
        /// <summary>
        /// Determines if two events overlap in time.
        /// </summary>
        /// <param name="event1">event one.</param>
        /// <param name="event2">event two.</param>
        /// <returns>true if events overlap.</returns>
        public static bool EventsOverlapInTime(SpectralEvent event1, SpectralEvent event2)
        {
            //check if event 1 starts within event 2
            if (event1.EventStartSeconds >= event2.EventStartSeconds && event1.EventStartSeconds <= event2.EventEndSeconds)
            {
                return(true);
            }

            // check if event 1 ends within event 2
            if (event1.EventEndSeconds >= event2.EventStartSeconds && event1.EventEndSeconds <= event2.EventEndSeconds)
            {
                return(true);
            }

            // now check possibility that event2 is inside event1.
            //check if event 2 starts within event 1
            if (event2.EventStartSeconds >= event1.EventStartSeconds && event2.EventStartSeconds <= event1.EventEndSeconds)
            {
                return(true);
            }

            // check if event 2 ends within event 1
            if (event2.EventEndSeconds >= event1.EventStartSeconds && event2.EventEndSeconds <= event1.EventEndSeconds)
            {
                return(true);
            }

            return(false);
        }
Пример #4
0
        /// <summary>
        /// Draws a "score" indicator on the left edge of an event.
        /// </summary>
        /// <param name="event">The event for which to draw the score indicator.</param>
        /// <param name="graphics">The image context to draw to.</param>
        /// <param name="options">The event rendering options to use.</param>
        public static void DrawScoreIndicator(this SpectralEvent @event, IImageProcessingContext graphics, EventRenderingOptions options)
        {
            if (!options.DrawScore)
            {
                return;
            }

            var normalizedScore = @event.ScoreNormalized.Clamp(0, 1);

            if (normalizedScore == 0 || double.IsNaN(normalizedScore))
            {
                return;
            }

            var rect = options.Converters.GetPixelRectangle(@event);

            // truncate score bar to neatest whole pixel after scaling by height
            var scaledHeight = (int)((float)normalizedScore * rect.Height);

            var top    = new PointF(rect.Left, rect.Bottom - scaledHeight);
            var bottom = new PointF(rect.Left, rect.Bottom);

            // the order of the supplied points is important!
            // DO NOT CHANGE
            graphics.NoAA().DrawLines(options.Score, top, bottom);
        }
Пример #5
0
        //#################################################################################################################
        //FOLLOWING METHODS DEAL WITH THE OVERLAP OF EVENTS

        /// <summary>
        /// Determines if two events overlap in frequency.
        /// </summary>
        /// <param name="event1">event one.</param>
        /// <param name="event2">event two.</param>
        /// <returns>true if events overlap.</returns>
        public static bool EventsOverlapInFrequency(SpectralEvent event1, SpectralEvent event2)
        {
            //check if event 1 freq band overlaps event 2 freq band
            if (event1.HighFrequencyHertz >= event2.LowFrequencyHertz && event1.HighFrequencyHertz <= event2.HighFrequencyHertz)
            {
                return(true);
            }

            // check if event 1 freq band overlaps event 2 freq band
            if (event1.LowFrequencyHertz >= event2.LowFrequencyHertz && event1.LowFrequencyHertz <= event2.HighFrequencyHertz)
            {
                return(true);
            }

            //check if event 2 freq band overlaps event 1 freq band
            if (event2.HighFrequencyHertz >= event1.LowFrequencyHertz && event2.HighFrequencyHertz <= event1.HighFrequencyHertz)
            {
                return(true);
            }

            // check if event 2 freq band overlaps event 1 freq band
            if (event2.LowFrequencyHertz >= event1.LowFrequencyHertz && event2.LowFrequencyHertz <= event1.HighFrequencyHertz)
            {
                return(true);
            }

            return(false);
        }
Пример #6
0
        public void TestEventMerging()
        {
            // make a list of three events
            var events           = new List <SpectralEvent>();
            var segmentStartTime = TimeSpan.FromSeconds(10);
            var event1           = new SpectralEvent(segmentStartOffset: segmentStartTime, eventStartRecordingRelative: 11.0, eventEndRecordingRelative: 16.0, minFreq: 1000, maxFreq: 6000)
            {
                Name  = "Event1",
                Score = 1.0,
            };

            events.Add(event1);

            var event2 = new SpectralEvent(segmentStartOffset: segmentStartTime, eventStartRecordingRelative: 12.0, eventEndRecordingRelative: 15.0, minFreq: 1500, maxFreq: 8000)
            {
                Name  = "Event2",
                Score = 5.0,
            };

            events.Add(event2);

            var event3 = new SpectralEvent(segmentStartOffset: segmentStartTime, eventStartRecordingRelative: 17.0, eventEndRecordingRelative: 19.0, minFreq: 1000, maxFreq: 8000)
            {
                Name  = "Event3",
                Score = 9.0,
            };

            events.Add(event3);

            // combine Overlapping acoustic events
            var newEvents = CompositeEvent.CombineOverlappingEvents(events: events.Cast <EventCommon>().ToList());

            events = newEvents.Cast <SpectralEvent>().ToList();

            //require two events, the first being a composite of two events.
            Assert.AreEqual(2, events.Count);
            Assert.AreEqual(typeof(CompositeEvent), events[0].GetType());

            Assert.AreEqual(2, ((CompositeEvent)events[0]).ComponentEvents.Count);

            Assert.AreEqual(11.0, events[0].EventStartSeconds);
            Assert.AreEqual(16.0, events[0].EventEndSeconds);
            Assert.AreEqual(1000, events[0].LowFrequencyHertz);
            Assert.AreEqual(8000, events[0].HighFrequencyHertz);
            Assert.AreEqual(5.0, events[0].Score);

            Assert.AreEqual(typeof(SpectralEvent), events[1].GetType());
            Assert.AreEqual(17.0, events[1].EventStartSeconds);
            Assert.AreEqual(19.0, events[1].EventEndSeconds);
            Assert.AreEqual(1000, events[1].LowFrequencyHertz);
            Assert.AreEqual(8000, events[1].HighFrequencyHertz);
            Assert.AreEqual(9.0, events[1].Score);
        }
Пример #7
0
        /// <summary>
        /// Calculates the average amplitude in the frequency just above the whistle.
        /// If it contains above threshold acoustic content, this is unlikely to be a whistle.
        /// </summary>
        /// <param name="ev">The event.</param>
        /// <param name="sonogramData">The spectrogram data as matrix with origin top/left.</param>
        /// <param name="bufferBins">THe badnwidth of the buffer zone in bins.</param>
        /// <param name="converter">A converter to convert seconds/Hertz to frames/bins.</param>
        /// <returns>Average of the spectrogram amplitude in buffer band above whistler.</returns>
        public static double GetAverageAmplitudeInNeighbourhood(SpectralEvent ev, double[,] sonogramData, int bufferBins, UnitConverters converter)
        {
            var bottomBufferBin    = converter.GetFreqBinFromHertz(ev.HighFrequencyHertz) + 5;
            var topBufferBin       = bottomBufferBin + bufferBins;
            var frameStart         = converter.FrameFromStartTime(ev.EventStartSeconds);
            var frameEnd           = converter.FrameFromStartTime(ev.EventEndSeconds);
            var subMatrix          = MatrixTools.Submatrix <double>(sonogramData, frameStart, bottomBufferBin, frameEnd, topBufferBin);
            var averageRowDecibels = MatrixTools.GetRowAverages(subMatrix);
            var av = averageRowDecibels.Average();

            return(av);
        }
Пример #8
0
        public static SpectralEvent OverlapsEventInList(SpectralEvent anEvent, List <SpectralEvent> events)
        {
            foreach (var se in events)
            {
                if (EventsOverlapInTime(anEvent, se))
                {
                    return(se);
                }
            }

            return(null);
        }
Пример #9
0
        public void DerivedPropertiesTest()
        {
            var @event = new SpectralEvent()
            {
                EventEndSeconds    = 9,
                EventStartSeconds  = 1,
                HighFrequencyHertz = 900,
                LowFrequencyHertz  = 100,
            };

            Assert.AreEqual(8, @event.EventDurationSeconds);
            Assert.AreEqual(800, @event.BandWidthHertz);
        }
Пример #10
0
        public static AcousticEvent ConvertSpectralEventToAcousticEvent(this SpectralEvent se)
        {
            var    segmentStartOffset = TimeSpan.FromSeconds(se.SegmentStartSeconds);
            double startTime          = se.EventStartSeconds;
            double duration           = se.EventDurationSeconds;
            double minHz = se.HighFrequencyHertz;
            double maxHz = se.HighFrequencyHertz;
            var    ae    = new AcousticEvent(segmentStartOffset, startTime, duration, minHz, maxHz)
            {
                Name = se.Name,
                SegmentDurationSeconds = se.SegmentDurationSeconds,
            };

            return(ae);
        }
Пример #11
0
        /// <summary>
        /// Merges two spectral events into one event.
        /// </summary>
        /// <param name="e1">first event.</param>
        /// <param name="e2">second event.</param>
        /// <returns>a composite event.</returns>
        public static CompositeEvent CombineTwoEvents(SpectralEvent e1, SpectralEvent e2)
        {
            // Assume that we only merge events that are in the same recording segment.
            // Therefore the value of segmentStartOffset has already been set and is the same for both events.
            var e1Type = e1.GetType();
            var e2Type = e2.GetType();

            // There are three possibilities
            if (e1Type == typeof(CompositeEvent) && e2Type == typeof(CompositeEvent))
            {
                var e2Events = ((CompositeEvent)e2).ComponentEvents;
                ((CompositeEvent)e1).ComponentEvents.AddRange(e2Events);
                return((CompositeEvent)e1);
            }
            else
            if (e1Type == typeof(CompositeEvent))
            {
                ((CompositeEvent)e1).ComponentEvents.Add(e2);
                return((CompositeEvent)e1);
            }
            else
            if (e2Type == typeof(CompositeEvent))
            {
                ((CompositeEvent)e2).ComponentEvents.Add(e1);
                return((CompositeEvent)e2);
            }

            var twoEvents = new List <SpectralEvent>
            {
                e1,
                e2,
            };

            var compositeEvent = new CompositeEvent(twoEvents)
            {
                Name = e1.Name,
            };

            return(compositeEvent);
        }
Пример #12
0
        public void TestGetPixelRectangle()
        {
            // arrange
            var @event = new SpectralEvent()
            {
                EventEndSeconds    = 9,
                EventStartSeconds  = 1,
                HighFrequencyHertz = 900,
                LowFrequencyHertz  = 100,
            };

            var converters = new UnitConverters(0, 10, 1000, 100, 100);

            // act
            var rect = converters.GetPixelRectangle(@event);

            // assert
            Assert.AreEqual(10, rect.Left);
            Assert.AreEqual(10, rect.Top);
            Assert.AreEqual(80, rect.Width);
            Assert.AreEqual(80, rect.Height);
        }
Пример #13
0
        /// <summary>
        /// Do your analysis. This method is called once per segment (typically one-minute segments).
        /// </summary>
        public override RecognizerResults Recognize(AudioRecording audioRecording, Config configuration, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth)
        {
            // Get a value from the config file - with a backup default
            int minHz = configuration.GetIntOrNull(AnalysisKeys.MinHz) ?? 600;

            // Get a value from the config file - with no default, throw an exception if value is not present
            //int maxHz = ((int?)configuration[AnalysisKeys.MaxHz]).Value;

            // Get a value from the config file - without a string accessor, as a double
            double someExampleSettingA = configuration.GetDoubleOrNull("SomeExampleSettingA") ?? 0.0;

            // common properties
            string speciesName            = configuration[AnalysisKeys.SpeciesName] ?? "<no species>";
            string abbreviatedSpeciesName = configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? "<no.sp>";

            /*
             * Examples of using profiles
             */

            // Examples of the APIs available. You don't need all of these commands! Pick and choose.
            bool hasProfiles = ConfigFile.HasProfiles(configuration);

            //Config profile = ConfigFile.GetProfile<Config, Aed.AedConfiguration>(configuration, "Groote");
            //Config profile2;

            //bool success = ConfigFile.TryGetProfile(configuration, "FemaleRelease", out profile2);
            //string[] profileNames = ConfigFile.GetProfileNames<Config>(configuration);
            //            IEnumerable<(string Name, object Profile)> allProfiles = ConfigFile.GetAllProfiles<IProfile<object>>(configuration);
            //            foreach (var profile in allProfiles)
            //            {
            //                object currentProfile = profile.Profile;
            //                Log.Info(profile.Name + ": " + ((int)currentProfile.MinHz).ToString());
            //            }

            // Profile example: running the same algorithm on every profile with different settings (regional variation)

            /*
             * List<AcousticEvent> allAcousticEvents = new List<AcousticEvent>();
             * Dictionary<string, Config> allProfiles = ConfigFile.GetAllProfiles(configuration);
             * foreach (var kvp in allProfiles)
             * {
             *  string profileName = kvp.Key;
             *  Log.Info($"Analyzing profile: {profileName}");
             *  Config currentProfile = kvp.Value;
             *
             *  // extract parameters
             *  int minHz = (int)configuration[AnalysisKeys.MinHz];
             *
             *  // ...
             *
             *  // run the algorithm
             *  List<AcousticEvent> acousticEvents;
             *  Oscillations2012.Execute( All the correct parameters, minHz);
             *
             *  // augment the returned events
             *  acousticEvents.ForEach(ae =>
             *  {
             *      ae.SpeciesName = speciesName;
             *      ae.Profile = profileName;
             *      ae.AnalysisIdealSegmentDuration = recordingDuration;
             *      ae.Name = abbreviatedSpeciesName;
             *  });
             *
             *  // add events found in this profile to the total list
             *  allAcousticEvents.AddRange(acousticEvents);
             * }
             */

            // Profile example: running a different algorithm on different profiles

            /*
             * bool hasProfiles = ConfigFile.HasProfiles(configuration);
             * if (hasProfiles)
             * {
             *  // add resulting events from each algorithm into the combined event list
             *  allAcousticEvents.AddRange(RunFemaleProfile(...all the arguments));
             *  allAcousticEvents.AddRange(RunMaleProfile(...all the arguments));
             * }
             *
             * // example method
             * private static List<AcousticEvent> RunFemaleProfile(configuration, rest of arguments)
             * {
             *  const string femaleProfile = "Female";
             *  Config currentProfile = ConfigFile.GetProfile(configuration, femaleProfile);
             *  Log.Info($"Analyzing profile: {femaleProfile}");
             *
             *  // extract parameters
             *  int minHz = (int)configuration[AnalysisKeys.MinHz];
             *
             *  // ...
             *
             *  // run the algorithm
             *  List<AcousticEvent> acousticEvents;
             *  Oscillations2012.Execute(All the correct parameters, minHz);
             *
             *  // augment the returned events
             *  acousticEvents.ForEach(ae =>
             *  {
             *      ae.SpeciesName = speciesName;
             *      ae.Profile = femaleProfile;
             *      ae.AnalysisIdealSegmentDuration = recordingDuration;
             *      ae.Name = abbreviatedSpeciesName;
             *  });
             *
             *  return acousticEvents;
             * }
             */

            // get samples
            var samples = audioRecording.WavReader.Samples;

            // make a spectrogram
            var config = new SonogramConfig
            {
                NoiseReductionType      = NoiseReductionType.Standard,
                NoiseReductionParameter = configuration.GetDoubleOrNull(AnalysisKeys.NoiseBgThreshold) ?? 0.0,
            };
            var sonogram = (BaseSonogram) new SpectrogramStandard(config, audioRecording.WavReader);

            // get high resolution indices

            // when the value is accessed, the indices are calculated
            var indices = getSpectralIndexes.Value;

            // check if the indices have been calculated - you shouldn't actually need this
            if (getSpectralIndexes.IsValueCreated)
            {
                // then indices have been calculated before
            }

            var foundEvents = new List <EventCommon>();

            // some kind of loop where you scan through the audio

            // 'find' an event - if you find an event, store the data in the AcousticEvent class

            var anEvent = new SpectralEvent
            {
                Name = "FAKE!",
            };

            foundEvents.Add(anEvent);

            return(new RecognizerResults()
            {
                //Plots = null,
                NewEvents = foundEvents,
                Hits = null,
                ScoreTrack = null,
                Sonogram = sonogram,
            });
        }