/// <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); }
/// <summary> /// Assume that we are processing data for one minute only. /// From this one minute of data, we produce images at three scales. /// A one minute recording framed at 20ms should yield 3000 frames. /// But to achieve this where sr= 22050 and frameSize=512, we need an overlap of 71 samples. /// Consequently only 2999 frames returned per minute. /// Therefore have to pad end to get 3000 frames. /// </summary> public static TimeOffsetSingleLayerSuperTile[] DrawSuperTilesFromSingleFrameSpectrogram(DirectoryInfo dataDir, LdSpectrogramConfig analysisConfig, Dictionary <string, IndexProperties> indexProperties, SpectrogramZoomingConfig zoomingConfig, int minute, double[] imageScales, string basename, IndexGenerationData indexGeneration, ImageChrome chromeOption, TimeSpan alignmentPadding) { string fileStem = basename; // string analysisType = analysisConfig.AnalysisType; TimeSpan indexScale = indexGeneration.IndexCalculationDuration; TimeSpan frameScale = TimeSpan.FromSeconds(zoomingConfig.SpectralFrameDuration); var expectedDataDurationInSeconds = (int)indexGeneration.MaximumSegmentDuration.Value.TotalSeconds; var expectedFrameCount = (int)Math.Round(expectedDataDurationInSeconds / zoomingConfig.SpectralFrameDuration); string fileName = fileStem + "_" + minute + "min.csv"; string csvPath = Path.Combine(dataDir.FullName, fileName); bool skipHeader = true; bool skipFirstColumn = true; // read spectrogram into a list of frames List <double[]> frameList = CsvTools.ReadCSVFileOfDoubles(csvPath, skipHeader, skipFirstColumn); if (frameList == null) { LoggedConsole.WriteErrorLine( "WARNING: METHOD DrawSuperTilesFromSingleFrameSpectrogram(): NO SPECTRAL DATA SUPPLIED"); return(null); } PadEndOfListOfFrames(frameList, expectedFrameCount); TrimEndOfListOfFrames(frameList, expectedFrameCount); //// frame count will be one less than expected for the recording segment because of frame overlap //// Therefore pad the end of the list of frames with the last frame. // int frameDiscrepancy = expectedFrameCount - frameList.Count; // if (frameDiscrepancy > 0) // { // double[] frame = frameList[frameList.Count - 1]; // for (int d = 0; d < frameDiscrepancy; d++) // { // frameList.Add(frame); // } // } var frameData = new TemporalMatrix("rows", MatrixTools.ConvertList2Matrix(frameList), frameScale); frameData.SwapTemporalDimension(); // so the two data matrices have the same temporal dimension TimeSpan startTime = indexGeneration.AnalysisStartOffset; // default = zero minute of day i.e. midnight TimeSpan startTimeOfData = startTime + TimeSpan.FromMinutes(minute); var str = new TimeOffsetSingleLayerSuperTile[imageScales.Length]; // make the images for (int scale = 0; scale < imageScales.Length; scale++) { TimeSpan imageScale = TimeSpan.FromSeconds(imageScales[scale]); var compressionFactor = (int)Math.Round(imageScale.TotalMilliseconds / frameData.DataScale.TotalMilliseconds); double columnDuration = imageScale.TotalSeconds; // int expectedFrameCount = (int)Math.Round(expectedDataDurationInSeconds / columnDuration); // ############## RESEARCH CHOICE HERE >>>> compress spectrograms to correct scale using either max or average // Average appears to offer better contrast. // double[,] data = frameData.CompressMatrixInTemporalDirectionByTakingMax(imageScale); double[,] data = frameData.CompressMatrixInTemporalDirectionByTakingAverage(imageScale); var spectrogramImage = DrawFrameSpectrogramAtScale( analysisConfig, zoomingConfig, startTimeOfData, imageScale, data, indexGeneration, chromeOption); str[scale] = new TimeOffsetSingleLayerSuperTile( alignmentPadding, SpectrogramType.Frame, imageScale, spectrogramImage.CloneAs <Rgba32>(), startTimeOfData); } return(str); }