/// <summary> /// Do your analysis. This method is called once per segment (typically one-minute segments). /// </summary> public override RecognizerResults Recognize(AudioRecording recording, Config configuration, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth) { string speciesName = configuration[AnalysisKeys.SpeciesName] ?? "<no species>"; string abbreviatedSpeciesName = configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? "<no.sp>"; int minHz = configuration.GetInt(AnalysisKeys.MinHz); int maxHz = configuration.GetInt(AnalysisKeys.MaxHz); // BETTER TO CALCULATE THIS. IGNORE USER! // double frameOverlap = Double.Parse(configDict[Keys.FRAME_OVERLAP]); // duration of DCT in seconds double dctDuration = configuration.GetDouble(AnalysisKeys.DctDuration); // minimum acceptable value of a DCT coefficient double dctThreshold = configuration.GetDouble(AnalysisKeys.DctThreshold); // ignore oscillations below this threshold freq int minOscilFreq = configuration.GetInt(AnalysisKeys.MinOscilFreq); // ignore oscillations above this threshold freq int maxOscilFreq = configuration.GetInt(AnalysisKeys.MaxOscilFreq); // min duration of event in seconds double minDuration = configuration.GetDouble(AnalysisKeys.MinDuration); // max duration of event in seconds double maxDuration = configuration.GetDouble(AnalysisKeys.MaxDuration); // min score for an acceptable event double eventThreshold = configuration.GetDouble(AnalysisKeys.EventThreshold); if (recording.WavReader.SampleRate != 22050) { throw new InvalidOperationException("Requires a 22050Hz file"); } // The default was 512 for Canetoad. // Set longer Framesize for calls having longer pulse periodicity. const int FrameSize = 128; double windowOverlap = Oscillations2012.CalculateRequiredFrameOverlap( recording.SampleRate, FrameSize, maxOscilFreq); //windowOverlap = 0.75; // previous default // i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, WindowSize = FrameSize, WindowOverlap = windowOverlap, //NoiseReductionType = NoiseReductionType.NONE, NoiseReductionType = NoiseReductionType.Standard, NoiseReductionParameter = 0.1, }; // sonoConfig.NoiseReductionType = SNR.Key2NoiseReductionType("STANDARD"); TimeSpan recordingDuration = recording.Duration; int sr = recording.SampleRate; double freqBinWidth = sr / (double)sonoConfig.WindowSize; BaseSonogram sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); int rowCount = sonogram.Data.GetLength(0); int colCount = sonogram.Data.GetLength(1); // double[,] subMatrix = MatrixTools.Submatrix(sonogram.Data, 0, minBin, (rowCount - 1), maxbin); // ###################################################################### // ii: DO THE ANALYSIS AND RECOVER SCORES OR WHATEVER // This window is used to smooth the score array before extracting events. // A short window (e.g. 3) preserves sharper score edges to define events but also keeps noise. int scoreSmoothingWindow = 13; Oscillations2012.Execute( (SpectrogramStandard)sonogram, minHz, maxHz, dctDuration, minOscilFreq, maxOscilFreq, dctThreshold, eventThreshold, minDuration, maxDuration, scoreSmoothingWindow, out var scores, out var oscillationEvents, out var hits, segmentStartOffset); var acousticEvents = oscillationEvents.ConvertSpectralEventsToAcousticEvents(); acousticEvents.ForEach(ae => { ae.SpeciesName = speciesName; ae.SegmentDurationSeconds = recordingDuration.TotalSeconds; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; ae.Name = abbreviatedSpeciesName; }); var plot = new Plot(this.DisplayName, scores, eventThreshold); var plots = new List <Plot> { plot }; this.WriteDebugImage(recording, outputDirectory, sonogram, acousticEvents, plots, hits); return(new RecognizerResults() { Sonogram = sonogram, Hits = hits, Plots = plots, Events = acousticEvents, }); }
/// <summary> /// THE KEY ANALYSIS METHOD. /// </summary> /// <param name="configDict"> /// The configuration for the analysis. /// </param> /// <returns> /// The results of the analysis. /// </returns> public static KoalaMaleResults Analysis(AudioRecording recording, IDictionary <string, string> configDict, TimeSpan segmentStartOffset) { int minHz = int.Parse(configDict[AnalysisKeys.MinHz]); int maxHz = int.Parse(configDict[AnalysisKeys.MaxHz]); // BETTER TO CALUCLATE THIS. IGNORE USER! // double frameOverlap = Double.Parse(configDict[Keys.FRAME_OVERLAP]); // duration of DCT in seconds double dctDuration = double.Parse(configDict[AnalysisKeys.DctDuration]); // minimum acceptable value of a DCT coefficient double dctThreshold = double.Parse(configDict[AnalysisKeys.DctThreshold]); // ignore oscillations below this threshold freq int minOscilFreq = int.Parse(configDict[AnalysisKeys.MinOscilFreq]); // ignore oscillations above this threshold freq int maxOscilFreq = int.Parse(configDict[AnalysisKeys.MaxOscilFreq]); // min duration of event in seconds double minDuration = double.Parse(configDict[AnalysisKeys.MinDuration]); // max duration of event in seconds double maxDuration = double.Parse(configDict[AnalysisKeys.MaxDuration]); // min score for an acceptable event double eventThreshold = double.Parse(configDict[AnalysisKeys.EventThreshold]); // seems to work -- frameSize = 512 and 1024 does not catch all oscillations; const int FrameSize = 256; double windowOverlap = Oscillations2012.CalculateRequiredFrameOverlap( recording.SampleRate, FrameSize, maxOscilFreq); // i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, WindowSize = FrameSize, WindowOverlap = windowOverlap, NoiseReductionType = NoiseReductionType.None, }; ////sonoConfig.NoiseReductionType = NoiseReductionType.STANDARD; TimeSpan recordingDuration = recording.Duration; double freqBinWidth = recording.SampleRate / (double)sonoConfig.WindowSize; /* ############################################################################################################################################# * window sr frameDuration frames/sec hz/bin 64frameDuration hz/64bins hz/128bins * 1024 22050 46.4ms 21.5 21.5 2944ms 1376hz 2752hz * 1024 17640 58.0ms 17.2 17.2 3715ms 1100hz 2200hz * 2048 17640 116.1ms 8.6 8.6 7430ms 551hz 1100hz */ BaseSonogram sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); int rowCount = sonogram.Data.GetLength(0); int colCount = sonogram.Data.GetLength(1); //double[,] subMatrix = MatrixTools.Submatrix(sonogram.Data, 0, minBin, (rowCount - 1), maxbin); // ###################################################################### // ii: DO THE ANALYSIS AND RECOVER SCORES OR WHATEVER // predefinition of score array Oscillations2012.Execute( (SpectrogramStandard)sonogram, minHz, maxHz, dctDuration, minOscilFreq, maxOscilFreq, dctThreshold, eventThreshold, minDuration, maxDuration, out var scores, out var oscillationEvents, out var hits, segmentStartOffset); var events = oscillationEvents.ConvertSpectralEventsToAcousticEvents(); // remove isolated koala events - this is to remove false positive identifications events = FilterMaleKoalaEvents(events); if (events == null) { events = new List <AcousticEvent>(); } else { events.ForEach( ae => { ae.SpeciesName = configDict[AnalysisKeys.SpeciesName]; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; ae.SegmentDurationSeconds = recordingDuration.TotalSeconds; }); } // ###################################################################### var plot = new Plot(AnalysisName, scores, eventThreshold); return(new KoalaMaleResults { Events = events, Hits = hits, Plot = plot, RecordingtDuration = recordingDuration, Sonogram = sonogram, }); }
/// <summary> /// Do your analysis. This method is called once per segment (typically one-minute segments). /// </summary> public override RecognizerResults Recognize(AudioRecording recording, Config configuration, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth) { // common properties var speciesName = configuration[AnalysisKeys.SpeciesName] ?? "<no species>"; var abbreviatedSpeciesName = configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? "<no.sp>"; int minHz = configuration.GetInt(AnalysisKeys.MinHz); int maxHz = configuration.GetInt(AnalysisKeys.MaxHz); // BETTER TO CALCULATE THIS. IGNORE USER! // double frameOverlap = Double.Parse(configDict[Keys.FRAME_OVERLAP]); // duration of DCT in seconds double dctDuration = configuration.GetDouble(AnalysisKeys.DctDuration); // minimum acceptable value of a DCT coefficient double dctThreshold = configuration.GetDouble(AnalysisKeys.DctThreshold); // ignore oscillations below this threshold freq int minOscilFreq = configuration.GetInt(AnalysisKeys.MinOscilFreq); // ignore oscillations above this threshold freq int maxOscilFreq = configuration.GetInt(AnalysisKeys.MaxOscilFreq); // min duration of event in seconds double minDuration = configuration.GetDouble(AnalysisKeys.MinDuration); // max duration of event in seconds double maxDuration = configuration.GetDouble(AnalysisKeys.MaxDuration); // min score for an acceptable event double eventThreshold = configuration.GetDouble(AnalysisKeys.EventThreshold); // this default framesize seems to work for Canetoad const int frameSize = 512; double windowOverlap = Oscillations2012.CalculateRequiredFrameOverlap( recording.SampleRate, frameSize, maxOscilFreq); //windowOverlap = 0.75; // previous default // DEBUG: Following line used to search for where indeterminism creeps into the spectrogram values which vary from run to run. //FileTools.AddArrayAdjacentToExistingArrays(Path.Combine(outputDirectory.FullName, recording.BaseName+"_RecordingSamples.csv"), recording.WavReader.GetChannel(0)); // i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, WindowSize = frameSize, WindowOverlap = windowOverlap, // the default window is HAMMING //WindowFunction = WindowFunctions.HANNING.ToString(), //WindowFunction = WindowFunctions.NONE.ToString(), // if do not use noise reduction can get a more sensitive recogniser. NoiseReductionType = NoiseReductionType.None, }; // sonoConfig.NoiseReductionType = SNR.Key2NoiseReductionType("STANDARD"); TimeSpan recordingDuration = recording.Duration; //int sr = recording.SampleRate; //double freqBinWidth = sr / (double)sonoConfig.WindowSize; /* ############################################################################################################################################# * window sr frameDuration frames/sec hz/bin 64frameDuration hz/64bins hz/128bins * 1024 22050 46.4ms 21.5 21.5 2944ms 1376hz 2752hz * 1024 17640 58.0ms 17.2 17.2 3715ms 1100hz 2200hz * 2048 17640 116.1ms 8.6 8.6 7430ms 551hz 1100hz */ // int minBin = (int)Math.Round(minHz / freqBinWidth) + 1; // int maxbin = minBin + numberOfBins - 1; BaseSonogram sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); //int rowCount = sonogram.Data.GetLength(0); //int colCount = sonogram.Data.GetLength(1); // DEBUG: Following lines used to search for where indeterminism creeps into the spectrogram values which vary from run to run. //double[] array = DataTools.Matrix2Array(sonogram.Data); //FileTools.AddArrayAdjacentToExistingArrays(Path.Combine(outputDirectory.FullName, recording.BaseName+".csv"), array); // ###################################################################### // ii: DO THE ANALYSIS AND RECOVER SCORES OR WHATEVER double minDurationOfAdvertCall = minDuration; // this boundary duration should = 5.0 seconds as of 4 June 2015. //double minDurationOfReleaseCall = 1.0; Oscillations2012.Execute( (SpectrogramStandard)sonogram, minHz, maxHz, dctDuration, minOscilFreq, maxOscilFreq, dctThreshold, eventThreshold, minDurationOfAdvertCall, maxDuration, out var scores, out var oscillationEvents, out var hits, segmentStartOffset); // DEBUG: Following line used to search for where indeterminism creeps into the event detection //FileTools.AddArrayAdjacentToExistingArrays(Path.Combine(outputDirectory.FullName, recording.BaseName+"_ScoreArray.csv"), scores); var events = oscillationEvents.ConvertSpectralEventsToAcousticEvents(); var prunedEvents = new List <AcousticEvent>(); foreach (AcousticEvent ae in events) { //if (ae.Duration < minDurationOfReleaseCall) { continue; } if (ae.EventDurationSeconds < minDurationOfAdvertCall) { continue; } if (ae.EventDurationSeconds > maxDuration) { continue; } // add additional info ae.SpeciesName = speciesName; ae.Name = abbreviatedSpeciesName; ae.SegmentDurationSeconds = recordingDuration.TotalSeconds; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; prunedEvents.Add(ae); //if (ae.Duration >= minDurationOfAdvertCall) //{ // ae.Name = abbreviatedSpeciesName; // + ".AdvertCall"; // prunedEvents.Add(ae); // continue; //} } // do a recognizer test. if (false) { if (MainEntry.InDEBUG) { RecognizerTest(scores, new FileInfo(recording.FilePath)); RecognizerTest(prunedEvents, new FileInfo(recording.FilePath)); } } var plot = new Plot(this.DisplayName, scores, eventThreshold); return(new RecognizerResults() { Sonogram = sonogram, Hits = hits, Plots = plot.AsList(), Events = prunedEvents, //Events = events }); }
/// <summary> /// Do your analysis. This method is called once per segment (typically one-minute segments). /// </summary> public override RecognizerResults Recognize(AudioRecording recording, Config configuration, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth) { // WARNING: TODO TODO TODO = this method simply duplicates the CANETOAD analyser!!!!!!!!!!!!!!!!!!!!! ################### string speciesName = configuration[AnalysisKeys.SpeciesName] ?? "<no species>"; string abbreviatedSpeciesName = configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? "<no.sp>"; int minHz = configuration.GetInt(AnalysisKeys.MinHz); int maxHz = configuration.GetInt(AnalysisKeys.MaxHz); // BETTER TO CALCULATE THIS. IGNORE USER! // double frameOverlap = Double.Parse(configDict[Keys.FRAME_OVERLAP]); // duration of DCT in seconds double dctDuration = configuration.GetDouble(AnalysisKeys.DctDuration); // minimum acceptable value of a DCT coefficient double dctThreshold = configuration.GetDouble(AnalysisKeys.DctThreshold); // ignore oscillations below this threshold freq int minOscilFreq = configuration.GetInt(AnalysisKeys.MinOscilFreq); // ignore oscillations above this threshold freq int maxOscilFreq = configuration.GetInt(AnalysisKeys.MaxOscilFreq); // min duration of event in seconds double minDuration = configuration.GetDouble(AnalysisKeys.MinDuration); // max duration of event in seconds double maxDuration = configuration.GetDouble(AnalysisKeys.MaxDuration); // min score for an acceptable event double eventThreshold = configuration.GetDouble(AnalysisKeys.EventThreshold); // The default was 512 for Canetoad. // Framesize = 128 seems to work for Littoria fallax. // frame size int frameSize = configuration.GetInt(AnalysisKeys.KeyFrameSize); if (recording.WavReader.SampleRate != 22050) { throw new InvalidOperationException("Requires a 22050Hz file"); } double windowOverlap = Oscillations2012.CalculateRequiredFrameOverlap( recording.SampleRate, frameSize, maxOscilFreq); //windowOverlap = 0.75; // previous default // i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, WindowSize = frameSize, WindowOverlap = windowOverlap, NoiseReductionType = NoiseReductionType.None, }; // sonoConfig.NoiseReductionType = SNR.Key2NoiseReductionType("STANDARD"); TimeSpan recordingDuration = recording.Duration; int sr = recording.SampleRate; double freqBinWidth = sr / (double)sonoConfig.WindowSize; /* ############################################################################################################################################# * window sr frameDuration frames/sec hz/bin 64frameDuration hz/64bins hz/128bins * 1024 22050 46.4ms 21.5 21.5 2944ms 1376hz 2752hz * 1024 17640 58.0ms 17.2 17.2 3715ms 1100hz 2200hz * 2048 17640 116.1ms 8.6 8.6 7430ms 551hz 1100hz */ // int minBin = (int)Math.Round(minHz / freqBinWidth) + 1; // int maxbin = minBin + numberOfBins - 1; BaseSonogram sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); int rowCount = sonogram.Data.GetLength(0); int colCount = sonogram.Data.GetLength(1); // double[,] subMatrix = MatrixTools.Submatrix(sonogram.Data, 0, minBin, (rowCount - 1), maxbin); // ###################################################################### // ii: DO THE ANALYSIS AND RECOVER SCORES OR WHATEVER //minDuration = 1.0; Oscillations2012.Execute( (SpectrogramStandard)sonogram, minHz, maxHz, dctDuration, minOscilFreq, maxOscilFreq, dctThreshold, eventThreshold, minDuration, maxDuration, out var scores, out var acousticEvents, out var hits, segmentStartOffset); acousticEvents.ForEach(ae => { ae.SpeciesName = speciesName; ae.SegmentDurationSeconds = recordingDuration.TotalSeconds; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; ae.Name = abbreviatedSpeciesName; }); var plot = new Plot(this.DisplayName, scores, eventThreshold); return(new RecognizerResults() { Sonogram = sonogram, Hits = hits, Plots = plot.AsList(), Events = acousticEvents, }); }
/// <summary> /// Do your analysis. This method is called once per segment (typically one-minute segments). /// </summary> public override RecognizerResults Recognize( AudioRecording recording, Config configuration, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth) { //string speciesName = (string)configuration[AnalysisKeys.SpeciesName] ?? "<no species>"; //string abbreviatedSpeciesName = (string)configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? "<no.sp>"; // this default framesize seems to work for Lewin's Rail const int frameSize = 512; // DO NOT SET windowOverlap. Calculate it below. if (imageWidth == null) { throw new ArgumentNullException(nameof(imageWidth)); } // check the sample rate. Must be 22050 if (recording.WavReader.SampleRate != 22050) { throw new InvalidOperationException("Requires a 22050Hz file"); } TimeSpan recordingDuration = recording.WavReader.Time; // check for the profiles in the config file bool hasProfiles = ConfigFile.HasProfiles(configuration); if (!hasProfiles) { throw new ConfigFileException("The Config file for L.pectoralis must contain a profiles object."); } // get the profile names string[] profileNames = ConfigFile.GetProfileNames(configuration); var recognizerConfig = new LewinsRailConfig(); var prunedEvents = new List <AcousticEvent>(); var plots = new List <Plot>(); BaseSonogram sonogram = null; // cycle through the profiles and analyse recording using each of them foreach (var name in profileNames) { Log.Debug($"Reading profile <{name}>."); recognizerConfig.ReadConfigFile(configuration, name); // ignore oscillations above this threshold freq int maxOscilRate = (int)Math.Ceiling(1 / recognizerConfig.MinPeriod); // calculate frame overlap and ignor any user inut. double windowOverlap = Oscillations2012.CalculateRequiredFrameOverlap( recording.SampleRate, frameSize, maxOscilRate); // i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, //set default values - ignore those set by user WindowSize = frameSize, WindowOverlap = windowOverlap, // the default window is HAMMING //WindowFunction = WindowFunctions.HANNING.ToString(), //WindowFunction = WindowFunctions.NONE.ToString(), // if do not use noise reduction can get a more sensitive recogniser. //NoiseReductionType = NoiseReductionType.NONE, NoiseReductionType = SNR.KeyToNoiseReductionType("STANDARD"), }; //############################################################################################################################################# //DO THE ANALYSIS AND RECOVER SCORES OR WHATEVER var results = Analysis(recording, sonoConfig, recognizerConfig, this.ReturnDebugImage, segmentStartOffset); // ###################################################################### if (results == null) { return(null); //nothing to process } sonogram = results.Item1; //var hits = results.Item2; var scoreArray = results.Item3; var predictedEvents = results.Item4; var debugImage = results.Item5; //############################################################################################################################################# if (debugImage == null) { Log.Debug("DebugImage is null, not writing file"); } else if (MainEntry.InDEBUG) { var imageName = AnalysisResultName(recording.BaseName, this.SpeciesName, "png", "DebugSpectrogram"); var debugPath = outputDirectory.Combine(imageName); //debugImage.Save(debugPath.FullName); } foreach (var ae in predictedEvents) { // add additional info if (!(ae.Score > recognizerConfig.EventThreshold)) { continue; } ae.Name = recognizerConfig.AbbreviatedSpeciesName; ae.SpeciesName = recognizerConfig.SpeciesName; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; ae.SegmentDurationSeconds = recordingDuration.TotalSeconds; prunedEvents.Add(ae); } // increase very low scores for (int j = 0; j < scoreArray.Length; j++) { scoreArray[j] *= 4; if (scoreArray[j] > 1.0) { scoreArray[j] = 1.0; } } var plot = new Plot(this.DisplayName, scoreArray, recognizerConfig.EventThreshold); plots.Add(plot); } return(new RecognizerResults() { Sonogram = sonogram, Hits = null, Plots = plots, Events = prunedEvents, }); }
/// <summary> /// THE KEY ANALYSIS METHOD /// </summary> /// <param name="recording"> /// The segment Of Source File. /// </param> /// <param name="configDict"> /// The config Dict. /// </param> /// <param name="value"></param> /// <returns> /// The <see cref="LitoriaFallaxResults"/>. /// </returns> internal static LitoriaFallaxResults Analysis( AudioRecording recording, Dictionary <string, string> configDict, TimeSpan segmentStartOffset) { // WARNING: TODO TODO TODO = this method simply duplicates the CANETOAD analyser!!!!!!!!!!!!!!!!!!!!! ################### int minHz = int.Parse(configDict[AnalysisKeys.MinHz]); int maxHz = int.Parse(configDict[AnalysisKeys.MaxHz]); // BETTER TO CALCULATE THIS. IGNORE USER! // double frameOverlap = Double.Parse(configDict[Keys.FRAME_OVERLAP]); // duration of DCT in seconds double dctDuration = double.Parse(configDict[AnalysisKeys.DctDuration]); // minimum acceptable value of a DCT coefficient double dctThreshold = double.Parse(configDict[AnalysisKeys.DctThreshold]); // ignore oscillations below this threshold freq int minOscilFreq = int.Parse(configDict[AnalysisKeys.MinOscilFreq]); // ignore oscillations above this threshold freq int maxOscilFreq = int.Parse(configDict[AnalysisKeys.MaxOscilFreq]); // min duration of event in seconds double minDuration = double.Parse(configDict[AnalysisKeys.MinDuration]); // max duration of event in seconds double maxDuration = double.Parse(configDict[AnalysisKeys.MaxDuration]); // min score for an acceptable event double eventThreshold = double.Parse(configDict[AnalysisKeys.EventThreshold]); // The default was 512 for Canetoad. // Framesize = 128 seems to work for Littoria fallax. const int FrameSize = 128; double windowOverlap = Oscillations2012.CalculateRequiredFrameOverlap( recording.SampleRate, FrameSize, maxOscilFreq); //windowOverlap = 0.75; // previous default // i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, WindowSize = FrameSize, WindowOverlap = windowOverlap, NoiseReductionType = NoiseReductionType.None, }; // sonoConfig.NoiseReductionType = SNR.Key2NoiseReductionType("STANDARD"); TimeSpan recordingDuration = recording.Duration; int sr = recording.SampleRate; double freqBinWidth = sr / (double)sonoConfig.WindowSize; /* ############################################################################################################################################# * window sr frameDuration frames/sec hz/bin 64frameDuration hz/64bins hz/128bins * 1024 22050 46.4ms 21.5 21.5 2944ms 1376hz 2752hz * 1024 17640 58.0ms 17.2 17.2 3715ms 1100hz 2200hz * 2048 17640 116.1ms 8.6 8.6 7430ms 551hz 1100hz */ // int minBin = (int)Math.Round(minHz / freqBinWidth) + 1; // int maxbin = minBin + numberOfBins - 1; BaseSonogram sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); int rowCount = sonogram.Data.GetLength(0); int colCount = sonogram.Data.GetLength(1); // double[,] subMatrix = MatrixTools.Submatrix(sonogram.Data, 0, minBin, (rowCount - 1), maxbin); // ###################################################################### // ii: DO THE ANALYSIS AND RECOVER SCORES OR WHATEVER //minDuration = 1.0; double[] scores; // predefinition of score array List <AcousticEvent> acousticEvents; double[,] hits; Oscillations2012.Execute( (SpectrogramStandard)sonogram, minHz, maxHz, dctDuration, minOscilFreq, maxOscilFreq, dctThreshold, eventThreshold, minDuration, maxDuration, out scores, out acousticEvents, out hits, segmentStartOffset); acousticEvents.ForEach(ae => { ae.SpeciesName = configDict[AnalysisKeys.SpeciesName]; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; ae.SegmentDurationSeconds = recordingDuration.TotalSeconds; ae.Name = AbbreviatedName; }); var plot = new Plot(AnalysisName, scores, eventThreshold); // DEBUG ONLY ################################ TEMPORARY ################################ // Draw a standard spectrogram and mark of hites etc. bool createStandardDebugSpectrogram = true; if (createStandardDebugSpectrogram) { string fileName = "LittoriaFallaxDEBUG"; throw new NotSupportedException("YOU NEED TO FIX THIS FOR PRODUCTION"); string path = @"G:\SensorNetworks\Output\Frogs\TestOfHiResIndices-2016July\Test\Towsey.HiResIndices\SpectrogramImages"; var imageDir = new DirectoryInfo(path); if (!imageDir.Exists) { imageDir.Create(); } string filePath2 = Path.Combine(imageDir.FullName, fileName + ".png"); Image sonoBmp = DrawSonogram(sonogram, hits, plot, acousticEvents, eventThreshold); sonoBmp.Save(filePath2); } // END DEBUG ################################ TEMPORARY ################################ return(new LitoriaFallaxResults { Sonogram = sonogram, Hits = hits, Plot = plot, Events = acousticEvents, RecordingDuration = recordingDuration, }); } // Analysis()
/// <summary> /// Do your analysis. This method is called once per segment (typically one-minute segments). /// </summary> public override RecognizerResults Recognize(AudioRecording recording, Config configuration, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth) { string speciesName = configuration[AnalysisKeys.SpeciesName] ?? "<no species>"; string abbreviatedSpeciesName = configuration[AnalysisKeys.AbbreviatedSpeciesName] ?? "<no.sp>"; int minHz = configuration.GetInt(AnalysisKeys.MinHz); int maxHz = configuration.GetInt(AnalysisKeys.MaxHz); // BETTER TO CALCULATE THIS. IGNORE USER! // double frameOverlap = Double.Parse(configDict[Keys.FRAME_OVERLAP]); // duration of DCT in seconds double dctDuration = configuration.GetDouble(AnalysisKeys.DctDuration); // minimum acceptable value of a DCT coefficient double dctThreshold = configuration.GetDouble(AnalysisKeys.DctThreshold); // ignore oscillations below this threshold freq int minOscilFreq = configuration.GetInt(AnalysisKeys.MinOscilFreq); // ignore oscillations above this threshold freq int maxOscilFreq = configuration.GetInt(AnalysisKeys.MaxOscilFreq); // min duration of event in seconds double minDuration = configuration.GetDouble(AnalysisKeys.MinDuration); // max duration of event in seconds double maxDuration = configuration.GetDouble(AnalysisKeys.MaxDuration); // min score for an acceptable event double eventThreshold = configuration.GetDouble(AnalysisKeys.EventThreshold); // The default was 512 for Canetoad. // Framesize = 128 seems to work for Littoria fallax. // frame size int frameSize = configuration.GetInt(AnalysisKeys.KeyFrameSize); if (recording.WavReader.SampleRate != 22050) { throw new InvalidOperationException("Requires a 22050Hz file"); } double windowOverlap = Oscillations2012.CalculateRequiredFrameOverlap( recording.SampleRate, frameSize, maxOscilFreq); //windowOverlap = 0.75; // previous default // i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, WindowSize = frameSize, WindowOverlap = windowOverlap, //NoiseReductionType = NoiseReductionType.NONE, NoiseReductionType = NoiseReductionType.Standard, NoiseReductionParameter = 0.2, }; TimeSpan recordingDuration = recording.Duration; int sr = recording.SampleRate; double freqBinWidth = sr / (double)sonoConfig.WindowSize; BaseSonogram sonogram = new SpectrogramStandard(sonoConfig, recording.WavReader); int rowCount = sonogram.Data.GetLength(0); int colCount = sonogram.Data.GetLength(1); // double[,] subMatrix = MatrixTools.Submatrix(sonogram.Data, 0, minBin, (rowCount - 1), maxbin); // ###################################################################### // ii: DO THE ANALYSIS AND RECOVER SCORES OR WHATEVER // This window is used to smooth the score array before extracting events. // A short window preserves sharper score edges to define events but also keeps noise. int scoreSmoothingWindow = 5; Oscillations2012.Execute( (SpectrogramStandard)sonogram, minHz, maxHz, dctDuration, minOscilFreq, maxOscilFreq, dctThreshold, eventThreshold, minDuration, maxDuration, scoreSmoothingWindow, out var scores, out var acousticEvents, out var hits, segmentStartOffset); acousticEvents.ForEach(ae => { ae.SpeciesName = speciesName; ae.SegmentDurationSeconds = recordingDuration.TotalSeconds; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; ae.Name = abbreviatedSpeciesName; }); var plot = new Plot(this.DisplayName, scores, eventThreshold); var plots = new List <Plot> { plot }; // DEBUG IMAGE this recognizer only. MUST set false for deployment. bool displayDebugImage = MainEntry.InDEBUG; if (displayDebugImage) { Image debugImage = DisplayDebugImage(sonogram, acousticEvents, plots, hits); var debugPath = outputDirectory.Combine(FilenameHelpers.AnalysisResultName(Path.GetFileNameWithoutExtension(recording.BaseName), this.Identifier, "png", "DebugSpectrogram")); debugImage.Save(debugPath.FullName); } return(new RecognizerResults() { Sonogram = sonogram, Hits = hits, Plots = plots, Events = acousticEvents, }); }
// OTHER CONSTANTS //private const string ImageViewer = @"C:\Windows\system32\mspaint.exe"; /// <summary> /// Do your analysis. This method is called once per segment (typically one-minute segments). /// </summary> public override RecognizerResults Recognize(AudioRecording recording, Config configuration, TimeSpan segmentStartOffset, Lazy <IndexCalculateResult[]> getSpectralIndexes, DirectoryInfo outputDirectory, int?imageWidth) { var recognizerConfig = new LitoriaBicolorConfig(); recognizerConfig.ReadConfigFile(configuration); if (recording.WavReader.SampleRate != 22050) { throw new InvalidOperationException("Requires a 22050Hz file"); } TimeSpan recordingDuration = recording.WavReader.Time; //// ignore oscillations below this threshold freq //int minOscilFreq = (int)configuration[AnalysisKeys.MinOscilFreq]; //// ignore oscillations above this threshold freq int maxOscilRate = (int)Math.Ceiling(1 / recognizerConfig.MinPeriod); // this default framesize seems to work const int frameSize = 128; double windowOverlap = Oscillations2012.CalculateRequiredFrameOverlap( recording.SampleRate, frameSize, maxOscilRate); // i: MAKE SONOGRAM var sonoConfig = new SonogramConfig { SourceFName = recording.BaseName, //set default values - ignore those set by user WindowSize = frameSize, WindowOverlap = windowOverlap, // the default window is HAMMING //WindowFunction = WindowFunctions.HANNING.ToString(), //WindowFunction = WindowFunctions.NONE.ToString(), // if do not use noise reduction can get a more sensitive recogniser. //NoiseReductionType = NoiseReductionType.NONE, NoiseReductionType = SNR.KeyToNoiseReductionType("STANDARD"), }; //############################################################################################################################################# //DO THE ANALYSIS AND RECOVER SCORES OR WHATEVER var results = Analysis(recording, sonoConfig, recognizerConfig, MainEntry.InDEBUG, segmentStartOffset); //###################################################################### if (results == null) { return(null); //nothing to process } var sonogram = results.Item1; var hits = results.Item2; var scoreArray = results.Item3; var predictedEvents = results.Item4; var debugImage = results.Item5; //############################################################################################################################################# var debugPath = outputDirectory.Combine(FilenameHelpers.AnalysisResultName(Path.GetFileNameWithoutExtension(recording.BaseName), this.SpeciesName, "png", "DebugSpectrogram")); debugImage.Save(debugPath.FullName); // Prune events here if erquired i.e. remove those below threshold score if this not already done. See other recognizers. foreach (AcousticEvent ae in predictedEvents) { // add additional info ae.Name = recognizerConfig.AbbreviatedSpeciesName; ae.SpeciesName = recognizerConfig.SpeciesName; ae.SegmentStartSeconds = segmentStartOffset.TotalSeconds; ae.SegmentDurationSeconds = recordingDuration.TotalSeconds; } var plot = new Plot(this.DisplayName, scoreArray, recognizerConfig.EventThreshold); return(new RecognizerResults() { Sonogram = sonogram, Hits = hits, Plots = plot.AsList(), Events = predictedEvents, }); }