From 0132e83ac813e146e934f40303a78ce6cbb1e772 Mon Sep 17 00:00:00 2001 From: towsey Date: Thu, 19 Sep 2019 21:15:04 +1000 Subject: [PATCH] Issue #252 Set up new data structures for content description Issue #252 Set up new data structures for content description based on discussion with Anthony. Separate out main template information into yaml files. --- .../ContentDescriptionTemplateManifest.yml | 111 ++++++++++++++++ .../ContentDescriptionTemplates.yml | 86 +++++++++++++ src/AnalysisPrograms/Sandpit.cs | 69 +++++----- .../AudioAnalysisTools.csproj | 3 + .../ContentAlgorithms.cs | 31 +++++ .../ContentDescription.cs | 69 +++++++--- .../ContentTemplate.cs | 119 ++++++++++++++++++ .../ContentDescriptionTools/DataProcessing.cs | 33 +++++ .../TemplateCollection.cs | 89 +++++++++++++ 9 files changed, 563 insertions(+), 47 deletions(-) create mode 100644 src/AnalysisConfigFiles/ContentDescriptionTemplateManifest.yml create mode 100644 src/AnalysisConfigFiles/ContentDescriptionTemplates.yml create mode 100644 src/AudioAnalysisTools/ContentDescriptionTools/ContentAlgorithms.cs create mode 100644 src/AudioAnalysisTools/ContentDescriptionTools/ContentTemplate.cs create mode 100644 src/AudioAnalysisTools/ContentDescriptionTools/TemplateCollection.cs diff --git a/src/AnalysisConfigFiles/ContentDescriptionTemplateManifest.yml b/src/AnalysisConfigFiles/ContentDescriptionTemplateManifest.yml new file mode 100644 index 000000000..88fbca2db --- /dev/null +++ b/src/AnalysisConfigFiles/ContentDescriptionTemplateManifest.yml @@ -0,0 +1,111 @@ +--- +# WARNING!!! The keys (i.e. root name of each object below) MUST BE EXACTLY the SAME as those +# declared in the class InitialiseIndexProperties (starting at line 30). +# The root object name maps to the Key property on the IndexProperties class +# and to the members in the class SummaryIndexValues:SummaryIndexBase + +RainLight1: + TemplateId: 0 + Name: "Light Rain 1" + Comment: "Detects light rain." + SourceFileName: "SM304256_0+1_20151114_071652" + # Status can be "locked", etc + Status: "locked" + RecordingLocation: "Mezzanine, Tasman Is, Tasmania" + RecordingDateTime: "2015-11-14 07:16:52 +1100" + FeatureExtractionAlgorithm: 1 + # Temporal Selection - minutes are inclusive" + StartMinute: 32 + EndMinute: 36 + TemporalReduction: "Average" + #Spectral Processing + SpectralReductionFactor: 16 + # Frequency band selection + BandMinHz: + BandMaxHz: +WindStrong1: + TemplateId: 01 + Name: Strong Wind 1 + Comment: "Detects strong wind." + SourceFileName: "SM304256_0+1_20151114_011652" + # Status can be "locked", etc + Status: "locked" + RecordingLocation: "Mezzanine, Tasman Is, Tasmania" + RecordingDateTime: "2015-11-14 01:16:52 +1100" + FeatureExtractionAlgorithm: 1 + # Temporal Selection - minutes are inclusive" + StartMinute: 2 + EndMinute: 27 + TemporalReduction: "Average" + #Spectral Processing + SpectralReductionFactor: 16 + # Frequency band selection + BandMinHz: + BandMaxHz: +WindLight1: + TemplateId: 02 + Name: Light Wind 1 + Comment: "Detects light wind." + SourceFileName: "SM304256_0+1_20151114_021652" + # Status can be "locked", etc + Status: "locked" + RecordingLocation: "Mezzanine, Tasman Is, Tasmania" + RecordingDateTime: "2015-11-14 02:16:52 +1100" + # NOTE: Remove the ENT index before applying this template + FeatureExtractionAlgorithm: 1 + # Temporal Selection - minutes are inclusive" + StartMinute: 50 + EndMinute: 53 + TemporalReduction: "Average" + #Spectral Processing + SpectralReductionFactor: 16 + # Frequency band selection + BandMinHz: + BandMaxHz: +BirdChorus1: + TemplateId: 03 + Name: Bird Chorus + Comment: "Detects chorus of bird song." + SourceFileName: "SM304256_0+1_20151114_041652" + # Status can be "locked", etc + Status: "locked" + RecordingLocation: "Mezzanine, Tasman Is, Tasmania" + RecordingDateTime: "2015-11-14 04:16:52 +1100" + # Only want the interval 2-8 kHz for bird morning chorus. + # After reducing 256 freq bins to 16, each bin has width 689Hz. + # Therefore to get band 2-8 kHz, need to remove the bottom two freq bins and the top four freq bins. + # This leaves a template with 10 freq bins. + FeatureExtractionAlgorithm: 1 + # Temporal Selection - minutes are inclusive" + StartMinute: 22 + EndMinute: 23 + TemporalReduction: "Average" + #Spectral Processing + SpectralReductionFactor: 16 + # Frequency band selection - select freq bins 2 to 11 + BandMinHz: 2000 + BandMaxHz: 8000 +SilverEye: + TemplateId: 04 + Name: Silver Eye + Comment: "Detects Silver Eye song in a Tasman Island recording." + SourceFileName: "SM304256_0+1_20151114_071652" + # Status can be "locked", etc + Status: "locked" + RecordingLocation: "Mezzanine, Tasman Is, Tasmania" + RecordingDateTime: "2015-11-14 07:16:52 +1100" + # Only want the interval 3-4 kHz for Silver-eye band. + # After reducing 256 freq bins to 16 bins, each bin has width 689Hz. + # Therefore to get band 3-4 kHz, this leaves a template with 2 or 3 freq bins which are then averaged. + # So each index ends up with only one value. + FeatureExtractionAlgorithm: 1 + # Temporal Selection - minutes are inclusive" + StartMinute: 6 + EndMinute: 11 + TemporalReduction: "Average" + #Spectral Processing + SpectralReductionFactor: 16 + # Frequency band selection + BandMinHz: 3000 + BandMaxHz: 4000 + diff --git a/src/AnalysisConfigFiles/ContentDescriptionTemplates.yml b/src/AnalysisConfigFiles/ContentDescriptionTemplates.yml new file mode 100644 index 000000000..c3618330e --- /dev/null +++ b/src/AnalysisConfigFiles/ContentDescriptionTemplates.yml @@ -0,0 +1,86 @@ +--- +RainLight1: + TemplateId: 0 + Name: "Light Rain 1" + Status: "locked" + Template: + ACI: [0.076, 0.046, 0.167, 0.360, 0.426, 0.443, 0.545, 0.595, 0.564, 0.612, 0.659, 0.570, 0.542, 0.520, 0.485, 0.485] + ENT: [0.065, 0.061, 0.176, 0.289, 0.249, 0.255, 0.296, 0.292, 0.262, 0.386, 0.462, 0.262, 0.222, 0.243, 0.217, 0.205] + EVN: [0.136, 0.009, 0.022, 0.051, 0.072, 0.092, 0.109, 0.150, 0.175, 0.176, 0.193, 0.155, 0.171, 0.135, 0.109, 0.133] + BGN: [0.366, 0.249, 0.181, 0.148, 0.122, 0.111, 0.106, 0.105, 0.104, 0.111, 0.111, 0.111, 0.105, 0.100, 0.090, 0.048] + PMN: [0.182, 0.076, 0.243, 0.459, 0.470, 0.501, 0.592, 0.651, 0.625, 0.699, 0.792, 0.599, 0.572, 0.550, 0.490, 0.488] + FeatureExtractionAlgorithm: 1 + StartMinute: 32 + EndMinute: 36 + TemporalReduction: "Average" + SpectralReductionFactor: 16 + BandMinHz: + BandMaxHz: +WindStrong1: + TemplateId: 01 + Name: Strong Wind 1 + Status: "locked" + Template: + ACI: [0.086, 0.043, 0.041, 0.023, 0.032, 0.027, 0.029, 0.031, 0.032, 0.032, 0.034, 0.069, 0.033, 0.024, 0.018, 0.018] + ENT: [0.124, 0.112, 0.146, 0.163, 0.157, 0.157, 0.143, 0.122, 0.113, 0.095, 0.087, 0.121, 0.075, 0.060, 0.054, 0.067] + EVN: [0.376, 0.440, 0.590, 0.621, 0.648, 0.621, 0.565, 0.363, 0.273, 0.191, 0.164, 0.221, 0.104, 0.040, 0.017, 0.032] + BGN: [0.472, 0.360, 0.273, 0.199, 0.156, 0.121, 0.096, 0.085, 0.075, 0.069, 0.064, 0.061, 0.060, 0.058, 0.054, 0.026] + PMN: [0.468, 0.507, 0.687, 0.743, 0.757, 0.751, 0.665, 0.478, 0.391, 0.317, 0.276, 0.367, 0.187, 0.109, 0.071, 0.096] + FeatureExtractionAlgorithm: 1 + StartMinute: 2 + EndMinute: 27 + TemporalReduction: "Average" + SpectralReductionFactor: 16 + BandMinHz: + BandMaxHz: +WindLight1: + TemplateId: 02 + Name: Light Wind 1 + Status: "locked" + Template: + ACI: [0.072, 0.035, 0.015, 0.008, 0.008, 0.009, 0.016, 0.018, 0.017, 0.015, 0.020, 0.022, 0.029, 0.026, 0.027, 0.098] + ENT: [0.063, 0.069, 0.071, 0.059, 0.052, 0.051, 0.050, 0.050, 0.050, 0.050, 0.050, 0.052, 0.051, 0.050, 0.051, 0.058] + EVN: [0.160, 0.099, 0.077, 0.023, 0.009, 0.009, 0.005, 0.004, 0.003, 0.004, 0.003, 0.006, 0.002, 0.004, 0.003, 0.015] + BGN: [0.387, 0.257, 0.151, 0.094, 0.069, 0.055, 0.049, 0.045, 0.042, 0.041, 0.041, 0.042, 0.043, 0.044, 0.043, 0.017] + PMN: [0.228, 0.199, 0.179, 0.087, 0.065, 0.048, 0.042, 0.034, 0.035, 0.035, 0.036, 0.038, 0.038, 0.037, 0.044, 0.081] + FeatureExtractionAlgorithm: 1 + StartMinute: 50 + EndMinute: 53 + TemporalReduction: "Average" + SpectralReductionFactor: 16 + BandMinHz: + BandMaxHz: +BirdChorus1: + TemplateId: 03 + Name: Bird Chorus + Status: "locked" + Template: + ACI: [0.274, 0.366, 0.591, 0.820, 0.997, 0.975, 0.796, 0.846, 0.605, 0.260] + ENT: [0.293, 0.415, 0.804, 0.972, 0.910, 0.876, 0.923, 0.971, 0.840, 0.491] + EVN: [0.445, 0.691, 0.291, 0.266, 0.407, 0.417, 0.306, 0.321, 0.199, 0.091] + BGN: [0.140, 0.099, 0.072, 0.059, 0.055, 0.051, 0.048, 0.048, 0.045, 0.042] + PMN: [0.671, 0.967, 0.924, 0.998, 1.000, 1.000, 0.998, 1.000, 0.952, 0.633] + FeatureExtractionAlgorithm: 10 + StartMinute: 22 + EndMinute: 23 + TemporalReduction: "Average" + SpectralReductionFactor: 16 + BandMinHz: 2000 + BandMaxHz: 8000 +SilverEye: + TemplateId: 04 + Name: Silver Eye + Status: "locked" + Template: + ACI: [0.779] + ENT: [0.393] + EVN: [0.686] + BGN: [0.085] + PMN: [0.883] + FeatureExtractionAlgorithm: 10 + StartMinute: 6 + EndMinute: 11 + TemporalReduction: "Average" + SpectralReductionFactor: 16 + BandMinHz: 3000 + BandMaxHz: 4000 \ No newline at end of file diff --git a/src/AnalysisPrograms/Sandpit.cs b/src/AnalysisPrograms/Sandpit.cs index a6cdfcc92..12c249d56 100644 --- a/src/AnalysisPrograms/Sandpit.cs +++ b/src/AnalysisPrograms/Sandpit.cs @@ -64,8 +64,8 @@ public override Task Execute(CommandLineApplication app) Log.WriteLine("# Start Time = " + tStart.ToString(CultureInfo.InvariantCulture)); // CONTENT DESCRIPTION - //ReadSpectralIndicesFromTwoFalseColourSpectrogramRibbons(); - ContentDescriptionDev(); + //ContentDescriptionCreateTemplates(); + ContentDescriptionApplyTemplates(); //AnalyseFrogDataSet(); //Audio2CsvOverOneFile(); @@ -119,7 +119,35 @@ public override Task Execute(CommandLineApplication app) } } - public static void ContentDescriptionDev() + public static void ContentDescriptionCreateTemplates() + { + //PREPARE STRONG WIND TEMPLATE + //var ipDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\Test\Test24HourRecording\TasmanIslandMez\Mez02\Towsey.Acoustic"); + //var opDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\ContentDescription"); + //WindStrong1.WriteTemplateToFile(ipDir, opDir); + + //PREPARE LIGHT WIND TEMPLATE + //var ipDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\Test\Test24HourRecording\TasmanIslandMez\Mez03\Towsey.Acoustic"); + //var opDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\ContentDescription"); + //WindLight1.WriteTemplateToFile(ipDir, opDir); + + //PREPARE LIGHT RAIN TEMPLATE + //var ipDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\Test\Test24HourRecording\TasmanIslandMez\Mez08\Towsey.Acoustic"); + //var opDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\ContentDescription"); + //WindLight1.WriteTemplateToFile(ipDir, opDir); + + //PREPARE BIRD MORNING CHORUS1 TEMPLATE + //var ipDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\Test\Test24HourRecording\TasmanIslandMez\Mez05\Towsey.Acoustic"); + //var opDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\ContentDescription"); + //BirdMorningChorus1.WriteTemplateToFile(ipDir, opDir); + + //PREPARE MEZZANINE-TASMAN ISLAND SILVER-EYE TEMPLATE + //var ipDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\Test\Test24HourRecording\TasmanIslandMez\Mez08\Towsey.Acoustic"); + //var opDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\ContentDescription"); + //SilverEyeMezTasmanIs.WriteTemplateToFile(ipDir, opDir); + } + + public static void ContentDescriptionApplyTemplates() { DirectoryInfo[] directories = { @@ -149,7 +177,9 @@ public static void ContentDescriptionDev() new DirectoryInfo(@"C:\Ecoacoustics\Output\Test\Test24HourRecording\TasmanIslandMez\Mez24\Towsey.Acoustic"), }; - string[] baseNames = { "SM304256_0+1_20151114_001652", + string[] baseNames = + { + "SM304256_0+1_20151114_001652", "SM304256_0+1_20151114_011652", "SM304256_0+1_20151114_021652", "SM304256_0+1_20151114_031652", @@ -173,34 +203,11 @@ public static void ContentDescriptionDev() "SM304256_0+1_20151114_211652", "SM304256_0+1_20151114_221652", "SM304256_0+1_20151114_231652", -}; - - //PREPARE STRONG WIND TEMPLATE - //var ipDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\Test\Test24HourRecording\TasmanIslandMez\Mez02\Towsey.Acoustic"); - //var opDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\ContentDescription"); - //WindStrong1.WriteTemplateToFile(ipDir, opDir); - - //PREPARE LIGHT WIND TEMPLATE - //var ipDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\Test\Test24HourRecording\TasmanIslandMez\Mez03\Towsey.Acoustic"); - //var opDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\ContentDescription"); - //WindLight1.WriteTemplateToFile(ipDir, opDir); - - //PREPARE LIGHT RAIN TEMPLATE - //var ipDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\Test\Test24HourRecording\TasmanIslandMez\Mez08\Towsey.Acoustic"); - //var opDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\ContentDescription"); - //WindLight1.WriteTemplateToFile(ipDir, opDir); - - //PREPARE BIRD MORNING CHORUS1 TEMPLATE - //var ipDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\Test\Test24HourRecording\TasmanIslandMez\Mez05\Towsey.Acoustic"); - //var opDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\ContentDescription"); - //BirdMorningChorus1.WriteTemplateToFile(ipDir, opDir); + }; - //PREPARE MEZZANINE-TASMAN ISLAND SILVER-EYE TEMPLATE - //var ipDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\Test\Test24HourRecording\TasmanIslandMez\Mez08\Towsey.Acoustic"); - //var opDir = new DirectoryInfo(@"C:\Ecoacoustics\Output\ContentDescription"); - //SilverEyeMezTasmanIs.WriteTemplateToFile(ipDir, opDir); + var templatesConfig = new FileInfo(@"C:\Work\GitHub\audio-analysis\src\AnalysisConfigFiles\ContentDescriptionTemplates.yml"); - var contentPlots = ContentDescription.ContentDescriptionOfMultipleRecordingFiles(directories, baseNames); + var contentPlots = ContentDescription.ContentDescriptionOfMultipleRecordingFiles(directories, baseNames, templatesConfig); // Attach content description plots to LDFC spectrogram var path = Path.Combine(@"C:\Ecoacoustics\Output\Test\Test24HourRecording", "Testing__2Maps.png"); @@ -208,7 +215,7 @@ public static void ContentDescriptionDev() //Write image + contentPlots to file. var image = ContentVisualization.DrawLdfcSpectrogramWithContentScoreTracks(ldfcSpectrogram, contentPlots); - var path1 = Path.Combine(@"C:\Ecoacoustics\Output\ContentDescription", "Testing_2Maps.CONTENT10.png"); + var path1 = Path.Combine(@"C:\Ecoacoustics\Output\ContentDescription", "Testing_2Maps.CONTENTnew01.png"); image.Save(path1); } diff --git a/src/AudioAnalysisTools/AudioAnalysisTools.csproj b/src/AudioAnalysisTools/AudioAnalysisTools.csproj index 89a027af0..af0cfd3d5 100644 --- a/src/AudioAnalysisTools/AudioAnalysisTools.csproj +++ b/src/AudioAnalysisTools/AudioAnalysisTools.csproj @@ -241,6 +241,7 @@ + @@ -252,6 +253,8 @@ + + diff --git a/src/AudioAnalysisTools/ContentDescriptionTools/ContentAlgorithms.cs b/src/AudioAnalysisTools/ContentDescriptionTools/ContentAlgorithms.cs new file mode 100644 index 000000000..5a9df0762 --- /dev/null +++ b/src/AudioAnalysisTools/ContentDescriptionTools/ContentAlgorithms.cs @@ -0,0 +1,31 @@ +// +// 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). +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AudioAnalysisTools.ContentDescriptionTools +{ + using TowseyLibrary; + + public static class ContentAlgorithms + { + public static double GetContent1(Dictionary oneMinuteOfIndices, ContentTemplate template, Dictionary templateIndices) + { + var reductionFactor = template.SpectralReductionFactor; + var reducedIndices = DataProcessing.ReduceIndicesByFactor(oneMinuteOfIndices, reductionFactor); + var oneMinuteVector = DataProcessing.ConvertDictionaryToVector(reducedIndices); + var templateVector = DataProcessing.ConvertDictionaryToVector(templateIndices); + + var distance = DataTools.EuclideanDistance(templateVector, oneMinuteVector); + + //normalise the distance + distance /= Math.Sqrt(templateVector.Length); + return 1 - distance; + } + } +} diff --git a/src/AudioAnalysisTools/ContentDescriptionTools/ContentDescription.cs b/src/AudioAnalysisTools/ContentDescriptionTools/ContentDescription.cs index 7bbb9f9ae..7bb978d40 100644 --- a/src/AudioAnalysisTools/ContentDescriptionTools/ContentDescription.cs +++ b/src/AudioAnalysisTools/ContentDescriptionTools/ContentDescription.cs @@ -4,8 +4,11 @@ namespace AudioAnalysisTools.ContentDescriptionTools { + using System; + using System.Collections; using System.Collections.Generic; using System.IO; + using Acoustics.Shared.ConfigFile; using AudioAnalysisTools.ContentDescriptionTools.ContentTypes; using TowseyLibrary; @@ -29,8 +32,12 @@ public class ContentDescription public static string[] IndexNames { get; } = { "ACI", "ENT", "EVN", "BGN", "PMN" }; - public static List ContentDescriptionOfMultipleRecordingFiles(DirectoryInfo[] directories, string[] baseNames) + public static List ContentDescriptionOfMultipleRecordingFiles(DirectoryInfo[] directories, string[] baseNames, FileInfo templatesConfig) { + // Read in all content templates + var templateCollection = ConfigFile.Deserialize(templatesConfig); + var templatesAsDictionary = DataProcessing.ExtractDictionaryOfTemplateDictionaries(templateCollection); + // init a list to collect description results var completeListOfResults = new List(); @@ -39,14 +46,14 @@ public static List ContentDescriptionOfMultipleRecordingFiles(DirectoryInf for (int i = 0; i < directories.Length; i++) { // read the spectral indices for the current file - var dictionary = DataProcessing.ReadIndexMatrices(directories[i], baseNames[i]); + var dictionaryOfRecordingIndices = DataProcessing.ReadIndexMatrices(directories[i], baseNames[i]); // Draw the index matrices for check/debug purposes // var dir1 = new DirectoryInfo(@"C:\Ecoacoustics\Output\ContentDescription"); // ContentDescription.DrawNormalisedIndexMatrices(dir1, baseName, dictionary); // get the rows and do something with them one by one. - var results = AnalyseMinutes(dictionary, i * 60); // WARNING: HACK: ASSUME ONE HOUR FILES + var results = AnalyseMinutes(templateCollection, templatesAsDictionary, dictionaryOfRecordingIndices, i * 60); // WARNING: HACK: ASSUME ONE HOUR FILES completeListOfResults.AddRange(results); } @@ -59,29 +66,59 @@ public static List ContentDescriptionOfMultipleRecordingFiles(DirectoryInf return contentPlots; } - public static List AnalyseMinutes(Dictionary dictionary, int elapsedMinutes) + public static List AnalyseMinutes( + TemplateCollection templateCollection, + Dictionary> templatesAsDictionary, + Dictionary dictionaryOfRecordingIndices, + int elapsedMinutes) { - int rowCount = dictionary[IndexNames[0]].GetLength(0); + int rowCount = dictionaryOfRecordingIndices[IndexNames[0]].GetLength(0); //int freqBinCount = dictionary[ContentDescription.IndexNames[0]].GetLength(1); + var rn = new RandomNumber(DateTime.Now.Millisecond); + var results = new List(); // over all rows assuming one minute per row. for (int i = 0; i < rowCount; i++) { - var oneMinuteOfIndices = DataProcessing.GetIndicesForOneMinute(dictionary, i); - var descriptionResult = new DescriptionResult(elapsedMinutes + i); + var oneMinuteOfIndices = DataProcessing.GetIndicesForOneMinute(dictionaryOfRecordingIndices, i); - // now send indices to various content searches - descriptionResult.AddDescription(WindStrong1.GetContent(oneMinuteOfIndices)); - descriptionResult.AddDescription(WindLight1.GetContent(oneMinuteOfIndices)); - descriptionResult.AddDescription(RainLight1.GetContent(oneMinuteOfIndices)); - descriptionResult.AddDescription(BirdMorningChorus1.GetContent(oneMinuteOfIndices)); - descriptionResult.AddDescription(SilverEyeMezTasmanIs.GetContent(oneMinuteOfIndices)); + // initialise where the results will be stored. + var descriptionResult = new DescriptionResult(elapsedMinutes + i); - // yet to do following - //descriptionResult.AddDescription(RainHeavy1.GetContent(oneMinuteOfIndices)); - //descriptionResult.AddDescription(RainHeavy2.GetContent(oneMinuteOfIndices)); + // now subject the indices to various content searches + foreach (var kvp in templateCollection) + { + var key = kvp.Key; + var template = kvp.Value; + var algorithmType = template.FeatureExtractionAlgorithm; + var templateIndices = templatesAsDictionary[key]; + double score = 0.0; + + switch (algorithmType) + { + case 1: + score = ContentAlgorithms.GetContent1(oneMinuteOfIndices, template, templateIndices); + break; + case 2: + score = ContentAlgorithms.GetContent1(oneMinuteOfIndices, template, templateIndices); + break; + default: + //LoggedConsole.WriteWarnLine("Algorithm " + algorithmType + " does not exist."); + score = rn.GetDouble(); + break; + } + + var result = new KeyValuePair(key, score); + descriptionResult.AddDescription(result); + + //descriptionResult.AddDescription(WindStrong1.GetContent(oneMinuteOfIndices)); + //descriptionResult.AddDescription(WindLight1.GetContent(oneMinuteOfIndices)); + //descriptionResult.AddDescription(RainLight1.GetContent(oneMinuteOfIndices)); + //descriptionResult.AddDescription(BirdMorningChorus1.GetContent(oneMinuteOfIndices)); + //descriptionResult.AddDescription(SilverEyeMezTasmanIs.GetContent(oneMinuteOfIndices)); + } results.Add(descriptionResult); } diff --git a/src/AudioAnalysisTools/ContentDescriptionTools/ContentTemplate.cs b/src/AudioAnalysisTools/ContentDescriptionTools/ContentTemplate.cs new file mode 100644 index 000000000..a1433a9a9 --- /dev/null +++ b/src/AudioAnalysisTools/ContentDescriptionTools/ContentTemplate.cs @@ -0,0 +1,119 @@ +// +// 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.ContentDescriptionTools +{ + using System.Collections.Generic; + using System.IO; + using Acoustics.Shared.ConfigFile; + using AudioAnalysisTools.Indices; + using YamlDotNet.Serialization; + + public enum TemplateStatus + { + None, + Standard, + Locked, + } + + public class ContentTemplate + { + //TEMPLATE DESCRIPTION + // Name of the template + public string Name { get; set; } + + public int TemplateId { get; set; } + + public TemplateStatus Status { get; set; } + + public byte FeatureExtractionAlgorithm { get; set; } + + /// + /// Gets or sets the factor by which a spectrum of index values is reduced. + /// Full array (256 freq bins) of spectral indices is reduced by the following factor by averaging. + /// This is to reduce correlation and computation. + /// + public int SpectralReductionFactor { get; set; } + + /// + /// Gets or sets the bottom freq of bandpass filter. + /// Bandpass filter to be applied where the target content exists only within a narrow band, e.g. 3-4 kHz for Silver-eye band. + /// Bottom of the required frequency band. + /// + public int BandMinHz { get; set; } + + /// + /// Gets or sets the top freq of bandpass filter. + /// Bandpass filter to be applied where the target content exists only within a narrow band, e.g. 3-4 kHz for Silver-eye band. + /// Top of the required frequency band. + /// + public int BandMaxHz { get; set; } + + public int FrameSize { get; set; } + + //private int FreqBinCount = FrameSize / ContentTemplate.SpectralReductionFactor; + + public double[] ACI { get; set; } + + public double[] ENT { get; set; } + + public double[] EVN { get; set; } + + public double[] BGN { get; set; } + + public double[] PMN { get; set; } + + public Dictionary Template { get; set; } + + /// + /// Returns a cached set of configuration properties. + /// WARNING CACHED!. + /// + public static IndexPropertiesCollection GetIndexProperties(FileInfo configFile) + { + return ConfigFile.Deserialize(configFile); + } + + /// + /// THis method changes for each content type. + /// + /// the indices for this minute. + /// A score value for the content in this one minute of recording. + public static KeyValuePair GetContent(Dictionary oneMinuteOfIndices) + { + double score = 0.0; + return new KeyValuePair("Name", score); + } + + /* + /// + /// THis method is the same for all Content Types but uses constants appropriate the template type. + /// + public static Dictionary GetTemplate(DirectoryInfo dir) + { + var dictionaryOfIndices = DataProcessing.ReadIndexMatrices(dir, BaseName); + var birdIndices = DataProcessing.AverageIndicesOverMinutes(dictionaryOfIndices, StartRowId, EndRowId); + var reducedIndices = DataProcessing.ReduceIndicesByFactor(birdIndices, ReductionFactor); + var freqBinBounds = DataProcessing.GetFreqBinBounds(BottomFreq, TopFreq, FreqBinCount); + reducedIndices = DataProcessing.ApplyBandPass(reducedIndices, freqBinBounds[0], freqBinBounds[1]); + return reducedIndices; + } + + /// + /// THis method is the same for all Content Types. + /// + public static void WriteTemplateToFile(DirectoryInfo ipDir, DirectoryInfo opDir) + { + var finalTemplate = GetTemplate(ipDir); + var opPath = Path.Combine(opDir.FullName, Name + "Template.csv"); + FileTools.WriteDictionaryToFile(finalTemplate, opPath); + } + */ + + // The following random data was used to try some statistical experiments. + // get dummy data + //var rn = new RandomNumber(DateTime.Now.Second + (int)DateTime.Now.Ticks + 333); + //var distance = rn.GetDouble(); + } +} diff --git a/src/AudioAnalysisTools/ContentDescriptionTools/DataProcessing.cs b/src/AudioAnalysisTools/ContentDescriptionTools/DataProcessing.cs index ddf9f72fd..3e80fed22 100644 --- a/src/AudioAnalysisTools/ContentDescriptionTools/DataProcessing.cs +++ b/src/AudioAnalysisTools/ContentDescriptionTools/DataProcessing.cs @@ -257,6 +257,39 @@ public static double[] ConvertDictionaryToVector(Dictionary di return list.ToArray(); } + public static Dictionary> ExtractDictionaryOfTemplateDictionaries(TemplateCollection collection) + { + var opDictionary = new Dictionary>(); + var keys = collection.Keys; + foreach (string key in keys) + { + var template = collection[key]; + var dictOfIndices = template.Template; + opDictionary.Add(key, dictOfIndices); + } + + return opDictionary; + } + + public static double[,] ConvertDictionaryOfIndicesToMatrix(Dictionary dictionary) + { + var indexCount = ContentDescription.IndexNames.Length; + + var colCount = dictionary.First().Value.Length; + var opMatrix = new double[indexCount, colCount]; + + for (int i = 0; i < indexCount; i++) + { + var success = dictionary.TryGetValue(ContentDescription.IndexNames[i], out double[] indices); + if (success) + { + MatrixTools.SetRow(opMatrix, i, indices); + } + } + + return opMatrix; + } + public static double[] GetFreqBinVector(Dictionary dictionary, int id) { var list = new List(); diff --git a/src/AudioAnalysisTools/ContentDescriptionTools/TemplateCollection.cs b/src/AudioAnalysisTools/ContentDescriptionTools/TemplateCollection.cs new file mode 100644 index 000000000..a466a46cd --- /dev/null +++ b/src/AudioAnalysisTools/ContentDescriptionTools/TemplateCollection.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; + +namespace AudioAnalysisTools.ContentDescriptionTools +{ + using Acoustics.Shared.ConfigFile; + using AnalysisBase; + //using Zio; + + public class TemplateCollection : Dictionary, IConfig + { + static TemplateCollection() + { + ConfigFile.Defaults.Add(typeof(TemplateCollection), "ContentDescriptionTemplates.yml"); + } + + public TemplateCollection() + { + void OnLoaded(IConfig config) + { + int i = 0; + foreach (var kvp in this) + { + // assign the key to the object for consistency + kvp.Value.Name = kvp.Key; + + // HACK: infer order of properties for visualization based on order of for-each + kvp.Value.TemplateId = i; + i++; + } + } + + this.Loaded += OnLoaded; + } + + public event Action Loaded; + + public string ConfigPath { get; set; } + + void IConfig.InvokeLoaded() + { + this.Loaded?.Invoke(this); + } + + // public static Dictionary GetTemplateMatrices(TemplateCollection templates) + // { + // // init dictionary of matrices + // var opTemplate = new Dictionary(); + + // foreach (var template in templates) + // { + // var name = template.Key; + // var templateData = template.Value; + // var dataDict = templateData.Template; + + // // init a matrix to contain template values + // var matrix = new double[,]; + // foreach (var kvp in dataDict) + // { + // var array = kvp.Value; + // matrix.AddRow(); + // } + + // opTemplate.Add(name, matrix); + // } + + // return opTemplate; + // } + } + + public abstract class TemplatesConfig : AnalyzerConfig + { + protected TemplatesConfig() + { + void OnLoaded(IConfig config) + { + //var indicesPropertiesConfig = Indices.IndexProperties.Find(this, this.ConfigPath); + this.TemplateConfig = @"C:\Work\GitHub\audio-analysis\src\AnalysisConfigFiles\ContentDescriptionTemplates.yml"; + this.DictionaryOfTemplates = ConfigFile.Deserialize(this.TemplateConfig); + } + + this.Loaded += OnLoaded; + } + + public string TemplateConfig { get; set; } + + public TemplateCollection DictionaryOfTemplates { get; private set; } + } +}