public void TestDrawRgbColorMatrix() { // init three matrices double[,] redM = new double[5, 5]; double[,] grnM = new double[5, 5]; double[,] bluM = new double[5, 5]; //convert some values to null or NaN redM[1, 1] = double.NaN; grnM[1, 1] = double.NaN; bluM[1, 1] = double.NaN; redM[2, 2] = 1.0; grnM[2, 2] = 1.0; bluM[2, 2] = double.NaN; redM[3, 3] = 0.01; grnM[3, 3] = 0.01; bluM[3, 3] = 0.11; var blueEnhanceParameter = 0.0; var image = (Bitmap)LDSpectrogramRGB.DrawRgbColorMatrix(redM, grnM, bluM, doReverseColor: true, blueEnhanceParameter); Assert.That.PixelIsColor(new Point(1, 1), Color.FromArgb(128, 128, 128), image); Assert.That.PixelIsColor(new Point(2, 2), Color.FromArgb(128, 128, 128), image); // empty values are rendered as white because of `doReverseColour` Assert.That.ImageRegionIsColor(Rectangle.FromLTRB(0, 0, 1, 5), Color.FromArgb(255, 255, 255), image); Assert.That.ImageRegionIsColor(Rectangle.FromLTRB(4, 0, 5, 5), Color.FromArgb(255, 255, 255), image); }
public static Image <Rgb24> Frame3DSpectrogram(Image <Rgb24> image, string key, int value, int year, string colorMap, TimeSpan xInterval, int nyquistFreq, int unitValue, FileInfo sunriseSetData) { if (key == KeyDayOfYear) { var title = string.Format("SPECTROGRAM (hours x Herz): {0}={1} (R-G-B={2})", key, value, colorMap, unitValue); var titleBar = LDSpectrogramRGB.DrawTitleBarOfFalseColourSpectrogram(title, image.Width); return(FrameSliceOf3DSpectrogram_DayOfYear(image, titleBar, year, value, xInterval, unitValue, sunriseSetData, nyquistFreq)); } else if (key == KeyFreqBin) { var title = string.Format("SPECTROGRAM (hours x months): {0}={1} (R-G-B={2})", key, value, colorMap, unitValue); var titleBar = LDSpectrogramRGB.DrawTitleBarOfFalseColourSpectrogram(title, image.Width); return(FrameSliceOf3DSpectrogram_ConstantFreq(image, titleBar, xInterval, unitValue, sunriseSetData, nyquistFreq)); } else if (key == KeyMinOfDay) { var title = string.Format("SPECTROGRAM (months x Herz): {0}={1} (R-G-B={2})", key, value, colorMap, unitValue); var titleBar = LDSpectrogramRGB.DrawTitleBarOfFalseColourSpectrogram(title, image.Width); return(FrameSliceOf3DSpectrogram_ConstantMin((Image <Rgb24>)image, titleBar, nyquistFreq, unitValue, sunriseSetData)); } return(null); }
/// <summary> /// This method compares the acoustic indices derived from two different long duration recordings of the same length. /// It takes as input six csv files of acoustic indices in spectrogram columns, three csv files for each of the original recordings to be compared. /// The method produces one spectrogram image files: /// 1) A false-colour difference spectrogram, where the difference is shown as a plus/minus departure from grey. /// </summary> public static void DrawDifferenceSpectrogram(DirectoryInfo ipdir, FileInfo ipFileName1, FileInfo ipFileName2, DirectoryInfo opdir) { var cs1 = new LDSpectrogramRGB(minuteOffset, xScale, sampleRate, frameWidth, colorMap) { FileName = ipFileName1.Name, ColorMode = colorMap, BackgroundFilter = backgroundFilterCoeff, }; string[] keys = colorMap.Split('-'); cs1.ReadCsvFiles(ipdir, ipFileName1.Name, keys); if (cs1.GetCountOfSpectrogramMatrices() == 0) { LoggedConsole.WriteLine("There are no spectrogram matrices in cs1.dictionary."); return; } var cs2 = new LDSpectrogramRGB(minuteOffset, xScale, sampleRate, frameWidth, colorMap) { FileName = ipFileName2.Name, ColorMode = colorMap, BackgroundFilter = backgroundFilterCoeff, }; cs2.ReadCsvFiles(ipdir, ipFileName2.Name, keys); if (cs2.GetCountOfSpectrogramMatrices() == 0) { LoggedConsole.WriteLine("There are no spectrogram matrices in cs2.dictionary."); return; } //string title1 = String.Format("DIFFERENCE SPECTROGRAM ({0} - {1}) (scale:hours x kHz) (colour: R-G-B={2})", ipFileName1, ipFileName2, cs1.ColorMODE); //Image deltaSp1 = LDSpectrogramDifference.DrawDifferenceSpectrogram(cs1, cs2, colourGain); //Image titleBar1 = LDSpectrogramRGB.DrawTitleBarOfFalseColourSpectrogram(title1, deltaSp1.Width, SpectrogramConstants.HEIGHT_OF_TITLE_BAR); //deltaSp1 = LDSpectrogramRGB.FrameSpectrogram(deltaSp1, titleBar1, minuteOffset, cs1.X_interval, cs1.Y_interval); //string opFileName1 = ipFileName1 + ".Difference.COLNEG.png"; //deltaSp1.Save(Path.Combine(opdir.FullName, opFileName1)); //Draw positive difference spectrograms in one image. double colourGain = 2.0; Image <Rgb24>[] images = DrawPositiveDifferenceSpectrograms(cs1, cs2, colourGain); int nyquist = cs1.SampleRate / 2; int herzInterval = 1000; string title = $"DIFFERENCE SPECTROGRAM where {ipFileName1} > {ipFileName2}. (scale:hours x kHz) (colour: R-G-B={cs1.ColorMode})"; var titleBar = LDSpectrogramRGB.DrawTitleBarOfFalseColourSpectrogram(title, images[0].Width); images[0] = LDSpectrogramRGB.FrameLDSpectrogram(images[0], titleBar, cs1, nyquist, herzInterval); title = string.Format("DIFFERENCE SPECTROGRAM where {1} > {0} (scale:hours x kHz) (colour: R-G-B={2})", ipFileName1, ipFileName2, cs1.ColorMode); titleBar = LDSpectrogramRGB.DrawTitleBarOfFalseColourSpectrogram(title, images[1].Width); images[1] = LDSpectrogramRGB.FrameLDSpectrogram(images[1], titleBar, cs1, nyquist, herzInterval); Image combinedImage = ImageTools.CombineImagesVertically(images); string opFileName = ipFileName1 + "-" + ipFileName2 + ".Difference.png"; combinedImage.Save(Path.Combine(opdir.FullName, opFileName)); }
public static Image DrawTStatisticSpectrogramsOfSingleIndex(string key, LDSpectrogramRGB cs1, LDSpectrogramRGB cs2, double tStatThreshold) { var image1 = cs1.DrawGreyscaleSpectrogramOfIndex(key); var image2 = cs2.DrawGreyscaleSpectrogramOfIndex(key); if (image1 == null || image2 == null) { Console.WriteLine("WARNING: From method ColourSpectrogram.DrawTStatisticGreyscaleSpectrogramOfIndex()"); Console.WriteLine(" Null image returned with key: {0}", key); return(null); } //frame image 1 int nyquist = cs1.SampleRate / 2; int herzInterval = 1000; string title = $"{key} SPECTROGRAM for: {cs1.FileName}. (scale:hours x kHz)"; var titleBar = LDSpectrogramRGB.DrawTitleBarOfGrayScaleSpectrogram(title, image1.Width); image1 = LDSpectrogramRGB.FrameLDSpectrogram(image1, titleBar, cs1, nyquist, herzInterval); //frame image 2 title = $"{key} SPECTROGRAM for: {cs2.FileName}. (scale:hours x kHz)"; titleBar = LDSpectrogramRGB.DrawTitleBarOfGrayScaleSpectrogram(title, image2.Width); image2 = LDSpectrogramRGB.FrameLDSpectrogram(image2, titleBar, cs1, nyquist, herzInterval); //get matrices required to calculate matrix of t-statistics double[,] avg1 = cs1.GetSpectrogramMatrix(key); if (key.Equals("ENT")) { avg1 = MatrixTools.SubtractValuesFromOne(avg1); } double[,] std1 = cs1.GetStandarDeviationMatrix(key); double[,] avg2 = cs2.GetSpectrogramMatrix(key); if (key.Equals("ENT")) { avg2 = MatrixTools.SubtractValuesFromOne(avg2); } double[,] std2 = cs2.GetStandarDeviationMatrix(key); //draw a spectrogram of t-statistic values //double[,] tStatMatrix = SpectrogramDifference.GetTStatisticMatrix(avg1, std1, cs1.SampleCount, avg2, std2, cs2.SampleCount); //Image image3 = SpectrogramDifference.DrawTStatisticSpectrogram(tStatMatrix); //titleBar = SpectrogramDifference.DrawTitleBarOfTStatisticSpectrogram(cs1.BaseName, cs2.BaseName, image1.Width, titleHt); //image3 = ColourSpectrogram.FrameSpectrogram(image3, titleBar, minOffset, cs2.X_interval, cs2.Y_interval); //draw a difference spectrogram derived from by thresholding a t-statistic matrix var image4 = DrawDifferenceSpectrogramDerivedFromSingleTStatistic(key, cs1, cs2, tStatThreshold, ColourGain); title = string.Format("{0} DIFFERENCE SPECTROGRAM (thresholded by t-statistic={3}) for: {1} - {2}. (scale:hours x kHz)", key, cs1.FileName, cs2.FileName, tStatThreshold); titleBar = LDSpectrogramRGB.DrawTitleBarOfGrayScaleSpectrogram(title, image2.Width); image4 = LDSpectrogramRGB.FrameLDSpectrogram(image4, titleBar, cs2, nyquist, herzInterval); var combinedImage = ImageTools.CombineImagesVertically(image1, image2, image4); return(combinedImage); }
/// <summary> /// This is cut down version of the method of same name in LDSpectrogramRGB.cs. /// </summary> /// <param name="ldSpectrogramConfig">config for ldfc spectrogram.</param> /// <param name="outputDirectory">outputDirectory.</param> /// <param name="indexGenerationData">indexGenerationData.</param> /// <param name="basename">stem name of the original recording.</param> /// <param name="indexSpectrograms">Optional spectra to pass in. If specified the spectra will not be loaded from disk!.</param> private static string DrawSpectrogramsFromSpectralIndices( LdSpectrogramConfig ldSpectrogramConfig, DirectoryInfo outputDirectory, IndexGenerationData indexGenerationData, string basename, Dictionary <string, double[, ]> indexSpectrograms = null) { string colorMap1 = ldSpectrogramConfig.ColorMap1; // SpectrogramConstants.RGBMap_ACI_ENT_EVN; string colorMap2 = ldSpectrogramConfig.ColorMap2; // SpectrogramConstants.RGBMap_BGN_PMN_OSC; double blueEnhanceParameter = ldSpectrogramConfig.BlueEnhanceParameter.Value; var cs1 = new LDSpectrogramRGB(ldSpectrogramConfig, indexGenerationData, colorMap1); string fileStem = basename; cs1.FileName = fileStem; // calculate start time by combining DatetimeOffset with minute offset. cs1.StartOffset = indexGenerationData.AnalysisStartOffset; if (indexGenerationData.RecordingStartDate.HasValue) { DateTimeOffset dto = (DateTimeOffset)indexGenerationData.RecordingStartDate; cs1.RecordingStartDate = dto; if (dto != null) { cs1.StartOffset = dto.TimeOfDay + cs1.StartOffset; } } var indexProperties = IndexCalculateSixOnly.GetIndexProperties(); cs1.SetSpectralIndexProperties(indexProperties); // Load the Index Spectrograms into a Dictionary cs1.LoadSpectrogramDictionary(indexSpectrograms); if (cs1.GetCountOfSpectrogramMatrices() == 0) { Log.Error("No spectrogram matrices in the dictionary. Spectrogram files do not exist?"); throw new InvalidOperationException("Cannot find spectrogram matrix files"); } // draw all available gray scale index spectrograms. var keys = indexProperties.Keys.ToArray(); cs1.DrawGreyScaleSpectrograms(outputDirectory, fileStem, keys); // create two false-color spectrogram images var image1NoChrome = cs1.DrawFalseColorSpectrogramChromeless(cs1.ColorMode, colorMap1, blueEnhanceParameter); var image2NoChrome = cs1.DrawFalseColorSpectrogramChromeless(cs1.ColorMode, colorMap2, blueEnhanceParameter); var spacer = new Image <Rgb24>(image1NoChrome.Width, 10); var imageList = new[] { image1NoChrome, spacer, image2NoChrome, spacer }; Image image3 = ImageTools.CombineImagesVertically(imageList); var outputPath = FilenameHelpers.AnalysisResultPath(outputDirectory, fileStem, "2Maps", "png"); image3.Save(outputPath); return(outputPath); }
// ####################################################################################################################################### // ### 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 void TestChromelessImage() { var indexPropertiesFile = ConfigFile.Default <IndexPropertiesCollection>(); var indexProperties = ConfigFile.Deserialize <IndexPropertiesCollection>(indexPropertiesFile); var indexSpectrograms = new Dictionary <string, double[, ]>(6); var indexStatistics = new Dictionary <string, IndexDistributions.SpectralStats>(); var keys = (LDSpectrogramRGB.DefaultColorMap1 + "-" + LDSpectrogramRGB.DefaultColorMap2).Split('-'); foreach (var key in keys) { var matrix = new double[256, 60].Fill(indexProperties[key].DefaultValue); indexSpectrograms.Add(key, matrix); double[] array = DataTools.Matrix2Array(matrix); indexStatistics.Add(key, IndexDistributions.GetModeAndOneTailedStandardDeviation(array, 300, IndexDistributions.UpperPercentileDefault)); } var images = LDSpectrogramRGB.DrawSpectrogramsFromSpectralIndices( inputDirectory: null, outputDirectory: this.outputDirectory, ldSpectrogramConfig: new LdSpectrogramConfig(), indexPropertiesConfigPath: indexPropertiesFile, indexGenerationData: new IndexGenerationData() { AnalysisStartOffset = 0.Seconds(), FrameLength = 512, IndexCalculationDuration = 60.0.Seconds(), RecordingBasename = "RGB_TEST", RecordingDuration = 60.0.Seconds(), SampleRateResampled = 22050, }, basename: "RGB_TEST", analysisType: AcousticIndices.AnalysisName, indexSpectrograms: indexSpectrograms, summaryIndices: Enumerable .Range(0, 60) .Select((x) => new SummaryIndexValues(60.0.Seconds(), indexProperties)) .Cast <SummaryIndexBase>() .ToArray(), indexStatistics: indexStatistics, imageChrome: ImageChrome.Without); foreach (var(image, key) in images) { Assert.That.ImageIsSize(60, 256, image); Assert.That.ImageRegionIsColor(Rectangle.FromLTRB(0, 0, 60, 256), Color.Black, (Bitmap)image); } }
public static Image DrawFalseColorSpectrograms(Arguments args, string fileStem, Dictionary <string, IndexProperties> indexProperties, Dictionary <string, double[, ]> spectra = null) { // note: the spectra are oriented as per visual orientation, i.e. xAxis = time framesDictionary<string, Int16>.KeyCollection keys = AuthorList.Keys // string[] keys = spectra.Keys.ToCommaSeparatedList().Split(','); // int frameCount = spectra[keys[0]].GetLength(1); int sampleRate = 22050; int frameWidth = 512; double backgroundFilter = 0.75; // 0.75 means small values are accentuated. var minuteOffset = TimeSpan.Zero; var dataScale = args.TemporalScale; string colorMap = args.ColourMap1 ?? LDSpectrogramRGB.DefaultColorMap1; var cs1 = new LDSpectrogramRGB(minuteOffset, dataScale, sampleRate, frameWidth, colorMap) { FileName = fileStem, BackgroundFilter = backgroundFilter, IndexCalculationDuration = dataScale, }; // set the relevant dictionary of index properties cs1.SetSpectralIndexProperties(indexProperties); cs1.SpectrogramMatrices = spectra; // get parameter from the config file. var configFile = args.FalseColourSpectrogramConfig.ToFileInfo(); var config = LdSpectrogramConfig.ReadYamlToConfig(configFile); var blueEnhanceParameter = config.BlueEnhanceParameter ?? 0.0; var image1 = cs1.DrawFalseColorSpectrogramChromeless("NEGATIVE", colorMap, blueEnhanceParameter); var fullDuration = TimeSpan.FromSeconds(image1.Width * dataScale.TotalSeconds); string title = fileStem; var titleImage = LDSpectrogramRGB.DrawTitleBarOfFalseColourSpectrogram(title, image1.Width); int trackHeight = 20; var timeScale = ImageTrack.DrawTimeRelativeTrack(fullDuration, image1.Width, trackHeight); colorMap = args.ColourMap2 ?? LDSpectrogramRGB.DefaultColorMap2; var image2 = cs1.DrawFalseColorSpectrogramChromeless("NEGATIVE", colorMap, blueEnhanceParameter); var list = new List <Image> { titleImage, image1, timeScale, image2 }; var combinedImage = ImageTools.CombineImagesVertically(list.ToArray()); return(combinedImage); }
public Image GetImageFullyAnnotated(Image image, string title, int[,] gridLineLocations) { if (image == null) { throw new ArgumentNullException(nameof(image)); } FrequencyScale.DrawFrequencyLinesOnImage((Bitmap)image, gridLineLocations, includeLabels: true); var titleBar = LDSpectrogramRGB.DrawTitleBarOfGrayScaleSpectrogram(title, image.Width); var timeBmp = ImageTrack.DrawTimeTrack(this.Duration, image.Width); var list = new List <Image> { titleBar, timeBmp, image, timeBmp }; var compositeImage = ImageTools.CombineImagesVertically(list); return(compositeImage); }
public static Image DrawDifferenceSpectrogram(LDSpectrogramRGB target, LDSpectrogramRGB reference, double colourGain) { string[] keys = target.ColorMap.Split('-'); double[,] tgtRedM = target.GetNormalisedSpectrogramMatrix(keys[0]); double[,] tgtGrnM = target.GetNormalisedSpectrogramMatrix(keys[1]); double[,] tgtBluM = target.GetNormalisedSpectrogramMatrix(keys[2]); double[,] refRedM = reference.GetNormalisedSpectrogramMatrix(keys[0]); double[,] refGrnM = reference.GetNormalisedSpectrogramMatrix(keys[1]); double[,] refBluM = reference.GetNormalisedSpectrogramMatrix(keys[2]); // assume all matricies are normalised and of the same dimensions int rows = tgtRedM.GetLength(0); //number of rows int cols = tgtRedM.GetLength(1); //number Image <Rgb24> bmp = new Image <Rgb24>(cols, rows); int maxRGBValue = 255; for (int row = 0; row < rows; row++) { for (int column = 0; column < cols; column++) { var d1 = (tgtRedM[row, column] - refRedM[row, column]) * colourGain; var d2 = (tgtGrnM[row, column] - refGrnM[row, column]) * colourGain; var d3 = (tgtBluM[row, column] - refBluM[row, column]) * colourGain; var i1 = 127 + Convert.ToInt32(d1 * maxRGBValue); i1 = Math.Max(0, i1); i1 = Math.Min(maxRGBValue, i1); var i2 = 127 + Convert.ToInt32(d2 * maxRGBValue); i2 = Math.Max(0, i2); i2 = Math.Min(maxRGBValue, i2); var i3 = 127 + Convert.ToInt32(d3 * maxRGBValue); i3 = Math.Max(0, i3); i3 = Math.Min(maxRGBValue, i3); //Color colour = Color.FromRgb(i1, i2, i3); bmp[column, row] = Color.FromRgb((byte)i1, (byte)i2, (byte)i3); } } return(bmp); }
/// <summary> /// ONLY Use this concatenation method when you want to concatenate the files for a fixed single day. /// The files to be concatenated must be somewhere in the subdirectory structure of the passed list of data directories /// Read them into a dictionary /// MOST RECENT METHOD TO CONCATENATE Spectral INDEX.CSV FILES - Early September 2015. /// It is designed to deal with Yvonne's case where want to concatenate files distributed over arbitrary directories. /// It only merges files for the passed fixed date. i.e only 24 hours /// </summary> public static void DrawSpectralIndexFiles( Dictionary <string, double[, ]> dictionary, LdSpectrogramConfig sgConfig, IndexGenerationData indexGenerationData, FileInfo indexPropertiesConfigFileInfo, DirectoryInfo opDir, SiteDescription siteDescription, FileInfo sunriseDataFile = null, List <GapsAndJoins> segmentErrors = null) { // derive new indices such as sqrt(PMN), NCDI etc -- main reason for this is to view what their distributions look like. dictionary = IndexMatrices.AddDerivedIndices(dictionary); // Calculate the index distribution statistics and write to a json file. Also save as png image if (indexGenerationData.RecordingStartDate != null) { DateTimeOffset dto = (DateTimeOffset)indexGenerationData.RecordingStartDate; string dateString = $"{dto.Year}{dto.Month:D2}{dto.Day:D2}"; string opFileStem = $"{siteDescription.SiteName}_{dateString}"; var indexDistributions = IndexDistributions.WriteSpectralIndexDistributionStatistics(dictionary, opDir, opFileStem); //SummaryIndexBase[] summaryIndices = null; string analysisType = "Towsey.Acoustic"; Tuple <Image, string>[] tuple = LDSpectrogramRGB.DrawSpectrogramsFromSpectralIndices( opDir, // topLevelDirectories[0], // this should not be required but it is - because things have gotten complicated ! opDir, sgConfig, indexPropertiesConfigFileInfo, indexGenerationData, opFileStem, analysisType, dictionary, null, //summaryIndices, indexDistributions, siteDescription, sunriseDataFile, segmentErrors, ImageChrome.With); } }
public static double[,] GetTStatisticMatrix(string key, LDSpectrogramRGB cs1, LDSpectrogramRGB cs2) { double[,] avg1 = cs1.GetSpectrogramMatrix(key); if (key.Equals("TEN")) { avg1 = MatrixTools.SubtractValuesFromOne(avg1); } double[,] std1 = cs1.GetStandarDeviationMatrix(key); double[,] avg2 = cs2.GetSpectrogramMatrix(key); if (key.Equals("TEN")) { avg2 = MatrixTools.SubtractValuesFromOne(avg2); } double[,] std2 = cs2.GetStandarDeviationMatrix(key); double[,] tStatMatrix = GetTStatisticMatrix(avg1, std1, cs1.SampleCount, avg2, std2, cs2.SampleCount); return(tStatMatrix); }
/// <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); }
public static Image DrawIndexSpectrogramCommon( LdSpectrogramConfig config, IndexGenerationData indexGenerationData, Dictionary <string, IndexProperties> indexProperties, TimeSpan startTime, TimeSpan endTime, TimeSpan dataScale, TimeSpan imageScale, int imageWidth, Dictionary <string, double[, ]> spectra, string basename) { double scalingFactor = Math.Round(imageScale.TotalMilliseconds / dataScale.TotalMilliseconds); Contract.Requires(scalingFactor >= 1.0, $"Compression scale `{scalingFactor}`is invalid"); // calculate data duration from column count of abitrary matrix //TimeSpan dataDuration = TimeSpan.FromSeconds(matrix.GetLength(1) * dataScale.TotalSeconds); int columnCount = spectra.FirstValue().GetLength(1); var startIndex = (int)(startTime.Ticks / dataScale.Ticks); var endIndex = (int)(endTime.Ticks / dataScale.Ticks); Contract.Ensures(endIndex <= columnCount); // extract subset of target data var spectralSelection = new Dictionary <string, double[, ]>(); foreach (string key in spectra.Keys) { var matrix = spectra[key]; int rowCount = matrix.GetLength(0); spectralSelection[key] = MatrixTools.Submatrix(matrix, 0, startIndex, rowCount - 1, endIndex - 1); Contract.Ensures( spectralSelection[key].GetLength(1) == (endTime - startTime).Ticks / dataScale.Ticks, "The expected number of frames should be extracted."); } // compress spectrograms to correct scale if (scalingFactor > 1) { // we add rounding to the compression so that fractional pixels get rendered spectralSelection = IndexMatrices.CompressIndexSpectrograms( spectralSelection, imageScale, dataScale, d => Math.Round(d, MidpointRounding.AwayFromZero)); } else { // this else is unnecessary - completely defensive code Contract.Ensures(scalingFactor == 1); } // check that have not compressed matrices to zero length if (spectralSelection.FirstValue().GetLength(0) == 0 || spectralSelection.FirstValue().GetLength(1) == 0) { throw new InvalidOperationException("Spectral matrices compressed to zero size"); } // DEFINE the DEFAULT colour maps for the false-colour spectrograms // Then obtain values from spectrogramDrawingConfig. NOTE: WE REQUIRE LENGTH = 11 chars. string colorMap1 = "ACI-ENT-EVN"; if (config.ColorMap1 != null && config.ColorMap1.Length == 11) { colorMap1 = config.ColorMap1; } string colorMap2 = "BGN-PMN-EVN"; if (config.ColorMap2 != null && config.ColorMap2.Length == 11) { colorMap2 = config.ColorMap2; } double backgroundFilterCoeff = indexGenerationData.BackgroundFilterCoeff; // double colourGain = (double?)configuration.ColourGain ?? SpectrogramConstants.COLOUR_GAIN; // determines colour saturation var cs1 = new LDSpectrogramRGB(config, indexGenerationData, colorMap1) { FileName = basename, BackgroundFilter = backgroundFilterCoeff, }; cs1.SetSpectralIndexProperties(indexProperties); // set the relevant dictionary of index properties cs1.LoadSpectrogramDictionary(spectralSelection); // set up piecewise linear function to determine colour weights var logResolution = Math.Log(imageScale.TotalMilliseconds, 2); double upperResolution = Math.Log(32768, 2); double lowerResolution = Math.Log(256, 2); double range = upperResolution - lowerResolution; double blendWeight1; if (logResolution >= upperResolution) { blendWeight1 = 1.0; } else if (logResolution <= lowerResolution) { blendWeight1 = 0.0; } else { blendWeight1 = (logResolution - lowerResolution) / range; } double blendWeight2 = 1 - blendWeight1; //else if (imageScaleInMsPerPixel > 2000) //{ // blendWeight1 = 0.7; // blendWeight2 = 0.3; //} //else if (imageScaleInMsPerPixel > 1000) //{ // blendWeight1 = 0.3; // blendWeight2 = 0.7; //} //else if (imageScaleInMsPerPixel > 500) //{ // // > 0.5 seconds // blendWeight1 = 0.2; // blendWeight2 = 0.8; //} //else if (imageScaleInMsPerPixel > 300) //{ // // > 0.5 seconds // blendWeight1 = 0.1; // blendWeight2 = 0.9; //} var ldfcSpectrogram = cs1.DrawBlendedFalseColourSpectrogram(colorMap1, colorMap2, blendWeight1, blendWeight2); if (ldfcSpectrogram == null) { throw new InvalidOperationException("Null Image returned from DrawBlendedFalseColourSpectrogram"); } return(ldfcSpectrogram); }
/// <summary> /// This method compares the acoustic indices derived from two different long duration recordings of the same length. /// It takes as input six csv files of acoustic indices in spectrogram columns, three csv files for each of the original recordings to be compared. /// The method produces four spectrogram image files: /// 1) A triple image. Top: The spectrogram for index 1, recording 1. /// Middle: The spectrogram for index 1, recording 2. /// Bottom: A t-statistic thresholded difference spectrogram for INDEX 1 (derived from recordings 1 and 2). /// 2) A triple image. Top: The spectrogram for index 2, recording 1. /// Middle: The spectrogram for index 2, recording 2. /// Bottom: A t-statistic thresholded difference spectrogram for INDEX 2 (derived from recordings 1 and 2). /// 3) A triple image. Top: The spectrogram for index 3, recording 1. /// Middle: The spectrogram for index 3, recording 2. /// Bottom: A t-statistic thresholded difference spectrogram for INDEX 3 (derived from recordings 1 and 2). /// 4) A double image. Top: A t-statistic thresholded difference spectrogram (t-statistic is positive). /// Bottom: A t-statistic thresholded difference spectrogram (t-statistic is negative). /// </summary> public static void DrawTStatisticThresholdedDifferenceSpectrograms(DirectoryInfo ipdir, FileInfo ipFileName1, FileInfo ipSdFileName1, FileInfo ipFileName2, FileInfo ipSdFileName2, DirectoryInfo opdir) { string opFileName1 = ipFileName1.Name; var cs1 = new LDSpectrogramRGB(minuteOffset, xScale, sampleRate, frameWidth, colorMap) { FileName = opFileName1, ColorMode = colorMap, BackgroundFilter = backgroundFilterCoeff, }; string[] keys = colorMap.Split('-'); cs1.ReadCsvFiles(ipdir, ipFileName1.Name, keys); // string imagePath = Path.Combine(opdir.FullName, opFileName1 + ".COLNEG.png"); string opFileName2 = ipFileName2.Name; var cs2 = new LDSpectrogramRGB(minuteOffset, xScale, sampleRate, frameWidth, colorMap) { FileName = opFileName2, ColorMode = colorMap, BackgroundFilter = backgroundFilterCoeff, }; cs2.ReadCsvFiles(ipdir, ipFileName2.Name, keys); bool allOk = true; int sampleCount = 30; allOk = cs1.ReadStandardDeviationSpectrogramCsvs(ipdir, ipSdFileName1.Name); if (!allOk) { Console.WriteLine("Cannot do t-test comparison because error reading standard deviation file: {0}", ipSdFileName1.Name); return; } cs1.SampleCount = sampleCount; allOk = cs2.ReadStandardDeviationSpectrogramCsvs(ipdir, ipSdFileName2.Name); if (!allOk) { Console.WriteLine("Cannot do t-test comparison because error reading standard deviation file: {0}", ipSdFileName2.Name); return; } cs2.SampleCount = sampleCount; string key = "ACI"; var tStatIndexImage = DrawTStatisticSpectrogramsOfSingleIndex(key, cs1, cs2, tStatThreshold); string opFileName3 = ipFileName1 + ".tTest." + key + ".png"; tStatIndexImage.Save(Path.Combine(opdir.FullName, opFileName3)); key = "TEN"; tStatIndexImage = DrawTStatisticSpectrogramsOfSingleIndex(key, cs1, cs2, tStatThreshold); opFileName3 = ipFileName1 + ".tTest." + key + ".png"; tStatIndexImage.Save(Path.Combine(opdir.FullName, opFileName3)); key = "CVR"; tStatIndexImage = DrawTStatisticSpectrogramsOfSingleIndex(key, cs1, cs2, tStatThreshold); opFileName3 = ipFileName1 + ".tTest." + key + ".png"; tStatIndexImage.Save(Path.Combine(opdir.FullName, opFileName3)); tStatIndexImage = DrawTStatisticSpectrogramsOfMultipleIndices(cs1, cs2, tStatThreshold, ColourGain); opFileName3 = ipFileName1 + "-" + ipFileName2 + ".Difference.tTestThreshold.png"; tStatIndexImage.Save(Path.Combine(opdir.FullName, opFileName3)); }
public static Image DrawTStatisticSpectrogramsOfMultipleIndices(LDSpectrogramRGB cs1, LDSpectrogramRGB cs2, double tStatThreshold, double colourGain) { string[] keys = cs1.ColorMap.Split('-'); //assume both spectorgrams have the same acoustic indices in same order double[,] m1 = GetDifferenceSpectrogramDerivedFromSingleTStatistic(keys[0], cs1, cs2, tStatThreshold); double[,] m2 = GetDifferenceSpectrogramDerivedFromSingleTStatistic(keys[1], cs1, cs2, tStatThreshold); double[,] m3 = GetDifferenceSpectrogramDerivedFromSingleTStatistic(keys[2], cs1, cs2, tStatThreshold); int rows = m1.GetLength(0); //number of rows int cols = m1.GetLength(1); //number var spg1Image = new Image <Rgb24>(cols, rows); var spg2Image = new Image <Rgb24>(cols, rows); int maxRgbValue = 255; for (int row = 0; row < rows; row++) { for (int column = 0; column < cols; column++) { var dR = m1[row, column] * colourGain; var dG = m2[row, column] * colourGain; var dB = m3[row, column] * colourGain; byte iR1 = 0; byte iR2 = 0; byte iG1 = 0; byte iG2 = 0; byte iB1 = 0; byte iB2 = 0; var value = Convert.ToByte(Math.Abs(dR) * maxRgbValue); value = (byte)Math.Min(maxRgbValue, value); if (dR > 0.0) { iR1 = value; } else { iR2 = value; } value = Convert.ToByte(Math.Abs(dG) * maxRgbValue); value = (byte)Math.Min(maxRgbValue, value); if (dG > 0.0) { iG1 = value; } else { iG2 = value; } value = Convert.ToByte(Math.Abs(dB) * maxRgbValue); value = (byte)Math.Min(maxRgbValue, value); if (dB > 0.0) { iB1 = value; } else { iB2 = value; } var colour1 = Color.FromRgb(iR1, iG1, iB1); var colour2 = Color.FromRgb(iR2, iG2, iB2); spg1Image[column, row] = colour1; spg2Image[column, row] = colour2; } } var images = new Image <Rgb24> [2]; int nyquist = cs1.SampleRate / 2; int herzInterval = 1000; string title = string.Format("DIFFERENCE SPECTROGRAM (thresholded by t-Statistic={2}) where {0} > {1} (scale:hours x kHz) (colour: R-G-B={2})", cs1.FileName, cs2.FileName, tStatThreshold); var titleBar = LDSpectrogramRGB.DrawTitleBarOfFalseColourSpectrogram(title, spg1Image.Width); images[0] = LDSpectrogramRGB.FrameLDSpectrogram(spg1Image, titleBar, cs1, nyquist, herzInterval); title = string.Format("DIFFERENCE SPECTROGRAM (thresholded by t-Statistic={2}) where {1} > {0} (scale:hours x kHz) (colour: R-G-B={2})", cs1.FileName, cs2.FileName, tStatThreshold); titleBar = LDSpectrogramRGB.DrawTitleBarOfFalseColourSpectrogram(title, spg2Image.Width); images[1] = LDSpectrogramRGB.FrameLDSpectrogram(spg2Image, titleBar, cs1, nyquist, herzInterval); var compositeImage = ImageTools.CombineImagesVertically(images); return(compositeImage); }
public static void Execute(Arguments arguments) { if (arguments == null) { throw new NoDeveloperMethodException(); } string date = "# DATE AND TIME: " + DateTime.Now; LoggedConsole.WriteLine("# DRAW LONG DURATION SPECTROGRAMS DERIVED FROM CSV FILES OF SPECTRAL INDICES OBTAINED FROM AN AUDIO RECORDING"); LoggedConsole.WriteLine(date); LoggedConsole.WriteLine("# Spectrogram Config file: " + arguments.FalseColourSpectrogramConfig); LoggedConsole.WriteLine("# Index Properties Config file: " + arguments.IndexPropertiesConfig); LoggedConsole.WriteLine(); (FileInfo indexGenerationDataFile, FileInfo indexDistributionsFile) = ZoomParameters.CheckNeededFilesExist(arguments.InputDataDirectory.ToDirectoryInfo()); var indexGenerationData = Json.Deserialize <IndexGenerationData>(indexGenerationDataFile); // spectral distribution statistics is required only when calcualting difference spectrograms. Dictionary <string, IndexDistributions.SpectralStats> indexDistributionsData = null; if (indexDistributionsFile != null && indexDistributionsFile.Exists) { indexDistributionsData = IndexDistributions.Deserialize(indexDistributionsFile); } // this config can be found in IndexGenerationData. If config argument not specified, simply take it from icd file LdSpectrogramConfig config; if (arguments.FalseColourSpectrogramConfig == null) { config = indexGenerationData.LongDurationSpectrogramConfig; } else { config = LdSpectrogramConfig.ReadYamlToConfig(arguments.FalseColourSpectrogramConfig.ToFileInfo()); } FilenameHelpers.ParseAnalysisFileName(indexGenerationDataFile, out var originalBaseName, out var _, out var _); // CHECK FOR ERROR SEGMENTS - get zero signal array var input = arguments.InputDataDirectory.ToDirectoryInfo(); var csvFile = new FileInfo(Path.Combine(input.FullName, originalBaseName + "__Towsey.Acoustic.Indices.csv")); //Dictionary<string, double[]> summaryIndices = CsvTools.ReadCSVFile2Dictionary(csvFile.FullName); //var summaryIndices = Csv.ReadFromCsv<Dictionary<string, double[]>>(csvFile); var summaryIndices = Csv.ReadFromCsv <SummaryIndexValues>(csvFile); var indexErrors = GapsAndJoins.DataIntegrityCheckForZeroSignal(summaryIndices); //config.IndexCalculationDuration = TimeSpan.FromSeconds(1.0); //config.XAxisTicInterval = TimeSpan.FromSeconds(60.0); //config.IndexCalculationDuration = TimeSpan.FromSeconds(60.0); //config.XAxisTicInterval = TimeSpan.FromSeconds(3600.0); LDSpectrogramRGB.DrawSpectrogramsFromSpectralIndices( inputDirectory: input, outputDirectory: arguments.OutputDirectory.ToDirectoryInfo(), ldSpectrogramConfig: config, indexPropertiesConfigPath: arguments.IndexPropertiesConfig.ToFileInfo(), indexGenerationData: indexGenerationData, basename: originalBaseName, analysisType: AcousticIndices.TowseyAcoustic, indexSpectrograms: null, indexStatistics: indexDistributionsData, segmentErrors: indexErrors, imageChrome: false.ToImageChrome()); Log.Success("Draw Long Duration Spectrograms complete!"); }
} // method DrawRidgeSpectrograms() public static Image DrawRidgeSpectrograms(DirectoryInfo inputDirectory, FileInfo ipConfig, string fileStem, double scale, Dictionary <string, double[, ]> spectra = null) { string analysisType = AcousticIndices.TowseyAcoustic; //double backgroundFilter = 0.0; // 0.0 means small values are removed. double backgroundFilter = 0.75; // 0.75 means small values are accentuated. var dataScale = TimeSpan.FromSeconds(scale); Dictionary <string, IndexProperties> indexProperties = IndexProperties.GetIndexProperties(ipConfig); string[] keys = SpectralPeakTracks.GetDefaultRidgeKeys(); // read the csv files of the indices in keys array if (spectra == null) { //C:\SensorNetworks\Output\BIRD50\Training\ID0001\Towsey.Acoustic\ID0001__Towsey.Acoustic.ACI spectra = IndexMatrices.ReadSpectralIndices(inputDirectory, fileStem, analysisType, keys); } var cs1 = new LDSpectrogramRGB(minuteOffset: TimeSpan.Zero, xScale: dataScale, sampleRate: 22050, frameWidth: 512, colorMap: null) { FileName = fileStem, BackgroundFilter = backgroundFilter, IndexCalculationDuration = dataScale, }; // set the relevant dictionary of index properties cs1.SetSpectralIndexProperties(indexProperties); cs1.SpectrogramMatrices = spectra; if (cs1.GetCountOfSpectrogramMatrices() == 0) { LoggedConsole.WriteLine("WARNING: " + fileStem + ": No spectrogram matrices in the dictionary. Spectrogram files do not exist?"); return(null); } else if (cs1.GetCountOfSpectrogramMatrices() < keys.Length) { LoggedConsole.WriteLine("WARNING: " + fileStem + ": Missing indices in the dictionary. Some files do not exist?"); return(null); } var stringFont = Drawing.Tahoma8; // constants for labels Color[] color = { Color.Blue, Color.Green, Color.Red, Color.Orange, Color.Purple }; int labelYvalue = 3; int labelIndex = 0; Image <Rgb24> ridges = null; foreach (string key in keys) { Image <Rgb24> greyScaleImage = (Image <Rgb24>)cs1.DrawGreyscaleSpectrogramOfIndex(key); var pixelWidth = greyScaleImage.Width; int height = greyScaleImage.Height; ridges.Mutate(g2 => { if (ridges == null) { ridges = new Image <Rgb24>(pixelWidth, height); g2.Clear(Color.White); } g2.DrawText(key, stringFont, color[labelIndex], new PointF(0, labelYvalue)); }); labelYvalue += 10; //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 // transfer greyscale image to colour image for (int y = 0; y < height; y++) { for (int x = 0; x < pixelWidth; x++) { var col = greyScaleImage[x, y]; if (col.G < 150) { ridges[x, y] = color[labelIndex]; } } } labelIndex += 1; } //foreach key return(ridges); } // method DrawRidgeSpectrograms()
/// <summary> /// This method compares the acoustic indices derived from two different long duration recordings of the same length. /// It takes as input any number of csv files of acoustic indices in spectrogram columns. /// Typically there will be at least three indices csv files for each of the original recordings to be compared. /// The method produces four spectrogram image files: /// 1) A negative false-color spectrogram derived from the indices of recording 1. /// 2) A negative false-color spectrogram derived from the indices of recording 2. /// 3) A spectrogram of euclidean distances between the two input files. /// 4) The above three spectrograms combined in one image. /// </summary> public static void DrawDistanceSpectrogram( DirectoryInfo inputDirectory, FileInfo inputFileName1, FileInfo inputFileName2, DirectoryInfo outputDirectory) { // PARAMETERS string outputFileName1 = inputFileName1.Name; var cs1 = new LDSpectrogramRGB(minuteOffset, xScale, sampleRate, frameWidth, colorMap); cs1.ColorMode = colorMap; cs1.BackgroundFilter = backgroundFilterCoeff; string[] keys = colorMap.Split('-'); cs1.ReadCsvFiles(inputDirectory, inputFileName1.Name, keys); double blueEnhanceParameter = 0.0; cs1.DrawNegativeFalseColorSpectrogram(outputDirectory, outputFileName1, blueEnhanceParameter); string imagePath = Path.Combine(outputDirectory.FullName, outputFileName1 + ".COLNEG.png"); var spg1Image = Image.Load <Rgb24>(imagePath); if (spg1Image == null) { LoggedConsole.WriteLine("SPECTROGRAM IMAGE DOES NOT EXIST: {0}", imagePath); return; } int nyquist = cs1.SampleRate / 2; int hertzInterval = 1000; string title = $"FALSE COLOUR SPECTROGRAM: {inputFileName1}. (scale:hours x kHz) (colour: R-G-B={cs1.ColorMode})"; var titleBar = LDSpectrogramRGB.DrawTitleBarOfFalseColourSpectrogram(title, spg1Image.Width); spg1Image = LDSpectrogramRGB.FrameLDSpectrogram( spg1Image, titleBar, cs1, nyquist, hertzInterval); string outputFileName2 = inputFileName2.Name; var cs2 = new LDSpectrogramRGB(minuteOffset, xScale, sampleRate, frameWidth, colorMap) { ColorMode = colorMap, BackgroundFilter = backgroundFilterCoeff, }; cs2.ReadCsvFiles(inputDirectory, inputFileName2.Name, keys); // cs2.DrawGreyScaleSpectrograms(opdir, opFileName2); cs2.DrawNegativeFalseColorSpectrogram(outputDirectory, outputFileName2, blueEnhanceParameter); imagePath = Path.Combine(outputDirectory.FullName, outputFileName2 + ".COLNEG.png"); var spg2Image = Image.Load <Rgb24>(imagePath); if (spg2Image == null) { LoggedConsole.WriteLine("SPECTROGRAM IMAGE DOES NOT EXIST: {0}", imagePath); return; } title = $"FALSE COLOUR SPECTROGRAM: {inputFileName2}. (scale:hours x kHz) (colour: R-G-B={cs2.ColorMode})"; titleBar = LDSpectrogramRGB.DrawTitleBarOfFalseColourSpectrogram(title, spg2Image.Width); spg2Image = LDSpectrogramRGB.FrameLDSpectrogram( spg2Image, titleBar, cs1, nyquist, hertzInterval); string outputFileName4 = inputFileName1 + ".EuclideanDistance.png"; var deltaSp = DrawDistanceSpectrogram(cs1, cs2); Color[] colorArray = LDSpectrogramRGB.ColourChart2Array(GetDifferenceColourChart()); titleBar = DrawTitleBarOfEuclidianDistanceSpectrogram( inputFileName1.Name, inputFileName2.Name, colorArray, deltaSp.Width, SpectrogramConstants.HEIGHT_OF_TITLE_BAR); deltaSp = LDSpectrogramRGB.FrameLDSpectrogram(deltaSp, titleBar, cs2, nyquist, hertzInterval); deltaSp.Save(Path.Combine(outputDirectory.FullName, outputFileName4)); string outputFileName5 = inputFileName1 + ".2SpectrogramsAndDistance.png"; var combinedImage = ImageTools.CombineImagesVertically(spg1Image, spg2Image, deltaSp); combinedImage.Save(Path.Combine(outputDirectory.FullName, outputFileName5)); }
public static Image <Rgb24>[] DrawPositiveDifferenceSpectrograms(LDSpectrogramRGB target, LDSpectrogramRGB reference, double colourGain) { string[] keys = target.ColorMap.Split('-'); double[,] tgtRedM = target.GetNormalisedSpectrogramMatrix(keys[0]); double[,] tgtGrnM = target.GetNormalisedSpectrogramMatrix(keys[1]); double[,] tgtBluM = target.GetNormalisedSpectrogramMatrix(keys[2]); double[,] refRedM = reference.GetNormalisedSpectrogramMatrix(keys[0]); double[,] refGrnM = reference.GetNormalisedSpectrogramMatrix(keys[1]); double[,] refBluM = reference.GetNormalisedSpectrogramMatrix(keys[2]); // assume all matricies are normalised and of the same dimensions int rows = tgtRedM.GetLength(0); //number of rows int cols = tgtRedM.GetLength(1); //number var spg1Image = new Image <Rgb24>(cols, rows); var spg2Image = new Image <Rgb24>(cols, rows); int maxRgbValue = 255; for (int row = 0; row < rows; row++) { for (int column = 0; column < cols; column++) { var dR = (tgtRedM[row, column] - refRedM[row, column]) * colourGain; var dG = (tgtGrnM[row, column] - refGrnM[row, column]) * colourGain; var dB = (tgtBluM[row, column] - refBluM[row, column]) * colourGain; var iR1 = 0; var iR2 = 0; var iG1 = 0; var iG2 = 0; var iB1 = 0; var iB2 = 0; var value = Convert.ToInt32(Math.Abs(dR) * maxRgbValue); value = Math.Min(maxRgbValue, value); if (dR > 0.0) { iR1 = value; } else { iR2 = value; } value = Convert.ToInt32(Math.Abs(dG) * maxRgbValue); value = Math.Min(maxRgbValue, value); if (dG > 0.0) { iG1 = value; } else { iG2 = value; } value = Convert.ToInt32(Math.Abs(dB) * maxRgbValue); value = Math.Min(maxRgbValue, value); if (dB > 0.0) { iB1 = value; } else { iB2 = value; } var colour1 = Color.FromRgb((byte)iR1, (byte)iG1, (byte)iB1); var colour2 = Color.FromRgb((byte)iR2, (byte)iG2, (byte)iB2); spg1Image[column, row] = colour1; spg2Image[column, row] = colour2; } } return(new[] { spg1Image, spg2Image }); }
public static Image <Rgb24> DrawDistanceSpectrogram(LDSpectrogramRGB cs1, LDSpectrogramRGB cs2) { string[] keys = cs1.ColorMap.Split('-'); string key = keys[0]; double[,] m1Red = cs1.GetNormalisedSpectrogramMatrix(key); IndexDistributions.SpectralStats stats = IndexDistributions.GetModeAndOneTailedStandardDeviation(m1Red); cs1.IndexStats.Add(key, stats); m1Red = MatrixTools.Matrix2ZScores(m1Red, stats.Mode, stats.StandardDeviation); ////LoggedConsole.WriteLine("1.{0}: Min={1:f2} Max={2:f2} Mode={3:f2}+/-{4:f3} (SD=One-tailed)", key, dict["min"], dict["max"], dict["mode"], dict["sd"]); key = keys[1]; double[,] m1Grn = cs1.GetNormalisedSpectrogramMatrix(key); stats = IndexDistributions.GetModeAndOneTailedStandardDeviation(m1Grn); cs1.IndexStats.Add(key, stats); m1Grn = MatrixTools.Matrix2ZScores(m1Grn, stats.Mode, stats.StandardDeviation); ////LoggedConsole.WriteLine("1.{0}: Min={1:f2} Max={2:f2} Mode={3:f2}+/-{4:f3} (SD=One-tailed)", key, dict["min"], dict["max"], dict["mode"], dict["sd"]); key = keys[2]; double[,] m1Blu = cs1.GetNormalisedSpectrogramMatrix(key); stats = IndexDistributions.GetModeAndOneTailedStandardDeviation(m1Blu); cs1.IndexStats.Add(key, stats); m1Blu = MatrixTools.Matrix2ZScores(m1Blu, stats.Mode, stats.StandardDeviation); ////LoggedConsole.WriteLine("1.{0}: Min={1:f2} Max={2:f2} Mode={3:f2}+/-{4:f3} (SD=One-tailed)", key, dict["min"], dict["max"], dict["mode"], dict["sd"]); key = keys[0]; double[,] m2Red = cs2.GetNormalisedSpectrogramMatrix(key); stats = IndexDistributions.GetModeAndOneTailedStandardDeviation(m2Red); cs2.IndexStats.Add(key, stats); m2Red = MatrixTools.Matrix2ZScores(m2Red, stats.Mode, stats.StandardDeviation); ////LoggedConsole.WriteLine("2.{0}: Min={1:f2} Max={2:f2} Mode={3:f2}+/-{4:f3} (SD=One-tailed)", key, dict["min"], dict["max"], dict["mode"], dict["sd"]); key = keys[1]; double[,] m2Grn = cs2.GetNormalisedSpectrogramMatrix(key); stats = IndexDistributions.GetModeAndOneTailedStandardDeviation(m2Grn); cs2.IndexStats.Add(key, stats); m2Grn = MatrixTools.Matrix2ZScores(m2Grn, stats.Mode, stats.StandardDeviation); ////LoggedConsole.WriteLine("2.{0}: Min={1:f2} Max={2:f2} Mode={3:f2}+/-{4:f3} (SD=One-tailed)", key, dict["min"], dict["max"], dict["mode"], dict["sd"]); key = keys[2]; double[,] m2Blu = cs2.GetNormalisedSpectrogramMatrix(key); stats = IndexDistributions.GetModeAndOneTailedStandardDeviation(m2Blu); cs2.IndexStats.Add(key, stats); m2Blu = MatrixTools.Matrix2ZScores(m2Blu, stats.Mode, stats.StandardDeviation); ////LoggedConsole.WriteLine("2.{0}: Min={1:f2} Max={2:f2} Mode={3:f2}+/-{4:f3} (SD=One-tailed)", key, dict["min"], dict["max"], dict["mode"], dict["sd"]); var v1 = new double[3]; double[] mode1 = { cs1.IndexStats[keys[0]].Mode, cs1.IndexStats[keys[1]].Mode, cs1.IndexStats[keys[2]].Mode, }; double[] stDv1 = { cs1.IndexStats[keys[0]].StandardDeviation, cs1.IndexStats[keys[1]].StandardDeviation, cs1.IndexStats[keys[2]].StandardDeviation, }; LoggedConsole.WriteLine( "1: avACI={0:f3}+/-{1:f3}; avTEN={2:f3}+/-{3:f3}; avCVR={4:f3}+/-{5:f3}", mode1[0], stDv1[0], mode1[1], stDv1[1], mode1[2], stDv1[2]); var v2 = new double[3]; double[] mode2 = { cs2.IndexStats[keys[0]].Mode, cs2.IndexStats[keys[1]].Mode, cs2.IndexStats[keys[2]].Mode, }; double[] stDv2 = { cs2.IndexStats[keys[0]].StandardDeviation, cs2.IndexStats[keys[1]].StandardDeviation, cs2.IndexStats[keys[2]].StandardDeviation, }; LoggedConsole.WriteLine( "2: avACI={0:f3}+/-{1:f3}; avTEN={2:f3}+/-{3:f3}; avCVR={4:f3}+/-{5:f3}", mode2[0], stDv2[0], mode2[1], stDv2[1], mode2[2], stDv2[2]); // assume all matrices are normalised and of the same dimensions int rows = m1Red.GetLength(0); // number of rows int cols = m1Red.GetLength(1); // number var d12Matrix = new double[rows, cols]; var d11Matrix = new double[rows, cols]; var d22Matrix = new double[rows, cols]; for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { v1[0] = m1Red[row, col]; v1[1] = m1Grn[row, col]; v1[2] = m1Blu[row, col]; v2[0] = m2Red[row, col]; v2[1] = m2Grn[row, col]; v2[2] = m2Blu[row, col]; d12Matrix[row, col] = DataTools.EuclideanDistance(v1, v2); d11Matrix[row, col] = (v1[0] + v1[1] + v1[2]) / 3; // get average of the normalised values d22Matrix[row, col] = (v2[0] + v2[1] + v2[2]) / 3; // following lines are for debugging purposes // if ((row == 150) && (col == 1100)) // { // LoggedConsole.WriteLine("V1={0:f3}, {1:f3}, {2:f3}", v1[0], v1[1], v1[2]); // LoggedConsole.WriteLine("V2={0:f3}, {1:f3}, {2:f3}", v2[0], v2[1], v2[2]); // LoggedConsole.WriteLine("EDist12={0:f4}; ED11={1:f4}; ED22={2:f4}", d12Matrix[row, col], d11Matrix[row, col], d22Matrix[row, col]); // } } } double[] array = DataTools.Matrix2Array(d12Matrix); NormalDist.AverageAndSD(array, out var avDist, out var sdDist); for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { d12Matrix[row, col] = (d12Matrix[row, col] - avDist) / sdDist; } } double zScore; Dictionary <string, Color> colourChart = GetDifferenceColourChart(); Color colour; var bmp = new Image <Rgb24>(cols, rows); for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { zScore = d12Matrix[row, col]; if (d11Matrix[row, col] >= d22Matrix[row, col]) { if (zScore > 3.08) { colour = colourChart["+99.9%"]; } // 99.9% conf else { if (zScore > 2.33) { colour = colourChart["+99.0%"]; } // 99.0% conf else { if (zScore > 1.65) { colour = colourChart["+95.0%"]; } // 95% conf else { if (zScore < 0.0) { colour = colourChart["NoValue"]; } else { // v = Convert.ToInt32(zScore * MaxRGBValue); // colour = Color.FromRgb(v, 0, v); colour = colourChart["+NotSig"]; } } } } // if() else bmp[col, row] = colour; } else { if (zScore > 3.08) { colour = colourChart["-99.9%"]; } // 99.9% conf else { if (zScore > 2.33) { colour = colourChart["-99.0%"]; } // 99.0% conf else { if (zScore > 1.65) { colour = colourChart["-95.0%"]; } // 95% conf else { if (zScore < 0.0) { colour = colourChart["NoValue"]; } else { // v = Convert.ToInt32(zScore * MaxRGBValue); // if() // colour = Color.FromRgb(0, v, v); colour = colourChart["-NotSig"]; } } } } // if() else bmp[col, row] = colour; } } // all rows } // all rows return(bmp); }
public void TestAnalyzeSr64000Recording() { int sampleRate = 64000; double duration = 420; // signal duration in seconds = 7 minutes int[] harmonics = { 500, 1000, 2000, 4000, 8000 }; var recording = DspFilters.GenerateTestRecording(sampleRate, duration, harmonics, WaveType.Cosine); string recordingName = "TemporaryRecording2"; var recordingPath = this.outputDirectory.CombineFile(recordingName + ".wav"); WavWriter.WriteWavFileViaFfmpeg(recordingPath, recording.WavReader); var fst = FreqScaleType.Linear125Octaves7Tones28Nyquist32000; var freqScale = new FrequencyScale(fst); /* * // draw the signal as spectrogram just for debugging purposes * // but can only draw a two minute spectrogram when sr=64000 - change duration above. * duration = 120; // if drawing sonogram, then set signal duration = 2 minutes * var sonogram = OctaveFreqScale.ConvertRecordingToOctaveScaleSonogram(recording, fst); * var sonogramImage = sonogram.GetImageFullyAnnotated(sonogram.GetImage(), "SPECTROGRAM", freqScale.GridLineLocations); * var outputImagePath = this.outputDirectory.CombineFile("SignalSpectrogram_OctaveFreqScale.png"); * sonogramImage.Save(outputImagePath.FullName); */ // Now need to rewrite the config file with new parameter settings var configPath = PathHelper.ResolveConfigFile("Towsey.Acoustic.yml"); // Convert the Config config to IndexCalculateConfig class and merge in the unnecesary parameters. //Config configuration = Yaml.Deserialise(configPath); //IndexCalculateConfig config = IndexCalculateConfig.GetConfig(configuration, false); // because of difficulties in dealing with Config config files, just edit the text file!!!!! var configLines = File.ReadAllLines(configPath.FullName); configLines[configLines.IndexOf(x => x.StartsWith("IndexCalculationDuration: "))] = "IndexCalculationDuration: 15.0"; //configLines[configLines.IndexOf(x => x.StartsWith("BgNoiseBuffer: "))] = "BgNoiseBuffer: 5.0"; configLines[configLines.IndexOf(x => x.StartsWith("FrequencyScale: Linear"))] = "FrequencyScale: " + fst; // the is the only octave scale currently functioning for IndexCalculate class configLines[configLines.IndexOf(x => x.StartsWith("FrameLength"))] = $"FrameLength: {freqScale.WindowSize}"; configLines[configLines.IndexOf(x => x.StartsWith("ResampleRate: "))] = "ResampleRate: 64000"; // write the edited Config file to temporary output directory var newConfigPath = this.outputDirectory.CombineFile("Towsey.Acoustic.yml"); File.WriteAllLines(newConfigPath.FullName, configLines); PathHelper.ResolveConfigFile("IndexPropertiesConfig.yml").CopyTo(this.outputDirectory.CombineFile("IndexPropertiesConfig.yml").FullName); var arguments = new AnalyseLongRecording.Arguments { Source = recordingPath, Config = newConfigPath.FullName, Output = this.outputDirectory, MixDownToMono = true, Parallel = !Debugger.IsAttached, }; AnalyseLongRecording.Execute(arguments); var resultsDirectory = this.outputDirectory.Combine("Towsey.Acoustic"); var listOfFiles = resultsDirectory.EnumerateFiles().ToArray(); Assert.AreEqual(19, listOfFiles.Length); var csvCount = listOfFiles.Count(f => f.Name.EndsWith(".csv")); Assert.AreEqual(15, csvCount); var jsonCount = listOfFiles.Count(f => f.Name.EndsWith(".json")); Assert.AreEqual(2, jsonCount); var pngCount = listOfFiles.Count(f => f.Name.EndsWith(".png")); Assert.AreEqual(2, pngCount); var bgnFile = resultsDirectory.CombineFile(recordingName + "__Towsey.Acoustic.BGN.csv"); double[,] actualBgn = Csv.ReadMatrixFromCsv <double>(bgnFile, TwoDimensionalArray.None); var expectedSpectrumFile = PathHelper.ResolveAsset("LongDuration", "BgnMatrix.OctaveScale.csv"); // uncomment the following line when first produce the array // bgnFile.CopyTo(expectedSpectrumFile.FullName); // compare actual BGN file with expected file. var expectedBgn = Csv.ReadMatrixFromCsv <double>(expectedSpectrumFile, TwoDimensionalArray.None); CollectionAssert.That.AreEqual(expectedBgn, actualBgn, 0.000_000_001); var array = MatrixTools.GetRow(actualBgn, 0); Assert.AreEqual(28, actualBgn.RowLength()); Assert.AreEqual(256, array.Length); // draw array just to check peaks are in correct places - just for debugging purposes var ldsBgnSpectrumFile = this.outputDirectory.CombineFile("Spectrum2.png"); GraphsAndCharts.DrawGraph(array, "LD BGN SPECTRUM Octave", ldsBgnSpectrumFile); // ########################################## // SECOND part of test is to create the LD spectrograms because they are not created when IndexCalcDuration < 60 seconds // first read in the index generation data var icdPath = resultsDirectory.CombineFile(recordingName + "__IndexGenerationData.json"); var indexConfigData = Json.Deserialize <IndexGenerationData>(icdPath); var indexPropertiesConfig = PathHelper.ResolveConfigFile("IndexPropertiesConfig.yml"); var ldSpectrogramConfigFile = PathHelper.ResolveConfigFile("SpectrogramFalseColourConfig.yml"); var ldSpectrogramConfig = LdSpectrogramConfig.ReadYamlToConfig(ldSpectrogramConfigFile); ldSpectrogramConfig.FreqScale = fst.ToString(); // finally read in the dictionary of spectra string analysisType = "Towsey.Acoustic"; var keys = LDSpectrogramRGB.GetArrayOfAvailableKeys(); var dictionaryOfSpectra = IndexMatrices.ReadSpectralIndices(resultsDirectory, recordingName, analysisType, keys); LDSpectrogramRGB.DrawSpectrogramsFromSpectralIndices( inputDirectory: resultsDirectory, outputDirectory: resultsDirectory, ldSpectrogramConfig: ldSpectrogramConfig, indexPropertiesConfigPath: indexPropertiesConfig, indexGenerationData: indexConfigData, basename: recordingName, analysisType: analysisType, indexSpectrograms: dictionaryOfSpectra); // test number of images - should now be 23 listOfFiles = resultsDirectory.EnumerateFiles().ToArray(); pngCount = listOfFiles.Count(f => f.Name.EndsWith(".png")); Assert.AreEqual(22, pngCount); var twoMapsImagePath = resultsDirectory.CombineFile(recordingName + "__2Maps.png"); var twoMapsImage = Image.Load <Rgb24>(twoMapsImagePath.FullName); // image is (7*4) * 652 Assert.AreEqual(28, twoMapsImage.Width); Assert.AreEqual(652, twoMapsImage.Height); }
} // 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()
} // 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()
/// <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); }
public static Image <Rgb24> DrawDifferenceSpectrogramDerivedFromSingleTStatistic(string key, LDSpectrogramRGB cs1, LDSpectrogramRGB cs2, double tStatThreshold, double colourGain) { double[,] m1 = cs1.GetNormalisedSpectrogramMatrix(key); //the TEN matrix is subtracted from 1. double[,] m2 = cs2.GetNormalisedSpectrogramMatrix(key); double[,] tStatM = GetTStatisticMatrix(key, cs1, cs2); return(DrawDifferenceSpectrogramDerivedFromSingleTStatistic(key, m1, m2, tStatM, tStatThreshold, colourGain)); }
public void SummariseResults(AnalysisSettings settings, FileSegment inputFileSegment, EventBase[] events, SummaryIndexBase[] indices, SpectralIndexBase[] spectralIndices, AnalysisResult2[] results) { var acousticIndicesConfig = (AcousticIndicesConfig)settings.AnalysisAnalyzerSpecificConfiguration; var sourceAudio = inputFileSegment.Source; var resultsDirectory = AnalysisCoordinator.GetNamedDirectory(settings.AnalysisOutputDirectory, this); bool tileOutput = acousticIndicesConfig.TileOutput; var frameWidth = acousticIndicesConfig.FrameLength; int sampleRate = AppConfigHelper.DefaultTargetSampleRate; sampleRate = acousticIndicesConfig.ResampleRate ?? sampleRate; // Gather settings for rendering false color spectrograms var ldSpectrogramConfig = acousticIndicesConfig.LdSpectrogramConfig; string basename = Path.GetFileNameWithoutExtension(sourceAudio.Name); // output to disk (so other analyzers can use the data, // only data - configuration settings that generated these indices // this data can then be used by post-process analyses /* NOTE: The value for FrameStep is used only when calculating a standard spectrogram * FrameStep is NOT used when calculating Summary and Spectral indices. */ var indexConfigData = new IndexGenerationData() { RecordingExtension = inputFileSegment.Source.Extension, RecordingBasename = basename, RecordingStartDate = inputFileSegment.TargetFileStartDate, RecordingDuration = inputFileSegment.TargetFileDuration.Value, SampleRateOriginal = inputFileSegment.TargetFileSampleRate.Value, SampleRateResampled = sampleRate, FrameLength = frameWidth, FrameStep = settings.Configuration.GetIntOrNull(AnalysisKeys.FrameStep) ?? frameWidth, IndexCalculationDuration = acousticIndicesConfig.IndexCalculationDurationTimeSpan, BgNoiseNeighbourhood = acousticIndicesConfig.BgNoiseBuffer, AnalysisStartOffset = inputFileSegment.SegmentStartOffset ?? TimeSpan.Zero, MaximumSegmentDuration = settings.AnalysisMaxSegmentDuration, BackgroundFilterCoeff = SpectrogramConstants.BACKGROUND_FILTER_COEFF, LongDurationSpectrogramConfig = ldSpectrogramConfig, }; var icdPath = FilenameHelpers.AnalysisResultPath( resultsDirectory, basename, IndexGenerationData.FileNameFragment, "json"); Json.Serialise(icdPath.ToFileInfo(), indexConfigData); // gather spectra to form spectrograms. Assume same spectra in all analyzer results // this is the most efficient way to do this // gather up numbers and strings store in memory, write to disk one time // this method also AUTOMATICALLY SORTS because it uses array indexing var dictionaryOfSpectra = spectralIndices.ToTwoDimensionalArray(SpectralIndexValues.CachedSelectors, TwoDimensionalArray.Rotate90ClockWise); // Calculate the index distribution statistics and write to a json file. Also save as png image var indexDistributions = IndexDistributions.WriteSpectralIndexDistributionStatistics(dictionaryOfSpectra, resultsDirectory, basename); // HACK: do not render false color spectrograms unless IndexCalculationDuration = 60.0 (the normal resolution) if (acousticIndicesConfig.IndexCalculationDurationTimeSpan != 60.0.Seconds()) { Log.Warn("False color spectrograms were not rendered"); } else { FileInfo indicesPropertiesConfig = acousticIndicesConfig.IndexPropertiesConfig.ToFileInfo(); // Actually draw false color / long duration spectrograms Tuple <Image <Rgb24>, string>[] images = LDSpectrogramRGB.DrawSpectrogramsFromSpectralIndices( inputDirectory: resultsDirectory, outputDirectory: resultsDirectory, ldSpectrogramConfig: ldSpectrogramConfig, indexPropertiesConfigPath: indicesPropertiesConfig, indexGenerationData: indexConfigData, basename: basename, analysisType: this.Identifier, indexSpectrograms: dictionaryOfSpectra, indexStatistics: indexDistributions, imageChrome: (!tileOutput).ToImageChrome()); if (tileOutput) { Debug.Assert(images.Length == 2); Log.Info("Tiling output at scale: " + acousticIndicesConfig.IndexCalculationDuration); foreach (var image in images) { TileOutput(resultsDirectory, Path.GetFileNameWithoutExtension(sourceAudio.Name), image.Item2 + ".Tile", inputFileSegment, image.Item1); } } } }
public static double[,] GetDifferenceSpectrogramDerivedFromSingleTStatistic(string key, LDSpectrogramRGB cs1, LDSpectrogramRGB cs2, double tStatThreshold) { double[,] m1 = cs1.GetNormalisedSpectrogramMatrix(key); //the TEN matrix is subtracted from 1. double[,] m2 = cs2.GetNormalisedSpectrogramMatrix(key); double[,] tStatM = GetTStatisticMatrix(key, cs1, cs2); int rows = m1.GetLength(0); //number of rows int cols = m2.GetLength(1); //number var differenceM = new double[rows, cols]; for (int row = 0; row < rows; row++) { for (int column = 0; column < cols; column++) { if (Math.Abs(tStatM[row, column]) >= tStatThreshold) { differenceM[row, column] = m1[row, column] - m2[row, column]; } } } return(differenceM); }