Skip to content

Commit f1a5a9f

Browse files
towseyatruskie
authored andcommitted
Attempt to implement pulase train detection
Did not work so far.
1 parent 14a3214 commit f1a5a9f

File tree

4 files changed

+192
-18
lines changed

4 files changed

+192
-18
lines changed

src/AnalysisConfigFiles/RecognizerConfigFiles/Towsey.PteropusSpecies.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Profiles:
3333
MinHz: 200
3434
MaxHz: 2000
3535
# duration of DCT in seconds
36-
DctDuration: 1.0
36+
DctDuration: 0.8
3737
# minimum acceptable value of a DCT coefficient
3838
DctThreshold: 0.5
3939
# ignore oscillation rates below the min & above the max threshold
@@ -46,7 +46,7 @@ Profiles:
4646
MinDuration: 1.0
4747
MaxDuration: 10.0
4848
# Event threshold - use this to determine FP / FN trade-off for events.
49-
EventThreshold: 0.50
49+
EventThreshold: 0.60
5050
#Agonist:
5151
# This notation means the Groote profile has all of the settings that the Standard profile has,
5252
# however, the MinHz and MaxHz properties have been overridden.

src/AnalysisPrograms/Recognizers/PteropusSpecies.cs

+28-16
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// 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).
44
// </copyright>
55
// <summary>
6-
// This is a template recognizer for the Australian Flying Fox.
6+
// This is a recognizer for the Australian Flying Fox.
77
// Since there are several species, this project is started using only the generic name for Flying Foxes.
88

99
// Proposed algorithm has 8 steps
@@ -74,7 +74,7 @@ public override void SummariseResults(
7474
/// Do your analysis. This method is called once per segment (typically one-minute segments).
7575
/// </summary>
7676
/// <param name="audioRecording">one minute of audio recording.</param>
77-
/// <param name="configuration">config file.</param>
77+
/// <param name="genericConfig">config file that contains parameters used by all profiles.</param>
7878
/// <param name="segmentStartOffset">when recording starts.</param>
7979
/// <param name="getSpectralIndexes">not sure what this is.</param>
8080
/// <param name="outputDirectory">where the recogniser results can be found.</param>
@@ -123,18 +123,6 @@ public override RecognizerResults Recognize(AudioRecording audioRecording, Confi
123123
log.Warn("Could not access Wingbeats configuration parameters");
124124
}
125125

126-
//RecognizerResults combinedResults = null;
127-
128-
//// combine the results
129-
//return new RecognizerResults()
130-
//{
131-
// Events = results1.Events.AddRange(results2.Events),
132-
// Hits = null,
133-
// ScoreTrack = null,
134-
// Plots = plots,
135-
// Sonogram = sonogram,
136-
//};
137-
138126
// combine the results
139127
if (results1 != null && results2 != null)
140128
{
@@ -346,8 +334,6 @@ internal static RecognizerResults WingBeats(AudioRecording audioRecording, Confi
346334
double dctThreshold = profile.GetDoubleOrNull("DctThreshold") ?? 0.5;
347335
double minOscilFreq = profile.GetDoubleOrNull("MinOscilFreq") ?? 4.0;
348336
double maxOscilFreq = profile.GetDoubleOrNull("MaxOscilFreq") ?? 6.0;
349-
//var minTimeSpan = TimeSpan.FromSeconds(minDurationSeconds);
350-
//var maxTimeSpan = TimeSpan.FromSeconds(maxDurationSeconds);
351337
double eventThreshold = profile.GetDoubleOrNull("EventThreshold") ?? 0.3;
352338

353339
//######################
@@ -385,6 +371,32 @@ internal static RecognizerResults WingBeats(AudioRecording audioRecording, Confi
385371
out var hits,
386372
segmentStartOffset);
387373

374+
/*
375+
// Look for wing beats using pulse train detector
376+
double pulsesPerSecond = 5.1;
377+
var scores = PulseTrain.GetPulseTrainScore(decibelArray, pulsesPerSecond, sonogram.FramesPerSecond, 1.0);
378+
379+
//iii: CONVERT Pulse Train SCORES TO ACOUSTIC EVENTS
380+
double pulseTrainThreshold = 0.5;
381+
var minTimeSpan = TimeSpan.FromSeconds(minDurationSeconds);
382+
var maxTimeSpan = TimeSpan.FromSeconds(maxDurationSeconds);
383+
var acousticEvents = AcousticEvent.GetEventsAroundMaxima(
384+
scores,
385+
segmentStartOffset,
386+
minHz,
387+
maxHz,
388+
pulseTrainThreshold,
389+
minTimeSpan,
390+
maxTimeSpan,
391+
sonogram.FramesPerSecond,
392+
sonogram.FBinWidth
393+
);
394+
395+
double scoreThreshold = 0.5;
396+
var normalisedScoreArray = DataTools.NormaliseInZeroOne(scores, 0, 1.0);
397+
var plot2 = new Plot(speciesName + " Wingbeat Pulse-train Score", normalisedScoreArray, scoreThreshold);
398+
*/
399+
388400
// prepare plots
389401
double decibelThreshold = 12.0;
390402
double intensityNormalisationMax = 3 * decibelThreshold;

src/TowseyLibrary/PulseTrain.cs

+161
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// --------------------------------------------------------------------------------------------------------------------
2+
// <copyright file="PulseTrain.cs" company="QutEcoacoustics">
3+
// 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).
4+
// </copyright>
5+
// <summary>
6+
// This class contains methods to recognise pulse trains.
7+
// It is an alternative to using the Oscillations class.
8+
9+
10+
using System;
11+
using System.Collections.Generic;
12+
using System.Linq;
13+
using System.Text;
14+
using System.Threading.Tasks;
15+
16+
namespace TowseyLibrary
17+
{
18+
using Acoustics.Shared.ConfigFile;
19+
using MathNet.Numerics.LinearAlgebra.Solvers;
20+
21+
public static class PulseTrain
22+
{
23+
/// <summary>
24+
/// This method creates a template to recognise two pulses that are possibly part of a pulse train.
25+
/// The template is designed to detect pulse trains of at least 2 pulses!
26+
/// The template is bounded either end by silence and then a pulse. i.e. .:|:. ... .:|:. where .=zero or a negative residual value, := 0.5 and |= 1.0.
27+
/// Any number of residual values may separate the pulses at either end. In this method, templates are created with 6 non-zero values and the remainder are negative.
28+
/// The sum of the positive values = 4.0.
29+
/// The sum of the values in the template should = zero.
30+
/// Designed this way, the minimum pulse length is about 4 or 5 and the minimum template length is about 10;
31+
/// </summary>
32+
/// <param name="pulseLength">length or number of frames between two pulses.</param>
33+
/// <returns>the template.</returns>
34+
public static double[] GetPulseTrainTemplate(int pulseLength)
35+
{
36+
int templateLength = pulseLength + 5;
37+
double residual = 4 / (double)(templateLength - 6);
38+
39+
var template = new double[templateLength];
40+
template[0] = -residual;
41+
template[1] = 0.5;
42+
template[2] = 1.0;
43+
template[3] = 0.5;
44+
for (int i = 4; i < templateLength - 4; i++)
45+
{
46+
template[i] = -residual;
47+
}
48+
49+
template[templateLength - 4] = 0.5;
50+
template[templateLength - 3] = 1.0;
51+
template[templateLength - 2] = 0.5;
52+
template[templateLength - 1] = -residual;
53+
template = DataTools.normalise2UnitLength(template);
54+
return template;
55+
}
56+
57+
/// <summary>
58+
/// Only three pulses included in the single template output by this method.
59+
/// Will generalise if it seems worthwhile.
60+
/// </summary>
61+
public static double[] GetPulseTrainTemplate(int pulseLength, int pulseCount)
62+
{
63+
int templateLength = (pulseLength * pulseCount) + 5;
64+
var template = new double[templateLength];
65+
int templateHalfLength = templateLength / 2;
66+
67+
for (int i = 0; i < templateLength; i++)
68+
{
69+
template[i] = -1.0;
70+
}
71+
72+
template[1] = 0.3;
73+
template[2] = 1.0;
74+
template[3] = 0.3;
75+
76+
template[templateHalfLength - 1] = 0.3;
77+
template[templateHalfLength] = 1.0;
78+
template[templateHalfLength + 1] = 0.3;
79+
80+
template[templateLength - 4] = 0.3;
81+
template[templateLength - 3] = 1.0;
82+
template[templateLength - 2] = 0.3;
83+
//template = DataTools.normalise2UnitLength(template);
84+
return template;
85+
}
86+
87+
/// <summary>
88+
/// returns the length of a pulse interval in frames given pulses and frame rates in seconds.
89+
/// </summary>
90+
/// <param name="pulsesPerSecond">number of pulses per second.</param>
91+
/// <param name="framesPerSecond">frames per second - i.e. assuming the application is applied to a sequence of spectral frames.</param>
92+
/// <returns>the template.</returns>
93+
public static double[] GetPulseTrainTemplate(double pulsesPerSecond, double framesPerSecond)
94+
{
95+
int frameCount = (int)Math.Round(framesPerSecond / pulsesPerSecond);
96+
return GetPulseTrainTemplate(frameCount);
97+
}
98+
99+
public static double[] GetPulseTrainScore(double[] signal, double pulsesPerSecond, double framesPerSecond, double thresholdValue)
100+
{
101+
int pulseCount = 2;
102+
int frameCount = (int)Math.Round(framesPerSecond / pulsesPerSecond);
103+
var templates = new List<double[]>
104+
{
105+
GetPulseTrainTemplate(frameCount, pulseCount),
106+
GetPulseTrainTemplate(frameCount - 1, pulseCount),
107+
GetPulseTrainTemplate(frameCount + 1, pulseCount),
108+
};
109+
int signalLength = signal.Length;
110+
111+
var scores = new double[signalLength];
112+
113+
for (int i = 2; i < signalLength - templates[2].Length; i++)
114+
{
115+
// skip if value is below threshold
116+
if (signal[i] < thresholdValue)
117+
{
118+
continue;
119+
}
120+
121+
// skip if value is not maximum
122+
if (signal[i] < signal[i - 1] || signal[i] < signal[i + 1])
123+
{
124+
continue;
125+
}
126+
127+
// get Cosine similarity for each of three templates.
128+
var templateScores = new double[3];
129+
130+
// get the local nh of signal for template 0 and get score
131+
var nh = DataTools.Subarray(signal, i, templates[0].Length);
132+
nh = DataTools.normalise2UnitLength(nh);
133+
templateScores[0] = DataTools.DotProduct(nh, templates[0]);
134+
135+
// get the local nh of signal for template 1
136+
nh = DataTools.Subarray(signal, i, templates[1].Length);
137+
nh = DataTools.normalise2UnitLength(nh);
138+
templateScores[1] = DataTools.DotProduct(nh, templates[1]);
139+
140+
// get the local nh of signal for template 2
141+
nh = DataTools.Subarray(signal, i, templates[2].Length);
142+
nh = DataTools.normalise2UnitLength(nh);
143+
templateScores[2] = DataTools.DotProduct(nh, templates[2]);
144+
145+
double maxScore = templateScores.Max();
146+
if (maxScore > 0.0)
147+
{
148+
for (int j = 0; j < templates[0].Length - 1; j++)
149+
{
150+
if (maxScore > scores[i + j])
151+
{
152+
scores[i + j] = maxScore;
153+
}
154+
}
155+
}
156+
}
157+
158+
return scores;
159+
}
160+
}
161+
}

src/TowseyLibrary/TowseyLibrary.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@
140140
<Compile Include="HoughTransform.cs" />
141141
<Compile Include="Matrix3D.cs" />
142142
<Compile Include="OtsuThresholder.cs" />
143+
<Compile Include="PulseTrain.cs" />
143144
<Compile Include="StructureTensor.cs" />
144145
<Compile Include="TemporalMatrix.cs" />
145146
<Compile Include="TernaryPlots.cs" />

0 commit comments

Comments
 (0)