public void SonogramDecibelMethodsAreEquivalent() { var recording = new AudioRecording(PathHelper.ResolveAsset("Recordings", "BAC2_20071008-085040.wav")); // specfied linear scale var freqScale = new FrequencyScale(nyquist: 11025, frameSize: 1024, hertzGridInterval: 1000); var sonoConfig = new SonogramConfig { WindowSize = freqScale.FinalBinCount * 2, WindowOverlap = 0.2, SourceFName = recording.BaseName, NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; // Method 1 var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); var expectedDecibelSonogram = MFCCStuff.DecibelSpectra(sonogram.Data, sonogram.Configuration.WindowPower, sonogram.SampleRate, sonogram.Configuration.epsilon); // Method 2: make sure that the decibel spectrum is the same no matter which path we take to calculate it. var actualDecibelSpectrogram = new SpectrogramStandard(sonoConfig, recording.WavReader); CollectionAssert.That.AreEqual(expectedDecibelSonogram, actualDecibelSpectrogram.Data, EnvelopeAndFftTests.Delta); }
public static Image <Rgb24> GetLcnSpectrogram( SonogramConfig sonoConfig, AudioRecording recordingSegment, string sourceRecordingName, double neighbourhoodSeconds, double lcnContrastLevel) { BaseSonogram sonogram = new AmplitudeSonogram(sonoConfig, recordingSegment.WavReader); int neighbourhoodFrames = (int)(sonogram.FramesPerSecond * neighbourhoodSeconds); LoggedConsole.WriteLine("LCN: FramesPerSecond (Prior to LCN) = {0}", sonogram.FramesPerSecond); LoggedConsole.WriteLine("LCN: Neighbourhood of {0} seconds = {1} frames", neighbourhoodSeconds, neighbourhoodFrames); // subtract the lowest 20% of frames. This is first step in LCN noise removal. Sets the baseline. const int lowPercentile = 20; sonogram.Data = NoiseRemoval_Briggs.NoiseReduction_byLowestPercentileSubtraction(sonogram.Data, lowPercentile); sonogram.Data = NoiseRemoval_Briggs.NoiseReduction_byLCNDivision(sonogram.Data, neighbourhoodFrames, lcnContrastLevel); //Matrix normalisation //MatrixTools.PercentileCutoffs(sonogram.Data, 10.0, 90, out double minCut, out double maxCut); //NoiseRemoval_Briggs.NoiseReduction_byLowestPercentileSubtraction(sonogram.Data, lowPercentile); var image = sonogram.GetImageFullyAnnotated( "AMPLITUDE SPECTROGRAM with freq bin Local Contrast Normalization - " + sourceRecordingName, ImageTags[AmplitudeSpectrogramLocalContrastNormalization]); return(image); }
public void TestDecibelSpectrogram() { var recording = new AudioRecording(PathHelper.ResolveAsset("Recordings", "BAC2_20071008-085040.wav")); // specfied linear scale var freqScale = new FrequencyScale(nyquist: 11025, frameSize: 1024, hertzGridInterval: 1000); var sonoConfig = new SonogramConfig { WindowSize = freqScale.FinalBinCount * 2, WindowOverlap = 0.2, SourceFName = recording.BaseName, NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; // DO EQUALITY TEST on the AMPLITUDE SONGOGRAM DATA // Do not bother with the image because this is only an amplitude spectrogram. var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); // DO FILE EQUALITY TEST on the DECIBEL SONGOGRAM DATA // Do not bother with the image because this has been tested elsewhere. var decibelSonogram = MFCCStuff.DecibelSpectra(sonogram.Data, sonogram.Configuration.WindowPower, sonogram.SampleRate, sonogram.Configuration.epsilon); var expectedFile = PathHelper.ResolveAsset("StandardSonograms", "BAC2_20071008_DecibelSonogramData.EXPECTED.bin"); // run this once to generate expected test data // uncomment this to update the binary data. Should be rarely needed // AT: Updated 2017-02-15 because FFT library changed in 864f7a491e2ea0e938161bd390c1c931ecbdf63c //Binary.Serialize(expectedFile, decibelSonogram); var expected = Binary.Deserialize <double[, ]>(expectedFile); CollectionAssert.That.AreEqual(expected, decibelSonogram, EnvelopeAndFftTests.Delta); }
/// <summary> /// This method takes an audio recording and returns an octave scale spectrogram. /// At the present time it only works for recordings with 64000 sample rate and returns a 256 bin sonogram. /// TODO: generalise this method for other recordings and octave scales. /// </summary> public static BaseSonogram ConvertRecordingToOctaveScaleSonogram(AudioRecording recording, FreqScaleType fst) { var freqScale = new FrequencyScale(fst); double windowOverlap = 0.75; var sonoConfig = new SonogramConfig { WindowSize = freqScale.WindowSize, WindowOverlap = windowOverlap, SourceFName = recording.BaseName, NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; // Generate amplitude sonogram and then conver to octave scale var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); // THIS IS THE CRITICAL LINE. // TODO: SHOULD DEVELOP A SEPARATE UNIT TEST for this method sonogram.Data = ConvertAmplitudeSpectrogramToDecibelOctaveScale(sonogram.Data, freqScale); // DO NOISE REDUCTION var dataMatrix = SNR.NoiseReduce_Standard(sonogram.Data); sonogram.Data = dataMatrix; int windowSize = freqScale.FinalBinCount * 2; sonogram.Configuration.WindowSize = windowSize; sonogram.Configuration.WindowStep = (int)Math.Round(windowSize * (1 - windowOverlap)); return(sonogram); }
public void TestFreqScaleOnArtificialSignal1() { int sampleRate = 22050; double duration = 20; // signal duration in seconds int[] harmonics = { 500, 1000, 2000, 4000, 8000 }; int windowSize = 512; var freqScale = new FrequencyScale(sampleRate / 2, windowSize, 1000); var outputImagePath = Path.Combine(this.outputDirectory.FullName, "Signal1_LinearFreqScale.png"); var recording = DspFilters.GenerateTestRecording(sampleRate, duration, harmonics, WaveType.Cosine); var sonoConfig = new SonogramConfig { WindowSize = freqScale.WindowSize, WindowOverlap = 0.0, SourceFName = "Signal1", NoiseReductionType = NoiseReductionType.Standard, NoiseReductionParameter = 0.12, }; var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); // pick a row, any row var oneSpectrum = MatrixTools.GetRow(sonogram.Data, 40); oneSpectrum = DataTools.filterMovingAverage(oneSpectrum, 5); var peaks = DataTools.GetPeaks(oneSpectrum); for (int i = 5; i < peaks.Length - 5; i++) { if (peaks[i]) { LoggedConsole.WriteLine($"bin ={freqScale.BinBounds[i, 0]}, Herz={freqScale.BinBounds[i, 1]}-{freqScale.BinBounds[i + 1, 1]} "); } } foreach (int h in harmonics) { LoggedConsole.WriteLine($"Harmonic {h}Herz should be in bin {freqScale.GetBinIdForHerzValue(h)}"); } // spectrogram without framing, annotation etc var image = sonogram.GetImage(); string title = $"Spectrogram of Harmonics: {DataTools.Array2String(harmonics)} SR={sampleRate} Window={windowSize}"; image = sonogram.GetImageFullyAnnotated(image, title, freqScale.GridLineLocations); image.Save(outputImagePath); // Check that image dimensions are correct Assert.AreEqual(861, image.Width); Assert.AreEqual(310, image.Height); Assert.IsTrue(peaks[11]); Assert.IsTrue(peaks[22]); Assert.IsTrue(peaks[45]); Assert.IsTrue(peaks[92]); Assert.IsTrue(peaks[185]); }
public static void TestMethod_GenerateSignal1() { int sampleRate = 22050; double duration = 20; // signal duration in seconds int[] harmonics = { 500, 1000, 2000, 4000, 8000 }; int windowSize = 512; var freqScale = new FrequencyScale(sampleRate / 2, windowSize, 1000); string path = @"C:\SensorNetworks\Output\Sonograms\UnitTestSonograms\SineSignal1.png"; var recording = GenerateTestRecording(sampleRate, duration, harmonics, WaveType.Cosine); var sonoConfig = new SonogramConfig { WindowSize = freqScale.WindowSize, WindowOverlap = 0.0, SourceFName = "Signal1", NoiseReductionType = NoiseReductionType.Standard, NoiseReductionParameter = 0.12, }; var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); // pick a row, any row var oneSpectrum = MatrixTools.GetRow(sonogram.Data, 40); oneSpectrum = DataTools.normalise(oneSpectrum); var peaks = DataTools.GetPeaks(oneSpectrum, 0.5); for (int i = 2; i < peaks.Length - 2; i++) { if (peaks[i]) { LoggedConsole.WriteLine($"bin ={freqScale.BinBounds[i, 0]}, Herz={freqScale.BinBounds[i, 1]}-{freqScale.BinBounds[i + 1, 1]} "); } } if (peaks[11] && peaks[22] && peaks[45] && peaks[92] && peaks[185]) { LoggedConsole.WriteSuccessLine("Spectral Peaks found at correct places"); } else { LoggedConsole.WriteErrorLine("Spectral Peaks found at INCORRECT places"); } foreach (int h in harmonics) { LoggedConsole.WriteLine($"Harmonic {h}Herz should be in bin {freqScale.GetBinIdForHerzValue(h)}"); } // spectrogram without framing, annotation etc var image = sonogram.GetImage(); string title = $"Spectrogram of Harmonics: {DataTools.Array2String(harmonics)} SR={sampleRate} Window={windowSize}"; image = sonogram.GetImageFullyAnnotated(image, title, freqScale.GridLineLocations); image.Save(path); }
public void SonogramDecibelMethodsAreEquivalent() { // Method 1 var sonogram = new AmplitudeSonogram(this.sonoConfig, this.recording.WavReader); var expectedDecibelSonogram = MFCCStuff.DecibelSpectra(sonogram.Data, sonogram.Configuration.WindowPower, sonogram.SampleRate, sonogram.Configuration.epsilon); // Method 2: make sure that the decibel spectrum is the same no matter which path we take to calculate it. var actualDecibelSpectrogram = new SpectrogramStandard(this.sonoConfig, this.recording.WavReader); CollectionAssert.That.AreEqual(expectedDecibelSonogram, actualDecibelSpectrogram.Data, EnvelopeAndFftTests.Delta); }
/// <summary> /// METHOD TO CHECK IF Octave FREQ SCALE IS WORKING /// Check it on standard one minute recording, SR=22050. /// </summary> public static void TESTMETHOD_OctaveFrequencyScale1() { var recordingPath = @"G:\SensorNetworks\WavFiles\LewinsRail\FromLizZnidersic\Lewinsrail_TasmanIs_Tractor_SM304253_0151119_0640_1minMono.wav"; var outputDir = @"C:\SensorNetworks\Output\LewinsRail\LewinsRail_ThreeCallTypes".ToDirectoryInfo(); //var recordingPath = @"C:\SensorNetworks\SoftwareTests\TestRecordings\BAC\BAC2_20071008-085040.wav"; //var outputDir = @"C:\SensorNetworks\SoftwareTests\TestFrequencyScale".ToDirectoryInfo(); //var expectedResultsDir = Path.Combine(outputDir.FullName, TestTools.ExpectedResultsDir).ToDirectoryInfo(); var outputImagePath = Path.Combine(outputDir.FullName, "octaveFrequencyScale1NoNoiseReduciton.png"); //var opFileStem = "Lewinsrail_TasmanIs_Tractor"; var recording = new AudioRecording(recordingPath); // default octave scale var fst = FreqScaleType.Linear125Octaves6Tones30Nyquist11025; var freqScale = new FrequencyScale(fst); var sonoConfig = new SonogramConfig { WindowSize = freqScale.WindowSize, WindowOverlap = 0.75, SourceFName = recording.BaseName, NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; // Generate amplitude sonogram and then conver to octave scale var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); sonogram.Data = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(sonogram.Data, freqScale); // DO NOISE REDUCTION //var dataMatrix = SNR.NoiseReduce_Standard(sonogram.Data); //sonogram.Data = dataMatrix; sonogram.Configuration.WindowSize = freqScale.WindowSize; var image = sonogram.GetImageFullyAnnotated(sonogram.GetImage(), "SPECTROGRAM: " + fst.ToString(), freqScale.GridLineLocations); image.Save(outputImagePath); // DO FILE EQUALITY TEST //string testName = "test1"; //var expectedTestFile = new FileInfo(Path.Combine(expectedResultsDir.FullName, "FrequencyOctaveScaleTest1.EXPECTED.json")); //var resultFile = new FileInfo(Path.Combine(outputDir.FullName, opFileStem + "FrequencyOctaveScaleTest1Results.json")); //Acoustics.Shared.Csv.Csv.WriteMatrixToCsv(resultFile, freqScale.GridLineLocations); //TestTools.FileEqualityTest(testName, resultFile, expectedTestFile); LoggedConsole.WriteLine("Completed Octave Frequency Scale test 1"); Console.WriteLine("\n\n"); }
public static Image GetLcnSpectrogram(SonogramConfig sonoConfig, AudioRecording recordingSegment, string sourceRecordingName, double neighbourhoodSeconds, double lcnContrastLevel) { BaseSonogram sonogram = new AmplitudeSonogram(sonoConfig, recordingSegment.WavReader); int neighbourhoodFrames = (int)(sonogram.FramesPerSecond * neighbourhoodSeconds); LoggedConsole.WriteLine("LCN: FramesPerSecond (Prior to LCN) = {0}", sonogram.FramesPerSecond); LoggedConsole.WriteLine("LCN: Neighbourhood of {0} seconds = {1} frames", neighbourhoodSeconds, neighbourhoodFrames); const int lowPercentile = 20; sonogram.Data = NoiseRemoval_Briggs.NoiseReduction_byLowestPercentileSubtraction(sonogram.Data, lowPercentile); sonogram.Data = NoiseRemoval_Briggs.NoiseReduction_byLCNDivision(sonogram.Data, neighbourhoodFrames, lcnContrastLevel); var image = sonogram.GetImageFullyAnnotated("AMPLITUDE SPECTROGRAM with freq bin Local Contrast Normalization - " + sourceRecordingName); return(image); }
public void TestAmplitudeSonogram() { // DO EQUALITY TEST on the AMPLITUDE SONGOGRAM DATA // Do not bother with the image because this is only an amplitude spectrogram. var sonogram = new AmplitudeSonogram(this.sonoConfig, this.recording.WavReader); var expectedFile = PathHelper.ResolveAsset("StandardSonograms", "BAC2_20071008_AmplSonogramData.EXPECTED.bin"); // run this once to generate expected test data // uncomment this to update the binary data. Should be rarely needed // AT: Updated 2017-02-15 because FFT library changed in 864f7a491e2ea0e938161bd390c1c931ecbdf63c //Binary.Serialize(expectedFile, sonogram.Data); var expected = Binary.Deserialize <double[, ]>(expectedFile); CollectionAssert.That.AreEqual(expected, sonogram.Data, EnvelopeAndFftTests.Delta); }
/// <summary> /// METHOD TO CHECK IF Octave FREQ SCALE IS WORKING /// Check it on MARINE RECORDING from JASCO, SR=64000. /// 24 BIT JASCO RECORDINGS from GBR must be converted to 16 bit. /// ffmpeg -i source_file.wav -sample_fmt s16 out_file.wav /// e.g. ". C:\Work\Github\audio-analysis\Extra Assemblies\ffmpeg\ffmpeg.exe" -i "C:\SensorNetworks\WavFiles\MarineRecordings\JascoGBR\AMAR119-00000139.00000139.Chan_1-24bps.1375012796.2013-07-28-11-59-56.wav" -sample_fmt s16 "C:\SensorNetworks\Output\OctaveFreqScale\JascoeMarineGBR116bit.wav" /// ffmpeg binaries are in C:\Work\Github\audio-analysis\Extra Assemblies\ffmpeg /// </summary> public static void TESTMETHOD_OctaveFrequencyScale2() { var recordingPath = @"C:\SensorNetworks\SoftwareTests\TestRecordings\MarineJasco_AMAR119-00000139.00000139.Chan_1-24bps.1375012796.2013-07-28-11-59-56-16bit.wav"; var outputDir = @"C:\SensorNetworks\SoftwareTests\TestFrequencyScale".ToDirectoryInfo(); var expectedResultsDir = Path.Combine(outputDir.FullName, TestTools.ExpectedResultsDir).ToDirectoryInfo(); var outputImagePath = Path.Combine(outputDir.FullName, "JascoMarineGBR1.png"); var opFileStem = "JascoMarineGBR1"; var recording = new AudioRecording(recordingPath); var fst = FreqScaleType.Linear125Octaves7Tones28Nyquist32000; var freqScale = new FrequencyScale(fst); var sonoConfig = new SonogramConfig { WindowSize = freqScale.WindowSize, WindowOverlap = 0.2, SourceFName = recording.BaseName, NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); sonogram.Data = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(sonogram.Data, freqScale); // DO NOISE REDUCTION var dataMatrix = SNR.NoiseReduce_Standard(sonogram.Data); sonogram.Data = dataMatrix; sonogram.Configuration.WindowSize = freqScale.WindowSize; var image = sonogram.GetImageFullyAnnotated(sonogram.GetImage(), "SPECTROGRAM: " + fst.ToString(), freqScale.GridLineLocations); image.Save(outputImagePath); // DO FILE EQUALITY TEST string testName = "test2"; var expectedTestFile = new FileInfo(Path.Combine(expectedResultsDir.FullName, "FrequencyOctaveScaleTest2.EXPECTED.json")); var resultFile = new FileInfo(Path.Combine(outputDir.FullName, opFileStem + "FrequencyOctaveScaleTest2Results.json")); Acoustics.Shared.Csv.Csv.WriteMatrixToCsv(resultFile, freqScale.GridLineLocations); TestTools.FileEqualityTest(testName, resultFile, expectedTestFile); LoggedConsole.WriteLine("Completed Octave Frequency Scale " + testName); Console.WriteLine("\n\n"); }
public void PcaWhiteningDefault() { var recordingPath = PathHelper.ResolveAsset("Recordings", "BAC2_20071008-085040.wav"); var fst = FreqScaleType.Linear; var freqScale = new FrequencyScale(fst); var recording = new AudioRecording(recordingPath); var sonoConfig = new SonogramConfig { WindowSize = freqScale.FinalBinCount * 2, WindowOverlap = 0.2, SourceFName = recording.BaseName, NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; // GENERATE AMPLITUDE SPECTROGRAM var spectrogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); spectrogram.Configuration.WindowSize = freqScale.WindowSize; // DO RMS NORMALIZATION spectrogram.Data = SNR.RmsNormalization(spectrogram.Data); // CONVERT NORMALIZED AMPLITUDE SPECTROGRAM TO dB SPECTROGRAM var sonogram = new SpectrogramStandard(spectrogram); // DO NOISE REDUCTION var dataMatrix = PcaWhitening.NoiseReduction(sonogram.Data); sonogram.Data = dataMatrix; // DO PCA WHITENING var whitenedSpectrogram = PcaWhitening.Whitening(sonogram.Data); // DO UNIT TESTING // check if the dimensions of the reverted spectrogram (second output of the pca whitening) is equal to the input matrix Assert.AreEqual(whitenedSpectrogram.Reversion.GetLength(0), sonogram.Data.GetLength(0)); Assert.AreEqual(whitenedSpectrogram.Reversion.GetLength(1), sonogram.Data.GetLength(1)); }
public void OctaveFrequencyScale2() { var recordingPath = PathHelper.ResolveAsset(@"Recordings\MarineJasco_AMAR119-00000139.00000139.Chan_1-24bps.1375012796.2013-07-28-11-59-56-16bit-60sec.wav"); var opFileStem = "JascoMarineGBR1"; var outputDir = this.outputDirectory; var outputImagePath = Path.Combine(this.outputDirectory.FullName, "Octave2ScaleSonogram.png"); var recording = new AudioRecording(recordingPath); var fst = FreqScaleType.Linear125Octaves7Tones28Nyquist32000; var freqScale = new FrequencyScale(fst); var sonoConfig = new SonogramConfig { WindowSize = freqScale.WindowSize, WindowOverlap = 0.2, SourceFName = recording.BaseName, NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); sonogram.Data = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(sonogram.Data, freqScale); // DO NOISE REDUCTION var dataMatrix = SNR.NoiseReduce_Standard(sonogram.Data); sonogram.Data = dataMatrix; sonogram.Configuration.WindowSize = freqScale.WindowSize; var image = sonogram.GetImageFullyAnnotated(sonogram.GetImage(), "SPECTROGRAM: " + fst.ToString(), freqScale.GridLineLocations); image.Save(outputImagePath, ImageFormat.Png); // DO FILE EQUALITY TESTS // Check that freqScale.OctaveBinBounds are correct var stemOfExpectedFile = opFileStem + "_Octave2ScaleBinBounds.EXPECTED.json"; var stemOfActualFile = opFileStem + "_Octave2ScaleBinBounds.ACTUAL.json"; var expectedFile1 = PathHelper.ResolveAsset("FrequencyScale\\" + stemOfExpectedFile); if (!expectedFile1.Exists) { LoggedConsole.WriteErrorLine("An EXPECTED results file does not exist. Test will fail!"); LoggedConsole.WriteErrorLine( $"If ACTUAL results file is correct, move it to dir `{PathHelper.TestResources}` and change its suffix to <.EXPECTED.json>"); } var resultFile1 = new FileInfo(Path.Combine(outputDir.FullName, stemOfActualFile)); Json.Serialise(resultFile1, freqScale.BinBounds); FileEqualityHelpers.TextFileEqual(expectedFile1, resultFile1); // Check that freqScale.GridLineLocations are correct stemOfExpectedFile = opFileStem + "_Octave2ScaleGridLineLocations.EXPECTED.json"; stemOfActualFile = opFileStem + "_Octave2ScaleGridLineLocations.ACTUAL.json"; var expectedFile2 = PathHelper.ResolveAsset("FrequencyScale\\" + stemOfExpectedFile); if (!expectedFile2.Exists) { LoggedConsole.WriteErrorLine("An EXPECTED results file does not exist. Test will fail!"); LoggedConsole.WriteErrorLine( $"If ACTUAL results file is correct, move it to dir `{PathHelper.TestResources}` and change its suffix to <.EXPECTED.json>"); } var resultFile2 = new FileInfo(Path.Combine(outputDir.FullName, stemOfActualFile)); Json.Serialise(resultFile2, freqScale.GridLineLocations); FileEqualityHelpers.TextFileEqual(expectedFile2, resultFile2); // Check that image dimensions are correct Assert.AreEqual(201, image.Width); Assert.AreEqual(310, image.Height); }
public void OctaveFrequencyScale1() { var recordingPath = PathHelper.ResolveAsset("Recordings", "BAC2_20071008-085040.wav"); var opFileStem = "BAC2_20071008"; var outputDir = this.outputDirectory; var outputImagePath = Path.Combine(outputDir.FullName, "Octave1ScaleSonogram.png"); var recording = new AudioRecording(recordingPath); // default octave scale var fst = FreqScaleType.Linear125Octaves6Tones30Nyquist11025; var freqScale = new FrequencyScale(fst); var sonoConfig = new SonogramConfig { WindowSize = freqScale.WindowSize, WindowOverlap = 0.75, SourceFName = recording.BaseName, NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; // Generate amplitude sonogram and then conver to octave scale var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); // THIS IS THE CRITICAL LINE. COULD DO WITH SEPARATE UNIT TEST sonogram.Data = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(sonogram.Data, freqScale); // DO NOISE REDUCTION var dataMatrix = SNR.NoiseReduce_Standard(sonogram.Data); sonogram.Data = dataMatrix; sonogram.Configuration.WindowSize = freqScale.WindowSize; var image = sonogram.GetImageFullyAnnotated(sonogram.GetImage(), "SPECTROGRAM: " + fst.ToString(), freqScale.GridLineLocations); image.Save(outputImagePath, ImageFormat.Png); // DO FILE EQUALITY TESTS // Check that freqScale.OctaveBinBounds are correct var stemOfExpectedFile = opFileStem + "_Octave1ScaleBinBounds.EXPECTED.json"; var stemOfActualFile = opFileStem + "_Octave1ScaleBinBounds.ACTUAL.json"; var expectedFile1 = PathHelper.ResolveAsset("FrequencyScale\\" + stemOfExpectedFile); if (!expectedFile1.Exists) { LoggedConsole.WriteErrorLine("An EXPECTED results file does not exist. Test will fail!"); LoggedConsole.WriteErrorLine( $"If ACTUAL results file is correct, move it to dir `{PathHelper.TestResources}` and change its suffix to <.EXPECTED.json>"); } var resultFile1 = new FileInfo(Path.Combine(outputDir.FullName, stemOfActualFile)); Json.Serialise(resultFile1, freqScale.BinBounds); FileEqualityHelpers.TextFileEqual(expectedFile1, resultFile1); // Check that freqScale.GridLineLocations are correct stemOfExpectedFile = opFileStem + "_Octave1ScaleGridLineLocations.EXPECTED.json"; stemOfActualFile = opFileStem + "_Octave1ScaleGridLineLocations.ACTUAL.json"; var expectedFile2 = PathHelper.ResolveAsset("FrequencyScale\\" + stemOfExpectedFile); if (!expectedFile2.Exists) { LoggedConsole.WriteErrorLine("An EXPECTED results file does not exist. Test will fail!"); LoggedConsole.WriteErrorLine( $"If ACTUAL results file is correct, move it to dir `{PathHelper.TestResources}` and change its suffix to <.EXPECTED.json>"); } var resultFile2 = new FileInfo(Path.Combine(outputDir.FullName, stemOfActualFile)); Json.Serialise(resultFile2, freqScale.GridLineLocations); FileEqualityHelpers.TextFileEqual(expectedFile2, resultFile2); // Check that image dimensions are correct Assert.AreEqual(645, image.Width); Assert.AreEqual(310, image.Height); }
public void TestPcaWhitening() { var recordingPath = PathHelper.ResolveAsset("Recordings", "BAC2_20071008-085040.wav"); var outputDir = this.outputDirectory; var outputImagePath = Path.Combine(outputDir.FullName, "ReconstrcutedSpectrogram.png"); var fst = FreqScaleType.Linear; var freqScale = new FrequencyScale(fst); var recording = new AudioRecording(recordingPath); var sonoConfig = new SonogramConfig { WindowSize = freqScale.FinalBinCount * 2, WindowOverlap = 0.2, SourceFName = recording.BaseName, NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; // GENERATE AMPLITUDE SPECTROGRAM var spectrogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); spectrogram.Configuration.WindowSize = freqScale.WindowSize; // DO RMS NORMALIZATION spectrogram.Data = SNR.RmsNormalization(spectrogram.Data); // CONVERT NORMALIZED AMPLITUDE SPECTROGRAM TO dB SPECTROGRAM var sonogram = new SpectrogramStandard(spectrogram); // DO NOISE REDUCTION var dataMatrix = PcaWhitening.NoiseReduction(sonogram.Data); sonogram.Data = dataMatrix; // Do Patch Sampling int rows = sonogram.Data.GetLength(0); int columns = sonogram.Data.GetLength(1); int patchWidth = columns; int patchHeight = 1; int numberOfPatches = (rows / patchHeight) * (columns / patchWidth); var sequentialPatches = PatchSampling.GetPatches(sonogram.Data, patchWidth, patchHeight, numberOfPatches, PatchSampling.SamplingMethod.Sequential); double[,] sequentialPatchMatrix = sequentialPatches.ToMatrix(); // DO PCA WHITENING var whitenedSpectrogram = PcaWhitening.Whitening(sequentialPatchMatrix); // reconstructing the spectrogram from sequential patches and the projection matrix obtained from random patches var projectionMatrix = whitenedSpectrogram.ProjectionMatrix;//whitenedSpectrogram.projectionMatrix; var eigenVectors = whitenedSpectrogram.EigenVectors; var numComponents = whitenedSpectrogram.Components; double[,] reconstructedSpec = PcaWhitening.ReconstructSpectrogram(projectionMatrix, sequentialPatchMatrix, eigenVectors, numComponents); sonogram.Data = PatchSampling.ConvertPatches(reconstructedSpec, patchWidth, patchHeight, columns); var reconstructedSpecImage = sonogram.GetImageFullyAnnotated(sonogram.GetImage(), "RECONSTRUCTEDSPECTROGRAM: " + fst.ToString(), freqScale.GridLineLocations); reconstructedSpecImage.Save(outputImagePath, ImageFormat.Png); // DO UNIT TESTING Assert.AreEqual(spectrogram.Data.GetLength(0), sonogram.Data.GetLength(0)); Assert.AreEqual(spectrogram.Data.GetLength(1), sonogram.Data.GetLength(1)); }
public static void Main(Arguments arguments) { // 1. set up the necessary files FileInfo sourceRecording = arguments.Source; FileInfo configFile = arguments.Config.ToFileInfo(); DirectoryInfo opDir = arguments.Output; opDir.Create(); if (arguments.StartOffset.HasValue ^ arguments.EndOffset.HasValue) { throw new InvalidStartOrEndException("If StartOffset or EndOffset is specified, then both must be specified"); } var offsetsProvided = arguments.StartOffset.HasValue && arguments.EndOffset.HasValue; // set default offsets - only use defaults if not provided in argments list TimeSpan?startOffset = null; TimeSpan?endOffset = null; if (offsetsProvided) { startOffset = TimeSpan.FromSeconds(arguments.StartOffset.Value); endOffset = TimeSpan.FromSeconds(arguments.EndOffset.Value); } const string Title = "# MAKE A SONOGRAM FROM AUDIO RECORDING and do OscillationsGeneric activity."; string date = "# DATE AND TIME: " + DateTime.Now; LoggedConsole.WriteLine(Title); LoggedConsole.WriteLine(date); LoggedConsole.WriteLine("# Input audio file: " + sourceRecording.Name); string sourceName = Path.GetFileNameWithoutExtension(sourceRecording.FullName); // 2. get the config dictionary Config configuration = ConfigFile.Deserialize(configFile); // below three lines are examples of retrieving info from Config config // string analysisIdentifier = configuration[AnalysisKeys.AnalysisName]; // bool saveIntermediateWavFiles = (bool?)configuration[AnalysisKeys.SaveIntermediateWavFiles] ?? false; // scoreThreshold = (double?)configuration[AnalysisKeys.EventThreshold] ?? scoreThreshold; // Resample rate must be 2 X the desired Nyquist. Default is that of recording. var resampleRate = configuration.GetIntOrNull(AnalysisKeys.ResampleRate) ?? AppConfigHelper.DefaultTargetSampleRate; var configDict = new Dictionary <string, string>(configuration.ToDictionary()); // #NOISE REDUCTION PARAMETERS //string noisereduce = configDict[ConfigKeys.Mfcc.Key_NoiseReductionType]; configDict[AnalysisKeys.NoiseDoReduction] = "false"; configDict[AnalysisKeys.NoiseReductionType] = "NONE"; configDict[AnalysisKeys.AddAxes] = configuration[AnalysisKeys.AddAxes] ?? "true"; configDict[AnalysisKeys.AddSegmentationTrack] = configuration[AnalysisKeys.AddSegmentationTrack] ?? "true"; configDict[ConfigKeys.Recording.Key_RecordingCallName] = sourceRecording.FullName; configDict[ConfigKeys.Recording.Key_RecordingFileName] = sourceRecording.Name; configDict[AnalysisKeys.AddTimeScale] = configuration[AnalysisKeys.AddTimeScale] ?? "true"; configDict[AnalysisKeys.AddAxes] = configuration[AnalysisKeys.AddAxes] ?? "true"; configDict[AnalysisKeys.AddSegmentationTrack] = configuration[AnalysisKeys.AddSegmentationTrack] ?? "true"; // #################################################################### // print out the sonogram parameters LoggedConsole.WriteLine("\nPARAMETERS"); foreach (KeyValuePair <string, string> kvp in configDict) { LoggedConsole.WriteLine("{0} = {1}", kvp.Key, kvp.Value); } LoggedConsole.WriteLine("Sample Length for detecting oscillations = {0}", SampleLength); // 3: GET RECORDING FileInfo tempAudioSegment = new FileInfo(Path.Combine(opDir.FullName, "tempWavFile.wav")); // delete the temp audio file if it already exists. if (File.Exists(tempAudioSegment.FullName)) { File.Delete(tempAudioSegment.FullName); } // This line creates a temporary version of the source file downsampled as per entry in the config file MasterAudioUtility.SegmentToWav(sourceRecording, tempAudioSegment, new AudioUtilityRequest() { TargetSampleRate = resampleRate }); // 1) get amplitude spectrogram AudioRecording recordingSegment = new AudioRecording(tempAudioSegment.FullName); SonogramConfig sonoConfig = new SonogramConfig(configDict); // default values config BaseSonogram sonogram = new AmplitudeSonogram(sonoConfig, recordingSegment.WavReader); Console.WriteLine("FramesPerSecond = {0}", sonogram.FramesPerSecond); // remove the DC bin sonogram.Data = MatrixTools.Submatrix(sonogram.Data, 0, 1, sonogram.FrameCount - 1, sonogram.Configuration.FreqBinCount); // ############################################################### // DO LocalContrastNormalisation //int fieldSize = 9; //sonogram.Data = LocalContrastNormalisation.ComputeLCN(sonogram.Data, fieldSize); // LocalContrastNormalisation over frequency bins is better and faster. int neighbourhood = 15; double contrastLevel = 0.5; sonogram.Data = NoiseRemoval_Briggs.NoiseReduction_byLCNDivision(sonogram.Data, neighbourhood, contrastLevel); // ############################################################### // lowering the sensitivity threshold increases the number of hits. if (configDict.ContainsKey(AnalysisKeys.OscilDetection2014SensitivityThreshold)) { Oscillations2014.DefaultSensitivityThreshold = double.Parse(configDict[AnalysisKeys.OscilDetection2014SensitivityThreshold]); } if (configDict.ContainsKey(AnalysisKeys.OscilDetection2014SampleLength)) { Oscillations2014.DefaultSampleLength = int.Parse(configDict[AnalysisKeys.OscilDetection2014SensitivityThreshold]); } var list1 = new List <Image>(); //var result = Oscillations2014.GetFreqVsOscillationsDataAndImage(sonogram, 64, "Autocorr-FFT"); //list1.Add(result.FreqOscillationImage); var result = Oscillations2014.GetFreqVsOscillationsDataAndImage(sonogram, "Autocorr-FFT"); list1.Add(result.FreqOscillationImage); result = Oscillations2014.GetFreqVsOscillationsDataAndImage(sonogram, "Autocorr-SVD-FFT"); list1.Add(result.FreqOscillationImage); result = Oscillations2014.GetFreqVsOscillationsDataAndImage(sonogram, "Autocorr-WPD"); list1.Add(result.FreqOscillationImage); Image compositeOscImage1 = ImageTools.CombineImagesInLine(list1.ToArray()); // ############################################################### // init the sonogram image stack var sonogramList = new List <Image>(); var image = sonogram.GetImageFullyAnnotated("AMPLITUDE SPECTROGRAM"); sonogramList.Add(image); //string testPath = @"C:\SensorNetworks\Output\Sonograms\amplitudeSonogram.png"; //image.Save(testPath, ImageFormat.Png); Image envelopeImage = ImageTrack.DrawWaveEnvelopeTrack(recordingSegment, image.Width); sonogramList.Add(envelopeImage); // 2) now draw the standard decibel spectrogram sonogram = new SpectrogramStandard(sonoConfig, recordingSegment.WavReader); // ############################################################### list1 = new List <Image>(); //result = Oscillations2014.GetFreqVsOscillationsDataAndImage(sonogram, 64, "Autocorr-FFT"); //list1.Add(result.FreqOscillationImage); result = Oscillations2014.GetFreqVsOscillationsDataAndImage(sonogram, "Autocorr-FFT"); list1.Add(result.FreqOscillationImage); result = Oscillations2014.GetFreqVsOscillationsDataAndImage(sonogram, "Autocorr-SVD-FFT"); list1.Add(result.FreqOscillationImage); result = Oscillations2014.GetFreqVsOscillationsDataAndImage(sonogram, "Autocorr-WPD"); list1.Add(result.FreqOscillationImage); Image compositeOscImage2 = ImageTools.CombineImagesInLine(list1.ToArray()); // ############################################################### //image = sonogram.GetImageFullyAnnotated("DECIBEL SPECTROGRAM"); //list.Add(image); // combine eight images list1 = new List <Image>(); list1.Add(compositeOscImage1); list1.Add(compositeOscImage2); Image compositeOscImage3 = ImageTools.CombineImagesVertically(list1.ToArray()); string imagePath3 = Path.Combine(opDir.FullName, sourceName + "_freqOscilMatrix.png"); compositeOscImage3.Save(imagePath3, ImageFormat.Png); Image segmentationImage = ImageTrack.DrawSegmentationTrack( sonogram, EndpointDetectionConfiguration.K1Threshold, EndpointDetectionConfiguration.K2Threshold, image.Width); sonogramList.Add(segmentationImage); // 3) now draw the noise reduced decibel spectrogram sonoConfig.NoiseReductionType = NoiseReductionType.Standard; sonoConfig.NoiseReductionParameter = configuration.GetDoubleOrNull(AnalysisKeys.NoiseBgThreshold) ?? 3.0; sonogram = new SpectrogramStandard(sonoConfig, recordingSegment.WavReader); image = sonogram.GetImageFullyAnnotated("NOISE-REDUCED DECIBEL SPECTROGRAM"); sonogramList.Add(image); // ############################################################### // deriving osscilation graph from this noise reduced spectrogram did not work well //Oscillations2014.SaveFreqVsOscillationsDataAndImage(sonogram, sampleLength, algorithmName, opDir); // ############################################################### Image compositeSonogram = ImageTools.CombineImagesVertically(sonogramList); string imagePath2 = Path.Combine(opDir.FullName, sourceName + ".png"); compositeSonogram.Save(imagePath2, ImageFormat.Png); LoggedConsole.WriteLine("\n##### FINISHED FILE ###################################################\n"); }
public static AudioToSonogramResult GenerateFourSpectrogramImages( FileInfo sourceRecording, FileInfo path2SoxSpectrogram, Dictionary <string, string> configDict, bool dataOnly = false, bool makeSoxSonogram = false) { var result = new AudioToSonogramResult(); if (dataOnly && makeSoxSonogram) { throw new ArgumentException("Can't produce data only for a SoX sonogram"); } if (makeSoxSonogram) { SpectrogramTools.MakeSonogramWithSox(sourceRecording, configDict, path2SoxSpectrogram); result.Path2SoxImage = path2SoxSpectrogram; } else if (dataOnly) { var recordingSegment = new AudioRecording(sourceRecording.FullName); var sonoConfig = new SonogramConfig(configDict); // default values config // disable noise removal sonoConfig.NoiseReductionType = NoiseReductionType.None; Log.Warn("Noise removal disabled!"); var sonogram = new SpectrogramStandard(sonoConfig, recordingSegment.WavReader); result.DecibelSpectrogram = sonogram; } else { // init the image stack var list = new List <Image>(); // IMAGE 1) draw amplitude spectrogram var recordingSegment = new AudioRecording(sourceRecording.FullName); var sonoConfig = new SonogramConfig(configDict); // default values config // disable noise removal for first two spectrograms var disabledNoiseReductionType = sonoConfig.NoiseReductionType; sonoConfig.NoiseReductionType = NoiseReductionType.None; BaseSonogram sonogram = new AmplitudeSonogram(sonoConfig, recordingSegment.WavReader); // remove the DC bin if it has not already been removed. // Assume test of divisible by 2 is good enough. int binCount = sonogram.Data.GetLength(1); if (!binCount.IsEven()) { sonogram.Data = MatrixTools.Submatrix(sonogram.Data, 0, 1, sonogram.FrameCount - 1, binCount - 1); } //save spectrogram data at this point - prior to noise reduction var spectrogramDataBeforeNoiseReduction = sonogram.Data; const double neighbourhoodSeconds = 0.25; int neighbourhoodFrames = (int)(sonogram.FramesPerSecond * neighbourhoodSeconds); const double lcnContrastLevel = 0.001; LoggedConsole.WriteLine("LCN: FramesPerSecond (Prior to LCN) = {0}", sonogram.FramesPerSecond); LoggedConsole.WriteLine("LCN: Neighbourhood of {0} seconds = {1} frames", neighbourhoodSeconds, neighbourhoodFrames); const int lowPercentile = 20; sonogram.Data = NoiseRemoval_Briggs.NoiseReduction_byLowestPercentileSubtraction(sonogram.Data, lowPercentile); sonogram.Data = NoiseRemoval_Briggs.NoiseReduction_byLCNDivision(sonogram.Data, neighbourhoodFrames, lcnContrastLevel); //sonogram.Data = NoiseRemoval_Briggs.NoiseReduction_byLowestPercentileSubtraction(sonogram.Data, lowPercentile); var image = sonogram.GetImageFullyAnnotated("AMPLITUDE SPECTROGRAM + Bin LCN (Local Contrast Normalisation)"); list.Add(image); //string path2 = @"C:\SensorNetworks\Output\Sonograms\dataInput2.png"; //Histogram.DrawDistributionsAndSaveImage(sonogram.Data, path2); // double[,] matrix = sonogram.Data; double[,] matrix = ImageTools.WienerFilter(sonogram.Data, 3); double ridgeThreshold = 0.25; byte[,] hits = RidgeDetection.Sobel5X5RidgeDetectionExperiment(matrix, ridgeThreshold); hits = RidgeDetection.JoinDisconnectedRidgesInMatrix(hits, matrix, ridgeThreshold); image = SpectrogramTools.CreateFalseColourAmplitudeSpectrogram(spectrogramDataBeforeNoiseReduction, null, hits); image = sonogram.GetImageAnnotatedWithLinearHerzScale(image, "AMPLITUDE SPECTROGRAM + LCN + ridge detection"); list.Add(image); Image envelopeImage = ImageTrack.DrawWaveEnvelopeTrack(recordingSegment, image.Width); list.Add(envelopeImage); // IMAGE 2) now draw the standard decibel spectrogram sonogram = new SpectrogramStandard(sonoConfig, recordingSegment.WavReader); result.DecibelSpectrogram = (SpectrogramStandard)sonogram; image = sonogram.GetImageFullyAnnotated("DECIBEL SPECTROGRAM"); list.Add(image); Image segmentationImage = ImageTrack.DrawSegmentationTrack( sonogram, EndpointDetectionConfiguration.K1Threshold, EndpointDetectionConfiguration.K2Threshold, image.Width); list.Add(segmentationImage); // keep the sonogram data for later use double[,] dbSpectrogramData = (double[, ])sonogram.Data.Clone(); // 3) now draw the noise reduced decibel spectrogram // #NOISE REDUCTION PARAMETERS - restore noise reduction ################################################################## sonoConfig.NoiseReductionType = disabledNoiseReductionType; sonoConfig.NoiseReductionParameter = double.Parse(configDict[AnalysisKeys.NoiseBgThreshold] ?? "2.0"); // #NOISE REDUCTION PARAMETERS - MARINE HACK ################################################################## //sonoConfig.NoiseReductionType = NoiseReductionType.FIXED_DYNAMIC_RANGE; //sonoConfig.NoiseReductionParameter = 80.0; sonogram = new SpectrogramStandard(sonoConfig, recordingSegment.WavReader); image = sonogram.GetImageFullyAnnotated("DECIBEL SPECTROGRAM + Lamel noise subtraction"); list.Add(image); // keep the sonogram data for later use double[,] nrSpectrogramData = sonogram.Data; // 4) A FALSE-COLOUR VERSION OF SPECTROGRAM // ########################### SOBEL ridge detection ridgeThreshold = 3.5; matrix = ImageTools.WienerFilter(dbSpectrogramData, 3); hits = RidgeDetection.Sobel5X5RidgeDetectionExperiment(matrix, ridgeThreshold); // ########################### EIGEN ridge detection //double ridgeThreshold = 6.0; //double dominanceThreshold = 0.7; //var rotatedData = MatrixTools.MatrixRotate90Anticlockwise(dbSpectrogramData); //byte[,] hits = RidgeDetection.StructureTensorRidgeDetection(rotatedData, ridgeThreshold, dominanceThreshold); //hits = MatrixTools.MatrixRotate90Clockwise(hits); // ########################### EIGEN ridge detection image = SpectrogramTools.CreateFalseColourDecibelSpectrogram(dbSpectrogramData, nrSpectrogramData, hits); image = sonogram.GetImageAnnotatedWithLinearHerzScale(image, "DECIBEL SPECTROGRAM - Colour annotated"); list.Add(image); // 5) TODO: ONE OF THESE YEARS FIX UP THE CEPTRAL SONOGRAM ////SpectrogramCepstral cepgram = new SpectrogramCepstral((AmplitudeSonogram)amplitudeSpg); ////var mti3 = SpectrogramTools.Sonogram2MultiTrackImage(sonogram, configDict); ////var image3 = mti3.GetImage(); ////image3.Save(fiImage.FullName + "3", ImageFormat.Png); // 6) COMBINE THE SPECTROGRAM IMAGES result.CompositeImage = ImageTools.CombineImagesVertically(list); } return(result); }
public void OctaveFrequencyScale2() { var recordingPath = PathHelper.ResolveAsset("Recordings", "MarineJasco_AMAR119-00000139.00000139.Chan_1-24bps.1375012796.2013-07-28-11-59-56-16bit-60sec.wav"); var opFileStem = "JascoMarineGBR1"; var outputDir = this.outputDirectory; var outputImagePath = Path.Combine(this.outputDirectory.FullName, "Octave2ScaleSonogram.png"); var recording = new AudioRecording(recordingPath); var fst = FreqScaleType.Linear125Octaves7Tones28Nyquist32000; var freqScale = new FrequencyScale(fst); var sonoConfig = new SonogramConfig { WindowSize = freqScale.WindowSize, WindowOverlap = 0.2, SourceFName = recording.BaseName, NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); sonogram.Data = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(sonogram.Data, freqScale); // DO NOISE REDUCTION var dataMatrix = SNR.NoiseReduce_Standard(sonogram.Data); sonogram.Data = dataMatrix; sonogram.Configuration.WindowSize = freqScale.WindowSize; var image = sonogram.GetImageFullyAnnotated(sonogram.GetImage(), "SPECTROGRAM: " + fst.ToString(), freqScale.GridLineLocations); image.Save(outputImagePath); // DO FILE EQUALITY TESTS // Check that freqScale.OctaveBinBounds are correct var expectedBinBoundsFile = PathHelper.ResolveAsset("FrequencyScale", opFileStem + "_Octave2ScaleBinBounds.EXPECTED.json"); var expectedBinBounds = Json.Deserialize <int[, ]>(expectedBinBoundsFile); Assert.That.MatricesAreEqual(expectedBinBounds, freqScale.BinBounds); // Check that freqScale.GridLineLocations are correct var expected = new[, ] { { 34, 125 }, { 62, 250 }, { 89, 500 }, { 117, 1000 }, { 145, 2000 }, { 173, 4000 }, { 201, 8000 }, { 229, 16000 }, { 256, 32000 }, }; Assert.That.MatricesAreEqual(expected, freqScale.GridLineLocations); // Check that image dimensions are correct Assert.AreEqual(201, image.Width); Assert.AreEqual(310, image.Height); }
public void OctaveFrequencyScale1() { var recordingPath = PathHelper.ResolveAsset("Recordings", "BAC2_20071008-085040.wav"); var opFileStem = "BAC2_20071008"; var outputDir = this.outputDirectory; var outputImagePath = Path.Combine(outputDir.FullName, "Octave1ScaleSonogram.png"); var recording = new AudioRecording(recordingPath); // default octave scale var fst = FreqScaleType.Linear125Octaves6Tones30Nyquist11025; var freqScale = new FrequencyScale(fst); var sonoConfig = new SonogramConfig { WindowSize = freqScale.WindowSize, WindowOverlap = 0.75, SourceFName = recording.BaseName, NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; // Generate amplitude sonogram and then conver to octave scale var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); // THIS IS THE CRITICAL LINE. COULD DO WITH SEPARATE UNIT TEST sonogram.Data = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(sonogram.Data, freqScale); // DO NOISE REDUCTION var dataMatrix = SNR.NoiseReduce_Standard(sonogram.Data); sonogram.Data = dataMatrix; sonogram.Configuration.WindowSize = freqScale.WindowSize; var image = sonogram.GetImageFullyAnnotated(sonogram.GetImage(), "SPECTROGRAM: " + fst.ToString(), freqScale.GridLineLocations); image.Save(outputImagePath); #pragma warning disable SA1500 // Braces for multi-line statements should not share line var expectedBinBounds = new[, ] { { 0, 0 }, { 1, 3 }, { 2, 5 }, { 3, 8 }, { 4, 11 }, { 5, 13 }, { 6, 16 }, { 7, 19 }, { 8, 22 }, { 9, 24 }, { 10, 27 }, { 11, 30 }, { 12, 32 }, { 13, 35 }, { 14, 38 }, { 15, 40 }, { 16, 43 }, { 17, 46 }, { 18, 48 }, { 19, 51 }, { 20, 54 }, { 21, 57 }, { 22, 59 }, { 23, 62 }, { 24, 65 }, { 25, 67 }, { 26, 70 }, { 27, 73 }, { 28, 75 }, { 29, 78 }, { 30, 81 }, { 31, 83 }, { 32, 86 }, { 33, 89 }, { 34, 92 }, { 35, 94 }, { 36, 97 }, { 37, 100 }, { 38, 102 }, { 39, 105 }, { 40, 108 }, { 41, 110 }, { 42, 113 }, { 43, 116 }, { 44, 118 }, { 45, 121 }, { 46, 124 }, { 47, 127 }, { 48, 129 }, { 49, 132 }, { 50, 135 }, { 51, 137 }, { 52, 140 }, { 53, 143 }, { 55, 148 }, { 56, 151 }, { 57, 153 }, { 58, 156 }, { 59, 159 }, { 61, 164 }, { 62, 167 }, { 63, 170 }, { 65, 175 }, { 66, 178 }, { 68, 183 }, { 69, 186 }, { 71, 191 }, { 72, 194 }, { 74, 199 }, { 75, 202 }, { 77, 207 }, { 79, 213 }, { 80, 215 }, { 82, 221 }, { 84, 226 }, { 86, 231 }, { 88, 237 }, { 89, 240 }, { 91, 245 }, { 93, 250 }, { 95, 256 }, { 97, 261 }, { 100, 269 }, { 102, 275 }, { 104, 280 }, { 106, 285 }, { 109, 293 }, { 111, 299 }, { 113, 304 }, { 116, 312 }, { 118, 318 }, { 121, 326 }, { 124, 334 }, { 126, 339 }, { 129, 347 }, { 132, 355 }, { 135, 363 }, { 138, 371 }, { 141, 380 }, { 144, 388 }, { 147, 396 }, { 150, 404 }, { 153, 412 }, { 157, 423 }, { 160, 431 }, { 164, 441 }, { 167, 450 }, { 171, 460 }, { 175, 471 }, { 178, 479 }, { 182, 490 }, { 186, 501 }, { 190, 511 }, { 194, 522 }, { 199, 536 }, { 203, 546 }, { 208, 560 }, { 212, 571 }, { 217, 584 }, { 221, 595 }, { 226, 608 }, { 231, 622 }, { 236, 635 }, { 241, 649 }, { 247, 665 }, { 252, 678 }, { 258, 694 }, { 263, 708 }, { 269, 724 }, { 275, 740 }, { 281, 756 }, { 287, 773 }, { 293, 789 }, { 300, 807 }, { 306, 824 }, { 313, 842 }, { 320, 861 }, { 327, 880 }, { 334, 899 }, { 341, 918 }, { 349, 939 }, { 356, 958 }, { 364, 980 }, { 372, 1001 }, { 380, 1023 }, { 388, 1044 }, { 397, 1069 }, { 406, 1093 }, { 415, 1117 }, { 424, 1141 }, { 433, 1165 }, { 442, 1190 }, { 452, 1217 }, { 462, 1244 }, { 472, 1270 }, { 482, 1297 }, { 493, 1327 }, { 504, 1357 }, { 515, 1386 }, { 526, 1416 }, { 537, 1445 }, { 549, 1478 }, { 561, 1510 }, { 573, 1542 }, { 586, 1577 }, { 599, 1612 }, { 612, 1647 }, { 625, 1682 }, { 639, 1720 }, { 653, 1758 }, { 667, 1795 }, { 682, 1836 }, { 697, 1876 }, { 712, 1916 }, { 728, 1960 }, { 744, 2003 }, { 760, 2046 }, { 776, 2089 }, { 793, 2134 }, { 811, 2183 }, { 829, 2231 }, { 847, 2280 }, { 865, 2328 }, { 884, 2379 }, { 903, 2431 }, { 923, 2484 }, { 943, 2538 }, { 964, 2595 }, { 985, 2651 }, { 1007, 2710 }, { 1029, 2770 }, { 1051, 2829 }, { 1074, 2891 }, { 1098, 2955 }, { 1122, 3020 }, { 1146, 3085 }, { 1172, 3155 }, { 1197, 3222 }, { 1223, 3292 }, { 1250, 3365 }, { 1278, 3440 }, { 1305, 3513 }, { 1334, 3591 }, { 1363, 3669 }, { 1393, 3749 }, { 1424, 3833 }, { 1455, 3916 }, { 1487, 4002 }, { 1519, 4089 }, { 1552, 4177 }, { 1586, 4269 }, { 1621, 4363 }, { 1657, 4460 }, { 1693, 4557 }, { 1730, 4657 }, { 1768, 4759 }, { 1806, 4861 }, { 1846, 4969 }, { 1886, 5076 }, { 1928, 5190 }, { 1970, 5303 }, { 2013, 5418 }, { 2057, 5537 }, { 2102, 5658 }, { 2148, 5782 }, { 2195, 5908 }, { 2243, 6037 }, { 2292, 6169 }, { 2343, 6307 }, { 2394, 6444 }, { 2446, 6584 }, { 2500, 6729 }, { 2555, 6877 }, { 2610, 7025 }, { 2668, 7181 }, { 2726, 7337 }, { 2786, 7499 }, { 2847, 7663 }, { 2909, 7830 }, { 2973, 8002 }, { 3038, 8177 }, { 3104, 8355 }, { 3172, 8538 }, { 3242, 8726 }, { 3313, 8917 }, { 3385, 9111 }, { 3459, 9310 }, { 3535, 9515 }, { 3612, 9722 }, { 3691, 9935 }, { 3772, 10153 }, { 3855, 10376 }, { 3939, 10602 }, { 4026, 10837 }, { 4095, 11022 }, { 4095, 11022 }, }; #pragma warning restore SA1500 // Braces for multi-line statements should not share line Assert.That.MatricesAreEqual(expectedBinBounds, freqScale.BinBounds); // Check that freqScale.GridLineLocations are correct var expected = new[, ] { { 46, 125 }, { 79, 250 }, { 111, 500 }, { 143, 1000 }, { 175, 2000 }, { 207, 4000 }, { 239, 8000 }, }; Assert.That.MatricesAreEqual(expected, freqScale.GridLineLocations); // Check that image dimensions are correct Assert.AreEqual(645, image.Width); Assert.AreEqual(310, image.Height); }
public static void Execute(Arguments arguments) { // this is a generic command for testing // input should be only one-minute wav file // read in the config file // pass the config to the algorithm // output the results var configPath = @"SpectralPeakTrackingConfig.yml"; var recordingPath = @""; var imagePath = @""; var configFile = configPath.ToFileInfo(); if (configFile == null) { throw new FileNotFoundException("No config file argument provided"); } else if (!configFile.Exists) { throw new ArgumentException($"Config file {configFile.FullName} not found"); } var configuration = ConfigFile.Deserialize <SpectralPeakTrackingConfig>(configFile); var recording = new AudioRecording(recordingPath); // get the nyquist value from the recording int nyquist = new AudioRecording(recordingPath).Nyquist; int frameSize = configuration.FrameWidth; double frameOverlap = configuration.FrameOverlap; int finalBinCount = 512; var hertzPerFreqBin = nyquist / finalBinCount; FreqScaleType scaleType = FreqScaleType.Linear; var sonoConfig = new SonogramConfig { WindowSize = frameSize, WindowOverlap = frameOverlap, DoMelScale = (scaleType == FreqScaleType.Mel) ? true : false, MelBinCount = (scaleType == FreqScaleType.Mel) ? finalBinCount : frameSize / 2, NoiseReductionType = NoiseReductionType.None, }; //var sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); var amplitudeSpectrogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); // Broken in merge b7e03070a9cd72ab0632789a3412967a6cc54cd6 //var energySpectrogram = new EnergySpectrogram(amplitudeSpectrogram); var decibelSpectrogram = new SpectrogramStandard(sonoConfig, recording.WavReader); double frameStepSize = sonoConfig.GetFrameOffset(); double stepDuration = frameStepSize / (nyquist * 2); // Noise Reduction to be added //var output = SpectralPeakTracking2018.SpectralPeakTracking(energySpectrogram.Data, configuration.SptSettings, hertzPerFreqBin); // draw the local peaks //double[,] hits = SpectralPeakTracking2018.MakeHitMatrix(energySpectrogram.Data, output.TargetPeakBinsIndex, output.BandIndex); //var image = SpectralPeakTracking2018.DrawSonogram(decibelSpectrogram, hits); //image.Save(imagePath, ImageFormat.Bmp); }
public static void Execute(Arguments arguments) { if (arguments == null) { throw new InvalidOperationException(); } string title = "# DETECT FROG RIBBIT."; string date = "# DATE AND TIME: " + DateTime.Now; Log.WriteLine(title); Log.WriteLine(date); // SET VERBOSITY Log.Verbosity = 1; //i: Set up the file names //string outputDir = Path.GetDirectoryName(iniPath) + "\\"; //ii: READ PARAMETER VALUES FROM INI FILE //var config = new Configuration(iniPath); //Dictionary<string, string> dict = config.GetTable(); //string sourceFile = dict[FeltTemplate_Create.key_SOURCE_RECORDING]; //string sourceDir = dict[FeltTemplate_Create.key_SOURCE_DIRECTORY]; //double dB_Threshold = Double.Parse(dict[FeltTemplate_Create.key_DECIBEL_THRESHOLD]); //double maxTemplateIntensity = Double.Parse(dict[FeltTemplate_Create.key_TEMPLATE_MAX_INTENSITY]); //int neighbourhood = Int32.Parse(dict[FeltTemplate_Create.key_DONT_CARE_NH]); //the do not care neighbourhood //int lineLength = Int32.Parse(dict[FeltTemplate_Create.key_LINE_LENGTH]); //double templateThreshold = dB_Threshold / maxTemplateIntensity; //int bitmapThreshold = (int)(255 - (templateThreshold * 255)); //IMPORTANT NOTE: // You must determine a value for the variable maxOscilScore. This is used to normalize the oscillation scores so that lie in 0,1. // The default Value = 60.0; but it must be determined for each species. // This is obtained from the score on training data. // Find the relevant commented Code in the FrogRibbitRecognizer() method. string frogName; //, filterName; //double windowDuration, windowOverlap, dctDuration, dctThreshold, int midBandFreq; //, minOscilRate, maxOscilRate; //bool normaliseDCT = false; //i: GET RECORDING AudioRecording recording = new AudioRecording(arguments.Source.FullName); var scores = new List <double[]>(); Log.WriteLine("# Scan Audio Recording: " + arguments.Source.FullName); //############### Buffo sp. - CANE TOAD ######################################################################### frogName = "Cane_toad"; Log.WriteLine("# Do Recognizer:- " + frogName); midBandFreq = 640; // middle of freq band of interest //Default windowDuration = 5.0 milliseconds - NOTE: 128 samples @ 22.050kHz = 5.805ms. Tuple <double[], AudioRecording, double[], double[]> results = FrogRibbitRecognizer( recording, "Chebyshev_Lowpass_1000", midBandFreq, windowDuration: 10.0, dctDuration: 0.5, minOscilRate: 11, maxOscilRate: 17, maxOscilScore: 30.0); scores.Add(results.Item1); //############### Litoria rothii - Laughing tree Frog ######################################################################### frogName = "Litoria_rothii"; Log.WriteLine("# Do Recognizer:- " + frogName); midBandFreq = 1850; // middle of freq band of interest results = FrogRibbitRecognizer(recording, "Chebyshev_Lowpass_3000", midBandFreq, dctDuration: 0.5, minOscilRate: 9, maxOscilRate: 11, maxOscilScore: 30.0); scores.Add(results.Item1); //############### Rheobatrachus silus - GASTRIC BROODING FROG ######################################################################### frogName = "Rheobatrachus silus"; Log.WriteLine("# Do Recognizer:- " + frogName); midBandFreq = 1550; // middle of freq band of interest results = FrogRibbitRecognizer(recording, "Chebyshev_Lowpass_3000", midBandFreq, dctDuration: 0.2, minOscilRate: 55, maxOscilRate: 65, maxOscilScore: 60.0); scores.Add(results.Item1); //############### Lymnodynastes peronii - TOCK FROG related to POBBLEBONK ########################################################## //WARNING####!!!!!! THIS IS NOT A RIBBIT FROG //frogName = "Lymnodynastes_peronii"; //Log.WriteLine("# Do Recognizer:- " + frogName); //midBandFreq = 1500; // middle of freq band of interest //results = FrogRibbitRecognizer(recording, "Chebyshev_Lowpass_3000", midBandFreq, dctDuration:0.2, minOscilRate:55, maxOscilRate:75, maxOscilScore:60.0); //scores.Add(results.Item1); //######################################################################################################## //######################################################################################################## //vii: MAKE SONOGRAM Log.WriteLine("# Make sonogram."); SonogramConfig sonoConfig = new SonogramConfig(); //default values config sonoConfig.SourceFName = recording.BaseName; sonoConfig.WindowSize = SonogramConfig.DEFAULT_WINDOW_SIZE; sonoConfig.WindowOverlap = 0.5; // set default value sonoConfig.DoMelScale = false; sonoConfig.NoiseReductionType = NoiseReductionType.None; //sonoConfig.NoiseReductionType = NoiseReductionType.STANDARD; var filteredRecording = results.Item2; //AmplitudeSonogram basegram = new AmplitudeSonogram(sonoConfig, recording.GetWavReader()); AmplitudeSonogram basegram = new AmplitudeSonogram(sonoConfig, filteredRecording.WavReader); SpectrogramStandard sonogram = new SpectrogramStandard(basegram); //spectrogram has dim[N,257] //viii WRITE FILTERED SIGNAL IF NEED TO DEBUG //write the signal: IMPORTANT: ENSURE VALUES ARE IN RANGE -32768 to +32768 //int bitRate = 16; //WavWriter.WriteWavFile(filteredRecording.GetWavReader().Samples, filteredRecording.SampleRate, bitRate, recordingPath + "filtered.wav"); // ix: DRAW SONOGRAM AND SCORES string imagePath = arguments.Source.Name + ".png"; var dBarray = results.Item3; var miscell = results.Item4; DrawSonogram(sonogram, imagePath, dBarray, miscell, scores); Log.WriteLine("# Finished everything!"); }
public static AudioToSonogramResult GenerateSpectrogramImages(FileInfo sourceRecording, Dictionary <string, string> configDict, DirectoryInfo outputDirectory) { // the source name was set up in the Analyse() method. But it could also be obtained directly from recording. string sourceName = configDict[ConfigKeys.Recording.Key_RecordingFileName]; sourceName = Path.GetFileNameWithoutExtension(sourceName); var result = new AudioToSonogramResult(); // init the image stack var list = new List <Image>(); // 1) draw amplitude spectrogram var recordingSegment = new AudioRecording(sourceRecording.FullName); // default values config except disable noise removal for first two spectrograms SonogramConfig sonoConfig = new SonogramConfig(configDict) { NoiseReductionType = NoiseReductionType.None }; BaseSonogram sonogram = new AmplitudeSonogram(sonoConfig, recordingSegment.WavReader); // remove the DC bin sonogram.Data = MatrixTools.Submatrix(sonogram.Data, 0, 1, sonogram.FrameCount - 1, sonogram.Configuration.FreqBinCount); // save spectrogram data at this point - prior to noise reduction double[,] spectrogramDataBeforeNoiseReduction = sonogram.Data; const int lowPercentile = 20; const double neighbourhoodSeconds = 0.25; int neighbourhoodFrames = (int)(sonogram.FramesPerSecond * neighbourhoodSeconds); const double lcnContrastLevel = 0.25; ////LoggedConsole.WriteLine("LCN: FramesPerSecond (Prior to LCN) = {0}", sonogram.FramesPerSecond); ////LoggedConsole.WriteLine("LCN: Neighbourhood of {0} seconds = {1} frames", neighbourhoodSeconds, neighbourhoodFrames); sonogram.Data = NoiseRemoval_Briggs.NoiseReduction_ShortRecordings_SubtractAndLCN(sonogram.Data, lowPercentile, neighbourhoodFrames, lcnContrastLevel); // draw amplitude spectrogram unannotated FileInfo outputImage1 = new FileInfo(Path.Combine(outputDirectory.FullName, sourceName + ".amplitd.bmp")); ImageTools.DrawReversedMatrix(MatrixTools.MatrixRotate90Anticlockwise(sonogram.Data), outputImage1.FullName); // draw amplitude spectrogram annotated var image = sonogram.GetImageFullyAnnotated("AMPLITUDE SPECTROGRAM + Bin LCN (Local Contrast Normalisation)"); list.Add(image); ////string path2 = @"C:\SensorNetworks\Output\Sonograms\dataInput2.png"; ////Histogram.DrawDistributionsAndSaveImage(sonogram.Data, path2); // 2) A FALSE-COLOUR VERSION OF AMPLITUDE SPECTROGRAM double ridgeThreshold = 0.20; double[,] matrix = ImageTools.WienerFilter(sonogram.Data, 3); byte[,] hits = RidgeDetection.Sobel5X5RidgeDetectionExperiment(matrix, ridgeThreshold); hits = RidgeDetection.JoinDisconnectedRidgesInMatrix(hits, matrix, ridgeThreshold); image = SpectrogramTools.CreateFalseColourAmplitudeSpectrogram(spectrogramDataBeforeNoiseReduction, null, hits); image = sonogram.GetImageAnnotatedWithLinearHerzScale(image, "AMPLITUDE SPECTROGRAM + LCN + ridge detection"); list.Add(image); Image envelopeImage = ImageTrack.DrawWaveEnvelopeTrack(recordingSegment, image.Width); list.Add(envelopeImage); // 3) now draw the standard decibel spectrogram sonogram = new SpectrogramStandard(sonoConfig, recordingSegment.WavReader); // remove the DC bin sonogram.Data = MatrixTools.Submatrix(sonogram.Data, 0, 1, sonogram.FrameCount - 1, sonogram.Configuration.FreqBinCount); // draw decibel spectrogram unannotated FileInfo outputImage2 = new FileInfo(Path.Combine(outputDirectory.FullName, sourceName + ".deciBel.bmp")); ImageTools.DrawReversedMatrix(MatrixTools.MatrixRotate90Anticlockwise(sonogram.Data), outputImage2.FullName); image = sonogram.GetImageFullyAnnotated("DECIBEL SPECTROGRAM"); list.Add(image); Image segmentationImage = ImageTrack.DrawSegmentationTrack( sonogram, EndpointDetectionConfiguration.K1Threshold, EndpointDetectionConfiguration.K2Threshold, image.Width); list.Add(segmentationImage); // keep the sonogram data (NOT noise reduced) for later use double[,] dbSpectrogramData = (double[, ])sonogram.Data.Clone(); // 4) now draw the noise reduced decibel spectrogram sonoConfig.NoiseReductionType = NoiseReductionType.Standard; sonoConfig.NoiseReductionParameter = 3; ////sonoConfig.NoiseReductionType = NoiseReductionType.SHORT_RECORDING; ////sonoConfig.NoiseReductionParameter = 50; sonogram = new SpectrogramStandard(sonoConfig, recordingSegment.WavReader); // draw decibel spectrogram unannotated FileInfo outputImage3 = new FileInfo(Path.Combine(outputDirectory.FullName, sourceName + ".noNoise_dB.bmp")); ImageTools.DrawReversedMatrix(MatrixTools.MatrixRotate90Anticlockwise(sonogram.Data), outputImage3.FullName); image = sonogram.GetImageFullyAnnotated("DECIBEL SPECTROGRAM + Lamel noise subtraction"); list.Add(image); // keep the sonogram data for later use double[,] nrSpectrogramData = sonogram.Data; // 5) A FALSE-COLOUR VERSION OF DECIBEL SPECTROGRAM ridgeThreshold = 2.5; matrix = ImageTools.WienerFilter(dbSpectrogramData, 3); hits = RidgeDetection.Sobel5X5RidgeDetectionExperiment(matrix, ridgeThreshold); image = SpectrogramTools.CreateFalseColourDecibelSpectrogram(dbSpectrogramData, nrSpectrogramData, hits); image = sonogram.GetImageAnnotatedWithLinearHerzScale(image, "DECIBEL SPECTROGRAM - Colour annotated"); list.Add(image); // 6) COMBINE THE SPECTROGRAM IMAGES Image compositeImage = ImageTools.CombineImagesVertically(list); FileInfo outputImage = new FileInfo(Path.Combine(outputDirectory.FullName, sourceName + ".5spectro.png")); compositeImage.Save(outputImage.FullName, ImageFormat.Png); result.SpectrogramFile = outputImage; // 7) Generate the FREQUENCY x OSCILLATIONS Graphs and csv data ////bool saveData = true; ////bool saveImage = true; ////double[] oscillationsSpectrum = Oscillations2014.GenerateOscillationDataAndImages(sourceRecording, configDict, saveData, saveImage); return(result); }
public static void TestMethod_GenerateSignal2() { int sampleRate = 64000; double duration = 30; // signal duration in seconds int[] harmonics = { 500, 1000, 2000, 4000, 8000 }; var freqScale = new FrequencyScale(FreqScaleType.Linear125Octaves7Tones28Nyquist32000); string path = @"C:\SensorNetworks\Output\Sonograms\UnitTestSonograms\SineSignal2.png"; var recording = GenerateTestRecording(sampleRate, duration, harmonics, WaveType.Cosine); // init the default sonogram config var sonoConfig = new SonogramConfig { WindowSize = freqScale.WindowSize, WindowOverlap = 0.2, SourceFName = "Signal2", NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); sonogram.Data = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(sonogram.Data, freqScale); // pick a row, any row var oneSpectrum = MatrixTools.GetRow(sonogram.Data, 40); oneSpectrum = DataTools.normalise(oneSpectrum); var peaks = DataTools.GetPeaks(oneSpectrum, 0.5); var peakIds = new List <int>(); for (int i = 5; i < peaks.Length - 5; i++) { if (peaks[i]) { int peakId = freqScale.BinBounds[i, 0]; peakIds.Add(peakId); LoggedConsole.WriteLine($"Spectral peak located in bin {peakId}, Herz={freqScale.BinBounds[i, 1]}"); } } //if (peaks[129] && peaks[257] && peaks[513] && peaks[1025] && peaks[2049]) if (peakIds[0] == 129 && peakIds[1] == 257 && peakIds[2] == 513 && peakIds[3] == 1025 && peakIds[4] == 2049) { LoggedConsole.WriteSuccessLine("Spectral Peaks found at correct places"); } else { LoggedConsole.WriteErrorLine("Spectral Peaks found at INCORRECT places"); } foreach (int h in harmonics) { LoggedConsole.WriteLine($"Harmonic {h}Hertz should be in bin {freqScale.GetBinIdForHerzValue(h)}"); } // spectrogram without framing, annotation etc var image = sonogram.GetImage(); string title = $"Spectrogram of Harmonics: {DataTools.Array2String(harmonics)} SR={sampleRate} Window={freqScale.WindowSize}"; image = sonogram.GetImageFullyAnnotated(image, title, freqScale.GridLineLocations); image.Save(path); }
/// <summary> /// Generates the FREQUENCY x OSCILLATIONS Graphs and csv /// </summary> public static Tuple <Image, double[, ], double[]> GenerateOscillationDataAndImages(FileInfo audioSegment, Dictionary <string, string> configDict, bool drawImage = false) { // set two oscillation detection parameters double sensitivity = DefaultSensitivityThreshold; if (configDict.ContainsKey(AnalysisKeys.OscilDetection2014SensitivityThreshold)) { sensitivity = double.Parse(configDict[AnalysisKeys.OscilDetection2014SensitivityThreshold]); } // Sample length i.e. number of frames spanned to calculate oscillations per second int sampleLength = DefaultSampleLength; if (configDict.ContainsKey(AnalysisKeys.OscilDetection2014SampleLength)) { sampleLength = int.Parse(configDict[AnalysisKeys.OscilDetection2014SampleLength]); } SonogramConfig sonoConfig = new SonogramConfig(configDict); // default values config if (configDict.ContainsKey(AnalysisKeys.OscilDetection2014FrameSize)) { sonoConfig.WindowSize = int.Parse(configDict[AnalysisKeys.OscilDetection2014FrameSize]); } var recordingSegment = new AudioRecording(audioSegment.FullName); BaseSonogram sonogram = new AmplitudeSonogram(sonoConfig, recordingSegment.WavReader); // remove the DC bin if it has not already been removed. // Assume test of divisible by 2 is good enough. int binCount = sonogram.Data.GetLength(1); if (!binCount.IsEven()) { sonogram.Data = MatrixTools.Submatrix(sonogram.Data, 0, 1, sonogram.FrameCount - 1, binCount - 1); } //LoggedConsole.WriteLine("Oscillation Detection: Sample rate = {0}", sonogram.SampleRate); //LoggedConsole.WriteLine("Oscillation Detection: FramesPerSecond = {0}", sonogram.FramesPerSecond); // Do LOCAL CONRAST Normalisation first. LCN over frequency bins is better and faster than standard noise removal. double neighbourhoodSeconds = 0.25; int neighbourhoodFrames = (int)(sonogram.FramesPerSecond * neighbourhoodSeconds); double lcnContrastLevel = 0.5; // was previously 0.1 LoggedConsole.WriteLine("LCN: FramesPerSecond (Prior to LCN) = {0}", sonogram.FramesPerSecond); LoggedConsole.WriteLine("LCN: Neighbourhood of {0} seconds = {1} frames", neighbourhoodSeconds, neighbourhoodFrames); sonogram.Data = NoiseRemoval_Briggs.NoiseReduction_byLCNDivision(sonogram.Data, neighbourhoodFrames, lcnContrastLevel); string algorithmName1 = "autocorr-svd-fft"; double[,] freqOscilMatrix1 = GetFrequencyByOscillationsMatrix(sonogram.Data, sensitivity, sampleLength, algorithmName1); //get the max spectral index - this reduces the matrix to an array double[] spectralIndex1 = ConvertMatrix2SpectralIndexBySummingFreqColumns(freqOscilMatrix1, 0); Image compositeImage = null; if (drawImage) { string algorithmName2 = "autocorr-fft"; double[,] freqOscilMatrix2 = GetFrequencyByOscillationsMatrix(sonogram.Data, sensitivity, sampleLength, algorithmName2); var image1 = GetFreqVsOscillationsImage(freqOscilMatrix1, sonogram.FramesPerSecond, sonogram.FBinWidth, sampleLength, algorithmName1); var image2 = GetFreqVsOscillationsImage(freqOscilMatrix2, sonogram.FramesPerSecond, sonogram.FBinWidth, sampleLength, algorithmName2); compositeImage = ImageTools.CombineImagesInLine(new[] { image1, image2 }); } // Return (1) composite image of oscillations, (2) data matrix from only one algorithm, // and (3) spectrum of oscillation values for accumulation into data from a multi-hour recording. return(Tuple.Create(compositeImage, freqOscilMatrix1, spectralIndex1)); }
public void TestFreqScaleOnArtificialSignal2() { int sampleRate = 64000; double duration = 30; // signal duration in seconds int[] harmonics = { 500, 1000, 2000, 4000, 8000 }; var freqScale = new FrequencyScale(FreqScaleType.Linear125Octaves7Tones28Nyquist32000); var outputImagePath = Path.Combine(this.outputDirectory.FullName, "Signal2_OctaveFreqScale.png"); var recording = DspFilters.GenerateTestRecording(sampleRate, duration, harmonics, WaveType.Cosine); // init the default sonogram config var sonoConfig = new SonogramConfig { WindowSize = freqScale.WindowSize, WindowOverlap = 0.2, SourceFName = "Signal2", NoiseReductionType = NoiseReductionType.None, NoiseReductionParameter = 0.0, }; var sonogram = new AmplitudeSonogram(sonoConfig, recording.WavReader); sonogram.Data = OctaveFreqScale.ConvertAmplitudeSpectrogramToDecibelOctaveScale(sonogram.Data, freqScale); // pick a row, any row var oneSpectrum = MatrixTools.GetRow(sonogram.Data, 40); oneSpectrum = DataTools.filterMovingAverage(oneSpectrum, 5); var peaks = DataTools.GetPeaks(oneSpectrum); var peakIds = new List <int>(); for (int i = 5; i < peaks.Length - 5; i++) { if (peaks[i]) { int peakId = freqScale.BinBounds[i, 0]; peakIds.Add(peakId); LoggedConsole.WriteLine($"Spectral peak located in bin {peakId}, Herz={freqScale.BinBounds[i, 1]}"); } } foreach (int h in harmonics) { LoggedConsole.WriteLine($"Harmonic {h}Herz should be in bin {freqScale.GetBinIdForHerzValue(h)}"); } Assert.AreEqual(5, peakIds.Count); Assert.AreEqual(129, peakIds[0]); Assert.AreEqual(257, peakIds[1]); Assert.AreEqual(513, peakIds[2]); Assert.AreEqual(1025, peakIds[3]); Assert.AreEqual(2049, peakIds[4]); var image = sonogram.GetImage(); string title = $"Spectrogram of Harmonics: {DataTools.Array2String(harmonics)} SR={sampleRate} Window={freqScale.WindowSize}"; image = sonogram.GetImageFullyAnnotated(image, title, freqScale.GridLineLocations); image.Save(outputImagePath); // Check that image dimensions are correct Assert.AreEqual(146, image.Width); Assert.AreEqual(310, image.Height); }
public static void Execute(Arguments arguments) { if (arguments == null) { arguments = Dev(); } string title = "# FIND OTHER ACOUSTIC EVENTS LIKE THIS"; string date = "# DATE AND TIME: " + DateTime.Now; Log.WriteLine(title); Log.WriteLine(date); Log.Verbosity = 1; Log.WriteIfVerbose("# Recording =" + arguments.Source); //the recording to be scanned Log.WriteIfVerbose("# Template list =" + arguments.Config); //the path to a file containing the paths to template locations, one template per line Log.WriteIfVerbose("# Output folder =" + arguments.Output); //name of output dir var allEvents = new List <AcousticEvent>(); var scoresList = new List <double[]>(); var thresholdList = new List <double>(); //i: GET RECORDING AudioRecording recording = new AudioRecording(arguments.Source.FullName); //if (recording.SampleRate != 22050) recording.ConvertSampleRate22kHz(); // THIS METHOD CALL IS OBSOLETE int sr = recording.SampleRate; //ii: MAKE SONOGRAM Log.WriteLine("Start sonogram."); SonogramConfig sonoConfig = new SonogramConfig(); //default values config sonoConfig.SourceFName = recording.BaseName; sonoConfig.WindowOverlap = FeltFrameOverlap; // set default value sonoConfig.DoMelScale = false; sonoConfig.NoiseReductionType = NoiseReductionType.Standard; AmplitudeSonogram basegram = new AmplitudeSonogram(sonoConfig, recording.WavReader); SpectrogramStandard sonogram = new SpectrogramStandard(basegram); //spectrogram has dim[N,257] Log.WriteLine("Signal: Duration={0}, Sample Rate={1}", sonogram.Duration, sr); Log.WriteLine("Frames: Size={0}, Count={1}, Duration={2:f1}ms, Overlap={5:f0}%, Offset={3:f1}ms, Frames/s={4:f1}", sonogram.Configuration.WindowSize, sonogram.FrameCount, (sonogram.FrameDuration * 1000), (sonogram.FrameStep * 1000), sonogram.FramesPerSecond, FeltFrameOverlap * 100); //iii: Get zip paths and the results Tuple List <string> zipList = FileTools.ReadTextFile(arguments.Config.FullName); Tuple <SpectrogramStandard, List <AcousticEvent>, double[]> results = null; //set up the results Tuple foreach (string zipPath in zipList) { if (zipPath.StartsWith("#")) { continue; // commented line } if (zipPath.Length < 2) { continue; // empty line } //i: get params file ZipFile.ExtractToDirectory(arguments.Output.FullName, zipPath); string zipName = Path.GetFileNameWithoutExtension(zipPath); string[] parts = zipName.Split('_'); string paramsPath = Path.Combine(arguments.Output.FullName, parts[0] + "_" + parts[1] + "_Params.txt"); string id = parts[0] + "_" + parts[1]; Log.WriteIfVerbose("################################################### " + id + " ########################################################"); //ii: READ PARAMETER VALUES FROM INI FILE var config = new ConfigDictionary(paramsPath); Dictionary <string, string> dict = config.GetTable(); //Dictionary<string, string>.KeyCollection keys = dict.Keys; //int DRAW_SONOGRAMS = Int32.Parse(dict[FeltTemplate_Create.key_DRAW_SONOGRAMS]); //options to draw sonogram dict[FeltTemplate_Create.key_DECIBEL_THRESHOLD] = "4.0"; dict[FeltTemplate_Create.key_MIN_DURATION] = "0.02"; if (zipName.EndsWith("binaryTemplate")) { string templatePath = Path.Combine(arguments.Output.FullName, id + "_binary.bmp"); double[,] templateMatrix = FindMatchingEvents.ReadImage2BinaryMatrixDouble(templatePath); results = FELTWithBinaryTemplate(sonogram, dict, templateMatrix, TimeSpan.Zero); } else if (zipName.EndsWith("trinaryTemplate")) { string templatePath = Path.Combine(arguments.Output.FullName, id + "_trinary.bmp"); double[,] templateMatrix = FindMatchingEvents.ReadImage2TrinaryMatrix(templatePath); results = FELTWithBinaryTemplate(sonogram, dict, templateMatrix, TimeSpan.Zero); } else if (zipName.EndsWith("syntacticTemplate")) { string templatePath = Path.Combine(arguments.Output.FullName, id + "_spr.txt"); char[,] templateMatrix = FindMatchingEvents.ReadTextFile2CharMatrix(templatePath); results = FELTWithSprTemplate(sonogram, dict, templateMatrix, TimeSpan.Zero); } else { Log.WriteLine("ERROR! UNKNOWN TEMPLATE: Zip file has unrecognised suffix:" + zipName); continue; } //get results sonogram = results.Item1; var matchingEvents = results.Item2; var scores = results.Item3; double matchThreshold = double.Parse(dict[FeltTemplate_Create.key_DECIBEL_THRESHOLD]); Log.WriteLine("# Finished detecting events like target: " + id); Log.WriteLine("# Matching Event Count = " + matchingEvents.Count); Log.WriteLine(" @ threshold = {0:f2}", matchThreshold); // accumulate results allEvents.AddRange(matchingEvents); scoresList.Add(scores); thresholdList.Add(matchThreshold); //v: write events count to results info file. double sigDuration = sonogram.Duration.TotalSeconds; string fname = arguments.Source.Name; string str = string.Format("{0}\t{1}\t{2}", fname, sigDuration, matchingEvents.Count); StringBuilder sb = AcousticEvent.WriteEvents(matchingEvents, str); FileTools.WriteTextFile("opPath", sb.ToString()); } // foreach (string zipPath in zipList) Log.WriteLine("\n\n\n##########################################################"); Log.WriteLine("# Finished detecting events"); Log.WriteLine("# Event Count = " + allEvents.Count); foreach (AcousticEvent ae in allEvents) { Log.WriteLine("# Event name = {0} ############################", ae.Name); Log.WriteLine("# Event time = {0:f2} to {1:f2} (frames {2}-{3}).", ae.TimeStart, ae.TimeEnd, ae.Oblong.RowTop, ae.Oblong.RowBottom); Log.WriteLine("# Event score= {0:f2}.", ae.Score); } int percentOverlap = 50; allEvents = PruneOverlappingEvents(allEvents, percentOverlap); Log.WriteLine("\n##########################################################"); Log.WriteLine("# Finished pruning events"); Log.WriteLine("# Event Count = " + allEvents.Count); WriteEventNames(allEvents); //WriteScoreAverages2Console(scoresList); //draw images of sonograms int DRAW_SONOGRAMS = 2; FileInfo opImagePath = arguments.Output.CombineFile(Path.GetFileNameWithoutExtension(arguments.Source.Name) + "_matchingEvents.png"); if (DRAW_SONOGRAMS == 2) { DrawSonogram(sonogram, opImagePath, allEvents, thresholdList, scoresList); } else if ((DRAW_SONOGRAMS == 1) && (allEvents.Count > 0)) { DrawSonogram(sonogram, opImagePath, allEvents, thresholdList, scoresList); } Log.WriteLine("# FINISHED passing all templates over recording:- " + arguments.Source.Name); }