Skip to content

Commit

Permalink
Changes as requested by Anthony
Browse files Browse the repository at this point in the history
Issue #300 Most of the changes are to terminology of temporal variables.
Also to signatures of test methods.
  • Loading branch information
towsey authored and atruskie committed Mar 20, 2020
1 parent 6de76cc commit 8a5a6f0
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 132 deletions.
6 changes: 3 additions & 3 deletions src/AnalysisBase/ResultBases/EventBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ public virtual double EventStartSeconds
/// <summary>
/// Sets both the Segment start and the Event start wrt to recording.
/// </summary>
protected void SetSegmentAndEventStartsWrtRecording(TimeSpan segmentStartWrtRecording, double eventStartWrtSegment)
protected void SetEventStartRelative(TimeSpan segmentStart, double eventStartSegmentRelative)
{
this.SegmentStartSeconds = segmentStartWrtRecording.TotalSeconds;
this.EventStartSeconds = this.SegmentStartSeconds + eventStartWrtSegment;
this.SegmentStartSeconds = segmentStart.TotalSeconds;
this.EventStartSeconds = this.SegmentStartSeconds + eventStartSegmentRelative;
}
}
}
6 changes: 3 additions & 3 deletions src/AnalysisPrograms/Recognizers/Base/WhistleParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static (List<AcousticEvent>, double[]) GetWhistles(
double decibelThreshold,
double minDuration,
double maxDuration,
TimeSpan segmentStartWrtRecording)
TimeSpan segmentStartOffset)
{
var sonogramData = sonogram.Data;
int frameCount = sonogramData.GetLength(0);
Expand Down Expand Up @@ -93,7 +93,7 @@ public static (List<AcousticEvent>, double[]) GetWhistles(
decibelThreshold,
minDuration,
maxDuration,
segmentStartWrtRecording);
segmentStartOffset);

// add to conbined intensity array
for (int t = 0; t < frameCount; t++)
Expand All @@ -107,7 +107,7 @@ public static (List<AcousticEvent>, double[]) GetWhistles(
} //end for all freq bins

// combine adjacent acoustic events
events = AcousticEvent.CombineOverlappingEvents(events, segmentStartWrtRecording);
events = AcousticEvent.CombineOverlappingEvents(events, segmentStartOffset);

return (events, combinedIntensityArray);
}
Expand Down
112 changes: 21 additions & 91 deletions src/AudioAnalysisTools/AcousticEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,6 @@ public AcousticEventClassMap()
}
}

// double I1MeandB; //mean intensity of pixels in the event prior to noise subtraction
// double I1Var; //,
// double I2MeandB; // mean intensity of pixels in the event after Wiener filter, prior to noise subtraction
// double I2Var; //,
// private double i3Mean; // mean intensity of pixels in the event AFTER noise reduciton - USED FOR CLUSTERING
// private double i3Var; // variance of intensity of pixels in the event.

/// <summary>
/// Gets the time offset from start of current segment to start of event in seconds.
/// NOTE: AcousticEvents do not have a notion of time offset wrt start of recording ; - only to start of current recording segment.
Expand Down Expand Up @@ -177,7 +170,6 @@ public AcousticEventClassMap()
public string Score2Name { get; set; }

/// <summary> Gets or sets second score if required.
/// Was used for Birgits recognisers but should now be used only for debug purposes.
/// </summary>
public double Score2 { get; set; }

Expand Down Expand Up @@ -234,11 +226,11 @@ public AcousticEvent()
/// <param name="eventDuration">event end with respect to start of segment.</param>
/// <param name="minFreq">Lower frequency bound of event.</param>
/// <param name="maxFreq">Upper frequency bound of event.</param>
public AcousticEvent(TimeSpan segmentStartWrtRecording, double startTimeWrtSegment, double eventDuration, double minFreq, double maxFreq)
public AcousticEvent(TimeSpan segmentStartOffset, double eventStartSegmentRelative, double eventDuration, double minFreq, double maxFreq)
: this()
{
var endTimeWrtSegment = startTimeWrtSegment + eventDuration;
this.SetEventPositionRelative(segmentStartWrtRecording, startTimeWrtSegment, endTimeWrtSegment);
var eventEndSegmentRelative = eventStartSegmentRelative + eventDuration;
this.SetEventPositionRelative(segmentStartOffset, eventStartSegmentRelative, eventEndSegmentRelative);
this.LowFrequencyHertz = minFreq;
this.HighFrequencyHertz = maxFreq;

Expand Down Expand Up @@ -304,14 +296,14 @@ public AcousticEvent(TimeSpan segmentStartOffset, Oblong o, int nyquistFrequency
/// AND also calls method to set event start time with respect the recording/file start.
/// </summary>
public void SetEventPositionRelative(
TimeSpan segmentStartWrtRecording,
double eventStartWrtSegment,
double eventEndWrtSegment)
TimeSpan segmentStartOffset,
double eventStartSegmentRelative,
double eventEndSegmentRelative)
{
this.TimeStart = eventStartWrtSegment;
this.TimeEnd = eventEndWrtSegment;
this.TimeStart = eventStartSegmentRelative;
this.TimeEnd = eventEndSegmentRelative;

this.SetSegmentAndEventStartsWrtRecording(segmentStartWrtRecording, eventStartWrtSegment);
this.SetEventStartRelative(segmentStartOffset, eventStartSegmentRelative);
}

/// <summary>
Expand Down Expand Up @@ -363,7 +355,10 @@ public void SetTimeAndFreqScales(double frameOffset, double frameDuration, doubl
}

/// <summary>
/// Converts frequency bounds of an event to left and right columns of object in sonogram matrix.
/// Converts the Hertz (frequency) bounds of an event to the frequency bin number.
/// The frequency bin is an index into the columns of the spectrogram data matrix.
/// Since the spectrogram data matrix is oriented with the origin at top left,
/// the low frequency bin will have a lower column index than the high freq bin.
/// </summary>
/// <param name="doMelscale">mel scale.</param>
/// <param name="minFreq">lower freq bound.</param>
Expand All @@ -372,7 +367,7 @@ public void SetTimeAndFreqScales(double frameOffset, double frameDuration, doubl
/// <param name="binWidth">frequency scale.</param>
/// <param name="leftCol">return bin index for lower freq bound.</param>
/// <param name="rightCol">return bin index for upper freq bound.</param>
public static void Freq2BinIDs(bool doMelscale, int minFreq, int maxFreq, int nyquist, double binWidth, out int leftCol, out int rightCol)
public static void ConvertHertzToFrequencyBin(bool doMelscale, int minFreq, int maxFreq, int nyquist, double binWidth, out int leftCol, out int rightCol)
{
if (doMelscale)
{
Expand Down Expand Up @@ -403,7 +398,7 @@ public static Oblong ConvertEvent2Oblong(AcousticEvent ae)
var bottomRow = (int)Math.Round((ae.TimeStart + ae.EventDurationSeconds) / ae.FrameOffset);

//Translate freq dimension = freq bins = matrix columns.
Freq2BinIDs(ae.IsMelscale, (int)ae.LowFrequencyHertz, (int)ae.HighFrequencyHertz, ae.FreqBinCount, ae.FreqBinWidth, out var leftCol, out var rightCol);
ConvertHertzToFrequencyBin(ae.IsMelscale, (int)ae.LowFrequencyHertz, (int)ae.HighFrequencyHertz, ae.FreqBinCount, ae.FreqBinWidth, out var leftCol, out var rightCol);

return new Oblong(topRow, leftCol, bottomRow, rightCol);
}
Expand Down Expand Up @@ -431,21 +426,6 @@ public void SetScores(double score, double min, double max)
}
}

//public string WriteProperties()
//{
// return " min-max=" + this.LowFrequencyHertz + "-" + this.HighFrequencyHertz + ", " + this.Oblong.ColumnLeft + "-" + this.Oblong.ColumnRight;
//}

/// <summary>
/// Draws an event on the image. Uses the fields already set on the audio event to determine correct placement.
/// Fields requireed to be set include: `FramesPerSecond`, `FreqBinWidth`.
/// </summary>
//public void DrawEvent<T>(Image<T> sonogram)
// where T : unmanaged, IPixel<T>
//{
// this.DrawEvent(sonogram, this.FramesPerSecond, this.FreqBinWidth, sonogram.Height);
//}

/// <summary>
/// Draws an event on the image. Allows for custom specification of variables.
/// Drawing the event requires a time scale and a frequency scale. Hence the additional arguments.
Expand Down Expand Up @@ -481,6 +461,8 @@ public void DrawEvent<T>(Image<T> imageToReturn, double framesPerSecond, double
t2 = this.Oblong.RowBottom;
}

// rectangle width = t2 - t1 + 1
// rectangle height = y2 - y1 + 1
imageToReturn.Mutate(g => g.DrawRectangle(borderPen, t1, y1, t2 - t1 + 1, y2 - y1 + 1));

if (this.HitElements != null)
Expand All @@ -501,31 +483,6 @@ public void DrawEvent<T>(Image<T> imageToReturn, double framesPerSecond, double
});
}

/*
/// <summary>
/// Passed point is relative to top-left corner of the Acoustic Event.
/// Oblong needs to be set for this method to work.
/// </summary>
public void DrawPoint(Image<Rgb24> bmp, Point point, Color colour)
{
if (bmp == null)
{
return;
}
int maxFreqBin = (int)Math.Round(this.HighFrequencyHertz / this.FreqBinWidth);
int row = bmp.Height - maxFreqBin - 1 + point.Y;
int t1 = (int)Math.Round(this.TimeStart * this.FramesPerSecond); // temporal start of event
int col = t1 + point.X;
if (row >= bmp.Height)
{
row = bmp.Height - 1;
}
bmp[col, row] = colour;
}
*/

//#################################################################################################################
//FOLLOWING METHODS DEAL WITH THE OVERLAP OF EVENTS

Expand Down Expand Up @@ -606,7 +563,7 @@ public static bool EventsOverlapInTime(AcousticEvent event1, AcousticEvent event
/// Freq dimension = bins = matrix columns. Origin is top left - as per matrix in the sonogram class.
/// Time dimension = frames = matrix rows.
/// </summary>
public static List<AcousticEvent> CombineOverlappingEvents(List<AcousticEvent> events, TimeSpan segmentStartWrtRecording)
public static List<AcousticEvent> CombineOverlappingEvents(List<AcousticEvent> events, TimeSpan segmentStartOffset)
{
if (events.Count < 2)
{
Expand All @@ -619,7 +576,7 @@ public static List<AcousticEvent> CombineOverlappingEvents(List<AcousticEvent> e
{
if (EventsOverlapInTime(events[i], events[j]) && EventsOverlapInFrequency(events[i], events[j]))
{
events[j] = AcousticEvent.MergeTwoEvents(events[i], events[j], segmentStartWrtRecording);
events[j] = AcousticEvent.MergeTwoEvents(events[i], events[j], segmentStartOffset);
events.RemoveAt(i);
break;
}
Expand All @@ -629,12 +586,12 @@ public static List<AcousticEvent> CombineOverlappingEvents(List<AcousticEvent> e
return events;
}

public static AcousticEvent MergeTwoEvents(AcousticEvent e1, AcousticEvent e2, TimeSpan segmentStartWrtRecording)
public static AcousticEvent MergeTwoEvents(AcousticEvent e1, AcousticEvent e2, TimeSpan segmentStartOffset)
{
//segmentStartOffset = TimeSpan.Zero;
var minTime = Math.Min(e1.TimeStart, e2.TimeStart);
var maxTime = Math.Max(e1.TimeEnd, e2.TimeEnd);
e1.SetEventPositionRelative(segmentStartWrtRecording, minTime, maxTime);
e1.SetEventPositionRelative(segmentStartOffset, minTime, maxTime);
e1.LowFrequencyHertz = Math.Min(e1.LowFrequencyHertz, e2.LowFrequencyHertz);
e1.HighFrequencyHertz = Math.Max(e1.HighFrequencyHertz, e2.HighFrequencyHertz);
e1.Score = Math.Max(e1.Score, e2.Score);
Expand Down Expand Up @@ -734,33 +691,6 @@ public static StringBuilder WriteEvents(List<AcousticEvent> eventList, string st
return sb;
}

/*
public static void WriteEvents(List<AcousticEvent> eventList, ref StringBuilder sb)
{
if (eventList.Count == 0)
{
string line =
$"# Event Name\t{"Start",8:f3}\t{"End",6:f3}\t{"MinF"}\t{"MaxF"}\t{"Score1":f2}\t{"Score2":f1}\t{"SourceFile"}";
sb.AppendLine(line);
line = $"{"NoEvent"}\t{0.000,8:f3}\t{0.000,8:f3}\t{"N/A"}\t{"N/A"}\t{0.000:f2}\t{0.000:f1}\t{"N/A"}";
sb.AppendLine(line);
}
else
{
AcousticEvent ae1 = eventList[0];
string line =
$"# Event Name\t{"Start",8:f3}\t{"End",6:f3}\t{"MinF"}\t{"MaxF"}\t{"Score":f2}\t{ae1.Score2Name:f1}\t{"SourceFile"}";
sb.AppendLine(line);
foreach (AcousticEvent ae in eventList)
{
line =
$"{ae.Name}\t{ae.TimeStart,8:f3}\t{ae.TimeEnd,8:f3}\t{ae.LowFrequencyHertz}\t{ae.HighFrequencyHertz}\t{ae.Score:f2}\t{ae.Score2:f1}\t{ae.FileName}";
sb.AppendLine(line);
}
}
}
*/

//#################################################################################################################
//METHODS FOR SEGMENTATION OF A FREQ BAND BASED ON ACOUSTIC ENERGY

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -636,14 +636,14 @@ public static Tuple<int[], int[]> HistogramOfSpectralPeaks(double[,] spectrogram

public static double[,] ExtractFreqSubband(double[,] m, int minHz, int maxHz, bool doMelscale, int binCount, double binWidth)
{
AcousticEvent.Freq2BinIDs(doMelscale, minHz, maxHz, binCount, binWidth, out var c1, out var c2);
AcousticEvent.ConvertHertzToFrequencyBin(doMelscale, minHz, maxHz, binCount, binWidth, out var c1, out var c2);
return DataTools.Submatrix(m, 0, c1, m.GetLength(0) - 1, c2);
}

public static double[] ExtractModalNoiseSubband(double[] modalNoise, int minHz, int maxHz, bool doMelScale, int nyquist, double binWidth)
{
//extract subband modal noise profile
AcousticEvent.Freq2BinIDs(doMelScale, minHz, maxHz, nyquist, binWidth, out var c1, out var c2);
AcousticEvent.ConvertHertzToFrequencyBin(doMelScale, minHz, maxHz, nyquist, binWidth, out var c1, out var c2);
int subbandCount = c2 - c1 + 1;
var subband = new double[subbandCount];
for (int i = 0; i < subbandCount; i++)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@ namespace Acoustics.Test.AudioAnalysisTools.EventStatistics
{
using System;
using System.Collections.Generic;
using Acoustics.Shared;
using Acoustics.Tools.Wav;
using global::AudioAnalysisTools;
using global::AudioAnalysisTools.DSP;
using global::AudioAnalysisTools.EventStatistics;
using global::AudioAnalysisTools.WavTools;
using global::TowseyLibrary;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
Expand All @@ -26,13 +20,8 @@ public void TestEventMerging()
// make a list of events
var events = new List<AcousticEvent>();

var segmentStartOffset = TimeSpan.Zero;
double maxPossibleScore = 10.0;
var startTime1 = 1.0;
var duration1 = 5.0;
var minHz1 = 1000;
var maxHz1 = 8000;
var event1 = new AcousticEvent(segmentStartOffset, startTime1, duration1, minHz1, maxHz1)
var event1 = new AcousticEvent(segmentStartOffset: TimeSpan.Zero, eventStartSegmentRelative: 1.0, eventDuration: 5.0, minFreq: 1000, maxFreq: 8000)
{
Name = "Event1",
Score = 1.0,
Expand All @@ -41,23 +30,15 @@ public void TestEventMerging()

events.Add(event1);

var startTime2 = 4.5;
var duration2 = 2.0;
var minHz2 = 1500;
var maxHz2 = 6000;
var event2 = new AcousticEvent(segmentStartOffset, startTime2, duration2, minHz2, maxHz2)
var event2 = new AcousticEvent(segmentStartOffset: TimeSpan.Zero, eventStartSegmentRelative: 4.5, eventDuration: 2.0, minFreq: 1500, maxFreq: 6000)
{
Name = "Event2",
Score = 5.0,
ScoreNormalised = 5.0 / maxPossibleScore,
};
events.Add(event2);

var startTime3 = 7.0;
var duration3 = 2.0;
var minHz3 = 1000;
var maxHz3 = 8000;
var event3 = new AcousticEvent(segmentStartOffset, startTime3, duration3, minHz3, maxHz3)
var event3 = new AcousticEvent(segmentStartOffset: TimeSpan.Zero, eventStartSegmentRelative: 7.0, eventDuration: 2.0, minFreq: 1000, maxFreq: 8000)
{
Name = "Event3",
Score = 9.0,
Expand All @@ -66,7 +47,7 @@ public void TestEventMerging()
events.Add(event3);

// combine adjacent acoustic events
events = AcousticEvent.CombineOverlappingEvents(events, segmentStartOffset);
events = AcousticEvent.CombineOverlappingEvents(events: events, segmentStartOffset: TimeSpan.Zero);

Assert.AreEqual(2, events.Count);
Assert.AreEqual(1.0, events[0].EventStartSeconds, 1E-4);
Expand Down Expand Up @@ -99,25 +80,18 @@ public void TestSonogramWithEventsOverlay()
// make a list of events
var framesPerSecond = 10.0;
var freqBinWidth = 43.0664;
var segmentStartOffset = TimeSpan.Zero;
var minHz = 1000;
var maxHz = 8000;
double maxPossibleScore = 10.0;

var events = new List<AcousticEvent>();
var startTime1 = 1.0;
var duration1 = 5.0;
var event1 = new AcousticEvent(segmentStartOffset, startTime1, duration1, minHz, maxHz)
var event1 = new AcousticEvent(segmentStartOffset: TimeSpan.Zero, eventStartSegmentRelative: 1.0, eventDuration: 5.0, minFreq: 1000, maxFreq: 8000)
{
Score = 10.0,
Name = "Event1",
ScoreNormalised = 10.0 / maxPossibleScore,
};

events.Add(event1);
var startTime2 = 7.0;
var duration2 = 2.0;
var event2 = new AcousticEvent(segmentStartOffset, startTime2, duration2, minHz, maxHz)
var event2 = new AcousticEvent(segmentStartOffset: TimeSpan.Zero, eventStartSegmentRelative: 7.0, eventDuration: 2.0, minFreq: 1000, maxFreq: 8000)
{
Score = 1.0,
Name = "Event2",
Expand All @@ -134,7 +108,6 @@ public void TestSonogramWithEventsOverlay()
ev.DrawEvent(substituteSonogram, framesPerSecond, freqBinWidth, height);
}

//substituteSonogram.Save("C:\\temp\\image.png");
var redPixel1 = new Argb32(110, 10, 30);
var expectedRed1 = new Color(redPixel1);
var redPixel2 = new Argb32(124, 11, 34);
Expand Down

0 comments on commit 8a5a6f0

Please sign in to comment.