public static void Execute(Arguments arguments) { const string Title = "# DETERMINING SIGNAL TO NOISE RATIO IN RECORDING"; string date = "# DATE AND TIME: " + DateTime.Now; Log.WriteLine(Title); Log.WriteLine(date); Log.Verbosity = 1; var input = arguments.Source; var sourceFileName = input.Name; var outputDir = arguments.Output; var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(input.FullName); var outputTxtPath = Path.Combine(outputDir.FullName, fileNameWithoutExtension + ".txt").ToFileInfo(); Log.WriteIfVerbose("# Recording file: " + input.FullName); Log.WriteIfVerbose("# Config file: " + arguments.Config); Log.WriteIfVerbose("# Output folder =" + outputDir.FullName); FileTools.WriteTextFile(outputTxtPath.FullName, date + "\n# Recording file: " + input.FullName); //READ PARAMETER VALUES FROM INI FILE // load YAML configuration Config configuration = ConfigFile.Deserialize(arguments.Config); //ii: SET SONOGRAM CONFIGURATION SonogramConfig sonoConfig = new SonogramConfig(); //default values config sonoConfig.SourceFName = input.FullName; sonoConfig.WindowSize = configuration.GetIntOrNull(AnalysisKeys.KeyFrameSize) ?? 512; sonoConfig.WindowOverlap = configuration.GetDoubleOrNull(AnalysisKeys.FrameOverlap) ?? 0.5; sonoConfig.WindowFunction = configuration[AnalysisKeys.KeyWindowFunction]; sonoConfig.NPointSmoothFFT = configuration.GetIntOrNull(AnalysisKeys.KeyNPointSmoothFft) ?? 256; sonoConfig.NoiseReductionType = SNR.KeyToNoiseReductionType(configuration[AnalysisKeys.NoiseReductionType]); int minHz = configuration.GetIntOrNull("MIN_HZ") ?? 0; int maxHz = configuration.GetIntOrNull("MAX_HZ") ?? 11050; double segK1 = configuration.GetDoubleOrNull("SEGMENTATION_THRESHOLD_K1") ?? 0; double segK2 = configuration.GetDoubleOrNull("SEGMENTATION_THRESHOLD_K2") ?? 0; double latency = configuration.GetDoubleOrNull("K1_K2_LATENCY") ?? 0; double vocalGap = configuration.GetDoubleOrNull("VOCAL_GAP") ?? 0; double minVocalLength = configuration.GetDoubleOrNull("MIN_VOCAL_DURATION") ?? 0; //bool DRAW_SONOGRAMS = (bool?)configuration.DrawSonograms ?? true; //options to draw sonogram //double intensityThreshold = Acoustics.AED.Default.intensityThreshold; //if (dict.ContainsKey(key_AED_INTENSITY_THRESHOLD)) intensityThreshold = Double.Parse(dict[key_AED_INTENSITY_THRESHOLD]); //int smallAreaThreshold = Acoustics.AED.Default.smallAreaThreshold; //if( dict.ContainsKey(key_AED_SMALL_AREA_THRESHOLD)) smallAreaThreshold = Int32.Parse(dict[key_AED_SMALL_AREA_THRESHOLD]); // COnvert input recording into wav var convertParameters = new AudioUtilityRequest { TargetSampleRate = 17640 }; var fileToAnalyse = new FileInfo(Path.Combine(outputDir.FullName, "temp.wav")); if (File.Exists(fileToAnalyse.FullName)) { File.Delete(fileToAnalyse.FullName); } var convertedFileInfo = AudioFilePreparer.PrepareFile( input, fileToAnalyse, convertParameters, outputDir); // (A) ########################################################################################################################## AudioRecording recording = new AudioRecording(fileToAnalyse.FullName); int signalLength = recording.WavReader.Samples.Length; TimeSpan wavDuration = TimeSpan.FromSeconds(recording.WavReader.Time.TotalSeconds); double frameDurationInSeconds = sonoConfig.WindowSize / (double)recording.SampleRate; TimeSpan frameDuration = TimeSpan.FromTicks((long)(frameDurationInSeconds * TimeSpan.TicksPerSecond)); int stepSize = (int)Math.Floor(sonoConfig.WindowSize * (1 - sonoConfig.WindowOverlap)); double stepDurationInSeconds = sonoConfig.WindowSize * (1 - sonoConfig.WindowOverlap) / recording.SampleRate; TimeSpan stepDuration = TimeSpan.FromTicks((long)(stepDurationInSeconds * TimeSpan.TicksPerSecond)); double framesPerSecond = 1 / stepDuration.TotalSeconds; int frameCount = signalLength / stepSize; // (B) ################################## EXTRACT ENVELOPE and SPECTROGRAM ################################## var dspOutput = DSP_Frames.ExtractEnvelopeAndFfts( recording, sonoConfig.WindowSize, sonoConfig.WindowOverlap); //double[] avAbsolute = dspOutput.Average; //average absolute value over the minute recording // (C) ################################## GET SIGNAL WAVEFORM ################################## double[] signalEnvelope = dspOutput.Envelope; double avSignalEnvelope = signalEnvelope.Average(); // (D) ################################## GET Amplitude Spectrogram ################################## double[,] amplitudeSpectrogram = dspOutput.AmplitudeSpectrogram; // get amplitude spectrogram. // (E) ################################## Generate deciBel spectrogram from amplitude spectrogram double epsilon = Math.Pow(0.5, recording.BitsPerSample - 1); double[,] deciBelSpectrogram = MFCCStuff.DecibelSpectra( dspOutput.AmplitudeSpectrogram, dspOutput.WindowPower, recording.SampleRate, epsilon); LoggedConsole.WriteLine("# Finished calculating decibel spectrogram."); StringBuilder sb = new StringBuilder(); sb.AppendLine("\nSIGNAL PARAMETERS"); sb.AppendLine("Signal Duration =" + wavDuration); sb.AppendLine("Sample Rate =" + recording.SampleRate); sb.AppendLine("Min Signal Value =" + dspOutput.MinSignalValue); sb.AppendLine("Max Signal Value =" + dspOutput.MaxSignalValue); sb.AppendLine("Max Absolute Ampl =" + signalEnvelope.Max().ToString("F3") + " (See Note 1)"); sb.AppendLine("Epsilon Ampl (1 bit)=" + epsilon); sb.AppendLine("\nFRAME PARAMETERS"); sb.AppendLine("Window Size =" + sonoConfig.WindowSize); sb.AppendLine("Frame Count =" + frameCount); sb.AppendLine("Envelope length=" + signalEnvelope.Length); sb.AppendLine("Frame Duration =" + frameDuration.TotalMilliseconds.ToString("F3") + " ms"); sb.AppendLine("Frame overlap =" + sonoConfig.WindowOverlap); sb.AppendLine("Step Size =" + stepSize); sb.AppendLine("Step duration =" + stepDuration.TotalMilliseconds.ToString("F3") + " ms"); sb.AppendLine("Frames Per Sec =" + framesPerSecond.ToString("F1")); sb.AppendLine("\nFREQUENCY PARAMETERS"); sb.AppendLine("Nyquist Freq =" + dspOutput.NyquistFreq + " Hz"); sb.AppendLine("Freq Bin Width =" + dspOutput.FreqBinWidth.ToString("F2") + " Hz"); sb.AppendLine("Nyquist Bin =" + dspOutput.NyquistBin); sb.AppendLine("\nENERGY PARAMETERS"); double val = dspOutput.FrameEnergy.Min(); sb.AppendLine( "Minimum dB / frame =" + (10 * Math.Log10(val)).ToString("F2") + " (See Notes 2, 3 & 4)"); val = dspOutput.FrameEnergy.Max(); sb.AppendLine("Maximum dB / frame =" + (10 * Math.Log10(val)).ToString("F2")); sb.AppendLine("\ndB NOISE SUBTRACTION"); double noiseRange = 2.0; //sb.AppendLine("Noise (estimate of mode) =" + sonogram.SnrData.NoiseSubtracted.ToString("F3") + " dB (See Note 5)"); //double noiseSpan = sonogram.SnrData.NoiseRange; //sb.AppendLine("Noise range =" + noiseSpan.ToString("F2") + " to +" + (noiseSpan * -1).ToString("F2") + " dB (See Note 6)"); //sb.AppendLine("SNR (max frame-noise) =" + sonogram.SnrData.Snr.ToString("F2") + " dB (See Note 7)"); //sb.Append("\nSEGMENTATION PARAMETERS"); //sb.Append("Segment Thresholds K1: {0:f2}. K2: {1:f2} (See Note 8)", segK1, segK2); //sb.Append("# Event Count = " + predictedEvents.Count()); FileTools.Append2TextFile(outputTxtPath.FullName, sb.ToString()); FileTools.Append2TextFile(outputTxtPath.FullName, GetSNRNotes(noiseRange).ToString()); // (F) ################################## DRAW IMAGE 1: original spectorgram Log.WriteLine("# Start drawing noise reduced sonograms."); TimeSpan X_AxisInterval = TimeSpan.FromSeconds(1); //int Y_AxisInterval = (int)Math.Round(1000 / dspOutput.FreqBinWidth); int nyquist = recording.SampleRate / 2; int hzInterval = 1000; var image1 = DrawSonogram(deciBelSpectrogram, wavDuration, X_AxisInterval, stepDuration, nyquist, hzInterval); // (G) ################################## Calculate modal background noise spectrum in decibels //double SD_COUNT = -0.5; // number of SDs above the mean for noise removal //NoiseReductionType nrt = NoiseReductionType.MODAL; //System.Tuple<double[,], double[]> tuple = SNR.NoiseReduce(deciBelSpectrogram, nrt, SD_COUNT); //double upperPercentileBound = 0.2; // lowest percentile for noise removal //NoiseReductionType nrt = NoiseReductionType.LOWEST_PERCENTILE; //System.Tuple<double[,], double[]> tuple = SNR.NoiseReduce(deciBelSpectrogram, nrt, upperPercentileBound); // (H) ################################## Calculate BRIGGS noise removal from amplitude spectrum int percentileBound = 20; // low energy percentile for noise removal //double binaryThreshold = 0.6; //works for higher SNR recordings double binaryThreshold = 0.4; //works for lower SNR recordings //double binaryThreshold = 0.3; //works for lower SNR recordings double[,] m = NoiseRemoval_Briggs.BriggsNoiseFilterAndGetMask( amplitudeSpectrogram, percentileBound, binaryThreshold); string title = "TITLE"; var image2 = NoiseRemoval_Briggs.DrawSonogram( m, wavDuration, X_AxisInterval, stepDuration, nyquist, hzInterval, title); //Image image2 = NoiseRemoval_Briggs.BriggsNoiseFilterAndGetSonograms(amplitudeSpectrogram, upperPercentileBound, binaryThreshold, // wavDuration, X_AxisInterval, stepDuration, Y_AxisInterval); // (I) ################################## Calculate MEDIAN noise removal from amplitude spectrum //double upperPercentileBound = 0.8; // lowest percentile for noise removal //NoiseReductionType nrt = NoiseReductionType.MEDIAN; //System.Tuple<double[,], double[]> tuple = SNR.NoiseReduce(deciBelSpectrogram, nrt, upperPercentileBound); //double[,] noiseReducedSpectrogram1 = tuple.Item1; // //double[] noiseProfile = tuple.Item2; // smoothed modal profile //SNR.NoiseProfile dBProfile = SNR.CalculateNoiseProfile(deciBelSpectrogram, SD_COUNT); // calculate noise value for each freq bin. //double[] noiseProfile = DataTools.filterMovingAverage(dBProfile.noiseThresholds, 7); // smooth modal profile //double[,] noiseReducedSpectrogram1 = SNR.TruncateBgNoiseFromSpectrogram(deciBelSpectrogram, dBProfile.noiseThresholds); //Image image2 = DrawSonogram(noiseReducedSpectrogram1, wavDuration, X_AxisInterval, stepDuration, Y_AxisInterval); var combinedImage = ImageTools.CombineImagesVertically(image1, image2); string imagePath = Path.Combine(outputDir.FullName, fileNameWithoutExtension + ".png"); combinedImage.Save(imagePath); // (G) ################################## Calculate modal background noise spectrum in decibels Log.WriteLine("# Finished recording:- " + input.Name); }
public static void ReadSpectralIndicesAndWriteToDataTable(string[] spectrogramKeys, DateTime thisDate, DirectoryInfo targetDirInfo, string targetFileName, string opFilePath) { TimeSpan roundingInterval = TimeSpan.FromMinutes(1); // thisDate.Round(roundingInterval); // could not get this to work int year = thisDate.Year; int thisDayOfYear = thisDate.DayOfYear; int thisStartMinute = (thisDate.Hour * 60) + thisDate.Minute; if (thisDate.Second > 30) { thisStartMinute++; } // reads all known files spectral indices Dictionary <string, double[, ]> dict = IndexMatrices.ReadSpectrogramCsvFiles(targetDirInfo, targetFileName, spectrogramKeys, out var freqBinCount); if (dict.Count() == 0) { LoggedConsole.WriteLine("No spectrogram matrices in the dictionary. Spectrogram files do not exist?"); return; } // set up the output file with headers if it does not exist if (!File.Exists(opFilePath)) { string outputCsvHeader = "Year,DayOfYear,MinOfDay,FreqBin"; foreach (string key in dict.Keys) { outputCsvHeader = outputCsvHeader + "," + key; } FileTools.WriteTextFile(opFilePath, outputCsvHeader); } List <string> lines = new List <string>(); string linestart = $"{year},{thisDayOfYear}"; //int minutesInThisMatrix = 2; // number of minutes = number of columns in matrix int minutesInThisMatrix = dict[spectrogramKeys[1]].GetLength(1); freqBinCount = dict[spectrogramKeys[1]].GetLength(0); for (int min = 0; min < minutesInThisMatrix; min++) { int numberOfMinutes = thisStartMinute + min; for (int bin = 0; bin < freqBinCount; bin++) { int binId = freqBinCount - bin - 1; StringBuilder line = new StringBuilder(linestart + "," + numberOfMinutes + "," + binId); foreach (string key in dict.Keys) { double[,] matrix = dict[key]; // do not need more than 6 decimal places for values which will ultimately transformed to colour bytes. // cuts file size from 12.2 MB to 7.4 MB string str = $",{matrix[bin, min]:F6}"; line.Append(str); } lines.Add(line.ToString()); } } FileTools.Append2TextFile(opFilePath, lines); }
/// <summary> /// Wraps up the resources into a template.ZIP file /// and then runs a test on the source recording. /// </summary> public static void Execute(Arguments arguments) { if (arguments == null) { arguments = Dev(); } string title = "# EDIT TEMPLATE."; string date = "# DATE AND TIME: " + DateTime.Now; Log.WriteLine(title); Log.WriteLine(date); //ZIP THE OUTPUT FILES const bool ZipOutput = true; FileInfo iniPath = arguments.Config; string[] nameComponents = (Path.GetFileNameWithoutExtension(iniPath.Name)).Split('_'); string targetName = nameComponents[0] + "_" + nameComponents[1]; //i: Set up the file names DirectoryInfo outputDir = iniPath.Directory; FileInfo targetImagePath = outputDir.CombineFile(targetName + "_target.png"); // input image file FileInfo binaryOpPath = outputDir.CombineFile(targetName + "_binary.bmp"); FileInfo trinaryOpPath = outputDir.CombineFile(targetName + "_trinary.bmp"); FileInfo sprOpPath = outputDir.CombineFile(targetName + "_spr.txt"); // syntactic pattern recognition file //additional files to be zipped up with template FileInfo targetPath = outputDir.CombineFile(targetName + "_target.txt"); FileInfo targetNoNoisePath = outputDir.CombineFile(targetName + "_targetNoNoise.txt"); FileInfo noisePath = outputDir.CombineFile(targetName + "_noise.txt"); //ii: READ PARAMETER VALUES FROM INI FILE var config = new ConfigDictionary(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 = int.Parse(dict[FeltTemplate_Create.key_DONT_CARE_NH]); //the do not care neighbourhood int lineLength = int.Parse(dict[FeltTemplate_Create.key_LINE_LENGTH]); double templateThreshold = dB_Threshold / maxTemplateIntensity; int bitmapThreshold = (int)(255 - (templateThreshold * 255)); Log.WriteLine("#################################### WRITE THE BINARY TEMPLATE ##################################"); Bitmap bitmap = ImageTools.ReadImage2Bitmap(targetImagePath.FullName); var binaryBmp = Image2BinaryBitmap(bitmap, bitmapThreshold); binaryBmp.Save(binaryOpPath.FullName); Log.WriteLine("#################################### WRITE THE TRINARY TEMPLATE ##################################"); var trinaryBmp = Image2TrinaryBitmap(binaryBmp, neighbourhood); trinaryBmp.Save(trinaryOpPath.FullName); Log.WriteLine("#################################### WRITE THE SPR TEMPLATE ##################################"); double[,] matrix = ImageTools.GreyScaleImage2Matrix(bitmap); matrix = DataTools.MatrixRotate90Clockwise(matrix); //rows=time cols=freq. //ImageTools.DrawMatrix(matrix, @"C:\SensorNetworks\Output\FELT_LewinsRail1\SPR_output1.bmp"); //int smallLengthThreshold = 10; //var tuple = SPT.doSPT(matrix, templateThreshold, smallLengthThreshold); //matrix = tuple.Item1; //ImageTools.DrawMatrix(matrix, @"C:\SensorNetworks\Output\FELT_LewinsRail1\SPR_output2.bmp"); char[,] spr = SprTools.Target2SymbolicTracks(matrix, templateThreshold, lineLength); FileTools.WriteMatrix2File(spr, sprOpPath.FullName); var tuple1 = FindMatchingEvents.Execute_One_Spr_Match(spr, matrix, templateThreshold); double sprScore = tuple1.Item1; double dBScore = sprScore * maxTemplateIntensity; Log.WriteLine("#################################### WRITE THE OSCILATION TEMPLATE ##################################"); double[,] target = FileTools.ReadDoubles2Matrix(targetPath.FullName); // oscillations in time double[,] rotatedtarget = DataTools.MatrixRotate90Clockwise(target); var colSums = DataTools.GetColumnsAverages(rotatedtarget); double[] periods = Oscillations2010.PeriodicityAnalysis(colSums); // frame periodicity LoggedConsole.WriteLine("Periodicity (sec) = {0:f3}, {1:f3}, {2:f3}", periods[0] * FeltTemplates_Use.FeltFrameOffset, periods[1] * FeltTemplates_Use.FeltFrameOffset, periods[2] * FeltTemplates_Use.FeltFrameOffset); //double oscilFreq = indexOfMaxValue / dctDuration * 0.5; //Times 0.5 because index = Pi and not 2Pi // oscillations in freq i.e. harmonics colSums = DataTools.GetColumnsAverages(target); periods = Oscillations2010.PeriodicityAnalysis(colSums); LoggedConsole.WriteLine("Periodicity (Hz) = {0:f0}, {1:f0}, {2:f0}.", periods[0] * FeltTemplates_Use.FeltFreqBinWidth, periods[1] * FeltTemplates_Use.FeltFreqBinWidth, periods[2] * FeltTemplates_Use.FeltFreqBinWidth); //double oscilFreq = indexOfMaxValue / dctDuration * 0.5; //Times 0.5 because index = Pi and not 2Pi //FileTools.WriteMatrix2File(spr, sprOpPath); // ZIP THE OUTPUT Log.WriteLine("#################################### ZIP THE TEMPLATES ##################################"); if (ZipOutput == true) { var filenames = new[] { iniPath, targetImagePath, binaryOpPath, targetPath, targetNoNoisePath, noisePath }; string biOutZipFile = outputDir + targetName + "_binaryTemplate.zip"; //FileTools.ZipFiles(filenames, biOutZipFile); filenames = new[] { iniPath, targetImagePath, trinaryOpPath, targetPath, targetNoNoisePath, noisePath }; string triOutZipFile = outputDir + targetName + "_trinaryTemplate.zip"; //FileTools.ZipFiles(filenames, triOutZipFile); filenames = new[] { iniPath, targetImagePath, sprOpPath, targetPath, targetNoNoisePath, noisePath }; string sprOutZipFile = outputDir + targetName + "_syntacticTemplate.zip"; //FileTools.ZipFiles(filenames, sprOutZipFile); // Zipping files can be done by using standard .NET 4.6 System.IO.Compression // however, this code hasn't been used in years and I've opted to just comment out the lines above throw new NotImplementedException("Zipping template files intentionally broken"); } Log.WriteLine("\n\n#################################### TEST THE EXTRACTED EVENT ON SOURCE FILE ##################################"); //vi: TEST THE EVENT ON ANOTHER FILE //felt "C:\SensorNetworks\WavFiles\Canetoad\DM420010_128m_00s__130m_00s - Toads.mp3" C:\SensorNetworks\Output\FELT_CaneToad\FELT_CaneToad_Params.txt events.txt //string testRecording = @"C:\SensorNetworks\WavFiles\Gecko\Suburban_March2010\geckos_suburban_104.mp3"; //string testRecording = @"C:\SensorNetworks\WavFiles\Gecko\Suburban_March2010\geckos_suburban_18.mp3"; //string testRecording = @"C:\SensorNetworks\WavFiles\Currawongs\Currawong_JasonTagged\West_Knoll_Bees_20091102-170000.mp3"; //string testRecording = @"C:\SensorNetworks\WavFiles\Curlew\Curlew2\Top_Knoll_-_St_Bees_20090517-210000.wav"; string listOpDir = "C:\\SensorNetworks\\Output\\FELT_templateList\\"; string templateListPath = listOpDir + "templateTestList.txt"; var list = new List <string>(); list.Add("#" + outputDir + targetName + "_binaryTemplate.zip"); list.Add("#" + outputDir + targetName + "_trinaryTemplate.zip"); list.Add("#" + outputDir + targetName + "_syntacticTemplate.zip"); FileTools.Append2TextFile(templateListPath, list); //write the template.ZIP file //TEST THE TEMPLATE ON SOURCE FILE //string[] arguments = new string[3]; /* * var args = new FeltTemplates_Use.Arguments() * { * Source = "", * arguments[0] = sourceDir + "\\" + sourceFile; * arguments[1] = templateListPath; * arguments[2] = listOpDir; * * }*/ //FeltTemplates_Use.Dev(arguments); Log.WriteLine("# Finished everything!"); }