/// <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 void DrawStackOfZoomedSpectrograms( DirectoryInfo inputDirectory, DirectoryInfo outputDirectory, AnalysisIoInputDirectory io, ZoomParameters common, string analysisTag, TimeSpan focalTime, int imageWidth) { var zoomConfig = common.SpectrogramZoomingConfig; LdSpectrogramConfig ldsConfig = common.SpectrogramZoomingConfig.LdSpectrogramConfig; //var distributions = common.IndexDistributions; string fileStem = common.OriginalBasename; var indexGeneration = common.IndexGenerationData; TimeSpan dataScale = indexGeneration.IndexCalculationDuration; // ####################### DERIVE ZOOMED OUT SPECTROGRAMS FROM SPECTRAL INDICES //var indexGenerationData = common.IndexGenerationData; var indexProperties = zoomConfig.IndexProperties; var(spectra, filteredIndexProperties) = ZoomCommon.LoadSpectra(io, analysisTag, fileStem, zoomConfig.LdSpectrogramConfig, indexProperties); Stopwatch sw = Stopwatch.StartNew(); // Set the default time-scales in seconds per pixel. // These were changed on 3rd April 2019 to better match those in the current zooming config file. double[] imageScales = { 60, 30, 15, 7.5, 3.2, 1.6, 0.8, 0.4, 0.2 }; if (zoomConfig.SpectralIndexScale != null) { imageScales = zoomConfig.SpectralIndexScale; } sw = Stopwatch.StartNew(); int scaleCount = imageScales.Length; var imageList = new List <Image <Rgb24> >(); for (int i = 0; i < scaleCount; i++) { var imageScale = TimeSpan.FromSeconds(imageScales[i]); var image = DrawIndexSpectrogramAtScale(ldsConfig, indexGeneration, filteredIndexProperties, focalTime, dataScale, imageScale, imageWidth, spectra, fileStem); if (image != null) { imageList.Add(image); string name = $"{fileStem}_FocalZoom_min{focalTime.TotalMinutes:f1}_scale{imageScales[i]}.png"; image.Save(Path.Combine(outputDirectory.FullName, name)); } } sw.Stop(); LoggedConsole.WriteLine("Finished spectrograms derived from spectral indices. Elapsed time = " + sw.Elapsed.TotalSeconds + " seconds"); // NOTE: The following code is deprecated. It was originally developed to provide some intermediate steps between the hi-resolution false-colour spectrograms // and the standard grey scale spectrograms. // ####################### DERIVE ZOOMED IN SPECTROGRAMS FROM STANDARD SPECTRAL FRAMES /* * int[] compressionFactor = { 8, 4, 2, 1 }; * int compressionCount = compressionFactor.Length; * sw = Stopwatch.StartNew(); * double frameStepInSeconds = indexGeneration.FrameStep / (double)indexGeneration.SampleRateResampled; * TimeSpan frameScale = TimeSpan.FromTicks((long)Math.Round(frameStepInSeconds * 10000000)); * if (zoomConfig.SpectralFrameScale != null) * { * imageScales = zoomConfig.SpectralFrameScale; * * // TODO: CONVERT IMAGE scales into Compression factors. * compressionCount = imageScales.Length; * compressionFactor = new int[compressionCount]; * compressionFactor[compressionCount - 1] = 1; * double denom = imageScales[compressionCount - 1]; * * for (int i = 0; i < compressionCount - 1; i++) * { * compressionFactor[i] = (int)Math.Round(imageScales[i] / denom); * } * } * * int maxCompression = compressionFactor[0]; * TimeSpan maxImageDuration = TimeSpan.FromTicks(maxCompression * imageWidth * frameScale.Ticks); * * TimeSpan halfMaxImageDuration = TimeSpan.FromMilliseconds(maxImageDuration.TotalMilliseconds / 2); * TimeSpan startTimeOfMaxImage = TimeSpan.Zero; * if (focalTime != TimeSpan.Zero) * { * startTimeOfMaxImage = focalTime - halfMaxImageDuration; * } * * TimeSpan startTimeOfData = TimeSpan.FromMinutes(Math.Floor(startTimeOfMaxImage.TotalMinutes)); * * List<double[]> frameData = ReadFrameData(inputDirectory, fileStem, startTimeOfMaxImage, maxImageDuration, zoomConfig, indexGeneration.MaximumSegmentDuration.Value); * * // get the index data to add into the * // TimeSpan imageScale1 = TimeSpan.FromSeconds(0.1); * double[,] indexData = spectra["PMN"]; * * // make the images * for (int i = 0; i < compressionCount; i++) * { * int factor = compressionFactor[i]; * var image = DrawFrameSpectrogramAtScale(ldsConfig, indexGeneration, startTimeOfData, factor, frameData, indexData, focalTime, frameScale, imageWidth); * if (image != null) * { * imageList.Add(image); * } * } * * sw.Stop(); * LoggedConsole.WriteLine("Finished spectrograms derived from standard frames. Elapsed time = " + sw.Elapsed.TotalSeconds + " seconds"); */ // combine the images into a stack var combinedImage = ImageTools.CombineImagesVertically(imageList); string fileName = $"{fileStem}_FocalZOOM_min{focalTime.TotalMinutes:f1}.png"; combinedImage.Save(Path.Combine(outputDirectory.FullName, fileName)); }
/// <summary> /// This method can add in absolute time if you want. /// Currently commented out - see below. /// </summary> public static Image <Rgb24> DrawIndexSpectrogramAtScale( LdSpectrogramConfig config, IndexGenerationData indexGenerationData, Dictionary <string, IndexProperties> indexProperties, TimeSpan focalTime, TimeSpan dataScale, TimeSpan imageScale, int imageWidth, Dictionary <string, double[, ]> spectra, string basename) { if (spectra == null) { LoggedConsole.WriteLine("WARNING: NO SPECTRAL DATA SUPPLIED"); return(null); } // check that scalingFactor >= 1.0 double scalingFactor = Math.Round(imageScale.TotalMilliseconds / dataScale.TotalMilliseconds); if (scalingFactor < 1.0) { LoggedConsole.WriteLine("WARNING: Scaling Factor < 1.0"); return(null); } Dictionary <string, IndexProperties> dictIp = indexProperties; dictIp = InitialiseIndexProperties.FilterIndexPropertiesForSpectralOnly(dictIp); // calculate start time by combining DatetimeOffset with minute offset. TimeSpan sourceMinuteOffset = indexGenerationData.AnalysisStartOffset; if (indexGenerationData.RecordingStartDate.HasValue) { DateTimeOffset dto = (DateTimeOffset)indexGenerationData.RecordingStartDate; sourceMinuteOffset = dto.TimeOfDay + sourceMinuteOffset; } // calculate data duration from column count of abitrary matrix var kvp = spectra.First(); var matrix = kvp.Value; //var matrix = spectra["ACI"]; // assume this key will always be present!! TimeSpan dataDuration = TimeSpan.FromSeconds(matrix.GetLength(1) * dataScale.TotalSeconds); TimeSpan recordingStartTime = TimeSpan.Zero; // default = zero minute of day i.e. midnight recordingStartTime = indexGenerationData.RecordingStartDate.Value.TimeOfDay.Add(indexGenerationData.AnalysisStartOffset); TimeSpan offsetTime = TimeSpan.Zero; TimeSpan imageDuration = TimeSpan.FromTicks(imageWidth * imageScale.Ticks); TimeSpan halfImageDuration = TimeSpan.FromTicks(imageWidth * imageScale.Ticks / 2); TimeSpan startTime = TimeSpan.Zero; if (focalTime != TimeSpan.Zero) { startTime = focalTime - halfImageDuration; } if (startTime < TimeSpan.Zero) { offsetTime = TimeSpan.Zero - startTime; startTime = TimeSpan.Zero; } TimeSpan endTime = imageDuration; if (focalTime != TimeSpan.Zero) { endTime = focalTime + halfImageDuration; } if (endTime > dataDuration) { endTime = dataDuration; } TimeSpan spectrogramDuration = endTime - startTime; int spectrogramWidth = (int)(spectrogramDuration.Ticks / imageScale.Ticks); // get the plain unchromed spectrogram var ldfcSpectrogram = ZoomCommon.DrawIndexSpectrogramCommon( config, indexGenerationData, indexProperties, startTime, endTime, dataScale, imageScale, imageWidth, spectra, basename); if (ldfcSpectrogram == null) { LoggedConsole.WriteLine("WARNING: NO SPECTROGRAM AT SCALE " + imageScale); return(null); } // now chrome spectrogram ldfcSpectrogram.Mutate(g2 => { // draw red line at focus time if (focalTime != TimeSpan.Zero) { Pen pen = new Pen(Color.Red, 1); TimeSpan focalOffset = focalTime - startTime; int x1 = (int)(focalOffset.Ticks / imageScale.Ticks); g2.DrawLine(pen, x1, 0, x1, ldfcSpectrogram.Height); } }); // draw the title bar int nyquist = 22050 / 2; // default if (indexGenerationData.SampleRateResampled > 0) { nyquist = indexGenerationData.SampleRateResampled / 2; } int herzInterval = 1000; if (config != null) { herzInterval = config.YAxisTicInterval; } string title = $"SCALE={imageScale.TotalSeconds}s/px. Duration={spectrogramDuration} "; //add chrome // NEXT LINE USED ONLY IF WANT ABSOLUTE TIME //startTime += recordingStartTime; var titleBar = DrawTitleBarOfZoomSpectrogram(title, ldfcSpectrogram.Width); ldfcSpectrogram = FrameZoomSpectrogram( ldfcSpectrogram, titleBar, startTime, imageScale, config.XAxisTicInterval, nyquist, herzInterval); // create the base canvas image on which to centre the focal image var image = Drawing.NewImage(imageWidth, ldfcSpectrogram.Height, Color.DarkGray); int xOffset = (int)(offsetTime.Ticks / imageScale.Ticks); image.Mutate(g1 => g1.DrawImage(ldfcSpectrogram, new Point(xOffset, 0), 1)); return(image); }
/// <summary> /// This method can add in the absolute recording start time. However currently disabled. /// </summary> /// <param name="config">v.</param> /// <param name="indexGenerationData">indexGenerationData.</param> /// <param name="startTimeOfData">startTimeOfData.</param> /// <param name="compressionFactor">compressionFactor.</param> /// <param name="frameData">frameData.</param> /// <param name="indexData">indexData.</param> /// <param name="focalTime">focalTime.</param> /// <param name="frameScale">frameScale.</param> /// <param name="imageWidth">imageWidth.</param> public static Image DrawFrameSpectrogramAtScale( LdSpectrogramConfig config, IndexGenerationData indexGenerationData, TimeSpan startTimeOfData, int compressionFactor, List <double[]> frameData, double[,] indexData, TimeSpan focalTime, TimeSpan frameScale, int imageWidth) { if (frameData == null || frameData.Count == 0) { LoggedConsole.WriteLine("WARNING: NO SPECTRAL SPECTROGRAM DATA SUPPLIED"); return(null); } // var recordingStartTime = TimeSpan.Zero; // default = zero minute of day i.e. midnight // var recordingStartTime = TimeTools.DateTimePlusTimeSpan(indexGenerationData.RecordingStartDate, indexGenerationData.AnalysisStartOffset); TimeSpan imageScale = TimeSpan.FromTicks(frameScale.Ticks * compressionFactor); TimeSpan imageDuration = TimeSpan.FromTicks(imageWidth * imageScale.Ticks); TimeSpan halfImageDuration = TimeSpan.FromTicks(imageWidth * imageScale.Ticks / 2); TimeSpan startTime = focalTime - halfImageDuration; if (startTime < TimeSpan.Zero) { startTime = TimeSpan.Zero; } int startIndex = (int)((startTime.Ticks - startTimeOfData.Ticks) / frameScale.Ticks); int requiredFrameCount = imageWidth * compressionFactor; List <double[]> frameSelection = frameData.GetRange(startIndex, requiredFrameCount); double[,] spectralSelection = MatrixTools.ConvertList2Matrix(frameSelection); // compress spectrograms to correct scale if (compressionFactor > 1) { spectralSelection = TemporalMatrix.CompressFrameSpectrograms(spectralSelection, compressionFactor); } var spectrogramImage = DrawStandardSpectrogramInFalseColour(spectralSelection); int x1 = (int)(halfImageDuration.Ticks / imageScale.Ticks); spectrogramImage.Mutate(g2 => { // draw focus time on image if (focalTime != TimeSpan.Zero) { Pen pen = new Pen(Color.Red, 1); g2.DrawLine(pen, x1, 0, x1, spectrogramImage.Height); } }); int nyquist = 22050 / 2; // default if (indexGenerationData.SampleRateResampled > 0) { nyquist = indexGenerationData.SampleRateResampled / 2; } int herzInterval = config.YAxisTicInterval; string title = $"ZOOM SCALE={imageScale.TotalMilliseconds}ms/pixel Image duration={imageDuration} "; var titleBar = DrawTitleBarOfZoomSpectrogram(title, spectrogramImage.Width); // add the recording start time ONLY IF WANT ABSOLUTE TIME SCALE - obtained from info in file name // startTime += recordingStartTime; spectrogramImage = FrameZoomSpectrogram(spectrogramImage, titleBar, startTime, imageScale, config.XAxisTicInterval, nyquist, herzInterval); // MAY WANT THESE CLIPPING TRACKS AT SOME POINT // read high amplitude and clipping info into an image //string indicesFile = Path.Combine(configuration.InputDirectoryInfo.FullName, fileStem + ".csv"); //string indicesFile = Path.Combine(config.InputDirectoryInfo.FullName, fileStem + ".Indices.csv"); //string indicesFile = Path.Combine(configuration.InputDirectoryInfo.FullName, fileStem + "_" + configuration.AnalysisType + ".csv"); //Image imageX = DrawSummaryIndices.DrawHighAmplitudeClippingTrack(indicesFile.ToFileInfo()); //if (null != imageX) imageX.Save(Path.Combine(outputDirectory.FullName, fileStem + ".ClipHiAmpl.png")); // create the base image Image image = new Image <Rgb24>(imageWidth, spectrogramImage.Height); image.Mutate(g1 => { g1.Clear(Color.DarkGray); //int xOffset = (int)(startTime.Ticks / imageScale.Ticks); int xOffset = (imageWidth / 2) - x1; g1.DrawImage(spectrogramImage, new Point(xOffset, 0), 1); }); return(image); }
public static void DrawStackOfZoomedSpectrograms(DirectoryInfo inputDirectory, DirectoryInfo outputDirectory, ZoomParameters common, TimeSpan focalTime, int imageWidth, string analysisType) { var zoomConfig = common.SpectrogramZoomingConfig; LdSpectrogramConfig ldsConfig = common.SpectrogramZoomingConfig.LdSpectrogramConfig; var distributions = common.IndexDistributions; var indexGeneration = common.IndexGenerationData; string fileStem = common.OriginalBasename; TimeSpan dataScale = indexGeneration.IndexCalculationDuration; // ####################### DERIVE ZOOMED OUT SPECTROGRAMS FROM SPECTRAL INDICES string[] keys = { "ACI", "BGN", "CVR", "DIF", "ENT", "EVN", "PMN", "POW", "RHZ", "RVT", "RPS", "RNG", "SUM", "SPT" }; var indexProperties = InitialiseIndexProperties.FilterIndexPropertiesForSpectralOnly(zoomConfig.IndexProperties); Dictionary <string, double[, ]> spectra = IndexMatrices.ReadSpectralIndices(inputDirectory, fileStem, analysisType, keys); Stopwatch sw = Stopwatch.StartNew(); // standard scales in seconds per pixel. double[] imageScales = { 60, 24, 12, 6, 2, 1, 0.6, 0.2 }; if (zoomConfig.SpectralIndexScale != null) { imageScales = zoomConfig.SpectralIndexScale; } sw = Stopwatch.StartNew(); int scaleCount = imageScales.Length; var imageList = new List <Image>(); for (int i = 0; i < scaleCount; i++) { var imageScale = TimeSpan.FromSeconds(imageScales[i]); var image = DrawIndexSpectrogramAtScale(ldsConfig, indexGeneration, indexProperties, focalTime, dataScale, imageScale, imageWidth, spectra, fileStem); if (image != null) { imageList.Add(image); string name = $"{fileStem}_FocalZoom_min{focalTime.TotalMinutes:f1}_scale{imageScales[i]}.png"; image.Save(Path.Combine(outputDirectory.FullName, name)); } } sw.Stop(); LoggedConsole.WriteLine("Finished spectrograms derived from spectral indices. Elapsed time = " + sw.Elapsed.TotalSeconds + " seconds"); // ####################### DERIVE ZOOMED IN SPECTROGRAMS FROM STANDARD SPECTRAL FRAMES int[] compressionFactor = { 8, 4, 2, 1 }; int compressionCount = compressionFactor.Length; sw = Stopwatch.StartNew(); double frameStepInSeconds = indexGeneration.FrameStep / (double)indexGeneration.SampleRateResampled; TimeSpan frameScale = TimeSpan.FromTicks((long)Math.Round(frameStepInSeconds * 10000000)); if (zoomConfig.SpectralFrameScale != null) { imageScales = zoomConfig.SpectralFrameScale; // TODO: CONVERT IMAGE scales into Compression factors. compressionCount = imageScales.Length; compressionFactor = new int[compressionCount]; compressionFactor[compressionCount - 1] = 1; double denom = imageScales[compressionCount - 1]; for (int i = 0; i < compressionCount - 1; i++) { compressionFactor[i] = (int)Math.Round(imageScales[i] / denom); } } int maxCompression = compressionFactor[0]; TimeSpan maxImageDuration = TimeSpan.FromTicks(maxCompression * imageWidth * frameScale.Ticks); TimeSpan halfMaxImageDuration = TimeSpan.FromMilliseconds(maxImageDuration.TotalMilliseconds / 2); TimeSpan startTimeOfMaxImage = TimeSpan.Zero; if (focalTime != TimeSpan.Zero) { startTimeOfMaxImage = focalTime - halfMaxImageDuration; } TimeSpan startTimeOfData = TimeSpan.FromMinutes(Math.Floor(startTimeOfMaxImage.TotalMinutes)); List <double[]> frameData = ReadFrameData(inputDirectory, fileStem, startTimeOfMaxImage, maxImageDuration, zoomConfig, indexGeneration.MaximumSegmentDuration.Value); // get the index data to add into the // TimeSpan imageScale1 = TimeSpan.FromSeconds(0.1); double[,] indexData = spectra["POW"]; // make the images for (int i = 0; i < compressionCount; i++) { int factor = compressionFactor[i]; var image = DrawFrameSpectrogramAtScale(ldsConfig, indexGeneration, startTimeOfData, factor, frameData, indexData, focalTime, frameScale, imageWidth); if (image != null) { imageList.Add(image); } } sw.Stop(); LoggedConsole.WriteLine("Finished spectrograms derived from standard frames. Elapsed time = " + sw.Elapsed.TotalSeconds + " seconds"); // combine the images into a stack Image combinedImage = ImageTools.CombineImagesVertically(imageList); string fileName = $"{fileStem}_FocalZOOM_min{focalTime.TotalMinutes:f1}.png"; combinedImage.Save(Path.Combine(outputDirectory.FullName, fileName)); }