Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
* develop:
  Adding silence calculator utility.
  Asserting how silence maps in the hash domain.
  • Loading branch information
AddictedCS committed Jul 9, 2024
2 parents a4922e5 + 40fa963 commit 97ab1c3
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 12 deletions.
4 changes: 2 additions & 2 deletions src/SoundFingerprinting.Tests/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("4cac962e-ebc5-4006-a1e0-7ffb3e2483c2")]
[assembly: AssemblyVersion("10.4.0.100")]
[assembly: AssemblyInformationalVersion("10.4.0.100")]
[assembly: AssemblyVersion("10.6.0.100")]
[assembly: AssemblyInformationalVersion("10.6.0.100")]
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,15 @@ public void FingerprintsCantMatchUniformlyAtRandom()
for (int i = 0; i < 100; ++i)
{
var schema = TestUtilities.GenerateRandomFingerprint(random, 200, 128, 32);
var hash = lshAlgorithm.Hash(new Fingerprint(schema, i * one, (uint)i, Array.Empty<byte>()), config);
var hash = lshAlgorithm.Hash(new Fingerprint(schema, i * one, (uint)i, []), config);
var subFingerprint = new SubFingerprintData(hash.HashBins, hash.SequenceNumber, hash.StartsAt, new ModelReference<uint>((uint)i), track);
storage.AddSubFingerprint(subFingerprint);
}

for (int i = 0; i < 10; ++i)
{
var schema = TestUtilities.GenerateRandomFingerprint(random, 200, 128, 32);
var hash = lshAlgorithm.Hash(new Fingerprint(schema, i * one, (uint)i, Array.Empty<byte>()), config);
var hash = lshAlgorithm.Hash(new Fingerprint(schema, i * one, (uint)i, []), config);
for (int j = 0; j < 25; ++j)
{
var ids = storage.GetSubFingerprintsByHashTableAndHash(j, hash.HashBins[j], MediaType.Audio);
Expand Down Expand Up @@ -199,16 +199,16 @@ public void ShouldBeAbleToControlReturnedCandidatesWithThresholdParameter()
var fingerprints = TestUtilities.GenerateSimilarFingerprints(random, howSimilar, topWavelets, width * height * 2);
int hammingDistance = similarity.CalculateHammingDistance(fingerprints.Item1.ConvertToBooleans(), fingerprints.Item2.ConvertToBooleans());
hammingDistances.Add(hammingDistance);
var hashed1 = lsh.HashImage(new Fingerprint(fingerprints.Item1, 0, 0, Array.Empty<byte>()), hashingConfig);
var hashed2 = lsh.HashImage(new Fingerprint(fingerprints.Item2, 0, 0, Array.Empty<byte>()), hashingConfig);
var hashed1 = lsh.HashImage(new Fingerprint(fingerprints.Item1, 0, 0, []), hashingConfig);
var hashed2 = lsh.HashImage(new Fingerprint(fingerprints.Item2, 0, 0, []), hashingConfig);
int agreeCount = AgreeOn(hashed1.HashBins, hashed2.HashBins);
agreeOn.Add(agreeCount);
}
int requested = (int)((1 - howSimilar) * topWavelets * 2);
Assert.AreEqual(requested, hammingDistances.Average(), 1);
Assert.AreEqual(expectedThresholds[r], Math.Floor(agreeOn.Average()));
logger.LogInformation($"Similarity: {howSimilar: 0.00}, Avg. Table Matches {agreeOn.Average(): 0.000}");
logger.LogInformation("Similarity: {HowSimilar}, Avg. Table Matches {Average}", howSimilar, agreeOn.Average());
});
}

Expand Down
10 changes: 10 additions & 0 deletions src/SoundFingerprinting.Tests/Unit/Math/SimilarityUtilityTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ public void CalculateJaccardSimilarityCorrect()
Assert.AreEqual(5f / 6, result, 0.0001);
}

[Test]
public void SilenceShouldMapToNegativeOne()
{
byte[] minHashes = [255, 255, 255, 255];
int[] hashes = hashConverter.ToInts(minHashes, 1);

Assert.AreEqual(1, hashes.Length);
Assert.AreEqual(-1, hashes[0]);
}

private byte[] GenerateByteArray(int length)
{
var ran = new Random();
Expand Down
26 changes: 26 additions & 0 deletions src/SoundFingerprinting.Tests/Unit/Utils/SilenceCalculatorTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace SoundFingerprinting.Tests.Unit.Utils;

using System.Linq;
using NUnit.Framework;
using SoundFingerprinting.Data;
using SoundFingerprinting.Utils;

[TestFixture]
public class SilenceCalculatorTest
{
[Test]
public void ShouldCalculateCorrectly()
{
var hashedFingerprints = Enumerable.Range(0, 10).Select(index =>
{
int[] hashBins = index % 2 == 0 ? Enumerable.Repeat(-1, 25).ToArray() : Enumerable.Repeat(1, 25).ToArray();
return new HashedFingerprint(hashBins, (uint)index, (float)index / 2, []);
}).ToArray();

var hashes = new Hashes(hashedFingerprints, 10, MediaType.Audio);

var timespan = SilenceCalculator.Calculate(hashes);

Assert.That(timespan.TotalSeconds, Is.EqualTo(2.5).Within(0.1));
}
}
2 changes: 1 addition & 1 deletion src/SoundFingerprinting/Data/AVHashes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public AVHashes WithStreamId(string streamId)
}

/// <summary>
/// Add an additional property to the Audio/Video hashes object.
/// Add a property to the Audio/Video hashes object.
/// </summary>
/// <param name="key">Property key.</param>
/// <param name="value">Property value.</param>
Expand Down
2 changes: 1 addition & 1 deletion src/SoundFingerprinting/FingerprintService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ internal IEnumerable<Fingerprint> CreateOriginalFingerprintsFromFrames(IEnumerab
var images = normalized.ToList();
if (!images.Any())
{
return Enumerable.Empty<Fingerprint>();
return [];
}

var fingerprints = new ConcurrentBag<Fingerprint>();
Expand Down
4 changes: 2 additions & 2 deletions src/SoundFingerprinting/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
[assembly: InternalsVisibleTo("SoundFingerprinting.FFT.FFTW")]
[assembly: InternalsVisibleTo("SoundFingerprinting.FFT.FFTW.Tests")]

[assembly: AssemblyVersion("10.4.0.100")]
[assembly: AssemblyInformationalVersion("10.4.0.100")]
[assembly: AssemblyVersion("10.6.0.100")]
[assembly: AssemblyInformationalVersion("10.6.0.100")]
2 changes: 1 addition & 1 deletion src/SoundFingerprinting/SoundFingerprinting.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Nullable>enable</Nullable>
<PackageVersion>10.4.0</PackageVersion>
<PackageVersion>10.6.0</PackageVersion>
<Authors>Sergiu Ciumac</Authors>
<PackageDescription>SoundFingerprinting is a C# framework that implements an efficient algorithm of audio fingerprinting and identification. Designed for developers, enthusiasts, researchers in the fields of audio processing, data mining, digital signal processing.</PackageDescription>
<PackageProjectUrl>https://github.com/addictedcs/soundfingerprinting</PackageProjectUrl>
Expand Down
63 changes: 63 additions & 0 deletions src/SoundFingerprinting/Utils/SilenceCalculator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
namespace SoundFingerprinting.Utils;

using System;
using System.Linq;
using SoundFingerprinting.Data;

/// <summary>
/// Silence calculator.
/// </summary>
/// <remarks>
/// A utility class allowing to calculate silence contained within audio and video hashes. <br />
/// It only makes sense to calculate silence for hashes if TreatAsSilence flag is set to true during fingerprinting. <br />
/// Do not set the flag to true unless you've explicitly arrived to the conclusion you need it.
/// </remarks>
public static class SilenceCalculator
{
/// <summary>
/// Silence value, derived from the fact that an array of [255, 255, 255, 255] min hashes will map to -1 during hashes conversion.
/// </summary>
private const int Silence = -1;

/// <summary>
/// Calculate silence contained within audio and video hashes.
/// </summary>
/// <param name="avHashes">AVHashes to analyze.</param>
/// <returns>
/// Tuple containing audio and video silence. For video hashes silence means black frames.
/// </returns>
public static (TimeSpan, TimeSpan) Calculate(AVHashes avHashes)
{
var audioSilence = Calculate(avHashes.Audio);
var videoSilence = Calculate(avHashes.Video);
return (audioSilence, videoSilence);
}

/// <summary>
/// Calculate contained silence within hashes.
/// </summary>
/// <param name="hashes">Hashes to analyze.</param>
/// <returns>Timespan.</returns>
/// <remarks>
/// It only makes sense to calculate silence for hashes if TreatAsSilence flag is set to true during fingerprinting.
/// </remarks>
public static TimeSpan Calculate(Hashes? hashes)
{
if (hashes == null || hashes.IsEmpty)
{
return TimeSpan.Zero;
}

var items = hashes.OrderBy(_ => _.SequenceNumber).ToArray();
double silence = 0;
for (int i = 1; i < items.Length; i++)
{
if (hashes[i - 1].HashBins.All(_ => _ == Silence))
{
silence += items[i].StartsAt - items[i - 1].StartsAt;
}
}

return TimeSpan.FromSeconds(silence);
}
}

0 comments on commit 97ab1c3

Please sign in to comment.