Skip to content

Commit

Permalink
More changes to FF code
Browse files Browse the repository at this point in the history
Issue #238 Changes mostly to get acoustic event information transfered to the FF methods.
  • Loading branch information
towsey committed Aug 29, 2019
1 parent 1d1c552 commit 03a304d
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 122 deletions.
7 changes: 7 additions & 0 deletions src/AnalysisPrograms/Recognizers/PteropusSpecies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ private static RecognizerResults TerritorialCall(AudioRecording audioRecording,
//iV add additional info to the acoustic events
acousticEvents.ForEach(ae =>
{
ae.FileName = audioRecording.BaseName;
ae.SpeciesName = speciesName;
ae.Name = abbreviatedSpeciesName + profileName;
ae.Profile = profileName;
Expand Down Expand Up @@ -401,13 +402,19 @@ private static RecognizerResults WingBeats(AudioRecording audioRecording, Config
var plots = new List<Plot> { plot1, plot2 };

// ######################################################################

// add additional information about the recording and sonogram properties from which the event is derived.
acousticEvents.ForEach(ae =>
{
ae.FileName = audioRecording.BaseName;
ae.SpeciesName = speciesName;
ae.Name = abbreviatedSpeciesName + profileName;
ae.Profile = profileName;
ae.SegmentDurationSeconds = audioRecording.Duration.TotalSeconds;
ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds;
var frameOffset = sonogram.FrameStep;
var frameDuration = sonogram.FrameDuration;
ae.SetTimeAndFreqScales(frameOffset, frameDuration, sonogram.FBinWidth);

//UNCOMMENT following lines to get spectral profiles of the Wingbeat events.
/* double[,] spectrogramData = sonogram.Data;
Expand Down
3 changes: 2 additions & 1 deletion src/AnalysisPrograms/Sandpit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ public static void Audio2CsvOverOneFile()

// FLYING FOX RECORDINGS
//string recordingPath = @"C:\Ecoacoustics\WavFiles\BradLawData\FlyingFox\20190127_Bellingen_Feeding_SM4.wav";
string recordingPath = @"C:\Ecoacoustics\WavFiles\FlyingFox\20190115_Bellingen_Feeding.wav";
//string recordingPath = @"C:\Ecoacoustics\WavFiles\FlyingFox\20190115_Bellingen_Feeding.wav";
string recordingPath = @"C:\Ecoacoustics\WavFiles\FlyingFox\20190115_Bellingen_Feeding_minute6.wav";
//string recordingPath = @"C:\Ecoacoustics\WavFiles\FlyingFox\20190121_2_Bellingen_Feeding.wav";
//string recordingPath = @"C:\Ecoacoustics\WavFiles\FlyingFox\20190127_Bellingen_Feeding_SM4.wav";
string configPath = @"C:\Work\GitHub\audio-analysis\src\AnalysisConfigFiles\RecognizerConfigFiles\Towsey.PteropusSpecies.yml";
Expand Down
113 changes: 48 additions & 65 deletions src/AudioAnalysisTools/AcousticEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,16 @@ namespace AudioAnalysisTools
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Acoustics.Shared.Contracts;
using Acoustics.Shared.Csv;
using AForge.Imaging.Filters;
using AnalysisBase.ResultBases;
using AudioAnalysisTools.DSP;
using AudioAnalysisTools.StandardSpectrograms;
using CsvHelper.Configuration;
using DSP;
using StandardSpectrograms;
using TowseyLibrary;

public class AcousticEvent : EventBase
Expand Down Expand Up @@ -113,8 +110,8 @@ public void SetEventPositionRelative(
}

/// <summary>
/// Gets or sets units = Hertz
/// Proxied to EventBase.MinHz
/// Gets or sets units = Hertz.
/// Proxied to EventBase.MinHz.
/// </summary>
public new double LowFrequencyHertz
{
Expand Down Expand Up @@ -301,15 +298,12 @@ public void DoMelScale(bool doMelscale, int freqBinCount)

public void SetTimeAndFreqScales(int samplingRate, int windowSize, int windowOffset)
{
double frameDuration, frameOffset, framesPerSecond;
CalculateTimeScale(samplingRate, windowSize, windowOffset, out frameDuration, out frameOffset, out framesPerSecond);
CalculateTimeScale(samplingRate, windowSize, windowOffset, out var frameDuration, out var frameOffset, out var framesPerSecond);
this.FrameDuration = frameDuration; //frame duration in seconds
this.FrameOffset = frameOffset; //frame offset in seconds
this.FramesPerSecond = framesPerSecond; //inverse of the frame offset

int binCount;
double binWidth;
CalculateFreqScale(samplingRate, windowSize, out binCount, out binWidth);
CalculateFreqScale(samplingRate, windowSize, out var binCount, out var binWidth);
this.FreqBinCount = binCount; //required for conversions to & from MEL scale
this.FreqBinWidth = binWidth; //required for freq-binID conversions

Expand All @@ -319,11 +313,21 @@ public void SetTimeAndFreqScales(int samplingRate, int windowSize, int windowOff
}
}

/// <summary>
/// This method assumes that there is no frame overlap i.e. frame duration = frame offset.
/// </summary>
/// <param name="framesPerSec">frames per second assuming no overlap.</param>
/// <param name="freqBinWidth">Number of hertz per freq bin.</param>
public void SetTimeAndFreqScales(double framesPerSec, double freqBinWidth)
{
//this.FrameDuration = frameDuration; //frame duration in seconds
this.FramesPerSecond = framesPerSec; //inverse of the frame offset
this.FrameOffset = 1 / framesPerSec; //frame offset in seconds
double frameOffset = 1 / framesPerSec; //frame offset in seconds
this.SetTimeAndFreqScales(frameOffset, frameOffset, freqBinWidth);
}

public void SetTimeAndFreqScales(double frameOffset, double frameDuration, double freqBinWidth)
{
this.FramesPerSecond = 1 / frameOffset; //inverse of the frame offset
this.FrameDuration = frameDuration; //frame duration in seconds

//this.FreqBinCount = binCount; //required for conversions to & from MEL scale
this.FreqBinWidth = freqBinWidth; //required for freq-binID conversions
Expand All @@ -343,14 +347,10 @@ public void SetTimeAndFreqScales(double framesPerSec, double freqBinWidth)
public static Oblong ConvertEvent2Oblong(AcousticEvent ae)
{
// Translate time dimension = frames = matrix rows.
int topRow;
int bottomRow;
Time2RowIDs(ae.TimeStart, ae.EventDurationSeconds, ae.FrameOffset, out topRow, out bottomRow);
Time2RowIDs(ae.TimeStart, ae.EventDurationSeconds, ae.FrameOffset, out var topRow, out var bottomRow);

//Translate freq dimension = freq bins = matrix columns.
int leftCol;
int rightCol;
Freq2BinIDs(ae.IsMelscale, (int)ae.LowFrequencyHertz, (int)ae.HighFrequencyHertz, ae.FreqBinCount, ae.FreqBinWidth, out leftCol, out rightCol);
Freq2BinIDs(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 @@ -934,15 +934,6 @@ public static void CalculateAccuracy(List<AcousticEvent> results, List<AcousticE
/// This method is similar to the one above except that it is assumed that all the events, both labelled and predicted
/// come from the same recording.
/// </summary>
/// <param name="results"></param>
/// <param name="labels"></param>
/// <param name="tp"></param>
/// <param name="fp"></param>
/// <param name="fn"></param>
/// <param name="precision"></param>
/// <param name="recall"></param>
/// <param name="accuracy"></param>
/// <param name="resultsText"></param>
public static void CalculateAccuracyOnOneRecording(List<AcousticEvent> results, List<AcousticEvent> labels, out int tp, out int fp, out int fn,
out double precision, out double recall, out double accuracy, out string resultsText)
{
Expand Down Expand Up @@ -1041,8 +1032,6 @@ public static void CalculateAccuracyOnOneRecording(List<AcousticEvent> results,
/// <param name="scoreThreshold"></param>
/// <param name="minDuration">duration of event must exceed this to count as an event</param>
/// <param name="maxDuration"></param>
/// <param name="threshold">array value must exceed this dB threshold to count as an event</param>
/// <param name="fileName">name of source file to be added to AcousticEvent class</param>
/// <returns>a list of acoustic events</returns>
//public static List<AcousticEvent> ConvertIntensityArray2Events(double[] values, int minHz, int maxHz,
// double framesPerSec, double freqBinWidth,
Expand Down Expand Up @@ -1079,7 +1068,7 @@ public static List<AcousticEvent> ConvertIntensityArray2Events(
startFrame = i;
}
else //check for the end of an event
if (isHit == true && values[i] <= scoreThreshold) //this is end of an event, so initialise it
if (isHit && values[i] <= scoreThreshold) //this is end of an event, so initialise it
{
isHit = false;
double endTime = i * frameOffset;
Expand Down Expand Up @@ -1146,6 +1135,7 @@ public static List<AcousticEvent> GetEventsAroundMaxima(
// convert min an max Hertz durations to freq bins
int minBin = (int)Math.Round(minHz / freqBinWidth);
int maxBin = (int)Math.Round(maxHz / freqBinWidth);
int binCount = maxBin - minBin + 1;

// tried smoothing but not advisable since event onset can be very sudden
//values = DataTools.filterMovingAverageOdd(values, 3);
Expand Down Expand Up @@ -1191,22 +1181,23 @@ public static List<AcousticEvent> GetEventsAroundMaxima(

endFrame = i;

int frameDuration = endFrame - startFrame + 1;
if (frameDuration >= minFrames && frameDuration <= maxFrames)
int frameCount = endFrame - startFrame + 1;
if (frameCount >= minFrames && frameCount <= maxFrames)
{
double startTime = startFrame * frameOffset; // time in seconds
double eventDuration = frameDuration * frameOffset; // time in seconds
double eventDuration = frameCount * frameOffset; // time in seconds
AcousticEvent ev = new AcousticEvent(segmentStartOffset, startTime, eventDuration, minHz, maxHz)
{
Name = "Event", //default name
FrameCount = frameDuration,
FrameCount = frameCount,
FreqBinCount = binCount,
Oblong = new Oblong(startFrame, minBin, endFrame, maxBin),
};

ev.SetTimeAndFreqScales(framesPerSec, freqBinWidth);

//obtain average intensity score. Note-first frame is not actually in the event.
var subArray = DataTools.Subarray(values, startFrame + 1, frameDuration);
var subArray = DataTools.Subarray(values, startFrame + 1, frameCount);
ev.Score = subArray.Average();
events.Add(ev);
}
Expand All @@ -1225,18 +1216,15 @@ public static List<AcousticEvent> GetEventsAroundMaxima(
/// Some analysis techniques (e.g. OD) have their own methods for extracting events from score arrays.
/// </summary>
/// <param name="scores">the array of scores</param>
/// <param name="minHz">lower freq bound of the acoustic event</param>
/// <param name="maxHz">upper freq bound of the acoustic event</param>
/// <param name="framesPerSec">the time scale required by AcousticEvent class</param>
/// <param name="freqBinWidth">the freq scale required by AcousticEvent class</param>
/// <param name="scoreThreshold"></param>
/// <param name="minDuration">duration of event must exceed this to count as an event</param>
/// <param name="maxDuration">duration of event must be less than this to count as an event</param>
/// <param name="segmentStartOffset"></param>
/// <param name="threshold">score must exceed this threshold to count as an event</param>
/// <param name="fileName">name of source file to be added to AcousticEvent class</param>
/// <param name="callID"> name of the event to be added to AcousticEvent class</param>
/// <returns>a list of acoustic events</returns>
/// <param name="minHz">lower freq bound of the acoustic event.</param>
/// <param name="maxHz">upper freq bound of the acoustic event.</param>
/// <param name="framesPerSec">the time scale required by AcousticEvent class.</param>
/// <param name="freqBinWidth">the freq scale required by AcousticEvent class.</param>
/// <param name="scoreThreshold">threshold.</param>
/// <param name="minDuration">duration of event must exceed this to count as an event.</param>
/// <param name="maxDuration">duration of event must be less than this to count as an event.</param>
/// <param name="segmentStartOffset">offset.</param>
/// <returns>a list of acoustic events.</returns>
public static List<AcousticEvent> ConvertScoreArray2Events(
double[] scores,
int minHz,
Expand All @@ -1250,23 +1238,26 @@ public static List<AcousticEvent> ConvertScoreArray2Events(
{
int count = scores.Length;
var events = new List<AcousticEvent>();
double maxPossibleScore = 5 * scoreThreshold; // used to calcualte a normalised score bewteen 0 - 1.0
double maxPossibleScore = 5 * scoreThreshold; // used to calculate a normalised score between 0 - 1.0
bool isHit = false;
double frameOffset = 1 / framesPerSec; // frame offset in fractions of second
double startTime = 0.0;
int startFrame = 0;

for (int i = 0; i < count; i++) // pass over all frames
// pass over all frames
for (int i = 0; i < count; i++)
{
if (isHit == false && scores[i] >= scoreThreshold) //start of an event
if (isHit == false && scores[i] >= scoreThreshold)
{
//start of an event
isHit = true;
startTime = i * frameOffset;
startFrame = i;
}
else // check for the end of an event
if (isHit == true && scores[i] <= scoreThreshold) // this is end of an event, so initialise it
if (isHit && scores[i] <= scoreThreshold)
{
// this is end of an event, so initialise it
isHit = false;
double endTime = i * frameOffset;
double duration = endTime - startTime;
Expand Down Expand Up @@ -1326,10 +1317,6 @@ public static List<AcousticEvent> ConvertScoreArray2Events(
/// The events are required to have the passed name.
/// The events are assumed to contain sufficient info about frame rate in order to populate the array.
/// </summary>
/// <param name="events"></param>
/// <param name="arraySize"></param>
/// <param name="nameOfTargetEvent"></param>
/// <returns></returns>
public static double[] ExtractScoreArrayFromEvents(List<AcousticEvent> events, int arraySize, string nameOfTargetEvent)
{
double[] scores = new double[arraySize];
Expand All @@ -1341,7 +1328,7 @@ public static double[] ExtractScoreArrayFromEvents(List<AcousticEvent> events, i
double windowOffset = events[0].FrameOffset;
double frameRate = 1 / windowOffset; //frames per second

int count = events.Count;
//int count = events.Count;
foreach ( AcousticEvent ae in events)
{
if (!ae.Name.Equals(nameOfTargetEvent))
Expand All @@ -1359,18 +1346,14 @@ public static double[] ExtractScoreArrayFromEvents(List<AcousticEvent> events, i
}

return scores;
} //end method
}

//##############################################################################################################################################

/// <summary>
/// This method is used to do unit test on lists of events.
/// First developed for frog recognizers - October 2016.
/// </summary>
/// <param name="fileName"></param>
/// <param name="opDir"></param>
/// <param name="testName"></param>
/// <param name="events"></param>
public static void TestToCompareEvents(string fileName, DirectoryInfo opDir, string testName, List<AcousticEvent> events)
{
var testDir = new DirectoryInfo(opDir + $"\\UnitTest_{testName}");
Expand All @@ -1385,7 +1368,7 @@ public static void TestToCompareEvents(string fileName, DirectoryInfo opDir, str
var eventsFile = new FileInfo(eventsFilePath);
Csv.WriteToCsv<EventBase>(eventsFile, events);

LoggedConsole.WriteLine($"# EVENTS TEST: Camparing List of {testName} events with those in benchmark file:");
LoggedConsole.WriteLine($"# EVENTS TEST: Comparing List of {testName} events with those in benchmark file:");
var benchmarkFile = new FileInfo(benchmarkFilePath);
if (!benchmarkFile.Exists)
{
Expand Down
Loading

0 comments on commit 03a304d

Please sign in to comment.