diff --git a/src/AudioAnalysisTools/AudioAnalysisTools.csproj b/src/AudioAnalysisTools/AudioAnalysisTools.csproj index 1a234ae2d..a324fb75c 100644 --- a/src/AudioAnalysisTools/AudioAnalysisTools.csproj +++ b/src/AudioAnalysisTools/AudioAnalysisTools.csproj @@ -36,4 +36,7 @@ + + + \ No newline at end of file diff --git a/src/AudioAnalysisTools/Events/Drawing/EventDrawer.cs b/src/AudioAnalysisTools/Events/Drawing/EventDrawer.cs index 2a1d40536..a6c7af275 100644 --- a/src/AudioAnalysisTools/Events/Drawing/EventDrawer.cs +++ b/src/AudioAnalysisTools/Events/Drawing/EventDrawer.cs @@ -28,11 +28,9 @@ public static void DrawScoreIndicator(this SpectralEvent @event, IImageProcessin return; } - // TODO: add a Interval ScoreRange property to EventCommon - // so we can properly normalize this value to the unit value. - // For now, we just assume it is normalized to [0,1]. - //var clampedScore = @event.Score.Clamp(0, 1); - var normalisedScore = @event.ScoreNormalised; + // Although an Interval ScoreRange property has been added to EventCommon, + // this does not clamp values. For now, we clamp before drawing. + var normalisedScore = @event.ScoreNormalised.Clamp(0, 1); if (normalisedScore == 0) { @@ -45,7 +43,7 @@ public static void DrawScoreIndicator(this SpectralEvent @event, IImageProcessin graphics.NoAA().DrawLines( options.Score, - new PointF(rect.Left, rect.Bottom - 1), // TODO minus one is a hack! just to make it work! + new PointF(rect.Left, rect.Bottom - 1), // minus one is to bring bottom of score line within event frame. new PointF(rect.Left, rect.Bottom - scaledHeight)); } diff --git a/src/AudioAnalysisTools/Events/EventCommon.cs b/src/AudioAnalysisTools/Events/EventCommon.cs index bd7ec23f2..404504f1d 100644 --- a/src/AudioAnalysisTools/Events/EventCommon.cs +++ b/src/AudioAnalysisTools/Events/EventCommon.cs @@ -55,7 +55,7 @@ public abstract class EventCommon : EventBase, IDrawableEvent /// Up to user to determine a suitable range maximum. /// Minimum of range assumed to be zero. /// - public double ScoreNormalised => Math.Min(1.0, this.Score / this.ScoreRange.Maximum); + public double ScoreNormalised => this.Score / this.ScoreRange.Maximum; /// /// Draw this event on an image. diff --git a/src/AudioAnalysisTools/Events/Tracks/PointData.cs b/src/AudioAnalysisTools/Events/Tracks/PointData.cs deleted file mode 100644 index 8ab54a03d..000000000 --- a/src/AudioAnalysisTools/Events/Tracks/PointData.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group). -// - -namespace AudioAnalysisTools -{ - using System.Collections.Generic; - using System.Linq; - using AudioAnalysisTools.Events.Drawing; - using SixLabors.ImageSharp; - using SixLabors.ImageSharp.Processing; - - public class PointData - { - public ISet Points { get; } - - public void DrawPointsAsFill(IImageProcessingContext graphics, EventRenderingOptions options) - { - // overlay point data on image with 50% opacity - // TODO: a much more efficient implementation exists if we derive from Region and convert - // our set to a region. - foreach (var point in this.Points) - { - var area = options.Converters.GetPixelRectangle(point); - graphics.Fill(options.Fill, area); - } - } - - public void DrawPointsAsPath(IImageProcessingContext graphics, EventRenderingOptions options) - { - // visits each point once - // assumes each point describes a line - // assumes a SortedSet is used (and that iteration order is signficant, unlike with HashSet) - var path = this.Points.Select(x => options.Converters.GetPoint(x)).ToArray(); - - // note not using AA here - // note could base pen thickness off ISpectralPoint thickness for a more accurate representation - graphics.DrawLines( - options.Border, - path); - } - } -} \ No newline at end of file diff --git a/src/AudioAnalysisTools/Events/Types/ChirpEvent.cs b/src/AudioAnalysisTools/Events/Types/ChirpEvent.cs index 1c6c80bfe..ec4ca357a 100644 --- a/src/AudioAnalysisTools/Events/Types/ChirpEvent.cs +++ b/src/AudioAnalysisTools/Events/Types/ChirpEvent.cs @@ -16,8 +16,6 @@ namespace AudioAnalysisTools public class ChirpEvent : SpectralEvent, ITracks { - private readonly double maxScore; - /// /// Initializes a new instance of the class. /// @@ -27,11 +25,11 @@ public class ChirpEvent : SpectralEvent, ITracks /// The normalised score is a linear conversion from 0 - maxScore to [0, 1]. /// /// A chirp track consisting of a sequence of spectral points. - /// A maximum score used to normalise the track score. - public ChirpEvent(Track chirp, double maxScore) + /// A min and maximum score used to normalise the track score. + public ChirpEvent(Track chirp, Interval interval) { this.Tracks.Add(chirp); - this.ScoreRange = new Interval(0, maxScore); + this.ScoreRange = interval; } public List Tracks { get; private set; } = new List(1); @@ -68,18 +66,6 @@ public override double Score } } - /// - /// Gets the normalised value for the event's track score. - /// NOTE: It is assumed that the minimum value of the score range = zero. - /// - public double ScoreNormalised - { - get - { - return this.Score / this.maxScore; - } - } - public override void Draw(IImageProcessingContext graphics, EventRenderingOptions options) { this.Tracks.First().Draw(graphics, options); diff --git a/src/AudioAnalysisTools/Events/Types/ClickEvent.cs b/src/AudioAnalysisTools/Events/Types/ClickEvent.cs index 6ad82c9d8..36ef1cdce 100644 --- a/src/AudioAnalysisTools/Events/Types/ClickEvent.cs +++ b/src/AudioAnalysisTools/Events/Types/ClickEvent.cs @@ -52,14 +52,7 @@ public override double Score public override void Draw(IImageProcessingContext graphics, EventRenderingOptions options) { - // foreach (var track in tracks) { - // track.Draw(...) - // } - this.Tracks.First().Draw(graphics, options); - - // base drawing (border) - // TODO: unless border is disabled base.Draw(graphics, options); } } diff --git a/src/AudioAnalysisTools/Events/Types/CompositeEvent.cs b/src/AudioAnalysisTools/Events/Types/CompositeEvent.cs index b3f0fb70a..f677121e1 100644 --- a/src/AudioAnalysisTools/Events/Types/CompositeEvent.cs +++ b/src/AudioAnalysisTools/Events/Types/CompositeEvent.cs @@ -57,6 +57,19 @@ public IEnumerable Tracks } } + public new double ScoreNormalised() + { + double sum = 0.0; + foreach (var @event in this.ComponentEvents) + { + sum += @event.Score; + } + + var score = sum / this.ComponentEvents.Count; + + return score / this.ScoreRange.Maximum; + } + public override void Draw(IImageProcessingContext graphics, EventRenderingOptions options) { foreach (var @event in this.ComponentEvents) diff --git a/src/AudioAnalysisTools/Events/Types/WhipEvent.cs b/src/AudioAnalysisTools/Events/Types/WhipEvent.cs index 55fc5a9e5..90fbdd106 100644 --- a/src/AudioAnalysisTools/Events/Types/WhipEvent.cs +++ b/src/AudioAnalysisTools/Events/Types/WhipEvent.cs @@ -16,10 +16,10 @@ namespace AudioAnalysisTools public class WhipEvent : SpectralEvent, ITracks { - public WhipEvent(Track whip, double maxScore) + public WhipEvent(Track whip, Interval interval) { this.Tracks.Add(whip); - this.ScoreRange = new Interval(0, maxScore); + this.ScoreRange = interval; } public List Tracks { get; private set; } = new List(1); diff --git a/src/AudioAnalysisTools/Events/Types/WhistleEvent.cs b/src/AudioAnalysisTools/Events/Types/WhistleEvent.cs index b30e4060f..f00485930 100644 --- a/src/AudioAnalysisTools/Events/Types/WhistleEvent.cs +++ b/src/AudioAnalysisTools/Events/Types/WhistleEvent.cs @@ -17,10 +17,10 @@ namespace AudioAnalysisTools public class WhistleEvent : SpectralEvent, ITracks { - public WhistleEvent(Track whistle, double maxScore) + public WhistleEvent(Track whistle, Interval interval) { this.Tracks.Add(whistle); - this.ScoreRange = new Interval(0, maxScore); + this.ScoreRange = interval; } public List Tracks { get; private set; } = new List(1); @@ -114,7 +114,8 @@ public static WhistleEvent MergeTwoWhistleEvents(WhistleEvent e1, WhistleEvent e track1.Points.Add(point); } - var newEvent = new WhistleEvent(track1, 1.0) + var scoreRange = new Interval(0, 1.0); + var newEvent = new WhistleEvent(track1, scoreRange) { Name = e1.Name, EventEndSeconds = Math.Max(e1.EventEndSeconds, e2.EventEndSeconds), diff --git a/src/AudioAnalysisTools/LongDurationSpectrograms/LDSpectrogramClusters.cs b/src/AudioAnalysisTools/LongDurationSpectrograms/LDSpectrogramClusters.cs index 8a2e981a1..11a058807 100644 --- a/src/AudioAnalysisTools/LongDurationSpectrograms/LDSpectrogramClusters.cs +++ b/src/AudioAnalysisTools/LongDurationSpectrograms/LDSpectrogramClusters.cs @@ -23,12 +23,12 @@ namespace AudioAnalysisTools.LongDurationSpectrograms /// It was written to deal with a set of recordings with protocol of Gianna Pavan (10 minutes every 30 minutes). /// /// The following Powershell command was constructed by Anthony to do the analysis and join the sequence of images so derived: - /// Y:\Italy_GianniPavan\Sassofratino1day | % {"&" "C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisPrograms\bin\Release\AnalysisPrograms.exe" audio2csv -so ($_.FullName) -o "Y:\Italy_GianniPavan\output" -c "C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Towsey.Acoustic.Parallel.yml" } + /// Y:\Italy_GianniPavan\Sassofratino1day | % { & "C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisPrograms\bin\Release\AnalysisPrograms.exe" audio2csv -so ($_.FullName) -o "Y:\Italy_GianniPavan\output" -c "C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Towsey.Acoustic.Parallel.yml" } /// where: /// Y:\Italy_GianniPavan\Sassofratino1day is the directory containing recordings /// | = a pipe /// % = foreach{} = perform the operation in curly brackets on each item piped from the directory. - /// "&" "C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisPrograms\bin\Release\AnalysisPrograms.exe" = runs an executable + /// & "C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisPrograms\bin\Release\AnalysisPrograms.exe" = runs an executable /// audio2csv = first command line argument which determines the "activity" performed /// -so ($_.FullName) = the input file /// -o "Y:\Italy_GianniPavan\output" = the output directory @@ -36,7 +36,7 @@ namespace AudioAnalysisTools.LongDurationSpectrograms /// /// The following PowerShell command was used by Anthony to stitch together a sequence of spectrogam images without any gap between them. /// It requires ImageMagick software to be installed: i.e. C:\Program Files\ImageMagick-6.8.9-Q16\montage.exe - /// Y:\Italy_GianniPavan\output\Towsey.Acoustic> "&" "C:\Program Files\ImageMagick-6.8.9-Q16\montage.exe" -mode concatenate -tile x1 *2MAP* "..\..\merge.png" + /// Y:\Italy_GianniPavan\output\Towsey.Acoustic> & "C:\Program Files\ImageMagick-6.8.9-Q16\montage.exe" -mode concatenate -tile x1 *2MAP* "..\..\merge.png" /// /// /// (2) ConcatenateSpectralIndexFiles() @@ -203,12 +203,15 @@ public static void ExtractSOMClusters1() // ############################################################### // VERY IMPORTANT: MUST MAKE SURE THE BELOW ARE CONSISTENT WITH THE DATA !!!!!!!!!!!!!!!!!!!! int sampleRate = 22050; - int frameWidth = 256; + + //int frameWidth = 256; int nyquist = sampleRate / 2; - int herzInterval = 1000; + + //int herzInterval = 1000; TimeSpan minuteOffset = TimeSpan.Zero; // assume recordings start at midnight - double backgroundFilterCoeff = SpectrogramConstants.BACKGROUND_FILTER_COEFF; - string colorMap = SpectrogramConstants.RGBMap_ACI_ENT_EVN; + + //double backgroundFilterCoeff = SpectrogramConstants.BACKGROUND_FILTER_COEFF; + //string colorMap = SpectrogramConstants.RGBMap_ACI_ENT_EVN; string title = $"SOM CLUSTERS of ACOUSTIC INDICES: recording {fileStem}"; TimeSpan indexCalculationDuration = TimeSpan.FromSeconds(60); // seconds TimeSpan xTicInterval = TimeSpan.FromMinutes(60); // 60 minutes or one hour. @@ -494,20 +497,20 @@ public static Image DrawTitleBarOfClusterSpectrogram(string title, int width) bmp.Mutate(g => { - SizeF stringSize = new SizeF(); + SizeF stringSize = default; - int X = 4; - g.DrawText(title, stringFont, Color.Wheat, new PointF(X, 3)); + int x1 = 4; + g.DrawText(title, stringFont, Color.Wheat, new PointF(x1, 3)); stringSize = g.MeasureString(title, stringFont); - X += stringSize.ToSize().Width + 70; + x1 += stringSize.ToSize().Width + 70; string text = Meta.OrganizationTag; stringSize = g.MeasureString(text, stringFont); - int X2 = width - stringSize.ToSize().Width - 2; - if (X2 > X) + int x2 = width - stringSize.ToSize().Width - 2; + if (x2 > x1) { - g.DrawText(text, stringFont, Color.Wheat, new PointF(X2, 3)); + g.DrawText(text, stringFont, Color.Wheat, new PointF(x2, 3)); } g.DrawLine(new Pen(Color.Gray, 1), 0, 0, width, 0); //draw upper boundary diff --git a/src/AudioAnalysisTools/Scales/LinearSecondsScale.cs b/src/AudioAnalysisTools/Scales/LinearSecondsScale.cs deleted file mode 100644 index 76b727790..000000000 --- a/src/AudioAnalysisTools/Scales/LinearSecondsScale.cs +++ /dev/null @@ -1,25 +0,0 @@ -// -// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group). -// - -namespace AudioAnalysisTools.Scales -{ - using System; - - /// - /// This class converts between frames and time duration in seconds. - /// A complication arises when the frames of a spectrogram are overlapped. - /// - public class LinearSecondsScale - { - public LinearSecondsScale(int sampleRate, int frameSize, int stepSize) - { - this.SecondsPerFrame = frameSize / (double)sampleRate; - this.SecondsPerFrameStep = stepSize / (double)sampleRate; - } - - public double SecondsPerFrame { get; } - - public double SecondsPerFrameStep { get; } - } -} diff --git a/src/AudioAnalysisTools/Tracks/ForwardTrackAlgorithm.cs b/src/AudioAnalysisTools/Tracks/ForwardTrackAlgorithm.cs index 4feb059ec..2456e35c7 100644 --- a/src/AudioAnalysisTools/Tracks/ForwardTrackAlgorithm.cs +++ b/src/AudioAnalysisTools/Tracks/ForwardTrackAlgorithm.cs @@ -8,6 +8,7 @@ namespace AudioAnalysisTools.Tracks using System.Collections.Generic; using System.Linq; using System.Text; + using Acoustics.Shared; using AnalysisPrograms.Recognizers.Base; using AudioAnalysisTools.Events; using AudioAnalysisTools.Events.Drawing; @@ -82,10 +83,11 @@ public static (List Events, double[] CombinedIntensity) GetForwardT // Initialise events with tracks. foreach (var track in tracks) { - //Following line used only for debug purposes. + //Following line used only for debug purposes. Can save as image. //spectrogram.Mutate(x => track.Draw(x, options)); var maxScore = decibelThreshold * 5; - var ae = new ChirpEvent(track, maxScore) + var scoreRange = new Interval(0, maxScore); + var ae = new ChirpEvent(track, scoreRange) { SegmentStartSeconds = segmentStartOffset.TotalSeconds, SegmentDurationSeconds = frameCount * converter.SecondsPerFrameStep, @@ -103,9 +105,6 @@ public static (List Events, double[] CombinedIntensity) GetForwardT } } - //Following line used only for debug purposes. - //spectrogram.Save("C:\\temp\\SpectrogramWithTracks.png"); - List returnEvents = events.Cast().ToList(); // Combine coincident events that are stacked one above other. diff --git a/src/AudioAnalysisTools/Tracks/OnebinTrackAlgorithm.cs b/src/AudioAnalysisTools/Tracks/OnebinTrackAlgorithm.cs index b0df55699..2b5b787fa 100644 --- a/src/AudioAnalysisTools/Tracks/OnebinTrackAlgorithm.cs +++ b/src/AudioAnalysisTools/Tracks/OnebinTrackAlgorithm.cs @@ -8,6 +8,7 @@ namespace AudioAnalysisTools.Tracks using System.Collections.Generic; using System.Linq; using System.Text; + using Acoustics.Shared; using AnalysisPrograms.Recognizers.Base; using AudioAnalysisTools.Events; using AudioAnalysisTools.Events.Tracks; @@ -80,10 +81,11 @@ public static (List ListOfevents, double[] CombinedIntensityArray) // Initialise tracks as events and get the combined intensity array. var events = new List(); var combinedIntensityArray = new double[frameCount]; - var maxScore = decibelThreshold * 5; + var scoreRange = new Interval(0, decibelThreshold * 5); + foreach (var track in tracks) { - var ae = new WhistleEvent(track, maxScore) + var ae = new WhistleEvent(track, scoreRange) { SegmentStartSeconds = segmentStartOffset.TotalSeconds, SegmentDurationSeconds = frameCount * converter.SecondsPerFrameStep, diff --git a/src/AudioAnalysisTools/Tracks/UpwardTrackAlgorithm.cs b/src/AudioAnalysisTools/Tracks/UpwardTrackAlgorithm.cs index 407049565..4e97cb384 100644 --- a/src/AudioAnalysisTools/Tracks/UpwardTrackAlgorithm.cs +++ b/src/AudioAnalysisTools/Tracks/UpwardTrackAlgorithm.cs @@ -8,6 +8,7 @@ namespace AudioAnalysisTools.Tracks using System.Collections.Generic; using System.Linq; using System.Text; + using Acoustics.Shared; using AudioAnalysisTools.Events; using AudioAnalysisTools.Events.Tracks; using AudioAnalysisTools.Events.Types; @@ -76,14 +77,15 @@ public static (List Events, double[] CombinedIntensity) GetUpwardTr // initialise tracks as events and get the combined intensity array. var events = new List(); var temporalIntensityArray = new double[frameCount]; - var maxScore = decibelThreshold * 5; + var scoreRange = new Interval(0.0, decibelThreshold * 5); + foreach (var track in tracks) { - var ae = new WhipEvent(track, maxScore) + var ae = new WhipEvent(track, scoreRange) { SegmentStartSeconds = segmentStartOffset.TotalSeconds, SegmentDurationSeconds = frameCount * converter.SecondsPerFrameStep, - Name = "noName", + Name = "Whip", }; events.Add(ae); diff --git a/src/AudioAnalysisTools/UnitConverters.cs b/src/AudioAnalysisTools/UnitConverters.cs index c5797c33a..c44c1d24b 100644 --- a/src/AudioAnalysisTools/UnitConverters.cs +++ b/src/AudioAnalysisTools/UnitConverters.cs @@ -127,7 +127,7 @@ public UnitConverters(double segmentStartOffset, int sampleRate, int frameSize, /// /// Measured in seconds per spectrogram frame. /// - public LinearSecondsScale SecondsScale { get; } + //public LinearSecondsScale SecondsScale { get; } /// /// Gets the spectral scale.