/// <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);
        }
Esempio n. 5
0
        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));
        }