public static Image <Rgb24> FrameZoomSpectrogram(Image <Rgb24> bmp1, Image <Rgb24> titleBar, TimeSpan startOffset, TimeSpan xAxisPixelDuration, TimeSpan xAxisTicInterval, int nyquist, int herzInterval) { TimeSpan fullDuration = TimeSpan.FromTicks(xAxisPixelDuration.Ticks * bmp1.Width); // init frequency scale int frameSize = bmp1.Height * 2; // THIS MIGHT BECOME A BUG ONE DAY!!!!! var freqScale = new FrequencyScale(nyquist, frameSize, herzInterval); SpectrogramTools.DrawGridLinesOnImage((Image <Rgb24>)bmp1, startOffset, fullDuration, xAxisTicInterval, freqScale); int trackHeight = 20; // put start offset into a datetime object. var dto = default(DateTimeOffset); dto = dto + startOffset; Image <Rgb24> timeBmp = ImageTrack.DrawTimeTrack(fullDuration, dto, bmp1.Width, trackHeight); int imageHt = bmp1.Height + titleBar.Height + trackHeight + 1; Image <Rgb24> compositeBmp = new Image <Rgb24>(bmp1.Width, imageHt); //get canvas for entire image compositeBmp.Mutate(gr => { gr.Clear(Color.Black); int offset = 0; gr.DrawImage(titleBar, 0, offset); //draw in the top time scale offset += titleBar.Height; gr.DrawImage(bmp1, 0, offset); //draw offset += bmp1.Height; gr.DrawImage(timeBmp, 0, offset); //draw }); return(compositeBmp); }
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 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); }
// ####################################################################################################################################### // ### ABOVE METHODS DRAW TIME GRID LINES ON SPECTROGRAMS #################################################################################### // ####################################################################################################################################### public static Image <Rgb24> GetImageFullyAnnotated(Image <Rgb24> image, string title, int[,] gridLineLocations, TimeSpan duration) { if (image == null) { throw new ArgumentNullException(nameof(image)); } FrequencyScale.DrawFrequencyLinesOnImage((Image <Rgb24>)image, gridLineLocations, includeLabels: true); var titleBar = LDSpectrogramRGB.DrawTitleBarOfGrayScaleSpectrogram(title, image.Width); var timeBmp = ImageTrack.DrawTimeTrack(duration, image.Width); var compositeImage = ImageTools.CombineImagesVertically(titleBar, timeBmp, image, timeBmp); return(compositeImage); }
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 Image <Rgb24> GetImageFullyAnnotated(Image <Rgb24> image, string title, int[,] gridLineLocations, Color?tag = null) { if (image == null) { throw new ArgumentNullException(nameof(image)); } FrequencyScale.DrawFrequencyLinesOnImage(image, gridLineLocations, includeLabels: true); var titleBar = DrawTitleBarOfGrayScaleSpectrogram(title, image.Width, tag); var timeBmp = ImageTrack.DrawTimeTrack(this.Duration, image.Width); var list = new List <Image <Rgb24> > { titleBar, timeBmp, image, timeBmp }; var compositeImage = ImageTools.CombineImagesVertically(list); return(compositeImage); }
/// <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); }
/// <summary> /// This method draws a spectrogram with other useful information attached. /// </summary> /// <param name="sonogram">of BaseSonogram class.</param> /// <param name="events">a list of acoustic events.</param> /// <param name="plots">a list of plots relevant to the spectrogram scores.</param> /// <param name="hits">not often used - can be null.</param> public static Image <Rgb24> GetSonogramPlusCharts( BaseSonogram sonogram, List <AcousticEvent> events, List <Plot> plots, double[,] hits) { var spectrogram = sonogram.GetImage(doHighlightSubband: false, add1KHzLines: true, doMelScale: false); Contract.RequiresNotNull(spectrogram, nameof(spectrogram)); var height = spectrogram.Height; var frameSize = sonogram.Configuration.WindowSize; // init with linear frequency scale and draw freq grid lines on image int hertzInterval = 1000; if (height < 200) { hertzInterval = 2000; } var freqScale = new FrequencyScale(sonogram.NyquistFrequency, frameSize, hertzInterval); FrequencyScale.DrawFrequencyLinesOnImage(spectrogram, freqScale.GridLineLocations, includeLabels: true); // draw event outlines onto spectrogram. if (events != null && events.Count > 0) { // set colour for the events foreach (AcousticEvent ev in events) { ev.BorderColour = AcousticEvent.DefaultBorderColor; ev.ScoreColour = AcousticEvent.DefaultScoreColor; ev.DrawEvent(spectrogram, sonogram.FramesPerSecond, sonogram.FBinWidth, height); } } // now add in hits to the spectrogram image. if (hits != null) { spectrogram = Image_MultiTrack.OverlayScoresAsRedTransparency(spectrogram, hits); // following line needs to be reworked if want to call OverlayRainbowTransparency(hits); //image.OverlayRainbowTransparency(hits); } int pixelWidth = spectrogram.Width; var titleBar = LDSpectrogramRGB.DrawTitleBarOfGrayScaleSpectrogram("TITLE", pixelWidth); var timeTrack = ImageTrack.DrawTimeTrack(sonogram.Duration, pixelWidth); var imageList = new List <Image <Rgb24> > { titleBar, timeTrack, spectrogram, timeTrack, }; if (plots != null) { foreach (var plot in plots) { // Next line assumes plot data normalised in 0,1 var plotImage = plot.DrawAnnotatedPlot(ImageTrack.DefaultHeight); // the following draws same plot without the title. //var plotImage = ImageTrack.DrawScoreArrayTrack(plot.data, plot.threshold, pixelWidth); imageList.Add(plotImage); } } var compositeImage = ImageTools.CombineImagesVertically(imageList); return(compositeImage); }
/// <summary> /// Converts summary indices to a tracks image, one track for each index. /// </summary> public static Image <Rgb24> DrawImageOfSummaryIndices( Dictionary <string, IndexProperties> listOfIndexProperties, Dictionary <string, double[]> dictionaryOfSummaryIndices, string titleText, TimeSpan indexCalculationDuration, DateTimeOffset?recordingStartDate, List <GapsAndJoins> errors = null, bool verbose = true) { const int trackHeight = DefaultTrackHeight; int scaleLength = 0; var backgroundColour = Color.White; // init list of bitmap images to store image tracks var bitmapList = new List <Tuple <IndexProperties, Image <Rgb24> > >(dictionaryOfSummaryIndices.Keys.Count); // set up strings to store info about which indices are used var s1 = new StringBuilder("Indices not found:"); var s2 = new StringBuilder("Indices not plotted:"); // accumulate the individual tracks in a List foreach (string key in dictionaryOfSummaryIndices.Keys) { if (!listOfIndexProperties.ContainsKey(key)) { s1.Append(" {0},".Format2(key)); continue; } IndexProperties ip = listOfIndexProperties[key]; if (!ip.DoDisplay) { s2.Append(" {0},".Format2(key)); continue; } //string name = ip.Name; double[] array = dictionaryOfSummaryIndices[key]; scaleLength = array.Length; // alternate rows have different colour to make tracks easier to read backgroundColour = backgroundColour == Color.LightGray ? Color.White : Color.LightGray; var bitmap = ip.GetPlotImage(array, backgroundColour, errors); bitmapList.Add(Tuple.Create(ip, bitmap)); } if (verbose) { Logger.Warn(s1.ToString()); Logger.Warn(s2.ToString()); } var listOfBitmaps = bitmapList // .OrderBy(tuple => tuple.Item1.Order) // don't order because want to preserve alternating gray/white rows. .Select(tuple => tuple.Item2) .Where(b => b != null).ToList(); //set up the composite image parameters int x_offset = 2; int graphWidth = x_offset + scaleLength; int imageWidth = x_offset + scaleLength + TrackEndPanelWidth; Image <Rgb24> titleBmp = ImageTrack.DrawTitleTrack(imageWidth, trackHeight, titleText); TimeSpan xAxisPixelDuration = indexCalculationDuration; TimeSpan fullDuration = TimeSpan.FromTicks(xAxisPixelDuration.Ticks * graphWidth); Image <Rgb24> timeBmp1 = ImageTrack.DrawTimeRelativeTrack(fullDuration, graphWidth, trackHeight); Image <Rgb24> timeBmp2 = timeBmp1; DateTimeOffset?dateTimeOffset = recordingStartDate; if (dateTimeOffset.HasValue) { // draw extra time scale with absolute start time. AND THEN Do SOMETHING WITH IT. timeBmp2 = ImageTrack.DrawTimeTrack(fullDuration, dateTimeOffset, graphWidth, trackHeight); } //draw the composite bitmap var imageList = new List <Image <Rgb24> > { titleBmp, timeBmp1, }; foreach (var image in listOfBitmaps) { imageList.Add(image); } imageList.Add(timeBmp2); var compositeBmp = (Image <Rgb24>)ImageTools.CombineImagesVertically(imageList); return(compositeBmp); }
/// <summary> /// Converts summary indices to a tracks image /// </summary> /// <param name="listOfIndexProperties"></param> /// <param name="dictionaryOfSummaryIndices"></param> /// <param name="titleText"></param> /// <param name="indexCalculationDuration"></param> /// <param name="recordingStartDate"></param> /// <param name="sunriseDataFile"></param> /// <param name="errors"></param> public static Bitmap DrawImageOfSummaryIndices( Dictionary <string, IndexProperties> listOfIndexProperties, Dictionary <string, double[]> dictionaryOfSummaryIndices, string titleText, TimeSpan indexCalculationDuration, DateTimeOffset?recordingStartDate, FileInfo sunriseDataFile = null, List <GapsAndJoins> errors = null, bool verbose = false) { // to translate past keys into current keys Dictionary <string, string> translationDictionary = InitialiseIndexProperties.GetKeyTranslationDictionary(); const int trackHeight = DefaultTrackHeight; int scaleLength = 0; var bitmapList = new List <Tuple <IndexProperties, Image> >(dictionaryOfSummaryIndices.Keys.Count); // accumulate the individual tracks in a List foreach (string key in dictionaryOfSummaryIndices.Keys) { string correctKey = key; if (!listOfIndexProperties.ContainsKey(key)) { if (translationDictionary.ContainsKey(key)) { correctKey = translationDictionary[key]; LoggedConsole.WriteWarnLine( "The csv header is an unknown index <{0}>. Translated to <{1}>", key, correctKey); } else { if (verbose) { Logger.Warn( "A index properties configuration could not be found for {0} (not even in the translation directory). Property is ignored and not rendered" .Format2(key)); } continue; } } IndexProperties ip = listOfIndexProperties[correctKey]; if (!ip.DoDisplay) { continue; } string name = ip.Name; double[] array = dictionaryOfSummaryIndices[key]; scaleLength = array.Length; Image bitmap = ip.GetPlotImage(array, errors); bitmapList.Add(Tuple.Create(ip, bitmap)); } var listOfBitmaps = bitmapList .OrderBy(tuple => tuple.Item1.Order) .Select(tuple => tuple.Item2) .Where(b => b != null).ToList(); //set up the composite image parameters int X_offset = 2; int graphWidth = X_offset + scaleLength; int imageWidth = X_offset + scaleLength + TrackEndPanelWidth; TimeSpan scaleDuration = TimeSpan.FromMinutes(scaleLength); int imageHt = trackHeight * (listOfBitmaps.Count + 4); //+3 for title and top and bottom time tracks Bitmap titleBmp = ImageTrack.DrawTitleTrack(imageWidth, trackHeight, titleText); //Bitmap time1Bmp = ImageTrack.DrawTimeTrack(scaleDuration, TimeSpan.Zero, DrawSummaryIndices.TimeScale, graphWidth, TrackHeight, "Time (hours)"); TimeSpan xAxisPixelDuration = indexCalculationDuration; TimeSpan fullDuration = TimeSpan.FromTicks(xAxisPixelDuration.Ticks * graphWidth); Bitmap timeBmp1 = ImageTrack.DrawTimeRelativeTrack(fullDuration, graphWidth, trackHeight); Bitmap timeBmp2 = timeBmp1; Bitmap suntrack = null; DateTimeOffset?dateTimeOffset = recordingStartDate; if (dateTimeOffset.HasValue) { // draw extra time scale with absolute start time. AND THEN Do SOMETHING WITH IT. timeBmp2 = ImageTrack.DrawTimeTrack(fullDuration, dateTimeOffset, graphWidth, trackHeight); suntrack = SunAndMoon.AddSunTrackToImage(scaleLength, dateTimeOffset, sunriseDataFile); } //draw the composite bitmap var imageList = new List <Image>(); imageList.Add(titleBmp); imageList.Add(timeBmp1); for (int i = 0; i < listOfBitmaps.Count; i++) { imageList.Add(listOfBitmaps[i]); } imageList.Add(timeBmp2); imageList.Add(suntrack); Bitmap compositeBmp = (Bitmap)ImageTools.CombineImagesVertically(imageList); return(compositeBmp); }
// ############################################################################################################## // ######################### 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")); }
/// <summary> /// This method draws a spectrogram with other useful information attached. /// </summary> /// <param name="sonogram">of BaseSonogram class.</param> /// <param name="events">a list of acoustic events.</param> /// <param name="plots">a list of plots relevant to the spectrogram scores.</param> /// <param name="hits">not often used - can be null.</param> public static Image <Rgb24> GetSonogramPlusCharts( BaseSonogram sonogram, List <EventCommon> events, List <Plot> plots, double[,] hits) { var spectrogram = sonogram.GetImage(doHighlightSubband: false, add1KHzLines: true, doMelScale: false); Contract.RequiresNotNull(spectrogram, nameof(spectrogram)); var height = spectrogram.Height; var width = spectrogram.Width; var frameSize = sonogram.Configuration.WindowSize; //var segmentDuration = sonogram.Duration; var spectrogramDuration = width * sonogram.FrameStep; // init with linear frequency scale and draw freq grid lines on image int hertzInterval = 1000; if (height < 200) { hertzInterval = 2000; } var nyquist = sonogram.NyquistFrequency; var freqScale = new FrequencyScale(nyquist, frameSize, hertzInterval); FrequencyScale.DrawFrequencyLinesOnImage(spectrogram, freqScale.GridLineLocations, includeLabels: true); // draw event outlines onto spectrogram. if (events != null && events.Count > 0) { foreach (SpectralEvent ev in events) { var options = new EventRenderingOptions(new UnitConverters(ev.SegmentStartSeconds, spectrogramDuration, nyquist, width, height)); spectrogram.Mutate(x => ev.Draw(x, options)); } } // now add in hits to the spectrogram image. if (hits != null) { spectrogram = Image_MultiTrack.OverlayScoresAsRedTransparency(spectrogram, hits); } int pixelWidth = spectrogram.Width; var titleBar = LDSpectrogramRGB.DrawTitleBarOfGrayScaleSpectrogram("TITLE", pixelWidth); var timeTrack = ImageTrack.DrawTimeTrack(sonogram.Duration, pixelWidth); var imageList = new List <Image <Rgb24> > { titleBar, timeTrack, spectrogram, timeTrack, }; if (plots != null) { foreach (var plot in plots) { // Next line assumes plot data normalised in 0,1 var plotImage = plot.DrawAnnotatedPlot(ImageTrack.DefaultHeight); // the following draws same plot without the title. //var plotImage = ImageTrack.DrawScoreArrayTrack(plot.data, plot.threshold, pixelWidth); imageList.Add(plotImage); } } var compositeImage = ImageTools.CombineImagesVertically(imageList); return(compositeImage); }