Skip to content

Commit

Permalink
Code review for content description
Browse files Browse the repository at this point in the history
Added an entry method for build content description models (functional templates).

Split TemplateManifest apart from FunctionalTemplate.

Left pseudo-code for various improvments. In particular the content description signatures can be calculated in Analyze (i.e. the parallel stage) which will speed up computation.
  • Loading branch information
atruskie committed Nov 13, 2019
1 parent 89355ce commit be0abf9
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 68 deletions.
1 change: 1 addition & 0 deletions src/AnalysisPrograms/AnalysisPrograms.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@
<Compile Include="AudioCutter.cs" />
<Compile Include="AudioFileCheck.cs" />
<Compile Include="CheckEnvironment.cs" />
<Compile Include="ContentDescription\BuildModel.cs" />
<Compile Include="Draw\Zooming\DrawZoomingSpectrograms.Arguments.cs" />
<Compile Include="EventStatistics\EventStatisticsEntry.Arguments.cs" />
<Compile Include="EventStatistics\EventStatisticsAnalysis.cs" />
Expand Down
45 changes: 30 additions & 15 deletions src/AnalysisPrograms/ContentDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// 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).
// </copyright>

namespace AnalysisPrograms
namespace AnalysisPrograms.ContentDescription
{
using System;
using System.Collections.Generic;
Expand All @@ -24,35 +24,31 @@ namespace AnalysisPrograms

/// <summary>
/// This class is derived from AbstractStrongAnalyser.
/// It is equivalent to AnalyseLongRecording.cs or a SpeciesRecognizer.cs.
/// It is equivalent to AnalyseLongRecording.cs or a species recognizer.
/// To call this class, the first argument on the commandline must be 'audio2csv'.
/// Given a one-minute recording segment, the ContentDescription.Analyze() method calls AudioAnalysisTools.Indices.IndexCalculateSixOnly.Analysis().
/// This calculates six spectral indices, ACI, ENT, EVN, BGN, PMN, OSC. This set of 6x256 acoustic features is used for content description.
/// The content description methods are called from ContentDescription.SummariseResults() method.
/// </summary>
public class ContentDescription : AbstractStrongAnalyser
public class UseModel : AbstractStrongAnalyser
{
public const string AnalysisName = "ContentDescription";

// TASK IDENTIFIERS
//public const string TaskAnalyze = AnalysisName;
//public const string TaskLoadCsv = "loadCsv";
public const string TowseyAcoustic = "Towsey." + AnalysisName;
public const string TowseyContentDescription = "Towsey." + AnalysisName;

private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

public override string DisplayName => "Content Description";

public override string Identifier => TowseyAcoustic;
public override string Identifier => TowseyContentDescription;

public override string Description => "[BETA] Generates six spectral indices for Content Description.";

public override AnalysisSettings DefaultSettings => new AnalysisSettings
{
AnalysisMaxSegmentDuration = TimeSpan.FromMinutes(1),
AnalysisMinSegmentDuration = TimeSpan.FromSeconds(30),
SegmentMediaType = MediaTypes.MediaTypeWav,
SegmentOverlapDuration = TimeSpan.Zero,
AnalysisTargetSampleRate = ContentSignatures.SampleRate,
};

Expand Down Expand Up @@ -89,30 +85,44 @@ public override AnalysisResult2 Analyze<T>(AnalysisSettings analysisSettings, Se

segmentResults.SpectralIndexValues.FileName = segmentSettings.Segment.SourceMetadata.Identifier;

// TODO: calculate description results here
DescriptionResult description = null; // e.g. GetContentDescription();

var analysisResults = new AnalysisResult2(analysisSettings, segmentSettings, recording.Duration)
{
AnalysisIdentifier = this.Identifier,
SpectralIndices = new SpectralIndexBase[1],
SpectralIndices = new SpectralIndexBase[]
{
// Transfer the spectral index results to AnalysisResults
// TODO: consider not returning this value if it is not needed in summarize
segmentResults.SpectralIndexValues,
},
MiscellaneousResults =
{
{ nameof(DescriptionResult), description },
},
};

//Transfer the spectral index results to AnalysisResults
analysisResults.SpectralIndices[0] = segmentResults.SpectralIndexValues;
return analysisResults;
}

public override void WriteEventsFile(FileInfo destination, IEnumerable<EventBase> results)
{
throw new NotImplementedException("Content Description should not produce events");
}

public override void WriteSummaryIndicesFile(FileInfo destination, IEnumerable<SummaryIndexBase> results)
{
throw new NotImplementedException("Content Description should not produce summary indices");
}

public override List<FileInfo> WriteSpectrumIndicesFiles(DirectoryInfo destination, string fileNameBase, IEnumerable<SpectralIndexBase> results)
{
//get selectors and removed unwanted because these indices were never calculated.
var spectralIndexBases = results.ToList();
var selectors = spectralIndexBases.First().GetSelectors();

// TODO: REMOVE unused index filter with new Spectral Indices child class
foreach (var indexName in ContentSignatures.UnusedIndexNames)
{
selectors.Remove(indexName);
Expand All @@ -122,7 +132,7 @@ public override List<FileInfo> WriteSpectrumIndicesFiles(DirectoryInfo destinati
foreach (var kvp in selectors)
{
// write spectrogram to disk as CSV file
var filename = FilenameHelpers.AnalysisResultPath(destination, fileNameBase, TowseyAcoustic + "." + kvp.Key, "csv").ToFileInfo();
var filename = FilenameHelpers.AnalysisResultPath(destination, fileNameBase, TowseyContentDescription + "." + kvp.Key, "csv").ToFileInfo();
spectralIndexFiles.Add(filename);
Csv.WriteMatrixToCsv(filename, spectralIndexBases, kvp.Value);
}
Expand Down Expand Up @@ -210,11 +220,16 @@ public override void SummariseResults(
// now get the content description for each minute.
var templatesFileName = cdConfiguration.TemplatesList;
var templatesFile = new FileInfo(Path.Combine(configDirectory, templatesFileName));

var contentDictionary = GetContentDescription(spectralIndices, templatesFile, startTimeOffset);
// TODO: use this when calculating content description results in the Analyze method
//var allContentDescriptionResults = results.Select(x => (DescriptionResult)x.MiscellaneousResults[nameof(DescriptionResult)]);

// Write the results to a csv file
var filePath = Path.Combine(resultsDirectory.FullName, "AcousticSignatures.csv");
FileTools.WriteDictionaryAsCsvFile(contentDictionary, filePath);
// TODO: use this when calculating content description results in the Analyze method
//Csv.WriteToCsv(filePath, allContentDescriptionResults);

// prepare graphical plots of the acoustic signatures.
var contentPlots = GetPlots(contentDictionary);
Expand All @@ -234,14 +249,14 @@ public override void SummariseResults(
/// </summary>
/// <param name="spectralIndices">set of spectral indices for each minute.</param>
/// <param name="templatesFile">json file containing description of templates.</param>
/// <param name="elapsedTimeAtStartOfRecording">minute Id for start of recording.</param>
/// <param name="elapsedTimeAtStartOfRecording">Offset into the recording where recording begun (may not be a whole or round minute).</param>
private static Dictionary<string, double[]> GetContentDescription(
SpectralIndexBase[] spectralIndices,
FileInfo templatesFile,
TimeSpan elapsedTimeAtStartOfRecording)
{
// Read in the content description templates
var templates = Json.Deserialize<TemplateManifest[]>(templatesFile);
var templates = Json.Deserialize<FunctionalTemplate[]>(templatesFile);
var templatesAsDictionary = DataProcessing.ExtractDictionaryOfTemplateDictionaries(templates);
var startMinuteId = (int)Math.Round(elapsedTimeAtStartOfRecording.TotalMinutes);

Expand Down
68 changes: 68 additions & 0 deletions src/AnalysisPrograms/ContentDescription/BuildModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AnalysisPrograms.ContentDescription
{
using System.ComponentModel.DataAnnotations;
using System.IO;
using AnalysisBase;
using AnalysisPrograms.Production;
using AnalysisPrograms.Production.Arguments;
using AnalysisPrograms.Production.Validation;
using AudioAnalysisTools.ContentDescriptionTools;
using McMaster.Extensions.CommandLineUtils;

public partial class BuildModel
{
public const string CommandName = "BuildContentDescriptionModel";

[Command(
CommandName,
Description = "TODO")]
public class Arguments: SubCommandBase
{
[Argument(
0,
Description = "TODO")]
[Required]
[FileExists]
[LegalFilePath]
public FileInfo TemplateManifest { get; set; }

[Argument(
1,
Description = "TODO")]
[Required]
[FileExists]
[LegalFilePath]
public FileInfo TemplateDefinitions { get; set; }

[Argument(
2,
Description = "A directory to write output to")]
[Required]
[DirectoryExistsOrCreate(createIfNotExists: true)]
[LegalFilePath]
public virtual DirectoryInfo Output { get; set; }

public override Task<int> Execute(CommandLineApplication app)
{
BuildModel.Execute(this);

return this.Ok();
}
}

public static void Execute(Arguments arguments)
{
// TODO: inline CreateNewFileOfTemplateDefinitions to this method.
TemplateManifest.CreateNewFileOfTemplateDefinitions(arguments.TemplateManifest,
arguments.TemplateDefinitions);

LoggedConsole.WriteSuccessLine("Completed");
}
}
}
1 change: 1 addition & 0 deletions src/AnalysisPrograms/Production/Arguments/MainArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ namespace AnalysisPrograms.Production.Arguments
[Subcommand(DrawZoomingSpectrograms.CommandName, typeof(DrawZoomingSpectrograms.Arguments))]
[Subcommand(RibbonPlot.CommandName, typeof(RibbonPlot.Arguments))]
[Subcommand(DrawEasyImage.CommandName, typeof(DrawEasyImage.Arguments))]
[Subcommand(ContentDescription.BuildModel.CommandName, typeof(ContentDescription.BuildModel.Arguments))]
[Subcommand(Audio2InputForConvCnn.CommandName, typeof(Audio2InputForConvCnn.Arguments))]
[Subcommand(DifferenceSpectrogram.CommandName, typeof(DifferenceSpectrogram.Arguments))]
[Subcommand(EPR.CommandName, typeof(EPR.Arguments))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace AudioAnalysisTools.ContentDescriptionTools
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Acoustics.Shared;
using TowseyLibrary;

Expand Down Expand Up @@ -62,12 +63,13 @@ public class ContentSignatures
/// <returns>A list of plots - each plot is the minute by minute scores for a single template.</returns>
public static List<Plot> ContentDescriptionOfMultipleRecordingFiles(FileInfo listOfIndexFiles, FileInfo templatesFile)
{
// TODO: inline this method into AnalysisPrograms.ContentDescription.UseModel.Analyse
// total length in minutes of all the recordings
const int totalMinutesDurationOverAllRecordings = 1440;
const int startMinute = 0;

// Read in all the prepared templates
var templates = Json.Deserialize<TemplateManifest[]>(templatesFile);
var templates = Json.Deserialize<FunctionalTemplate[]>(templatesFile);
var templatesAsDictionary = DataProcessing.ExtractDictionaryOfTemplateDictionaries(templates);

// Read in list of paths to index files
Expand Down Expand Up @@ -114,7 +116,7 @@ public static List<Plot> ContentDescriptionOfMultipleRecordingFiles(FileInfo lis
}

public static List<DescriptionResult> AnalyzeMinutes(
TemplateManifest[] templates,
FunctionalTemplate[] templates,
Dictionary<string, Dictionary<string, double[]>> templatesAsDictionary,
Dictionary<string, double[,]> dictionaryOfRecordingIndices,
int elapsedMinutesAtStart)
Expand Down Expand Up @@ -149,7 +151,7 @@ public static List<DescriptionResult> AnalyzeMinutes(
/// <param name="minuteId">The minute ID, i.e. its temporal position.</param>
/// <returns>A single instance of a DescriptionResult.</returns>
public static DescriptionResult AnalyzeOneMinute(
TemplateManifest[] templates,
FunctionalTemplate[] templates,
Dictionary<string, Dictionary<string, double[]>> templatesAsDictionary,
Dictionary<string, double[]> oneMinuteOfIndices,
int minuteId)
Expand All @@ -165,8 +167,8 @@ public static DescriptionResult AnalyzeOneMinute(
continue;
}

var algorithmType = template.FeatureExtractionAlgorithm;
var templateIndices = templatesAsDictionary[template.Name];
var algorithmType = template.Manifest.FeatureExtractionAlgorithm;
var templateIndices = templatesAsDictionary[template.Manifest.Name];
double score;

// Following line used where want to return a set of random scores for testing reasons.
Expand All @@ -175,21 +177,21 @@ public static DescriptionResult AnalyzeOneMinute(
switch (algorithmType)
{
case 1:
score = ContentAlgorithms.GetFullBandContent1(oneMinuteOfIndices, template, templateIndices);
score = ContentAlgorithms.GetFullBandContent1(oneMinuteOfIndices, template.Manifest, templateIndices);
break;
case 2:
score = ContentAlgorithms.GetBroadbandContent1(oneMinuteOfIndices, template, templateIndices);
score = ContentAlgorithms.GetBroadbandContent1(oneMinuteOfIndices, template.Manifest, templateIndices);
break;
case 3:
score = ContentAlgorithms.GetNarrowBandContent1(oneMinuteOfIndices, template, templateIndices);
score = ContentAlgorithms.GetNarrowBandContent1(oneMinuteOfIndices, template.Manifest, templateIndices);
break;
default:
LoggedConsole.WriteWarnLine("Algorithm " + algorithmType + " does not exist.");
score = 0.0;
break;
}

var result = new KeyValuePair<string, double>(template.Description, score);
var result = new KeyValuePair<string, double>(template.Manifest.Description, score);
descriptionResult.AddDescription(result);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,12 +310,12 @@ public static double[] ConvertDictionaryToVector(Dictionary<string, double> dict
return list.ToArray();
}

public static Dictionary<string, Dictionary<string, double[]>> ExtractDictionaryOfTemplateDictionaries(TemplateManifest[] templates)
public static Dictionary<string, Dictionary<string, double[]>> ExtractDictionaryOfTemplateDictionaries(FunctionalTemplate[] templates)
{
var opDictionary = new Dictionary<string, Dictionary<string, double[]>>();
foreach (TemplateManifest template in templates)
foreach (FunctionalTemplate template in templates)
{
var name = template.Name;
var name = template.Manifest.Name;
var dictOfIndices = template.Template;
opDictionary.Add(name, dictOfIndices);
}
Expand Down
Loading

0 comments on commit be0abf9

Please sign in to comment.