private static (Dictionary <string, double[, ]>, Dictionary <string, IndexProperties>) LoadSpectra( AnalysisIoInputDirectory io, string analysisTag, string fileStem, Dictionary <string, IndexProperties> indexProperties) { indexProperties = InitialiseIndexProperties.FilterIndexPropertiesForSpectralOnly(indexProperties); string[] keys = indexProperties.Keys.ToArray(); Dictionary <string, double[, ]> spectra = IndexMatrices.ReadSpectralIndices( io.InputBase, fileStem, analysisTag, keys); return(spectra, indexProperties); }
/// <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 used to construct slices out of implicit 3-D spectrograms. /// As of December 2014 it contains hard coded variables just to get it working. /// </summary> public static void Main(Arguments arguments) { if (!arguments.OutputDir.Exists) { arguments.OutputDir.Create(); } const string title = "# READ LD data table files to prepare a 3D Spectrogram"; string dateNow = "# DATE AND TIME: " + DateTime.Now; LoggedConsole.WriteLine(title); LoggedConsole.WriteLine(dateNow); LoggedConsole.WriteLine("# Index Properties: " + arguments.IndexPropertiesConfig.Name); LoggedConsole.WriteLine("# Input directory: " + arguments.InputDir.Name); //LoggedConsole.WriteLine("# SonogramConfig: " + arguments.SonoConfig.Name); LoggedConsole.WriteLine("# Table directory: " + arguments.TableDir.Name); LoggedConsole.WriteLine("# Output directory: " + arguments.OutputDir.Name); LoggedConsole.WriteLine("# Analysis SampleRate: " + arguments.SampleRate); LoggedConsole.WriteLine("# Analysis FrameSize: " + arguments.FrameSize); bool verbose = arguments.Verbose; // 1. set up the necessary files DirectoryInfo inputDirInfo = arguments.InputDir; DirectoryInfo dataTableDirInfo = arguments.TableDir; DirectoryInfo opDir = arguments.OutputDir; // FileInfo configFile = arguments.SonoConfig; FileInfo indexPropertiesConfig = arguments.IndexPropertiesConfig; FileInfo sunriseSetData = arguments.BrisbaneSunriseDatafile; int sampleRate = arguments.SampleRate; int frameSize = arguments.FrameSize; int nyquistFreq = sampleRate / 2; int freqBinCount = frameSize / 2; double freqBinWidth = nyquistFreq / (double)freqBinCount; // 2. convert spectral indices to a data table - need only do this once // ### IMPORTANT ###################################################################################################################### // Uncomment the next line when converting spectral indices to a data table for the first time. // It calls method to read in index spectrograms and combine all the info into one index table per day //SpectralIndicesToAndFromTable.ReadAllSpectralIndicesAndWriteToDataTable(indexPropertiesConfig, inputDirInfo, dataTableDirInfo); // ############ use next seven lines to obtain slices at constant DAY OF YEAR string key = KeyDayOfYear; int step = 1; int firstIndex = 71; int maxSliceCount = TotalDaysInYear + 1; var xInterval = TimeSpan.FromMinutes(60); // one hour intervals = 60 pixels int rowId = 3; // FreqBin int colId = 2; // MinOfDay // ############ use next seven lines to obtain slices at constant FREQUENCY //string key = keyFreqBin; //int step = 100; //int firstIndex = 0; //int maxSliceCount = nyquistFreq; //var XInterval = TimeSpan.FromMinutes(60); //int rowID = 1; // DayOfYear //int colID = 2; // MinOfDay // ############ use next seven lines to obtain slices at constant MINUTE OF DAY //string key = keyMinOfDay; //int step = 5; //int firstIndex = 0; //int maxSliceCount = LDSpectrogram3D.Total_Minutes_In_Day; //var XInterval = TimeSpan.FromDays(30.4); // average days per month //int rowID = 3; // FreqBin //int colID = 1; // DayOfYear // These are the column names and order in the csv data strings. // Year, DayOfYear, MinOfDay, FreqBin, ACI, AVG, BGN, CVR, TEN, VAR string colorMap = "ACI-TEN-CVR"; int redId = 4; // ACI int grnId = 8; // TEN int bluId = 7; // CVR int year = 2013; for (int sliceId = firstIndex; sliceId < maxSliceCount; sliceId += step) { // DEFINE THE SLICE //sliceID = 300; // Herz int arrayId = sliceId; if (key == "FreqBin") { arrayId = (int)Math.Round(sliceId / freqBinWidth); } var fileStem = string.Format("SERF_2013_" + key + "_{0:d4}", arrayId); // 3. Read a data slice from the data table files List <string> data; var outputFileName = $"{fileStem}.csv"; var path = Path.Combine(opDir.FullName, outputFileName); if (File.Exists(path)) { data = FileTools.ReadTextFile(path); } else { if (key == KeyDayOfYear) { data = GetDaySlice(dataTableDirInfo, year, arrayId); } else { data = GetDataSlice(dataTableDirInfo, key, arrayId); } FileTools.WriteTextFile(path, data); } // 4. Read the yaml file describing the Index Properties Dictionary <string, IndexProperties> dictIp = IndexProperties.GetIndexProperties(indexPropertiesConfig); dictIp = InitialiseIndexProperties.FilterIndexPropertiesForSpectralOnly(dictIp); // 5. Convert data slice to image string[] indexNames = colorMap.Split('-'); IndexProperties ipRed = dictIp[indexNames[0]]; IndexProperties ipGrn = dictIp[indexNames[1]]; IndexProperties ipBlu = dictIp[indexNames[2]]; Image <Rgb24> image = GetImageSlice(key, data, rowId, colId, redId, grnId, bluId, ipRed, ipGrn, ipBlu, freqBinCount); // 6. frame the image and save image = Frame3DSpectrogram(image, key, arrayId, year, colorMap, xInterval, nyquistFreq, sliceId, sunriseSetData); // 7. save the image outputFileName = $"{fileStem}.png"; path = Path.Combine(opDir.FullName, outputFileName); image.Save(path); } // end loop through slices } // end Main()
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)); }