diff --git a/src/AnalysisConfigFiles/Towsey.SpectrogramGenerator.yml b/src/AnalysisConfigFiles/Towsey.SpectrogramGenerator.yml index f00048b9a..ba03f0a00 100644 --- a/src/AnalysisConfigFiles/Towsey.SpectrogramGenerator.yml +++ b/src/AnalysisConfigFiles/Towsey.SpectrogramGenerator.yml @@ -29,6 +29,7 @@ Images: - MelScaleSpectrogram - CepstralSpectrogram - OctaveScaleSpectrogram + - RibbonSpectrogram - AmplitudeSpectrogramLocalContrastNormalization diff --git a/src/AnalysisPrograms/SpectrogramGenerator/SpectrogramGenerator.Core.cs b/src/AnalysisPrograms/SpectrogramGenerator/SpectrogramGenerator.Core.cs index 510abd8f4..abe06cec2 100644 --- a/src/AnalysisPrograms/SpectrogramGenerator/SpectrogramGenerator.Core.cs +++ b/src/AnalysisPrograms/SpectrogramGenerator/SpectrogramGenerator.Core.cs @@ -28,8 +28,12 @@ public partial class SpectrogramGenerator static SpectrogramGenerator() { var values = (SpectrogramImageType[])Enum.GetValues(typeof(SpectrogramImageType)); + + // This next line is to do with adding a colour tag to images so that they can be more easily identified in unit tests. + // Need a pallette that is large enough to include all the number of images produced. + // Check the ColorBrewer class and change the pallette set if need to. ImageTags = values - .Zip(ColorBrewer.Qualitative.Set1.ForClassCount(values.Length)) + .Zip(ColorBrewer.Qualitative.Set3.ForClassCount(values.Length)) .ToImmutableDictionary(x => x.First, x => x.Second); } @@ -41,6 +45,7 @@ static SpectrogramGenerator() /// MelScaleSpectrogram /// CepstralSpectrogram. /// OctaveScaleSpectrogram + /// RibbonSpectrogram. /// DifferenceSpectrogram. /// AmplitudeSpectrogramLocalContrastNormalization. /// Experimental. @@ -240,7 +245,29 @@ public static AudioToSonogramResult GenerateSpectrogramImages( GetOctaveScaleSpectrogram(octaveConfig, scale, recordingSegment, sourceRecordingName)); } - // IMAGE 9) AmplitudeSpectrogram_LocalContrastNormalization + // IMAGE 9) RibbonSpectrogram + if (@do.Contains(RibbonSpectrogram)) + { + //Create new config because calling the octave spectrogram changes it. + var octaveConfig = new SonogramConfig() + { + epsilon = recordingSegment.Epsilon, + SampleRate = sampleRate, + WindowSize = frameSize, + WindowStep = frameStep, + WindowOverlap = frameOverlap, + WindowPower = dspOutput1.WindowPower, + Duration = recordingSegment.Duration, + NoiseReductionType = NoiseReductionType.Standard, + NoiseReductionParameter = bgNoiseThreshold, + }; + + images.Add( + RibbonSpectrogram, + GetRibbonSpectrograms(octaveConfig, recordingSegment, sourceRecordingName)); + } + + // IMAGE 10) AmplitudeSpectrogram_LocalContrastNormalization if (@do.Contains(AmplitudeSpectrogramLocalContrastNormalization)) { var neighborhoodSeconds = config.NeighborhoodSeconds; @@ -455,6 +482,86 @@ public static Image GetOctaveScaleSpectrogram( return image; } + public static Image GetRibbonSpectrograms( + SonogramConfig sgConfig, + AudioRecording recording, + string sourceRecordingName) + { + //var type = FreqScaleType.OctaveDataReduction; + //var freqScale = new FrequencyScale(type); + + //// ensure that the freq scale and the spectrogram config are consistent. + //sgConfig.WindowSize = freqScale.WindowSize; + //freqScale.WindowStep = sgConfig.WindowStep; + //sgConfig.WindowOverlap = SonogramConfig.CalculateFrameOverlap(freqScale.WindowSize, freqScale.WindowStep); + + //// TODO at present noise reduction type must be set = Standard. + //sgConfig.NoiseReductionType = NoiseReductionType.Standard; + //sgConfig.NoiseReductionParameter = 3.0; + + //var octaveScaleGram = new SpectrogramOctaveScale(sgConfig, freqScale, recording.WavReader); + var octaveScaleGram = GetOctaveReducedSpectrogram(sgConfig, recording); + var image1 = octaveScaleGram.GetImage(); + + var linearScaleGram = GetLinearReducedSpectrogram(sgConfig, recording); + var image2 = linearScaleGram.GetImage(); + var spacer = new Image(image1.Width, 5); + + var imageList = new List> { image2, spacer, image1 }; + + var combinedImage = ImageTools.CombineImagesVertically(imageList); + var title = "RIBBON SPECTROGRAMS-Linear32 & Octave20: " + sourceRecordingName; + + //var titleBar = BaseSonogram.DrawTitleBarOfGrayScaleSpectrogram(title, image.Width, ImageTags[OctaveScaleSpectrogram]); + //var startTime = TimeSpan.Zero; + //var xAxisTicInterval = TimeSpan.FromSeconds(1); + //TimeSpan xAxisPixelDuration = TimeSpan.FromSeconds(sgConfig.WindowStep / (double)sgConfig.SampleRate); + //var labelInterval = TimeSpan.FromSeconds(5); + //image = BaseSonogram.FrameSonogram(image, titleBar, startTime, xAxisTicInterval, xAxisPixelDuration, labelInterval); + var image = octaveScaleGram.GetImageFullyAnnotated(combinedImage, title, null); + return image; + } + + public static SpectrogramOctaveScale GetOctaveReducedSpectrogram(SonogramConfig sgConfig, AudioRecording recording) + { + var type = FreqScaleType.OctaveDataReduction; + var freqScale = new FrequencyScale(type); + + // ensure that the freq scale and the spectrogram config are consistent. + sgConfig.WindowSize = freqScale.WindowSize; + freqScale.WindowStep = sgConfig.WindowStep; + sgConfig.WindowOverlap = SonogramConfig.CalculateFrameOverlap(freqScale.WindowSize, freqScale.WindowStep); + + // TODO at present noise reduction type must be set = Standard. + sgConfig.NoiseReductionType = NoiseReductionType.Standard; + sgConfig.NoiseReductionParameter = 3.0; + + var octaveScaleGram = new SpectrogramOctaveScale(sgConfig, freqScale, recording.WavReader); + return octaveScaleGram; + } + + public static SpectrogramStandard GetLinearReducedSpectrogram(SonogramConfig sgConfig, AudioRecording recording) + { + int sampleRate = recording.SampleRate; + var type = FreqScaleType.Linear; + int nyquist = sampleRate / 2; + int finalBinCount = 32; + int frameSize = 512; + int hertzGridInterval = 11000; + var freqScale = new FrequencyScale(type, nyquist, frameSize, finalBinCount, hertzGridInterval); + + // ensure that the freq scale and the spectrogram config are consistent. + sgConfig.WindowSize = freqScale.WindowSize; + freqScale.WindowStep = sgConfig.WindowStep; + sgConfig.WindowOverlap = SonogramConfig.CalculateFrameOverlap(freqScale.WindowSize, freqScale.WindowStep); + + sgConfig.NoiseReductionType = NoiseReductionType.Standard; + sgConfig.NoiseReductionParameter = 3.0; + + var spectrogram = new SpectrogramStandard(sgConfig, freqScale, recording.WavReader); + return spectrogram; + } + public static Image GetLcnSpectrogram( SonogramConfig sonoConfig, AudioRecording recordingSegment, diff --git a/src/AnalysisPrograms/SpectrogramGenerator/SpectrogramImageType.cs b/src/AnalysisPrograms/SpectrogramGenerator/SpectrogramImageType.cs index 9f024b92f..d7b73ae6f 100644 --- a/src/AnalysisPrograms/SpectrogramGenerator/SpectrogramImageType.cs +++ b/src/AnalysisPrograms/SpectrogramGenerator/SpectrogramImageType.cs @@ -14,6 +14,7 @@ public enum SpectrogramImageType MelScaleSpectrogram = 5, CepstralSpectrogram = 6, OctaveScaleSpectrogram = 7, - AmplitudeSpectrogramLocalContrastNormalization = 8, + RibbonSpectrogram = 8, + AmplitudeSpectrogramLocalContrastNormalization = 9, } } \ No newline at end of file