public static Image <Rgb24> FrameSliceOf3DSpectrogram_DayOfYear(Image <Rgb24> bmp1, Image <Rgb24> titleBar, int year, int dayOfYear, TimeSpan xInterval, int herzValue, FileInfo sunriseSetData, int nyquistFreq) { Image <Rgb24> suntrack = SunAndMoon.AddSunTrackToImage(bmp1.Width, sunriseSetData, year, dayOfYear); bmp1.Mutate(g => { Pen pen = new Pen(Color.White, 1); var stringFont = Drawing.Arial12; //Font stringFont = Drawing.Tahoma9; DateTime theDate = new DateTime(year, 1, 1).AddDays(dayOfYear - 1); string dateString = $"{year} {DataTools.MonthNames[theDate.Month - 1]} {theDate.Day:d2}"; g.DrawText(dateString, stringFont, Color.Wheat, new PointF(10, 3)); }); TimeSpan xAxisPixelDuration = TimeSpan.FromSeconds(60); var minuteOffset = TimeSpan.Zero; double secondsDuration = xAxisPixelDuration.TotalSeconds * bmp1.Width; TimeSpan fullDuration = TimeSpan.FromSeconds(secondsDuration); // init frequency scale int herzInterval = 1000; int frameSize = bmp1.Height; var freqScale = new DSP.FrequencyScale(nyquistFreq, frameSize, herzInterval); SpectrogramTools.DrawGridLinesOnImage((Image <Rgb24>)bmp1, minuteOffset, fullDuration, xInterval, freqScale); int trackHeight = 20; int imageHt = bmp1.Height + trackHeight + trackHeight + trackHeight; var xAxisTicInterval = TimeSpan.FromMinutes(60); // assume 60 pixels per hour var timeScale24Hour = ImageTrack.DrawTimeTrack(fullDuration, minuteOffset, xAxisTicInterval, bmp1.Width, trackHeight, "hours"); var imageList = new List <Image <Rgb24> > { titleBar, timeScale24Hour, suntrack, bmp1, timeScale24Hour }; var compositeBmp = ImageTools.CombineImagesVertically(imageList); // trackHeight = compositeBmp.Height; // Image<Rgb24> timeScale12Months = ImageTrack.DrawYearScaleVertical(40, trackHeight); // Image<Rgb24> freqScale = DrawFreqScale_vertical(40, trackHeight, HerzValue, nyquistFreq); imageList = new List <Image <Rgb24> >(); // imageList.Add(timeScale12Months); imageList.Add(compositeBmp); // imageList.Add(freqScale); compositeBmp = ImageTools.CombineImagesInLine(imageList.ToArray()); return(compositeBmp); }
public static Image <Rgb24> ConcatenateFourChannelImages(FileInfo[] imageFiles, DirectoryInfo imageDirectory, string fileSuffix, string date) { // get first image to find its dimensions var image = (Image <Rgb24>)Image.Load(imageFiles[0].FullName); var brush = Color.White; Font stringFont = Drawing.Tahoma12; //create spacer image int width = 1; int height = image.Height; Image <Rgb24> spacerImage = new Image <Rgb24>(width, height); spacerImage.Mutate(g => { g.Clear(Color.DarkGray); }); // init output list of images var fourChannelList = new List <Image <Rgb24> >(); for (int channel = 0; channel < 4; channel++) { var imageList = new List <Image <Rgb24> >(); // Monitoring_Rosin_20120329T000000 + 0200_.merged.wav.channel_0__2Maps.png; string fileMatch = $@"0000+0200_.merged.wav.channel_{channel}__{fileSuffix}"; foreach (FileInfo imageFile in imageFiles) { if (!imageFile.Name.EndsWith(fileMatch)) { continue; } image = (Image <Rgb24>)Image.Load(imageFile.FullName); imageList.Add(image); imageList.Add(spacerImage); } imageList.Add(spacerImage); imageList.Add(spacerImage); var concatImage = ImageTools.CombineImagesInLine(imageList); concatImage.Mutate(g => { string chn = $"ch{channel + 1}"; g.DrawTextSafe(chn, stringFont, brush, new PointF(2, 40)); }); fourChannelList.Add(concatImage); } var combinedImage = ImageTools.CombineImagesVertically(fourChannelList); return(combinedImage); }
/// <summary> /// Creates an image from the frequency/oscillations matrix. /// The y-axis scale = frequency bins as per normal spectrogram. /// The x-axis scale is oscillations per second. /// </summary> /// <param name="freqOscilMatrix">the input frequency/oscillations matrix</param> /// <param name="framesPerSecond">to give the time scale</param> /// <param name="freqBinWidth">to give the frequency scale</param> /// <param name="sampleLength">to allow calculation of the oscillations scale</param> /// <param name="algorithmName">the algorithm used to compute the oscillations.</param> /// <returns>bitmap image</returns> public static Image GetFreqVsOscillationsImage(double[,] freqOscilMatrix, double framesPerSecond, double freqBinWidth, int sampleLength, string algorithmName) { // remove the high cycles/sec end of the matrix because nothing really happens here. int maxRows = freqOscilMatrix.GetLength(0) / 2; freqOscilMatrix = MatrixTools.Submatrix(freqOscilMatrix, 0, 0, maxRows - 1, freqOscilMatrix.GetLength(1) - 1); // get the OSC spectral index var spectralIndex = MatrixTools.GetMaximumColumnValues(freqOscilMatrix); // Convert spectrum index to oscillations per second double oscillationBinWidth = framesPerSecond / sampleLength; //draw an image freqOscilMatrix = MatrixTools.MatrixRotate90Anticlockwise(freqOscilMatrix); // each value is to be drawn as a 5 pixel x 5 pixel square int xscale = 5; int yscale = 5; if (maxRows < 10) { xscale = 10; } //var image1 = ImageTools.DrawMatrixInColour(freqOscilMatrix, xPixelsPerCell: xscale, yPixelsPerCell: yscale); //var image2 = ImageTools.DrawVectorInColour(DataTools.reverseArray(spectralIndex), cellWidth: xscale); var image1 = ImageTools.DrawMatrixInGrayScale(freqOscilMatrix, xPixelsPerCell: xscale, yPixelsPerCell: yscale, reverse: true); spectralIndex = DataTools.NormaliseByScalingMaxValueToOne(spectralIndex); var image2 = ImageTools.DrawVectorInGrayScaleWithoutNormalisation(DataTools.reverseArray(spectralIndex), xscale, yscale, reverse: true); var image = ImageTools.CombineImagesInLine(new[] { image1, image2 }); // place a grid line every 5 cycles per second. double cycleInterval = 5.0; double xTicInterval = cycleInterval / oscillationBinWidth * xscale; // a tic every 1000 Hz. int herzInterval = 1000; double yTicInterval = herzInterval / freqBinWidth * yscale; int xOffset = xscale / 2; int yOffset = yscale / 2; image = ImageTools.DrawYaxisScale(image, 10, herzInterval, yTicInterval, yOffset); image = ImageTools.DrawXaxisScale(image, 15, cycleInterval, xTicInterval, 10, -xOffset); var titleBar = DrawTitleBarOfOscillationSpectrogram(algorithmName, image.Width); var imageList = new List <Image> { titleBar, image }; var compositeBmp = (Bitmap)ImageTools.CombineImagesVertically(imageList); return(compositeBmp); }
public static Image FrameSliceOf3DSpectrogram_DayOfYear(Image bmp1, Image titleBar, int year, int dayOfYear, TimeSpan xInterval, int herzValue, FileInfo sunriseSetData, int nyquistFreq) { Bitmap suntrack = SunAndMoon.AddSunTrackToImage(bmp1.Width, sunriseSetData, year, dayOfYear); Graphics g = Graphics.FromImage(bmp1); Pen pen = new Pen(Color.White); Font stringFont = new Font("Arial", 12); //Font stringFont = new Font("Tahoma", 9); DateTime theDate = new DateTime(year, 1, 1).AddDays(dayOfYear - 1); string dateString = string.Format("{0} {1} {2:d2}", year, DataTools.MonthNames[theDate.Month - 1], theDate.Day); g.DrawString(dateString, stringFont, Brushes.Wheat, new PointF(10, 3)); TimeSpan xAxisPixelDuration = TimeSpan.FromSeconds(60); var minuteOffset = TimeSpan.Zero; double secondsDuration = xAxisPixelDuration.TotalSeconds * bmp1.Width; TimeSpan fullDuration = TimeSpan.FromSeconds(secondsDuration); // init frequency scale int herzInterval = 1000; int frameSize = bmp1.Height; var freqScale = new DSP.FrequencyScale(nyquistFreq, frameSize, herzInterval); SpectrogramTools.DrawGridLinesOnImage((Bitmap)bmp1, minuteOffset, fullDuration, xInterval, freqScale); int trackHeight = 20; int imageHt = bmp1.Height + trackHeight + trackHeight + trackHeight; var xAxisTicInterval = TimeSpan.FromMinutes(60); // assume 60 pixels per hour var timeScale24Hour = ImageTrack.DrawTimeTrack(fullDuration, minuteOffset, xAxisTicInterval, bmp1.Width, trackHeight, "hours"); var imageList = new List <Image>(); imageList.Add(titleBar); imageList.Add(timeScale24Hour); imageList.Add(suntrack); imageList.Add(bmp1); imageList.Add(timeScale24Hour); Image compositeBmp = ImageTools.CombineImagesVertically(imageList.ToArray()); // trackHeight = compositeBmp.Height; // Bitmap timeScale12Months = ImageTrack.DrawYearScaleVertical(40, trackHeight); // Bitmap freqScale = DrawFreqScale_vertical(40, trackHeight, HerzValue, nyquistFreq); imageList = new List <Image>(); // imageList.Add(timeScale12Months); imageList.Add(compositeBmp); // imageList.Add(freqScale); compositeBmp = ImageTools.CombineImagesInLine(imageList.ToArray()); return(compositeBmp); }
/// <summary> /// Generates the FREQUENCY x OSCILLATIONS Graphs and csv /// I have experimented with five methods to search for oscillations: /// 1: string algorithmName = "Autocorr-FFT"; /// use this if want more detailed output - but not necessrily accurate! /// 2: string algorithmName = "Autocorr-SVD-FFT"; /// use this if want only dominant oscillations /// 3: string algorithmName = "Autocorr-Cwt"; /// a Wavelets option but could not get it to work well /// 4: string algorithmName = "Autocorr-WPD"; /// another Wavelets option but could not get it to work well /// 5: Discrete Cosine Transform /// The DCT only works well when you know which periodicity you are looking for. e.g. Canetoad. /// </summary> public static Tuple <Image <Rgb24>, 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]); } var 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); var spgm = GetSpectrogramMatrix(recordingSegment, DefaultFrameLength); double framesPerSecond = DefaultResampleRate / (double)DefaultFrameLength; double freqBinWidth = framesPerSecond; // get the appropriate sampleLength (patch size) for short recordings int framecount = spgm.GetLength(0); sampleLength = AdjustSampleSize(framecount, sampleLength); var algorithmName1 = "autocorr-svd-fft"; var freqOscilMatrix1 = GetFrequencyByOscillationsMatrix(spgm, sensitivity, sampleLength, algorithmName1); var image1 = GetFreqVsOscillationsImage(freqOscilMatrix1, framesPerSecond, freqBinWidth, sampleLength, algorithmName1); var algorithmName2 = "autocorr-fft"; var freqOscilMatrix2 = GetFrequencyByOscillationsMatrix(spgm, sensitivity, sampleLength, algorithmName2); var image2 = GetFreqVsOscillationsImage(freqOscilMatrix2, framesPerSecond, freqBinWidth, sampleLength, algorithmName2); //IMPORTANT NOTE: To generate an OSC spectral index matrix for making LDFC spectrograms, use the following line: //var spectralIndex = MatrixTools.GetMaximumColumnValues(freqOscilMatrix2); var compositeImage = ImageTools.CombineImagesInLine(image2, image1); // Return (1) composite image of oscillations, // (2) data matrix from only one algorithm, return(Tuple.Create(compositeImage, freqOscilMatrix2)); }
public static Image ConcatenateFourChannelImages(FileInfo[] imageFiles, DirectoryInfo imageDirectory, string fileSuffix, string date) { // get first image to find its dimensions Image image = Image.FromFile(imageFiles[0].FullName); Brush brush = Brushes.White; Font stringFont = new Font("Tahoma", 12); //create spacer image int width = 1; int height = image.Height; Bitmap spacerImage = new Bitmap(width, height); Graphics g = Graphics.FromImage(spacerImage); g.Clear(Color.DarkGray); // init output list of images var fourChannelList = new List <Image>(); for (int channel = 0; channel < 4; channel++) { var imageList = new List <Image>(); // Monitoring_Rosin_20120329T000000 + 0200_.merged.wav.channel_0__2Maps.png; string fileMatch = string.Format(@"0000+0200_.merged.wav.channel_{0}__{1}", channel, fileSuffix); foreach (FileInfo imageFile in imageFiles) { if (!imageFile.Name.EndsWith(fileMatch)) { continue; } image = Image.FromFile(imageFile.FullName); imageList.Add(image); imageList.Add(spacerImage); } imageList.Add(spacerImage); imageList.Add(spacerImage); Image concatImage = ImageTools.CombineImagesInLine(imageList); g = Graphics.FromImage(concatImage); string chn = string.Format("ch{0}", channel + 1); g.DrawString(chn, stringFont, brush, new PointF(2, 40)); fourChannelList.Add(concatImage); } Image combinedImage = ImageTools.CombineImagesVertically(fourChannelList); return(combinedImage); }
public void TestCombineImagesInLine() { var actual = ImageTools.CombineImagesInLine( null, Drawing.NewImage(10, 100, Color.Red), Drawing.NewImage(100, 100, Color.Red), Drawing.NewImage(20, 100, Color.Red), null, Drawing.NewImage(200, 100, Color.Red), null); Assert.That.ImageIsSize(330, 100, actual); Assert.That.ImageRegionIsColor(actual.Bounds(), Color.Red, actual); }
public static Image FrameSliceOf3DSpectrogram_ConstantFreq(Image bmp1, Image titleBar, TimeSpan xInterval, int herzValue, FileInfo sunriseSetData, int nyquistFreq) { SunAndMoon.AddSunRiseSetLinesToImage((Bitmap)bmp1, sunriseSetData, 0, 365, 1); // assume full year and 1px/day var g = Graphics.FromImage(bmp1); var pen = new Pen(Color.White); var stringFont = new Font("Arial", 12); var str = $"Freq = {herzValue} Hz"; g.DrawString(str, stringFont, Brushes.Wheat, new PointF(10, 7)); var xAxisPixelDuration = TimeSpan.FromSeconds(60); var startOffset = TimeSpan.Zero; double secondsDuration = xAxisPixelDuration.TotalSeconds * bmp1.Width; var fullDuration = TimeSpan.FromSeconds(secondsDuration); // init frequency scale int herzInterval = 1000; int frameSize = bmp1.Height; var freqScale = new DSP.FrequencyScale(nyquistFreq, frameSize, herzInterval); SpectrogramTools.DrawGridLinesOnImage((Bitmap)bmp1, startOffset, fullDuration, xInterval, freqScale); int trackHeight = 20; var xAxisTicInterval = TimeSpan.FromMinutes(60); // assume 60 pixels per hour var timeScale24Hour = ImageTrack.DrawTimeTrack(fullDuration, startOffset, xAxisTicInterval, bmp1.Width, trackHeight, "hours"); var imageList = new List <Image> { titleBar, timeScale24Hour, bmp1, timeScale24Hour }; var compositeBmp = ImageTools.CombineImagesVertically(imageList.ToArray()); if (compositeBmp == null) { throw new ArgumentNullException(nameof(compositeBmp)); } trackHeight = compositeBmp.Height; Bitmap timeScale12Months = ImageTrack.DrawYearScaleVertical(40, trackHeight); Bitmap freqScaleImage = DrawFreqScale_vertical(40, trackHeight, herzValue, nyquistFreq); imageList = new List <Image> { timeScale12Months, compositeBmp, freqScaleImage }; compositeBmp = ImageTools.CombineImagesInLine(imageList.ToArray()); return(compositeBmp); }
public void TestCombineImagesInLineDefaultFill() { var actual = ImageTools.CombineImagesInLine( null, Drawing.NewImage(100, 80, Color.Red), Drawing.NewImage(100, 100, Color.Red), Drawing.NewImage(100, 80, Color.Red), null, Drawing.NewImage(100, 100, Color.Red), null); Assert.That.ImageIsSize(400, 100, actual); Assert.That.ImageRegionIsColor((0, 0, 400, 80).AsRect(), Color.Red, actual); Assert.That.ImageRegionIsColor((0, 80, 100, 20).AsRect(), Color.Black, actual); Assert.That.ImageRegionIsColor((100, 80, 100, 20).AsRect(), Color.Red, actual); Assert.That.ImageRegionIsColor((200, 80, 100, 20).AsRect(), Color.Black, actual); Assert.That.ImageRegionIsColor((300, 80, 100, 20).AsRect(), Color.Red, actual); }
public static Image GetFreqVsOscillationsImage(double[,] freqOscilMatrix, double framesPerSecond, double freqBinWidth, int sampleLength, string algorithmName) { // remove the high cycles/sec end of the matrix because nothing really happens here. freqOscilMatrix = MatrixTools.Submatrix(freqOscilMatrix, 0, 0, 30, freqOscilMatrix.GetLength(1) - 1); // get the OSC spectral index // double[] spectralIndex = ConvertMatrix2SpectralIndexBySummingFreqColumns(freqOscilMatrix, skipNrows: 0); var spectralIndex = MatrixTools.GetMaximumColumnValues(freqOscilMatrix); // Convert spectrum index to oscillations per second double oscillationBinWidth = framesPerSecond / sampleLength; //draw an image freqOscilMatrix = MatrixTools.MatrixRotate90Anticlockwise(freqOscilMatrix); int xscale = 5; int yscale = 5; var image1 = ImageTools.DrawMatrixInColour(freqOscilMatrix, xPixelsPerCell: xscale, yPixelsPerCell: yscale); var image2 = ImageTools.DrawVectorInColour(DataTools.reverseArray(spectralIndex), cellWidth: xscale); var image = ImageTools.CombineImagesInLine(new[] { image1, image2 }); // a tic every 5cpsec. double cycleInterval = 5.0; double xTicInterval = cycleInterval / oscillationBinWidth * xscale; // a tic every 1000 Hz. int herzInterval = 1000; double yTicInterval = herzInterval / freqBinWidth * yscale; int xOffset = xscale / 2; int yOffset = yscale / 2; image = ImageTools.DrawXandYaxes(image, 18, cycleInterval, xTicInterval, xOffset, herzInterval, yTicInterval, yOffset); var titleBar = DrawTitleBarOfOscillationSpectrogram(algorithmName, image.Width); var imageList = new List <Image> { titleBar, image }; var compositeBmp = (Bitmap)ImageTools.CombineImagesVertically(imageList); return(compositeBmp); }
/// <summary> /// Can be used for visual checking and debugging purposes. /// </summary> public static void DrawNormalisedIndexMatrices(DirectoryInfo dir, string baseName, Dictionary <string, double[, ]> dictionary) { var list = new List <Image>(); foreach (string key in ContentSignatures.IndexNames) { var bmp = ImageTools.DrawReversedMatrixWithoutNormalisation(dictionary[key]); // need to rotate spectrogram to get correct orientation. bmp.RotateFlip(RotateFlipType.Rotate270FlipNone); // draw grid lines and add axis scales var xAxisPixelDuration = TimeSpan.FromSeconds(60); var fullDuration = TimeSpan.FromTicks(xAxisPixelDuration.Ticks * bmp.Width); var freqScale = new FrequencyScale(11025, 512, 1000); SpectrogramTools.DrawGridLinesOnImage((Bitmap)bmp, TimeSpan.Zero, fullDuration, xAxisPixelDuration, freqScale); const int trackHeight = 20; var recordingStartDate = default(DateTimeOffset); var timeBmp = ImageTrack.DrawTimeTrack(fullDuration, recordingStartDate, bmp.Width, trackHeight); var array = new Image[2]; array[0] = bmp; array[1] = timeBmp; var image = ImageTools.CombineImagesVertically(array); // add a header to the spectrogram var header = new Bitmap(image.Width, 20); Graphics g = Graphics.FromImage(header); g.Clear(Color.LightGray); g.SmoothingMode = SmoothingMode.AntiAlias; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.PixelOffsetMode = PixelOffsetMode.HighQuality; g.DrawString(key, new Font("Tahoma", 9), Brushes.Black, 4, 4); list.Add(ImageTools.CombineImagesVertically(new List <Image>(new[] { header, image }))); } // save the image - the directory for the path must exist var path = Path.Combine(dir.FullName, baseName + "__Towsey.Acoustic.GreyScaleImages.png"); var indexImage = ImageTools.CombineImagesInLine(list); indexImage?.Save(path); }
/// <summary> /// Can be used for visual checking and debugging purposes. /// </summary> public static void DrawNormalisedIndexMatrices(DirectoryInfo dir, string baseName, Dictionary <string, double[, ]> dictionary) { var list = new List <Image <Rgb24> >(); foreach (string key in ContentSignatures.IndexNames) { var bmp = ImageTools.DrawReversedMatrixWithoutNormalisation(dictionary[key]); // need to rotate spectrogram to get correct orientation. bmp.RotateFlip(RotateFlipType.Rotate270FlipNone); // draw grid lines and add axis scales var xAxisPixelDuration = TimeSpan.FromSeconds(60); var fullDuration = TimeSpan.FromTicks(xAxisPixelDuration.Ticks * bmp.Width); var freqScale = new FrequencyScale(11025, 512, 1000); SpectrogramTools.DrawGridLinesOnImage((Image <Rgb24>)bmp, TimeSpan.Zero, fullDuration, xAxisPixelDuration, freqScale); const int trackHeight = 20; var recordingStartDate = default(DateTimeOffset); var timeBmp = ImageTrack.DrawTimeTrack(fullDuration, recordingStartDate, bmp.Width, trackHeight); var image = ImageTools.CombineImagesVertically(bmp, timeBmp); // add a header to the spectrogram var header = Drawing.NewImage(image.Width, 20, Color.LightGray); header.Mutate(g => { g.DrawText(key, Drawing.Tahoma9, Color.Black, new PointF(4, 4)); list.Add(ImageTools.CombineImagesVertically(header, image)); }); } // save the image - the directory for the path must exist var path = Path.Combine(dir.FullName, baseName + "__Towsey.Acoustic.GreyScaleImages.png"); var indexImage = ImageTools.CombineImagesInLine(list); indexImage?.Save(path); }
// ############################################################################################################## // ######################### ORIGINAL METHOD FOR STITCHING Gianna Pavan's DATA (10 minutes every 30 minutes) /// <summary> /// This method stitches together spectrogram images derived from consecutive shorter recordings over a 24 hour period. /// Currently set for the recording protocol of Gianna Pavan (10 minutes every 30 minutes). /// /// Call this method from Sandpit or where ever! /// /// IMPORTANT NOTE: This method does NOT check to see if the images are in temporal order. /// A SORT line should be inserted somewhere /// </summary> public static void StitchPartialSpectrograms() { //###################################################### // ********************* set the below parameters var inputDirectory = new DirectoryInfo(@"Z:\Italy_GianniPavan\output4\Towsey.Acoustic"); string opFileStem = "Sassofratino_24hours_v3"; var outputDirectory = new DirectoryInfo(@"Z:\Italy_GianniPavan\output4\"); // a filter to select images to be stitched string endString = "_000.2MAPS.png"; // recording protocol int minutesBetweenRecordingStarts = 30; TimeSpan minOffset = TimeSpan.Zero; // assume first recording in sequence started at midnight // X-axis timescale int pixelColumnsPerHour = 60; int trackHeight = IndexDisplay.DefaultTrackHeight; // ********************* set the above parameters //###################################################### string[] fileEntries = Directory.GetFiles(inputDirectory.FullName); var images = new List <Image>(); bool interpolateSpacer = true; var imagePair = new Image[2]; TimeSpan xAxisTicInterval = TimeSpan.FromMinutes(pixelColumnsPerHour); // assume 60 pixels per hour // loop through all files in the required directory foreach (string path in fileEntries) { // filter files. if (!path.EndsWith(endString)) { continue; } var image = new Bitmap(path); int spacerWidth = minutesBetweenRecordingStarts - image.Width; if (interpolateSpacer) { var spacer = new Bitmap(spacerWidth, image.Height); imagePair[0] = image; imagePair[1] = spacer; image = (Bitmap)ImageTools.CombineImagesInLine(imagePair); } images.Add(image); } var compositeBmp = ImageTools.CombineImagesInLine(images.ToArray()); var fullDuration = TimeSpan.FromMinutes(compositeBmp.Width); var timeBmp = ImageTrack.DrawTimeTrack(fullDuration, minOffset, xAxisTicInterval, compositeBmp.Width, trackHeight, "hours"); var gr = Graphics.FromImage(compositeBmp); int halfHeight = compositeBmp.Height / 2; //add in the title bars string title = $"24 hour FALSE-COLOUR SPECTROGRAM (scale: hours x kHz) (colour: R-G-B = BGN-AVG-CVR) {Meta.OrganizationTag} "; var titleBmp = ImageTrack.DrawTitleTrack(compositeBmp.Width, trackHeight, title); int offset = 0; gr.DrawImage(titleBmp, 0, offset); //draw in the top time scale title = $"24 hour FALSE-COLOUR SPECTROGRAM (scale: hours x kHz) (colour: R-G-B = ACI-ENT-EVN) {Meta.OrganizationTag} "; titleBmp = ImageTrack.DrawTitleTrack(compositeBmp.Width, trackHeight, title); offset = halfHeight; gr.DrawImage(titleBmp, 0, offset); //draw in the top time scale //add in the timescale tracks offset = trackHeight; gr.DrawImage(timeBmp, 0, offset); //draw in the top time scale offset = compositeBmp.Height - trackHeight; gr.DrawImage(timeBmp, 0, offset); //draw in the top time scale offset = halfHeight - trackHeight; gr.DrawImage(timeBmp, 0, offset); //draw in the top time scale offset = halfHeight + trackHeight; gr.DrawImage(timeBmp, 0, offset); //draw in the top time scale compositeBmp.Save(Path.Combine(outputDirectory.FullName, opFileStem + ".png")); }
public static void Execute(Arguments arguments) { var inputDirs = arguments.InputDataDirectories.Select(FileInfoExtensions.ToDirectoryInfo); var output = arguments.OutputDirectory.ToDirectoryInfo(); string date = "# DATE AND TIME: " + DateTime.Now; LoggedConsole.WriteLine("\n# DRAW an EASY IMAGE from consecutive days of SUMMARY INDICES in CSV files."); LoggedConsole.WriteLine("# IT IS ASSUMED THAT THE CSV files are already concatenated into 24 hour files."); LoggedConsole.WriteLine(date); LoggedConsole.WriteLine("# Summary Index.csv files are in directories:"); foreach (DirectoryInfo dir in inputDirs) { LoggedConsole.WriteLine(" {0}", dir.FullName); } LoggedConsole.WriteLine("# Output directory: " + output); if (arguments.StartDate == null) { LoggedConsole.WriteLine("# Start date = NULL (No argument provided). Will revise start date ...."); } else { LoggedConsole.WriteLine("# Start date = " + arguments.StartDate.ToString()); } if (arguments.EndDate == null) { LoggedConsole.WriteLine("# End date = NULL (No argument provided). Will revise end date ...."); } else { LoggedConsole.WriteLine("# End date = " + arguments.EndDate.ToString()); } LoggedConsole.WriteLine("# FILE FILTER = " + arguments.FileFilter); LoggedConsole.WriteLine(); // PATTERN SEARCH FOR SUMMARY INDEX FILES. //string pattern = "*__Towsey.Acoustic.Indices.csv"; FileInfo[] csvFiles = IndexMatrices.GetFilesInDirectories(inputDirs.ToArray(), arguments.FileFilter); //LoggedConsole.WriteLine("# Subdirectories Count = " + subDirectories.Length); LoggedConsole.WriteLine("# SummaryIndexFiles.csv Count = " + csvFiles.Length); if (csvFiles.Length == 0) { LoggedConsole.WriteErrorLine("\n\nWARNING from method DrawEasyImage.Execute():"); LoggedConsole.WriteErrorLine(" No SUMMARY index files were found."); LoggedConsole.WriteErrorLine(" RETURNING EMPTY HANDED!"); return; } // Sort the files by date and return as a dictionary: sortedDictionaryOfDatesAndFiles<DateTimeOffset, FileInfo> //var sortedDictionaryOfDatesAndFiles = LDSpectrogramStitching.FilterFilesForDates(csvFiles, arguments.TimeSpanOffsetHint); // calculate new start date if passed value = null. DateTimeOffset?startDate = arguments.StartDate; DateTimeOffset?endDate = arguments.EndDate; TimeSpan totalTimespan = (DateTimeOffset)endDate - (DateTimeOffset)startDate; int dayCount = totalTimespan.Days + 1; // assume last day has full 24 hours of recording available. LoggedConsole.WriteLine("\n# Start date = " + startDate.ToString()); LoggedConsole.WriteLine("# End date = " + endDate.ToString()); LoggedConsole.WriteLine(string.Format("# Elapsed time = {0:f1} hours", dayCount * 24)); LoggedConsole.WriteLine("# Day count = " + dayCount + " (inclusive of start and end days)"); LoggedConsole.WriteLine("# Time Zone = " + arguments.TimeSpanOffsetHint.ToString()); // create top level output directory if it does not exist. DirectoryInfo opDir = output; if (!opDir.Exists) { opDir.Create(); } // SET UP DEFAULT SITE LOCATION INFO -- DISCUSS IWTH ANTHONY // The following location data is used only to draw the sunrise/sunset tracks on images. double?latitude = null; double?longitude = null; var siteDescription = new SiteDescription(); siteDescription.SiteName = arguments.FileStemName; siteDescription.Latitude = latitude; siteDescription.Longitude = longitude; // the following required if drawing the index images FileInfo indexPropertiesConfig = null; // require IndexGenerationData and indexPropertiesConfig for drawing //indexGenerationData = IndexGenerationData.GetIndexGenerationData(csvFiles[0].Directory); indexPropertiesConfig = arguments.IndexPropertiesConfig.ToFileInfo(); Dictionary <string, IndexProperties> listOfIndexProperties = IndexProperties.GetIndexProperties(indexPropertiesConfig); Tuple <List <string>, List <double[]> > tuple = CsvTools.ReadCSVFile(csvFiles[0].FullName); var names = tuple.Item1; // default EASY indices int redID = 3; // backgroundNoise int grnID = 5; // avSNROfActiveframes int bluID = 7; // events per second string rep = @"bgn-avsnr-evn"; // ACI Ht Hpeaks EASY indices if (false) { redID = 11; // ACI grnID = 12; // Ht //bluID = 13; // HavgSp //bluID = 14; // Hvariance //bluID = 15; // Hpeaks bluID = 16; // Hcov //bluID = 7; // SPT rep = @"aci-ht-hcov"; //rep = @"aci-ht-spt"; } // LF, MF, HF if (true) { redID = 10; // LF grnID = 9; // MF bluID = 8; // HF rep = @"lf-mf-hf"; } IndexProperties redIndexProps = listOfIndexProperties[names[redID]]; IndexProperties grnIndexProps = listOfIndexProperties[names[grnID]]; IndexProperties bluIndexProps = listOfIndexProperties[names[bluID]]; int dayPixelHeight = 4; int rowCount = (dayPixelHeight * dayCount) + 35; // +30 for grid lines int colCount = 1440; var bitmap = new Image <Rgb24>(colCount, rowCount); var colour = Color.Yellow; int currentRow = 0; var oneDay = TimeSpan.FromHours(24); int graphWidth = colCount; int trackHeight = 20; var stringFont = Drawing.Arial8; string[] monthNames = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; // for drawing the y-axis scale int scaleWidth = trackHeight + 7; var yAxisScale = new Image <Rgb24>(scaleWidth, rowCount + (2 * trackHeight)); yAxisScale.Mutate(g => { g.Clear(Color.Black); // loop over days for (int d = 0; d < dayCount; d++) { var thisday = ((DateTimeOffset)startDate).AddDays(d); if (thisday.Day == 1) { int nextRow = currentRow + 1; for (int c = 0; c < colCount; c++) { bitmap[c, currentRow] = Color.Gray; bitmap[c, nextRow] = Color.Gray; } for (int c = 0; c < scaleWidth; c++) { yAxisScale[c, currentRow + trackHeight] = Color.Gray; yAxisScale[c, nextRow + trackHeight] = Color.Gray; } string month = monthNames[thisday.Month - 1]; if (thisday.Month == 1) // January { g.DrawText(thisday.Year.ToString(), stringFont, Color.White, new PointF(0, nextRow + trackHeight + 1)); //draw time g.DrawText(month, stringFont, Color.White, new PointF(1, nextRow + trackHeight + 11)); //draw time } else { g.DrawText(month, stringFont, Color.White, new PointF(1, nextRow + trackHeight + 1)); //draw time } currentRow += 2; } // get the exact date and time LoggedConsole.WriteLine($"READING DAY {d + 1} of {dayCount}: {thisday.ToString()}"); // CREATE DAY LEVEL OUTPUT DIRECTORY for this day string dateString = $"{thisday.Year}{thisday.Month:D2}{thisday.Day:D2}"; tuple = CsvTools.ReadCSVFile(csvFiles[d].FullName); var arrays = tuple.Item2; var redArray = arrays[redID]; var grnArray = arrays[grnID]; var bluArray = arrays[bluID]; // NormaliseMatrixValues the indices redArray = DataTools.NormaliseInZeroOne(redArray, redIndexProps.NormMin, redIndexProps.NormMax); grnArray = DataTools.NormaliseInZeroOne(grnArray, grnIndexProps.NormMin, grnIndexProps.NormMax); bluArray = DataTools.NormaliseInZeroOne(bluArray, bluIndexProps.NormMin, bluIndexProps.NormMax); for (int c = 0; c < colCount; c++) { for (int r = 0; r < dayPixelHeight; r++) { //transformedValue = Math.Sqrt(redArray[c]); var transformedValue = redArray[c] * redArray[c]; int redVal = (int)Math.Round(transformedValue * 255); if (redVal < 0) { redVal = 0; } else if (redVal > 255) { redVal = 255; } //transformedValue = Math.Sqrt(grnArray[c]); transformedValue = grnArray[c] * grnArray[c]; // square the value int grnVal = (int)Math.Round(transformedValue * 255); if (grnVal < 0) { grnVal = 0; } else if (grnVal > 255) { grnVal = 255; } //transformedValue = Math.Sqrt(bluArray[c]); transformedValue = bluArray[c] * bluArray[c]; // square the value int bluVal = (int)Math.Round(transformedValue * 255); if (bluVal < 0) { bluVal = 0; } else if (bluVal > 255) { bluVal = 255; } bitmap[c, currentRow + r] = Color.FromRgb((byte)redVal, (byte)grnVal, (byte)bluVal); } } // over all columns currentRow += dayPixelHeight; if (thisday.Day % 7 == 0) { for (int c = 0; c < colCount; c++) { bitmap[c, currentRow] = Color.Gray; } currentRow++; } } // over days }); // draw on civil dawn and dusk lines int startdayOfYear = ((DateTimeOffset)startDate).DayOfYear; int endDayOfYear = ((DateTimeOffset)endDate).DayOfYear; SunAndMoon.AddSunRiseSetLinesToImage(bitmap, arguments.BrisbaneSunriseDatafile.ToFileInfo(), startdayOfYear, endDayOfYear, dayPixelHeight); // add the time scales Image <Rgb24> timeBmp1 = ImageTrack.DrawTimeRelativeTrack(oneDay, graphWidth, trackHeight); var imageList = new [] { timeBmp1, bitmap, timeBmp1 }; Image <Rgb24> compositeBmp1 = (Image <Rgb24>)ImageTools.CombineImagesVertically(imageList); imageList = new [] { yAxisScale, compositeBmp1 }; Image <Rgb24> compositeBmp2 = (Image <Rgb24>)ImageTools.CombineImagesInLine(imageList); // indices used for image string indicesDescription = $"{redIndexProps.Name}|{grnIndexProps.Name}|{bluIndexProps.Name}"; string startString = $"{startDate.Value.Year}/{startDate.Value.Month}/{startDate.Value.Day}"; string endString = $"{endDate.Value.Year}/{endDate.Value.Month}/{endDate.Value.Day}"; string title = $"EASY: {arguments.FileStemName} From {startString} to {endString} Indices: {indicesDescription}"; Image <Rgb24> titleBar = ImageTrack.DrawTitleTrack(compositeBmp2.Width, trackHeight, title); imageList = new [] { titleBar, compositeBmp2 }; compositeBmp2 = (Image <Rgb24>)ImageTools.CombineImagesVertically(imageList); var outputFileName = Path.Combine(opDir.FullName, arguments.FileStemName + "." + rep + ".EASY.png"); compositeBmp2.Save(outputFileName); } // Execute()
public override AnalysisResult2 Analyze <T>(AnalysisSettings analysisSettings, SegmentSettings <T> segmentSettings) { var configuration = (StandardizedFeatureExtractionConfig)analysisSettings.Configuration; var audioFile = segmentSettings.SegmentAudioFile; var recording = new AudioRecording(audioFile.FullName); // Configurations non-specific for bands TimeSpan indexCalculationDuration = configuration.IndexCalculationDurationTimeSpan; TimeSpan bgNoiseNeighbourhood = configuration.BgNoiseBuffer; // Bands List <StandardizedFeatureExtractionConfig.BandsProperties> bandsList = configuration.Bands; // Check if there are identical bands CheckForIdenticalBands(bandsList); // Estimate total number of subsegments double segmentDurationSeconds = segmentSettings.AnalysisIdealSegmentDuration.TotalSeconds; double subsegmentDuration = indexCalculationDuration.TotalSeconds; int subsegmentCount = (int)Math.Round(segmentDurationSeconds / subsegmentDuration); int totalSubsegmentCount = subsegmentCount * bandsList.Count; // Store results of all subsegments var analysisResults = new AnalysisResult2(analysisSettings, segmentSettings, recording.Duration); analysisResults.AnalysisIdentifier = this.Identifier; var trackScores = new List <Plot>(totalSubsegmentCount); var tracks = new List <SpectralTrack>(totalSubsegmentCount); analysisResults.SummaryIndices = new SummaryIndexBase[totalSubsegmentCount]; analysisResults.SpectralIndices = new SpectralIndexBase[totalSubsegmentCount]; // Create list to store images, one for each band. They are later combined into one image. var list = new List <Image <Rgb24> >(); string imagePath = segmentSettings.SegmentImageFile.FullName; int maxImageWidth = 0; int bandCount = 0; foreach (var band in bandsList) { Log.DebugFormat("Starting band {0}/{1}", bandCount + 1, bandsList.Count); // Calculate spectral indices // get a fresh copy of the ICC config var config = (IndexCalculateConfig)((ICloneable)configuration).Clone(); // Add values specific for band from custom configuration file to config config.MinBandWidth = band.Bandwidth.Min; config.MaxBandWidth = band.Bandwidth.Max; config.FrameLength = band.FftWindow; if (band.MelScale != 0) { config.FrequencyScale = FreqScaleType.Mel; config.MelScale = band.MelScale; } else { config.FrequencyScale = FreqScaleType.Linear; } // Calculate indices for each subsegment and for each band IndexCalculateResult[] subsegmentResults = AcousticIndices.CalculateIndicesInSubsegments( recording, segmentSettings.SegmentStartOffset, segmentSettings.AnalysisIdealSegmentDuration, indexCalculationDuration, config.IndexProperties, segmentSettings.Segment.SourceMetadata.SampleRate, config); int columnsAmplitudeSpectrogram = subsegmentResults[0].AmplitudeSpectrogram.GetLength(1); double[,] amplitudeSpectrogramSegment = new double[0, columnsAmplitudeSpectrogram]; for (int i = 0; i < subsegmentResults.Length; i++) { var indexCalculateResult = subsegmentResults[i]; indexCalculateResult.SummaryIndexValues.FileName = segmentSettings.Segment.SourceMetadata.Identifier; indexCalculateResult.SpectralIndexValues.FileName = segmentSettings.Segment.SourceMetadata.Identifier; analysisResults.SummaryIndices[bandCount + (i * bandsList.Count)] = indexCalculateResult.SummaryIndexValues; analysisResults.SpectralIndices[bandCount + (i * bandsList.Count)] = indexCalculateResult.SpectralIndexValues; trackScores.AddRange(indexCalculateResult.TrackScores); if (indexCalculateResult.Tracks != null) { tracks.AddRange(indexCalculateResult.Tracks); } if (analysisSettings.AnalysisImageSaveBehavior.ShouldSave()) { // Add amplitude spectrograms of each subsegment together to get amplitude spectrogram of one segment double[,] amplitudeSpectrogramSubsegment = indexCalculateResult.AmplitudeSpectrogram; amplitudeSpectrogramSegment = MatrixTools.ConcatenateMatrixRows( amplitudeSpectrogramSegment, amplitudeSpectrogramSubsegment); } } if (analysisSettings.AnalysisImageSaveBehavior.ShouldSave()) { // Create image of amplitude spectrogram var image = ImageTools.DrawReversedMatrix(MatrixTools.MatrixRotate90Anticlockwise(amplitudeSpectrogramSegment)); // Label information string minBandWidth = band.Bandwidth.Min.ToString(); string maxBandWidth = band.Bandwidth.Max.ToString(); string fftWindow = band.FftWindow.ToString(); string mel; string melScale; if (band.MelScale != 0) { mel = "Mel"; melScale = band.MelScale.ToString(); } else { mel = "Standard"; melScale = 0.ToString(); } // Create label string segmentSeparator = "_"; string[] segments = { minBandWidth, maxBandWidth, fftWindow, mel, melScale }; string labelText = segments.Aggregate(string.Empty, (aggregate, item) => aggregate + segmentSeparator + item); var stringFont = Drawing.Arial14; int width = 250; int height = image.Height; var label = new Image <Rgb24>(width, height); label.Mutate(g1 => { g1.Clear(Color.Gray); g1.DrawText(labelText, stringFont, Color.Black, new PointF(4, 30)); g1.DrawLine(new Pen(Color.Black, 1), 0, 0, width, 0); //draw upper boundary g1.DrawLine(new Pen(Color.Black, 1), 0, 1, width, 1); //draw upper boundary }); var labelledImage = ImageTools.CombineImagesInLine(label, image); // Add labeled image to list list.Add(labelledImage); // Update maximal width of image if (image.Width > maxImageWidth) { maxImageWidth = image.Width; } } bandCount += 1; Log.InfoFormat("Completed band {0}/{1}", bandCount, bandsList.Count); } if (analysisSettings.AnalysisDataSaveBehavior) { this.WriteSummaryIndicesFile(segmentSettings.SegmentSummaryIndicesFile, analysisResults.SummaryIndices); analysisResults.SummaryIndicesFile = segmentSettings.SegmentSummaryIndicesFile; analysisResults.SpectraIndicesFiles = this.WriteSpectrumIndicesFiles( segmentSettings.SegmentSpectrumIndicesDirectory, Path.GetFileNameWithoutExtension(segmentSettings.SegmentAudioFile.Name), analysisResults.SpectralIndices); } if (analysisSettings.AnalysisImageSaveBehavior.ShouldSave()) { var finalImage = ImageTools.CombineImagesVertically(list, maxImageWidth); finalImage.Save(imagePath); analysisResults.ImageFile = new FileInfo(imagePath); LoggedConsole.WriteLine("See {0} for spectrogram pictures", imagePath); } return(analysisResults); }
/// <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 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"); }
} // method DrawAggregatedSpectrograms() public static Image DrawGrayScaleSpectrograms(Arguments arguments, string fileStem, TimeSpan dataScale, Dictionary <string, double[, ]> spectra = null) { int sampleRate = 22050; int frameWidth = 512; //double backgroundFilter = 0.0; // 0.0 means small values are removed. double backgroundFilter = 0.75; // 0.75 means small values are accentuated. string analysisType = AcousticIndices.TowseyAcoustic; string[] keys = LDSpectrogramRGB.GetArrayOfAvailableKeys(); //LoggedConsole.WriteLine("# Spectrogram Config file: " + arguments.SpectrogramConfigPath); //LoggedConsole.WriteLine("# Index Properties Config file: " + arguments.IndexPropertiesConfig); var inputDirectory = arguments.InputDataDirectory; Dictionary <string, IndexProperties> indexProperties = IndexProperties.GetIndexProperties(arguments.IndexPropertiesConfig.ToFileInfo()); if (spectra == null) { //C:\SensorNetworks\Output\BIRD50\Training\ID0001\Towsey.Acoustic\ID0001__Towsey.Acoustic.ACI spectra = IndexMatrices.ReadSpectralIndices(inputDirectory.ToDirectoryInfo(), fileStem, analysisType, keys); } // note: the spectra are oriented as per visual orientation, i.e. xAxis = time frames //int frameCount = spectra[keys[0]].GetLength(1); var cs1 = new LDSpectrogramRGB(minuteOffset: TimeSpan.Zero, xScale: dataScale, sampleRate: sampleRate, frameWidth: frameWidth, colourMap: null) { FileName = fileStem, BackgroundFilter = backgroundFilter, IndexCalculationDuration = dataScale, }; cs1.SetSpectralIndexProperties(indexProperties); // set the relevant dictionary of index properties cs1.SpectrogramMatrices = spectra; if (cs1.GetCountOfSpectrogramMatrices() == 0) { LoggedConsole.WriteLine("WARNING: " + fileStem + ": No spectrogram matrices in the dictionary. Spectrogram files do not exist?"); return(null); } List <Image> list = new List <Image>(); Font stringFont = new Font("Arial", 14); foreach (string key in keys) { var image = cs1.DrawGreyscaleSpectrogramOfIndex(key); int width = 70; int height = image.Height; var label = new Bitmap(width, height); var g1 = Graphics.FromImage(label); g1.Clear(Color.Gray); g1.DrawString(key, stringFont, Brushes.Black, new PointF(4, 30)); g1.DrawLine(new Pen(Color.Black), 0, 0, width, 0); //draw upper boundary g1.DrawLine(new Pen(Color.Black), 0, 1, width, 1); //draw upper boundary Image[] imagearray = { label, image }; var labelledImage = ImageTools.CombineImagesInLine(imagearray); list.Add(labelledImage); } //foreach key var combinedImage = ImageTools.CombineImagesVertically(list.ToArray()); return(combinedImage); } // method DrawGrayScaleSpectrograms()
} // method DrawAggregatedSpectrograms() public static Image <Rgb24> DrawGrayScaleSpectrograms(Arguments arguments, string fileStem, TimeSpan dataScale, Dictionary <string, double[, ]> spectra = null) { // default values int sampleRate = 22050; int frameWidth = 512; //double backgroundFilter = 0.0; // 0.0 means small values are removed. double backgroundFilter = 0.75; // 0.75 means small values are accentuated. string analysisType = AcousticIndices.TowseyAcoustic; string[] keys = LDSpectrogramRGB.GetArrayOfAvailableKeys(); var inputDirectory = arguments.InputDataDirectory; Dictionary <string, IndexProperties> indexProperties = IndexProperties.GetIndexProperties(arguments.IndexPropertiesConfig.ToFileInfo()); if (spectra == null) { spectra = IndexMatrices.ReadSpectralIndices(inputDirectory.ToDirectoryInfo(), fileStem, analysisType, keys); } // note: the spectra are oriented as per visual orientation, i.e. xAxis = time frames //int frameCount = spectra[keys[0]].GetLength(1); var cs1 = new LDSpectrogramRGB(minuteOffset: TimeSpan.Zero, xScale: dataScale, sampleRate: sampleRate, frameWidth: frameWidth, colorMap: null) { FileName = fileStem, BackgroundFilter = backgroundFilter, IndexCalculationDuration = dataScale, }; cs1.SetSpectralIndexProperties(indexProperties); // set the relevant dictionary of index properties cs1.SpectrogramMatrices = spectra; if (cs1.GetCountOfSpectrogramMatrices() == 0) { LoggedConsole.WriteLine("WARNING: " + fileStem + ": No spectrogram matrices in the dictionary. Spectrogram files do not exist?"); return(null); } var list = new List <Image <Rgb24> >(); var stringFont = Drawing.Arial14; foreach (string key in keys) { var image = cs1.DrawGreyscaleSpectrogramOfIndex(key); int width = 70; int height = image.Height; var label = new Image <Rgb24>(width, height); label.Mutate(g1 => { g1.Clear(Color.Gray); g1.DrawText(key, stringFont, Color.Black, new PointF(4, 30)); g1.DrawLine(new Pen(Color.Black, 1), 0, 0, width, 0); //draw upper boundary g1.DrawLine(new Pen(Color.Black, 1), 0, 1, width, 1); //draw upper boundary }); var imagearray = new[] { label, image }; var labelledImage = ImageTools.CombineImagesInLine(imagearray); list.Add(labelledImage); } //foreach key var combinedImage = ImageTools.CombineImagesVertically(list); return(combinedImage); } // method DrawGrayScaleSpectrograms()
public static void ConcatenateDays() { DirectoryInfo parentDir = new DirectoryInfo(@"C:\SensorNetworks\Output\Frommolt"); DirectoryInfo dataDir = new DirectoryInfo(parentDir + @"\AnalysisOutput\mono"); var imageDirectory = new DirectoryInfo(parentDir + @"\ConcatImageOutput"); //string indexPropertiesConfig = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\IndexPropertiesConfigHiRes.yml"; DateTimeOffset?startDate = new DateTimeOffset(2012, 03, 29, 0, 0, 0, TimeSpan.Zero); DateTimeOffset?endDate = new DateTimeOffset(2012, 06, 20, 0, 0, 0, TimeSpan.Zero); var timeSpanOffsetHint = new TimeSpan(01, 0, 0); //string fileSuffix = @"2Maps.png"; //string fileSuffix = @"ACI-ENT-EVN.png"; // WARNING: POW was removed in December 2018 string fileSuffix = @"BGN-POW-EVN.png"; TimeSpan totalTimespan = (DateTimeOffset)endDate - (DateTimeOffset)startDate; int dayCount = totalTimespan.Days + 1; // assume last day has full 24 hours of recording available. bool verbose = true; if (verbose) { LoggedConsole.WriteLine("\n# Start date = " + startDate.ToString()); LoggedConsole.WriteLine("# End date = " + endDate.ToString()); LoggedConsole.WriteLine(string.Format("# Elapsed time = {0:f1} hours", dayCount * 24)); LoggedConsole.WriteLine("# Day count = " + dayCount + " (inclusive of start and end days)"); LoggedConsole.WriteLine("# Time Zone = " + timeSpanOffsetHint.ToString()); } //string dirMatch = "Monitoring_Rosin_2012*T*+0200_.merged.wav.channel_0.wav"; string stem = "Monitoring_Rosin_2012????T??0000+0200_.merged.wav.channel_"; string dirMatch = stem + "?.wav"; DirectoryInfo[] subDirectories = dataDir.GetDirectories(dirMatch, SearchOption.AllDirectories); string format = "yyyyMMdd"; string startDay = ((DateTimeOffset)startDate).ToString(format); //string fileMatch = stem + "?__" + fileSuffix; //FileInfo[] files = IndexMatrices.GetFilesInDirectories(subDirectories, fileMatch); // Sort the files by date and return as a dictionary: sortedDictionaryOfDatesAndFiles<DateTimeOffset, FileInfo> //var sortedDictionaryOfDatesAndFiles = FileDateHelpers.FilterFilesForDates(files, timeSpanOffsetHint); //following needed if a day is missing. int defaultDayWidth = 20; int defaultDayHeight = 300; Brush brush = Brushes.White; Font stringFont = new Font("Tahoma", 12); var list = new List <Image>(); // loop over days for (int d = 0; d < dayCount; d++) { Console.WriteLine(string.Format("Day {0} of {1} days", d, dayCount)); var thisday = ((DateTimeOffset)startDate).AddDays(d); string date = thisday.ToString(format); stem = "Monitoring_Rosin_" + date + "T??0000+0200_.merged.wav.channel_"; string fileMatch = stem + "?__" + fileSuffix; FileInfo[] files = IndexMatrices.GetFilesInDirectories(subDirectories, fileMatch); if (files.Length == 0) { Bitmap gapImage = new Bitmap(defaultDayWidth, defaultDayHeight); Graphics g5 = Graphics.FromImage(gapImage); g5.Clear(Color.Gray); g5.DrawString("Day", stringFont, brush, new PointF(2, 5)); g5.DrawString("missing", stringFont, brush, new PointF(2, 35)); list.Add(gapImage); continue; } // Sort the files by date and return as a dictionary: sortedDictionaryOfDatesAndFiles<DateTimeOffset, FileInfo> //var sortedDictionaryOfDatesAndFiles = FileDateHelpers.FilterFilesForDates(files, timeSpanOffsetHint); Image image = ConcatenateFourChannelImages(files, imageDirectory, fileSuffix, date); defaultDayHeight = image.Height; list.Add(image); } Image combinedImage = ImageTools.CombineImagesInLine(list); Bitmap labelImage1 = new Bitmap(combinedImage.Width, 24); Graphics g1 = Graphics.FromImage(labelImage1); g1.Clear(Color.Black); g1.DrawString(fileSuffix, stringFont, brush, new PointF(2, 2)); //labelImage1.Save(Path.Combine(imageDirectory.FullName, suffix1)); Graphics g = Graphics.FromImage(combinedImage); g.DrawImage(labelImage1, 0, 0); string fileName = string.Format(startDay + "." + fileSuffix); combinedImage.Save(Path.Combine(imageDirectory.FullName, fileName)); }