From 83eb327139fc615542e83651892603de95c46226 Mon Sep 17 00:00:00 2001 From: towsey Date: Fri, 17 Apr 2020 14:18:24 +1000 Subject: [PATCH] Debug UnitConverters Issue #297 Also make changes ready to store multiple tracks in one AcousticEvent. --- src/Acoustics.Shared/Interval.cs | 8 +-- src/AudioAnalysisTools/AcousticEvent.cs | 8 ++- src/AudioAnalysisTools/Events/Tracks/Track.cs | 6 ++- .../Events/Tracks/TrackExtractor.cs | 3 +- src/AudioAnalysisTools/UnitConverters.cs | 14 ++--- .../GenericRecognizerTests.cs | 53 ++++++++++++++++--- .../AudioAnalysisTools/UnitConverterTests.cs | 40 ++++++++++++++ 7 files changed, 110 insertions(+), 22 deletions(-) diff --git a/src/Acoustics.Shared/Interval.cs b/src/Acoustics.Shared/Interval.cs index 23a4c74df..3ea86442c 100644 --- a/src/Acoustics.Shared/Interval.cs +++ b/src/Acoustics.Shared/Interval.cs @@ -251,10 +251,10 @@ public override int GetHashCode() /// /// String representation. /// - //public override string ToString(string v) - //{ - // return this.ToString(false); - //} + public override string ToString() + { + return this.ToString(false); + } /// /// Gets string representation of the Interval. diff --git a/src/AudioAnalysisTools/AcousticEvent.cs b/src/AudioAnalysisTools/AcousticEvent.cs index c58c2ef58..1868c3610 100644 --- a/src/AudioAnalysisTools/AcousticEvent.cs +++ b/src/AudioAnalysisTools/AcousticEvent.cs @@ -126,10 +126,16 @@ public AcousticEventClassMap() public double Bandwidth => this.HighFrequencyHertz - this.LowFrequencyHertz + 1; /// - /// Gets or sets a horizontal or vertical spectral track. + /// Gets or sets a spectral track. /// public Track TheTrack { get; set; } + /// + /// Gets or sets a list of tracks. + /// This will be used when two or more events containing single tracks are merged into one combined event. + /// + public List Tracks { get; set; } + public bool IsMelscale { get; set; } /// diff --git a/src/AudioAnalysisTools/Events/Tracks/Track.cs b/src/AudioAnalysisTools/Events/Tracks/Track.cs index 6de9938c5..2350c9548 100644 --- a/src/AudioAnalysisTools/Events/Tracks/Track.cs +++ b/src/AudioAnalysisTools/Events/Tracks/Track.cs @@ -26,6 +26,8 @@ public class Track : ITrack private readonly TrackType trackType; + private readonly List _pointCopy; + /// /// Initializes a new instance of the class. /// Constructor. @@ -40,6 +42,7 @@ public Track(UnitConverters converter, TrackType aTrackType) this.converter = converter; this.trackType = aTrackType; this.Points = new SortedSet(); + this._pointCopy = new List(); } /// @@ -105,6 +108,7 @@ public void SetPoint(int frame, int bin, double amplitude) amplitude); this.Points.Add(point); + this._pointCopy.Add(point); } /// @@ -130,7 +134,7 @@ public string CheckPoint(int frame, int bin) if (frame != outFrame || bin != outBin) { LoggedConsole.WriteWarnLine("WARNING" + info); - //throw new Exception("WARNING" + info); + throw new Exception("WARNING" + info); } else { diff --git a/src/AudioAnalysisTools/Events/Tracks/TrackExtractor.cs b/src/AudioAnalysisTools/Events/Tracks/TrackExtractor.cs index fae233bd3..ed5d4cad4 100644 --- a/src/AudioAnalysisTools/Events/Tracks/TrackExtractor.cs +++ b/src/AudioAnalysisTools/Events/Tracks/TrackExtractor.cs @@ -208,6 +208,7 @@ public static Track GetUpwardTrack(double[,] peaks, int startRow, int startBin, var maxValue = directionOptions[maxId] / 2.0; if (maxValue < threshold) { + peaks[row, bin] = 0.0; return track; } @@ -230,7 +231,7 @@ public static Track GetUpwardTrack(double[,] peaks, int startRow, int startBin, track.SetPoint(row, bin, maxValue); // next line is for debug purposes - var info = track.CheckPoint(row, bin); + //var info = track.CheckPoint(row, bin); } return track; diff --git a/src/AudioAnalysisTools/UnitConverters.cs b/src/AudioAnalysisTools/UnitConverters.cs index 45f9469a4..b65342366 100644 --- a/src/AudioAnalysisTools/UnitConverters.cs +++ b/src/AudioAnalysisTools/UnitConverters.cs @@ -221,14 +221,14 @@ public SizeF GetSize(ISpectralEvent @event) public double RecordingRelativeToSegmentRelative(double seconds) => seconds + this.SegmentStartOffset; - //public double SecondsDurationFromFrameCount(int frameCount) - //{ - // return this.SecondsScale.GetSecondsDurationFromFrameCount(frameCount); - //} - public int FrameFromStartTime(double startTime) { - return (int)(startTime / this.SecondsPerFrameStep); + return (int)Math.Round((startTime - this.SegmentStartOffset) / this.SecondsPerFrameStep, MidpointRounding.AwayFromZero); + } + + public int FrameFromEndTime(double endTime) + { + return (int)Math.Round((endTime - this.SegmentStartOffset - this.SecondsPerFrame) / this.SecondsPerFrameStep, MidpointRounding.AwayFromZero); } public double GetStartTimeInSecondsOfFrame(int frameId) @@ -238,7 +238,7 @@ public double GetStartTimeInSecondsOfFrame(int frameId) public double GetEndTimeInSecondsOfFrame(int frameId) { - return this.SegmentStartOffset + (this.GetStartTimeInSecondsOfFrame(frameId) + this.SecondsPerFrame); + return this.GetStartTimeInSecondsOfFrame(frameId) + this.SecondsPerFrame; } /// diff --git a/tests/Acoustics.Test/AnalysisPrograms/Recognizers/GenericRecognizer/GenericRecognizerTests.cs b/tests/Acoustics.Test/AnalysisPrograms/Recognizers/GenericRecognizer/GenericRecognizerTests.cs index 1dbb101b9..d765a7a8a 100644 --- a/tests/Acoustics.Test/AnalysisPrograms/Recognizers/GenericRecognizer/GenericRecognizerTests.cs +++ b/tests/Acoustics.Test/AnalysisPrograms/Recognizers/GenericRecognizer/GenericRecognizerTests.cs @@ -497,7 +497,7 @@ public void TestClickAlgorithm() /// Tests the upward-track recognizer on the same artifical spectrogram as used for foward-tracks and harmonics. /// [TestMethod] - public void TestUpwardsTrackAlgorithm() + public void Test1UpwardsTrackAlgorithm() { // Set up the recognizer parameters. var windowSize = 512; @@ -585,12 +585,48 @@ public void TestUpwardsTrackAlgorithm() Assert.AreEqual(11.24, @event.EventEndSeconds, 0.1); Assert.AreEqual(6450, @event.LowFrequencyHertz); Assert.AreEqual(7310, @event.HighFrequencyHertz); + } + + /// + /// Tests the upward-track recognizer on the same artifical spectrogram as used for foward-tracks and harmonics. + /// + [TestMethod] + public void Test2UpwardsTrackAlgorithm() + { + // Set up the recognizer parameters. + var windowSize = 512; + var windowStep = 512; + var minHertz = 500; + var maxHertz = 6000; + var minBandwidthHertz = 100; + var maxBandwidthHertz = 5000; + var decibelThreshold = 2.0; + var combineProximalSimilarEvents = false; + + //Set up the virtual recording. + var segmentStartOffset = TimeSpan.Zero; + 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 plots = new List(); // do a SECOND TEST of the vertical tracks - minHertz = 500; - maxHertz = 6000; - minBandwidthHertz = 100; - maxBandwidthHertz = 5000; + double[] dBArray; + List acousticEvents; (acousticEvents, dBArray) = UpwardTrackParameters.GetUpwardTracks( spectrogram, minHertz, @@ -602,7 +638,9 @@ public void TestUpwardsTrackAlgorithm() segmentStartOffset); // draw a plot of max decibels in each frame - normalisedDecibelArray = DataTools.NormaliseInZeroOne(dBArray, 0, decibelNormalizationMax); + double decibelNormalizationMax = 3 * decibelThreshold; + var dBThreshold = decibelThreshold / decibelNormalizationMax; + var normalisedDecibelArray = DataTools.NormaliseInZeroOne(dBArray, 0, decibelNormalizationMax); var plot2 = new Plot("decibel max", normalisedDecibelArray, dBThreshold); plots.Add(plot2); @@ -618,11 +656,10 @@ public void TestUpwardsTrackAlgorithm() // 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 + var outputDirectory = new DirectoryInfo("C:\\temp"); GenericRecognizer.SaveDebugSpectrogram(allResults2, null, outputDirectory, "UpwardTracks2"); Assert.AreEqual(5, allResults2.Events.Count); diff --git a/tests/Acoustics.Test/AudioAnalysisTools/UnitConverterTests.cs b/tests/Acoustics.Test/AudioAnalysisTools/UnitConverterTests.cs index ff124e498..02749947d 100644 --- a/tests/Acoustics.Test/AudioAnalysisTools/UnitConverterTests.cs +++ b/tests/Acoustics.Test/AudioAnalysisTools/UnitConverterTests.cs @@ -7,6 +7,8 @@ namespace Acoustics.Test.AudioAnalysisTools using global::AudioAnalysisTools; using global::AudioAnalysisTools.Events; using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Collections.Generic; + using System.Linq; [TestClass] public class UnitConverterTests @@ -35,5 +37,43 @@ public void TestGetPixelRectangle() Assert.AreEqual(80, rect.Height); } + + public static IEnumerable FrameAndBinData + { + get + { + var frames = Enumerable.Range(0, 10); + var bins = Enumerable.Range(0, 10); + + var combinations = from f in frames from b in bins select new object[] { f, b }; + + return combinations; + } + } + + [DataTestMethod] + [DynamicData(nameof(FrameAndBinData))] + public void TestBackwardsAndForwardsConversionSpectrograms(int frame, int bin) + { + UnitConverters converter = new UnitConverters( + 60, + 22050, + 512, + 256); + + var endFrame = frame + 1; + + var secondsStart = converter.GetStartTimeInSecondsOfFrame(frame); + var secondsEnd = converter.GetEndTimeInSecondsOfFrame(endFrame); + var hertz = converter.GetHertzFromFreqBin(bin); + + var outFrame = converter.FrameFromStartTime(secondsStart); + var endOutFrame = converter.FrameFromEndTime(secondsEnd); + var outBin = converter.GetFreqBinFromHertz(hertz); + + Assert.AreEqual(frame, outFrame, $"Frames do not match, expected: {frame}, actual: {outFrame}, seconds was: {secondsStart}"); + Assert.AreEqual(endFrame, endOutFrame, $"Frame ends do not match, expected: {endFrame}, actual: {endOutFrame}, seconds was: {secondsEnd}"); + Assert.AreEqual(bin, outBin, $"Bins do not match, expected: {bin}, actual: {outBin}, hertz was: {hertz}"); + } } }