Skip to content

Commit

Permalink
Fix bugs add more validation
Browse files Browse the repository at this point in the history
- Adds an interval converter to the JSON serializer
- Adds logger pass through methods for convenience
-  Fixed a windowing index out of bounds exception in AcousticEvent
  • Loading branch information
atruskie committed Apr 9, 2021
1 parent ff72a48 commit 1320b09
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 11 deletions.
6 changes: 3 additions & 3 deletions docs/guides/generic_recognizers.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ For details on configuring this step see <xref:AudioAnalysisTools.Events.Types.E
#### Remove events that are enclosed by other events

Running profiles with multiple decibel thresholds can produce sets of enclosed (wholly overlapped by another event) events
that are actually the result of detecting the same acoustic syllable.
that are actually the result of detecting the same acoustic syllable.
This final (optional) post-processing step is to
remove all but the outermost event of any nested set. Enable this option by setting the parameter `RemoveEnclosedEvents` to `true`.
You would typically do this only after reviewing the output spectrograms to confirm that you have sets of overlapping events.
Expand All @@ -490,15 +490,15 @@ You would typically do this only after reviewing the output spectrograms to conf

##### How `RemoveEnclosedEvents` is applied

Suppose you have three decibel thresholds (6, 9 and 12 dB is a typical set of values)
Suppose you have three decibel thresholds (6, 9 and 12 dB is a typical set of values)
in each of two profiles.
There will be three rounds of post-processing:

1. All the events detected (by both profiles) at threshold 6 dB will be subject to those post-processing steps 1-5 enabled by the user.
2. Next, all events detected at the 9 dB threshold will be passed through the same post-processing steps.
3. Next, all events detected at the 12 dB threshold will be passed through the same post-processing steps.

The final option (step 6) is to collect all events emerging from all previous rounds of post-processing and to remove those that are enclosed by another event.
The final option (step 6) is to collect all events emerging from all previous rounds of post-processing and to remove those that are enclosed by another event.

</aside>

Expand Down
2 changes: 1 addition & 1 deletion docs/technical/apidoc/SyllableSequenceConfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ uid: AudioAnalysisTools.Events.Types.EventPostProcessing.SyllableSequenceConfig
---


Set `CombinePossibleSyllableSequence` true where you want to combine possible syllable sequences. A typical example is
Use `SyllableSequence` where you want to combine possible syllable sequences. A typical example is
a sequence of chirps in a honeyeater call.

`SyllableStartDifference` and `SyllableHertzGap` set the allowed tolerances when combining events into sequences
Expand Down
22 changes: 22 additions & 0 deletions src/Acoustics.Shared/Extensions/ConfigFileExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,27 @@ public static ValidationResult ValidateNotNull(this object value, string name, s

return ValidationResult.Success;
}

public static ValidationResult ValidateNotEmpty<T>(this T[] value, string name, string message = "is an empty list - we need at least one value in the config file")
{
if (value?.Length == 0)
{
return new ValidationResult(message, name.Wrap());
}

return ValidationResult.Success;
}

public static ValidationResult ValidateLessThan<T>(this object _, T? a, string nameA, T? b, string nameB, string message = "{0} is not less than {1} - adjust the values in the config file")
where T : struct, IComparable<T>
{
if (a.HasValue && b.HasValue && a.Value.CompareTo(b.Value) != -1)
{
return new ValidationResult(message.Format(nameA, nameB), new[] { nameA, nameB });
}

return ValidationResult.Success;
}
}

}
12 changes: 11 additions & 1 deletion src/Acoustics.Shared/Extensions/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,13 @@ public static bool All(this IEnumerable<bool> sequence)

public static string Join(this IEnumerable items, string delimiter = " ") => Join(items.Cast<object>(), delimiter);

public static string Join<T>(this IEnumerable<T> items, string delimiter = " ")
public static string Join<T>(this IEnumerable<T> items, string delimiter = " ", string prefix = "", string suffix = "")
{
if (items is null)
{
return string.Empty;
}

var result = new StringBuilder();
foreach (var item in items)
{
Expand All @@ -324,6 +329,11 @@ public static string JoinFormatted(this IEnumerable<double> items, string format
public static string JoinFormatted<T>(this IEnumerable<T> items, string formatString, string delimiter = " ")
where T : IFormattable
{
if (items?.Any() != true)
{
return string.Empty;
}

var result = new StringBuilder();
foreach (var item in items)
{
Expand Down
21 changes: 21 additions & 0 deletions src/Acoustics.Shared/Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ static Json()
{
Formatting = Formatting.Indented,
};

Serializer.Converters.Add(new IntervalConverter());
}

public static JsonSerializer Serializer { get; }
Expand Down Expand Up @@ -114,5 +116,24 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s
throw new NotImplementedException();
}
}

public class IntervalConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Interval<double>);
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var interval = (Interval<double>)value;
writer.WriteValue(interval.ToString(true, "F3"));
}
}
}
}
13 changes: 13 additions & 0 deletions src/Acoustics.Shared/Logging/LogExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,5 +178,18 @@ public static void Trace(this ILog log, object message, Exception exception)
{
log.Logger.Log(null, Level.Trace, message, exception);
}

public static T PassThrough<T>(this ILog log, T value, Level level = null, string message = "value")
{
log.Logger.Log(null, level ?? Level.Debug, $"{message}: {value}", null);
return value;
}

public static bool Choice(this ILog log, bool condition, string message, string trueMessage = "yes", string falseMessage = "no", Level level = null)
{
var formatted = condition ? trueMessage : falseMessage;
log.Logger.Log(null, level ?? Level.Debug, $"{message}: {formatted}", null);
return condition;
}
}
}
4 changes: 2 additions & 2 deletions src/Acoustics.Tools/Audio/AbstractUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -376,11 +376,11 @@ protected void RunExe(ProcessRunner processRunner, string arguments, string work
stopwatch.Stop();

this.Log.DebugFormat(
"Executed {0} in working directory {1}. Took {2} ({3}ms). Exit code: {4}",
"Executed {0} in working directory {1}. Took {2} ({3} s). Exit code: {4}",
processRunner.ExecutableFile.Name,
workingDirectory,
stopwatch.Elapsed.Humanise(),
stopwatch.Elapsed.TotalMilliseconds,
stopwatch.Elapsed.TotalSeconds,
processRunner.ExitCode);

this.Log.Verbose(processRunner.BuildLogOutput());
Expand Down
4 changes: 2 additions & 2 deletions src/AudioAnalysisTools/AcousticEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -779,8 +779,8 @@ public static List<AcousticEvent> GetEventsAroundMaxima(
int startFrame = 0;
int endFrame = 0;

// for all frames
for (int i = 1; i < count - minFrames; i++)
// for all frames - except the first and last frames
for (int i = 1; i < Math.Min(count - minFrames, count - 1); i++)
{
// skip if value is below threshold
if (values[i] < thresholdValue)
Expand Down
7 changes: 5 additions & 2 deletions src/AudioAnalysisTools/CommonParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,11 @@ public abstract class CommonParameters : IValidatableObject

public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
yield return this.MinHertz.ValidateNotNull(nameof(this.MinHertz));
yield return this.MaxHertz.ValidateNotNull(nameof(this.MaxHertz));
yield return this.MinHertz.ValidateNotNull(nameof(this.MinHertz));
yield return this.MaxHertz.ValidateNotNull(nameof(this.MaxHertz));
yield return this.ValidateLessThan(this.MinHertz, nameof(this.MinHertz), this.MaxHertz, nameof(this.MaxHertz));
yield return this.DecibelThresholds.ValidateNotNull(nameof(this.DecibelThresholds));
yield return this.DecibelThresholds.ValidateNotEmpty(nameof(this.DecibelThresholds));
}
}
}
14 changes: 14 additions & 0 deletions src/AudioAnalysisTools/Events/BlobParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
namespace AnalysisPrograms.Recognizers.Base
{
using Acoustics.Shared;
using Acoustics.Shared.ConfigFile;
using MoreLinq;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

/// <summary>
/// Parameters needed from a config file to detect blob components.
Expand All @@ -22,5 +26,15 @@ public BlobParameters()
this.MinHertz = 800;
this.MaxHertz = 8000;
}

public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return base
.Validate(validationContext)
.Append(
this.BottomHertzBuffer.ValidateNotNull(nameof(this.BottomHertzBuffer)))
.Append(
this.TopHertzBuffer.ValidateNotNull(nameof(this.TopHertzBuffer)));
}
}
}
5 changes: 5 additions & 0 deletions src/AudioAnalysisTools/Tracks/OnebinTrackAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@ namespace AudioAnalysisTools.Tracks
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Acoustics.Shared;
using AnalysisPrograms.Recognizers.Base;
using AudioAnalysisTools.Events;
using AudioAnalysisTools.Events.Tracks;
using AudioAnalysisTools.StandardSpectrograms;
using log4net;
using TowseyLibrary;
using TrackType = AudioAnalysisTools.Events.Tracks.TrackType;

public static class OnebinTrackAlgorithm
{
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

public static (List<EventCommon> Events, List<Plot> DecibelPlots) GetOnebinTracks(
SpectrogramStandard spectrogram,
OnebinTrackParameters parameters,
Expand Down Expand Up @@ -50,6 +54,7 @@ public static (List<EventCommon> Events, List<Plot> DecibelPlots) GetOnebinTrack
/// </summary>
/// <param name="sonogram">The spectrogram to be searched.</param>
/// <returns>A list of acoustic events containing whistle tracks.</returns>

public static (List<EventCommon> ListOfevents, double[] CombinedIntensityArray) GetOnebinTracks(
SpectrogramStandard sonogram,
OnebinTrackParameters parameters,
Expand Down

0 comments on commit 1320b09

Please sign in to comment.