From fb5a196ab86b66dd5183567be908604a30c6ee4f Mon Sep 17 00:00:00 2001 From: Michael Towsey Date: Thu, 30 Nov 2017 15:04:50 +1000 Subject: [PATCH] Rendering of Gaps & Joins in concatenated sp.grams Changed name of class to GapsAndJoins.cs. Split indices class in two. Chanded the rendering of NoGaps option. A little bit of Resharper. --- .../AnalysisPrograms/ConcatenateIndexFiles.cs | 16 +- .../DrawLongDurationSpectrograms.cs | 2 +- AudioAnalysis/AnalysisPrograms/Sandpit.cs | 53 ++-- .../AudioAnalysisTools.csproj | 3 +- ...oneousIndexSegments.cs => GapsAndJoins.cs} | 256 ++++++++++------- .../Indices/IndexDisplay.cs | 2 +- .../Indices/IndexMatrices.cs | 25 +- .../Indices/IndexProperties.cs | 4 +- .../Indices/SpectralIndexValues.cs | 243 +++++++++++++++++ .../Indices/SummaryIndexValues.cs | 257 ++---------------- .../LDSpectrogramRGB.cs | 6 +- .../LDSpectrogramStitching.cs | 17 +- 12 files changed, 486 insertions(+), 398 deletions(-) rename AudioAnalysis/AudioAnalysisTools/Indices/{ErroneousIndexSegments.cs => GapsAndJoins.cs} (62%) create mode 100644 AudioAnalysis/AudioAnalysisTools/Indices/SpectralIndexValues.cs diff --git a/AudioAnalysis/AnalysisPrograms/ConcatenateIndexFiles.cs b/AudioAnalysis/AnalysisPrograms/ConcatenateIndexFiles.cs index fd412ca01..a1338df5c 100644 --- a/AudioAnalysis/AnalysisPrograms/ConcatenateIndexFiles.cs +++ b/AudioAnalysis/AnalysisPrograms/ConcatenateIndexFiles.cs @@ -305,10 +305,10 @@ public static void Execute(Arguments arguments) var dictionaryOfSummaryIndices = LdSpectrogramStitching.ConcatenateAllSummaryIndexFiles(summaryIndexFiles, resultsDir, indexGenerationData, opFileStem); - // REALITY CHECK - check for continuous zero indices or anything else that might indicate defective signal - // or incomplete analysis of recordings - var indexErrors = ErroneousIndexSegments.DataIntegrityCheck(dictionaryOfSummaryIndices, arguments.GapRendering); - ErroneousIndexSegments.WriteErrorsToFile(indexErrors, resultsDir, opFileStem); + // REALITY CHECK - check for continuous zero indices or anything else that might indicate defective signal, + // incomplete analysis of recordings, recording gaps or file joins. + var gapsAndJoins = GapsAndJoins.DataIntegrityCheck(dictionaryOfSummaryIndices, arguments.GapRendering); + GapsAndJoins.WriteErrorsToFile(gapsAndJoins, resultsDir, opFileStem); if (arguments.DrawImages) { @@ -333,7 +333,7 @@ public static void Execute(Arguments arguments) // ###### NOW CONCATENATE THE SPECTRAL INDICES, DRAW IMAGES AND SAVE IN RESULTS DIRECTORY var dictionaryOfSpectralIndices1 = LdSpectrogramStitching.ConcatenateAllSpectralIndexFiles(subDirectories, keys, indexGenerationData); - indexErrors.AddRange(ErroneousIndexSegments.DataIntegrityCheck(dictionaryOfSpectralIndices1, arguments.GapRendering)); + gapsAndJoins.AddRange(GapsAndJoins.DataIntegrityCheck(dictionaryOfSpectralIndices1, arguments.GapRendering)); // Calculate the index distribution statistics and write to a json file. Also save as png image var indexDistributions = IndexDistributions.WriteSpectralIndexDistributionStatistics(dictionaryOfSpectralIndices1, resultsDir, opFileStem); @@ -353,7 +353,7 @@ public static void Execute(Arguments arguments) indexDistributions, siteDescription, arguments.SunRiseDataFile, - indexErrors, + gapsAndJoins, ImageChrome.With); } @@ -414,8 +414,8 @@ public static void Execute(Arguments arguments) } // REALITY CHECK - check for zero signal and anything else that might indicate defective signal - List indexErrors = ErroneousIndexSegments.DataIntegrityCheck(summaryDict, arguments.GapRendering); - ErroneousIndexSegments.WriteErrorsToFile(indexErrors, resultsDir, opFileStem1); + List indexErrors = GapsAndJoins.DataIntegrityCheck(summaryDict, arguments.GapRendering); + GapsAndJoins.WriteErrorsToFile(indexErrors, resultsDir, opFileStem1); // DRAW SUMMARY INDEX IMAGES AND SAVE IN RESULTS DIRECTORY if (arguments.DrawImages) diff --git a/AudioAnalysis/AnalysisPrograms/DrawLongDurationSpectrograms.cs b/AudioAnalysis/AnalysisPrograms/DrawLongDurationSpectrograms.cs index 6008c699e..bdd7a2376 100644 --- a/AudioAnalysis/AnalysisPrograms/DrawLongDurationSpectrograms.cs +++ b/AudioAnalysis/AnalysisPrograms/DrawLongDurationSpectrograms.cs @@ -140,7 +140,7 @@ public static void Execute(Arguments arguments) double[] zeroSignalArray = summaryIndices.Select(si => si.ZeroSignal).ToArray(); - var indexErrors = ErroneousIndexSegments.DataIntegrityCheckForZeroSignal(zeroSignalArray); + var indexErrors = GapsAndJoins.DataIntegrityCheckForZeroSignal(zeroSignalArray); //config.IndexCalculationDuration = TimeSpan.FromSeconds(1.0); //config.XAxisTicInterval = TimeSpan.FromSeconds(60.0); diff --git a/AudioAnalysis/AnalysisPrograms/Sandpit.cs b/AudioAnalysis/AnalysisPrograms/Sandpit.cs index 265131cbb..102dbffb3 100644 --- a/AudioAnalysis/AnalysisPrograms/Sandpit.cs +++ b/AudioAnalysis/AnalysisPrograms/Sandpit.cs @@ -57,9 +57,9 @@ public static void Dev(Arguments arguments) Log.WriteLine("# Start Time = " + tStart.ToString(CultureInfo.InvariantCulture)); //Audio2CsvOverOneFile(); - Audio2CsvOverMultipleFiles(); + //Audio2CsvOverMultipleFiles(); //DrawLongDurationSpectrogram(); - //ConcatenateIndexFilesAndSpectrograms(); + ConcatenateIndexFilesAndSpectrograms(); //TestReadingFileOfSummaryIndices(); //TestsOfFrequencyScales(); //TestAnalyseLongRecordingUsingArtificialSignal(); @@ -93,31 +93,34 @@ public static void Audio2CsvOverMultipleFiles() { string drive = "G"; string outputDir = $"{ drive}:\\SensorNetworks\\Output\\IvanCampos\\Indexdata"; - /* - string recordingDir = $"{ drive}:\\SensorNetworks\\WavFiles\\IvanCampos"; - string configPath = - @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Towsey.Acoustic.yml"; - string searchPattern = "*.wav"; - - //FileInfo[] csvFiles = IndexMatrices.GetFilesInDirectories(subDirectories, pattern); - string[] files = Directory.GetFiles(recordingDir, searchPattern); - LoggedConsole.WriteLine("File Count = " + files.Length); - for (int i = 0; i < files.Length; i++) + // (1) calculate the indices looping over mulitple files. + if (false) { - string outputDirectory = outputDir + "\\" + i; - var devArguments = new AnalyseLongRecording.Arguments + string recordingDir = $"{drive}:\\SensorNetworks\\WavFiles\\IvanCampos"; + string configPath = + @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\Towsey.Acoustic.yml"; + string searchPattern = "*.wav"; + + //FileInfo[] csvFiles = IndexMatrices.GetFilesInDirectories(subDirectories, pattern); + string[] files = Directory.GetFiles(recordingDir, searchPattern); + LoggedConsole.WriteLine("File Count = " + files.Length); + + for (int i = 0; i < files.Length; i++) { - Source = files[i].ToFileInfo(), - Config = configPath.ToFileInfo(), - Output = outputDirectory.ToDirectoryInfo(), - MixDownToMono = true, - }; - AnalyseLongRecording.Execute(devArguments); + string outputDirectory = $"{outputDir}\\{i:d3}"; + var devArguments = new AnalyseLongRecording.Arguments + { + Source = files[i].ToFileInfo(), + Config = configPath.ToFileInfo(), + Output = outputDirectory.ToDirectoryInfo(), + MixDownToMono = true, + }; + AnalyseLongRecording.Execute(devArguments); + } } - */ - // now do the CONCATENATION + // (2) now do the CONCATENATION DirectoryInfo[] dataDirs = { new DirectoryInfo(outputDir), @@ -596,11 +599,10 @@ public static void ConcatenateIndexFilesAndSpectrograms() string gapRendering = "TimedGaps"; // the default bool concatenateEverythingYouCanLayYourHandsOn = false; // default is 24-hour blocks + /* // ########################## CONCATENATION of Sarah Lowe's recordings // The drive: work = G; home = E drive = "G"; - - /* // top level directory DirectoryInfo[] dataDirs = { @@ -617,8 +619,8 @@ public static void ConcatenateIndexFilesAndSpectrograms() // change PMN to POW because PMN not available in these recordings colorMap2 = "BGN-PMN-R3D"; - */ // ########################## END of Sarah Lowe's recordings + */ /* // ########################## CONCATENATION of Yvonne's recordings of SM2 and SM4 @@ -655,6 +657,7 @@ public static void ConcatenateIndexFilesAndSpectrograms() FileInfo sunriseDatafile = null; concatenateEverythingYouCanLayYourHandsOn = true; + // start and end dates INCLUSIVE dtoStart = new DateTimeOffset(2012, 08, 08, 0, 0, 0, TimeSpan.Zero); dtoEnd = new DateTimeOffset(2012, 08, 08, 0, 0, 0, TimeSpan.Zero); diff --git a/AudioAnalysis/AudioAnalysisTools/AudioAnalysisTools.csproj b/AudioAnalysis/AudioAnalysisTools/AudioAnalysisTools.csproj index 245b929ef..79bd92b81 100644 --- a/AudioAnalysis/AudioAnalysisTools/AudioAnalysisTools.csproj +++ b/AudioAnalysis/AudioAnalysisTools/AudioAnalysisTools.csproj @@ -261,13 +261,14 @@ - + + diff --git a/AudioAnalysis/AudioAnalysisTools/Indices/ErroneousIndexSegments.cs b/AudioAnalysis/AudioAnalysisTools/Indices/GapsAndJoins.cs similarity index 62% rename from AudioAnalysis/AudioAnalysisTools/Indices/ErroneousIndexSegments.cs rename to AudioAnalysis/AudioAnalysisTools/Indices/GapsAndJoins.cs index 22b2dd65d..c165c5329 100644 --- a/AudioAnalysis/AudioAnalysisTools/Indices/ErroneousIndexSegments.cs +++ b/AudioAnalysis/AudioAnalysisTools/Indices/GapsAndJoins.cs @@ -1,4 +1,4 @@ -// +// // All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group). // @@ -13,11 +13,12 @@ namespace AudioAnalysisTools.Indices /// /// Choices in how recording gaps are visualised. - /// NoGaps: Recording gaps will be ignored. Segments joined without space. Time scale will be broken. This may sometimes be required. - /// TimedGaps: Recording gaps will be filled with grey "error" segment of same duration as gap. Time scale remains linear and complete. - /// This is the normal mode for visualisation - /// EchoGaps: Recording gaps are filled with some blend of pre- and post-gap spectra. - /// Use this when recordings are one minute in 10, for example. + /// NoGaps: Recording gaps will be ignored. Segments joined without space. Continuity of the time scale will be broken. + /// This will be best option when viewing 12-hour night-time recordings. + /// TimedGaps: Recording gaps will be filled with a grey "gap" segment of same duration as gap. Time scale remains linear and complete. + /// this is, continuity of the time scale is preserved. This is the default mode for visualisation + /// EchoGaps: Recording gaps are filled with a repeat of the last three-index spectrum prior to the gap. + /// Continuity of the time scale is preserved. Use this when recordings are one minute in 10, for example. /// public enum ConcatMode { @@ -26,15 +27,21 @@ public enum ConcatMode EchoGaps, } - public class ErroneousIndexSegments + public class GapsAndJoins { - public const string ErroneousIndexSegmentsFilenameFragment = "WARNING-IndexErrors"; + public const string ErroneousIndexSegmentsFilenameFragment = "WARNING-IndexGapsAndJoins"; - private static string errorMissingData = "No Recording"; - private static string errorZeroSignal = "ERROR: Zero Signal"; - private static string invalidIndexValue = "Invalid Index Value"; + // keys used to extract indices to do with gaps and joins. + public const string KeyRecordingExists = "RecordingExists"; + public const string KeyFileJoin = "FileJoin"; + public const string KeyZeroSignal = "ZeroSignal"; - public string ErrorDescription { get; set; } + private static string gapDescriptionMissingData = "No Recording"; + private static string gapDescriptionZeroSignal = "ERROR: Zero Signal"; + private static string gapDescriptionInvalidValue = "Invalid Index Value"; + private static string gapDescriptionFileJoin = "File Join"; + + public string GapDescription { get; set; } public int StartPosition { get; set; } @@ -50,20 +57,27 @@ public class ErroneousIndexSegments // ##################################################################################################################### /// - /// Does two or three data integrity checks. + /// Does several data integrity checks. /// /// a dictionary of summary indices + /// describes how recording gaps are to be rendered /// a list of erroneous segments - public static List DataIntegrityCheck(Dictionary summaryIndices, ConcatMode gapRendering) + public static List DataIntegrityCheck(Dictionary summaryIndices, ConcatMode gapRendering) { - // Integrity check 1 - var errors = DataIntegrityCheckForNoRecording(summaryIndices, gapRendering); + // Integrity check 1: look for whether a recording minute exists + double[] gapArray = summaryIndices[KeyRecordingExists]; + var errors = DataIntegrityCheckForNoRecording(gapArray, gapRendering); + + // Integrity check 2: look for whether there is join between two recording files + gapArray = summaryIndices[KeyFileJoin]; + errors.AddRange(DataIntegrityCheckForFileJoins(gapArray, gapRendering)); - // Integrity check 2 - errors.AddRange(DataIntegrityCheckForZeroSignal(summaryIndices)); + // Integrity check 3: reads the ZeroSignal array to make sure there was actually a signal to analyse. + gapArray = summaryIndices[KeyZeroSignal]; + errors.AddRange(DataIntegrityCheckForZeroSignal(gapArray)); - // Integrity check 3. This error check not done for time being - a bit unrealistic - // errors.AddRange(DataIntegrityCheckIndices(summaryIndices)); + // Integrity check 4. This error check not done for time being - a bit unrealistic + // errors.AddRange(DataIntegrityCheckIndexValues(summaryIndices)); return errors; } @@ -73,7 +87,7 @@ public static List DataIntegrityCheck(Dictionarya dictionary of spectral indices /// how to render the gap in image terms /// a list of erroneous segments - public static List DataIntegrityCheck(Dictionary spectralIndices, ConcatMode gapRendering) + public static List DataIntegrityCheck(Dictionary spectralIndices, ConcatMode gapRendering) { var errors = DataIntegrityCheckSpectralIndices(spectralIndices, gapRendering); return errors; @@ -85,7 +99,7 @@ public static List DataIntegrityCheck(Dictionarylist of erroneous segments /// directory in which json file to be written /// name of json file - public static void WriteErrorsToFile(List errors, DirectoryInfo outputDirectory, string fileStem) + public static void WriteErrorsToFile(List errors, DirectoryInfo outputDirectory, string fileStem) { // write info to file if (errors.Count == 0) @@ -96,80 +110,126 @@ public static void WriteErrorsToFile(List errors, Direct string path = FilenameHelpers.AnalysisResultPath(outputDirectory, fileStem, ErroneousIndexSegmentsFilenameFragment, "json"); // ReSharper disable once RedundantTypeArgumentsOfMethod - Json.Serialise>(new FileInfo(path), errors); + Json.Serialise>(new FileInfo(path), errors); } /// - /// This method reads through a ZeroIndex SUMMARY array. - /// It reads the ZeroSignal array to make sure there was actually a signal to analyse. - /// If this occurs an error is flagged. + /// This method reads through a SUMMARY index array to make sure there was actually a signal to analyse. + /// If this occurs an gap/join event is flagged. /// - /// Dictionary of the currently calculated summary indices + /// array of gap data /// how to render the gap in image terms /// a list of erroneous segments - public static List DataIntegrityCheckForNoRecording( - Dictionary summaryIndices, - ConcatMode gapRendering) + public static List DataIntegrityCheckForNoRecording(double[] gapArray, ConcatMode gapRendering) { - double tolerance = 0.00001; + double tolerance = 0.0001; - // init list of errors - var errors = new List(); + // init list of gaps and joins + var gaps = new List(); - double[] zeroSignalArray = summaryIndices["NoFile"]; - int arrayLength = zeroSignalArray.Length; + int arrayLength = gapArray.Length; bool allOk = true; - var error = new ErroneousIndexSegments - { - GapRendering = gapRendering, - }; - for (int i = 0; i < zeroSignalArray.Length; i++) + GapsAndJoins gap = null; + //var gap = new GapsAndJoins + //{ + // GapDescription = gapDescriptionMissingData, + // GapRendering = gapRendering, + //}; + + // now loop through the rows/vectors of indices + for (int i = 0; i < gapArray.Length; i++) { - // if (zeroSignal index > 0), i.e. if signal == zero - if (Math.Abs(zeroSignalArray[i]) > tolerance) + // if (RecordingExists index == 0), i.e. if signal exists + if (Math.Abs(gapArray[i]) < tolerance) { if (allOk) { allOk = false; - error = new ErroneousIndexSegments + gap = new GapsAndJoins { StartPosition = i, - ErrorDescription = errorMissingData, + GapDescription = gapDescriptionMissingData, GapRendering = gapRendering, }; } } else - if (!allOk && Math.Abs(zeroSignalArray[i]) < tolerance) + if (!allOk && Math.Abs(gapArray[i]) > tolerance) { // come to end of a bad patch allOk = true; - error.EndPosition = i - 1; - errors.Add(error); + gap.EndPosition = i - 1; + gaps.Add(gap); } } // end of loop - // if not OK at end of the array, need to close the error. + // if not OK at end of the array, need to terminate the gap. if (!allOk) { - errors[errors.Count - 1].EndPosition = arrayLength - 1; + gaps[gaps.Count - 1].EndPosition = arrayLength - 1; } - return errors; + return gaps; } /// - /// This method reads through a ZeroIndex SUMMARY array. - /// It reads the ZeroSignal array to make sure there was actually a signal to analyse. - /// If this occurs an error is flagged. + /// This method reads through a SUMMARY index array to check for file joins. /// - /// Dictionary of the currently calculated summary indices + /// array of gap data + /// how to render the gap in image terms /// a list of erroneous segments - public static List DataIntegrityCheckForZeroSignal(Dictionary summaryIndices) + public static List DataIntegrityCheckForFileJoins(double[] gapArray, ConcatMode gapRendering) { - double[] zeroSignalArray = summaryIndices["ZeroSignal"]; - return DataIntegrityCheckForZeroSignal(zeroSignalArray); + double tolerance = 0.00001; + + // init list of gaps and joins + var gaps = new List(); + + int arrayLength = gapArray.Length; + + bool allOk = true; + GapsAndJoins gap = null; + //var gap = new GapsAndJoins + //{ + // GapDescription = gapDescriptionFileJoin, + // GapRendering = gapRendering, + //}; + + // now loop through the rows/vectors of indices + for (int i = 0; i < gapArray.Length; i++) + { + // if (zeroSignal index > 0), i.e. if signal == zero + if (Math.Abs(gapArray[i]) > tolerance) + { + if (allOk) + { + allOk = false; + gap = new GapsAndJoins + { + StartPosition = i, + GapDescription = gapDescriptionFileJoin, + GapRendering = gapRendering, + }; + } + } + else + if (!allOk && Math.Abs(gapArray[i]) < tolerance) + { + // come to end of a bad patch + allOk = true; + gap.EndPosition = i - 1; + gaps.Add(gap); + } + } // end of loop + + // if not OK at end of the array, need to terminate the gap. + if (!allOk) + { + gaps[gaps.Count - 1].EndPosition = gaps[gaps.Count - 1].EndPosition; + } + + return gaps; } /// @@ -181,17 +241,17 @@ public static List DataIntegrityCheckForZeroSignal(Dicti /// /// array indicating zero signal /// a list of erroneous segments - public static List DataIntegrityCheckForZeroSignal(double[] zeroSignalArray) + public static List DataIntegrityCheckForZeroSignal(double[] zeroSignalArray) { double tolerance = 0.00001; // init list of errors - var errors = new List(); + var errors = new List(); int arrayLength = zeroSignalArray.Length; bool allOk = true; - var error = new ErroneousIndexSegments(); + var error = new GapsAndJoins(); for (int i = 0; i < zeroSignalArray.Length; i++) { // if (zeroSignal index > 0), i.e. if signal == zero @@ -200,10 +260,10 @@ public static List DataIntegrityCheckForZeroSignal(doubl if (allOk) { allOk = false; - error = new ErroneousIndexSegments + error = new GapsAndJoins { StartPosition = i, - ErrorDescription = errorZeroSignal, + GapDescription = gapDescriptionZeroSignal, GapRendering = ConcatMode.TimedGaps, // all zero signal errors must be drawn as timed gaps }; } @@ -237,13 +297,13 @@ public static List DataIntegrityCheckForZeroSignal(doubl /// /// Dictionary of the currently calculated summary indices /// a list of erroneous segments - public static List DataIntegrityCheckIndices(Dictionary summaryIndices) + public static List DataIntegrityCheckIndexValues(Dictionary summaryIndices) { int errorStart; double tolerance = 0.00001; // init list of errors - var errors = new List(); + var errors = new List(); bool allOk = true; bool zeroIndex = summaryIndices["AcousticComplexity"][0] < tolerance || @@ -254,8 +314,8 @@ public static List DataIntegrityCheckIndices(Dictionary< { allOk = false; errorStart = 0; - errors.Add(new ErroneousIndexSegments()); - errors[errors.Count - 1].ErrorDescription = invalidIndexValue; + errors.Add(new GapsAndJoins()); + errors[errors.Count - 1].GapDescription = gapDescriptionInvalidValue; errors[errors.Count - 1].StartPosition = errorStart; errors[errors.Count - 1].GapRendering = ConcatMode.TimedGaps; // all zero signal errors must be drawn as timed gaps } @@ -272,8 +332,8 @@ public static List DataIntegrityCheckIndices(Dictionary< if (allOk) { errorStart = i; - errors.Add(new ErroneousIndexSegments()); - errors[errors.Count - 1].ErrorDescription = invalidIndexValue; + errors.Add(new GapsAndJoins()); + errors[errors.Count - 1].GapDescription = gapDescriptionInvalidValue; errors[errors.Count - 1].StartPosition = errorStart; errors[errors.Count - 1].GapRendering = ConcatMode.TimedGaps; // all zero signal errors must be drawn as timed gaps } @@ -306,7 +366,7 @@ public static List DataIntegrityCheckIndices(Dictionary< /// Dictionary of the currently calculated spectral indices /// how to render the gap in image terms /// a list of erroneous segments - public static List DataIntegrityCheckSpectralIndices( + public static List DataIntegrityCheckSpectralIndices( Dictionary spectralIndices, ConcatMode gapRendering) { @@ -317,7 +377,7 @@ public static List DataIntegrityCheckSpectralIndices( var rowSums = MatrixTools.SumRows(spectralIndices["ACI"]); // init list of errors - var errors = new List(); + var gaps = new List(); bool allOk = true; @@ -325,10 +385,10 @@ public static List DataIntegrityCheckSpectralIndices( { allOk = false; errorStart = 0; - errors.Add(new ErroneousIndexSegments()); - errors[errors.Count - 1].ErrorDescription = invalidIndexValue; - errors[errors.Count - 1].StartPosition = errorStart; - errors[errors.Count - 1].GapRendering = gapRendering; + gaps.Add(new GapsAndJoins()); + gaps[gaps.Count - 1].GapDescription = gapDescriptionInvalidValue; + gaps[gaps.Count - 1].StartPosition = errorStart; + gaps[gaps.Count - 1].GapRendering = gapRendering; } int arrayLength = rowSums.Length; @@ -339,10 +399,10 @@ public static List DataIntegrityCheckSpectralIndices( if (allOk) { errorStart = i; - errors.Add(new ErroneousIndexSegments()); - errors[errors.Count - 1].ErrorDescription = invalidIndexValue; - errors[errors.Count - 1].StartPosition = errorStart; - errors[errors.Count - 1].GapRendering = gapRendering; + gaps.Add(new GapsAndJoins()); + gaps[gaps.Count - 1].GapDescription = gapDescriptionInvalidValue; + gaps[gaps.Count - 1].StartPosition = errorStart; + gaps[gaps.Count - 1].GapRendering = gapRendering; } allOk = false; @@ -351,17 +411,17 @@ public static List DataIntegrityCheckSpectralIndices( if (!allOk) { allOk = true; - errors[errors.Count - 1].EndPosition = i - 1; + gaps[gaps.Count - 1].EndPosition = i - 1; } } // end of loop // do final clean up if (!allOk) { - errors[errors.Count - 1].EndPosition = arrayLength - 1; + gaps[gaps.Count - 1].EndPosition = arrayLength - 1; } - return errors; + return gaps; } /// @@ -371,18 +431,18 @@ public static List DataIntegrityCheckSpectralIndices( /// The chromeless spectrogram to have segments drawn on it. /// list of erroneous segments /// spectrogram with erroneous segments marked. - public static Image DrawErrorSegments(Image bmp, List list) + public static Image DrawErrorSegments(Image bmp, List list) { - var newBmp = DrawErrorPatches(bmp, list, invalidIndexValue, bmp.Height, true); - newBmp = DrawErrorPatches(newBmp, list, errorZeroSignal, bmp.Height, true); + var newBmp = DrawGapPatches(bmp, list, gapDescriptionInvalidValue, bmp.Height, true); + newBmp = DrawGapPatches(newBmp, list, gapDescriptionZeroSignal, bmp.Height, true); // This one must be done last, in case user requests no gap rendering. // In this case, we have to cut out parts of image, starting at the end and working forward. - newBmp = DrawErrorPatches(newBmp, list, errorMissingData, bmp.Height, true); + newBmp = DrawGapPatches(newBmp, list, gapDescriptionMissingData, bmp.Height, true); return newBmp; } - public static Image DrawErrorPatches(Image bmp, List errorList, string errorDescription, int height, bool textInVerticalOrientation) + public static Image DrawGapPatches(Image bmp, List errorList, string errorDescription, int height, bool textInVerticalOrientation) { var g = Graphics.FromImage(bmp); @@ -390,17 +450,17 @@ public static Image DrawErrorPatches(Image bmp, List err for (int i = errorList.Count - 1; i >= 0; i--) { var error = errorList[i]; - if (error.ErrorDescription.Equals(errorDescription)) + if (error.GapDescription.Equals(errorDescription)) { // where user requests no gap rendering, have to remove gap portion of image. if (error.GapRendering.Equals(ConcatMode.NoGaps)) { - bmp = RemoveErrorPatch(bmp, error); + bmp = RemoveGapPatch(bmp, error); } else if (error.GapRendering.Equals(ConcatMode.EchoGaps)) { - bmp = DrawBlendedPatch(bmp, error); + bmp = DrawEchoPatch(bmp, error); } else { @@ -429,7 +489,7 @@ public Bitmap DrawErrorPatch(int height, bool textInVerticalOrientation) int fontVerticalPosition = (height / 2) - 10; var g = Graphics.FromImage(bmp); - g.Clear(this.ErrorDescription.Equals(errorMissingData) ? Color.LightGray : Color.HotPink); + g.Clear(this.GapDescription.Equals(gapDescriptionMissingData) ? Color.LightGray : Color.HotPink); // Draw error message and black cross over error patch only if is wider than arbitrary 10 pixels. if (width > 10) @@ -443,11 +503,11 @@ public Bitmap DrawErrorPatch(int height, bool textInVerticalOrientation) if (textInVerticalOrientation) { var drawFormat = new StringFormat(StringFormatFlags.DirectionVertical); - g.DrawString(" " + this.ErrorDescription, font, Brushes.Black, 2, 10, drawFormat); + g.DrawString(" " + this.GapDescription, font, Brushes.Black, 2, 10, drawFormat); } else { - g.DrawString(" " + this.ErrorDescription, font, Brushes.Black, 2, fontVerticalPosition); + g.DrawString(" " + this.GapDescription, font, Brushes.Black, 2, fontVerticalPosition); } } @@ -457,7 +517,7 @@ public Bitmap DrawErrorPatch(int height, bool textInVerticalOrientation) /// /// Cuts out gap portion of a spectrogram image /// - public static Image RemoveErrorPatch(Image source, ErroneousIndexSegments error) + public static Image RemoveGapPatch(Image source, GapsAndJoins error) { int ht = source.Height; int width = source.Width; @@ -478,17 +538,17 @@ public static Image RemoveErrorPatch(Image source, ErroneousIndexSegments error) g.DrawImage(source, gapStart, 0, srcRect, GraphicsUnit.Pixel); // draw separator at the join - //g.DrawLine(new Pen(Color.LightGray), gapStart, 0, gapStart, ht); + g.DrawLine(new Pen(Color.LightGray), gapStart - 1, 0, gapStart, 15); + g.DrawLine(new Pen(Color.LightGray), gapStart, 0, gapStart, 15); return newBmp; } /// - /// Draws a blended patch into a spectrogram image + /// Draws an echo patch into a spectrogram image /// - public static Image DrawBlendedPatch(Image source, ErroneousIndexSegments error) + public static Image DrawEchoPatch(Image source, GapsAndJoins error) { int ht = source.Height; - int width = source.Width; int gapStart = error.StartPosition; int gapEnd = error.EndPosition; @@ -503,7 +563,7 @@ public static Image DrawBlendedPatch(Image source, ErroneousIndexSegments error) } //g.DrawLine(new Pen(Color.LightGray), gapStart, 0, gapStart, ht); - g.DrawLine(new Pen(Color.LightGray), gapEnd, 0, gapEnd, ht); + g.DrawLine(new Pen(Color.LightGray), gapEnd, 0, gapEnd, 15); return source; } } diff --git a/AudioAnalysis/AudioAnalysisTools/Indices/IndexDisplay.cs b/AudioAnalysis/AudioAnalysisTools/Indices/IndexDisplay.cs index 1885d8403..14c30921e 100644 --- a/AudioAnalysis/AudioAnalysisTools/Indices/IndexDisplay.cs +++ b/AudioAnalysis/AudioAnalysisTools/Indices/IndexDisplay.cs @@ -103,7 +103,7 @@ public static Bitmap DrawImageOfSummaryIndices( TimeSpan indexCalculationDuration, DateTimeOffset? recordingStartDate, FileInfo sunriseDataFile = null, - List errors = null, + List errors = null, bool verbose = false) { // to translate past keys into current keys diff --git a/AudioAnalysis/AudioAnalysisTools/Indices/IndexMatrices.cs b/AudioAnalysis/AudioAnalysisTools/Indices/IndexMatrices.cs index 04daa0ba0..5d7f06eb1 100644 --- a/AudioAnalysis/AudioAnalysisTools/Indices/IndexMatrices.cs +++ b/AudioAnalysis/AudioAnalysisTools/Indices/IndexMatrices.cs @@ -67,6 +67,12 @@ public static List ConcatenateSummaryIndexFilesWithTimeCheck // Log.Debug("Reading of file started: " + files[i].FullName); var rowsOfCsvFile = Csv.ReadFromCsv(files[i], throwOnMissingField: false); + if (i > 0) + { + // mark a file join + rowsOfCsvFile.First().FileJoin = 1.0; // should be a boolean = true + } + summaryIndices.AddRange(rowsOfCsvFile); // track the row counts @@ -101,7 +107,9 @@ public static List ConcatenateSummaryIndexFilesWithTimeCheck // add in the missing summary index rows for (int j = 0; j < rows2Add; j++) { - summaryIndices.Add(new SummaryIndexValues()); + var vector = new SummaryIndexValues(); + vector.RecordingExists = 0.0; // should be a boolean = false; + summaryIndices.Add(vector); } } } @@ -123,13 +131,16 @@ public static List ConcatenateSummaryIndexFilesWithTimeCheck /// /// WARNING: THIS METHOD ONLY GETS FIXED LIST OF INDICES. + /// Also it requires every index to be of type DOUBLE even when htis is not appropriate. /// TODO: This needs to be generalized /// public static Dictionary GetDictionaryOfSummaryIndices(List summaryIndices) { var dictionary = new Dictionary { - { "ZeroSignal", summaryIndices.Select(x => x.ZeroSignal).ToArray() }, + { GapsAndJoins.KeyRecordingExists, summaryIndices.Select(x => x.RecordingExists).ToArray() }, + { GapsAndJoins.KeyFileJoin, summaryIndices.Select(x => x.FileJoin).ToArray() }, + { GapsAndJoins.KeyZeroSignal, summaryIndices.Select(x => x.ZeroSignal).ToArray() }, { "ClippingIndex", summaryIndices.Select(x => x.ClippingIndex).ToArray() }, { "BackgroundNoise", summaryIndices.Select(x => x.BackgroundNoise).ToArray() }, { "Snr", summaryIndices.Select(x => x.Snr).ToArray() }, @@ -146,16 +157,6 @@ public static Dictionary GetDictionaryOfSummaryIndices(List x.ThreeGramCount).ToArray() }, }; - // Generate the following index to keep track of missing recording files - // This index is only constructed when concatenating summary index files. - // It will be used to draw erroneous segments on Long Duration spectrograms - var list = new List(); - foreach (var siv in summaryIndices) - { - list.Add(siv.FileName == null ? 1.0 : 0.0); - } - - dictionary.Add("NoFile", list.ToArray()); return dictionary; } diff --git a/AudioAnalysis/AudioAnalysisTools/Indices/IndexProperties.cs b/AudioAnalysis/AudioAnalysisTools/Indices/IndexProperties.cs index fe166824f..32d68b36f 100644 --- a/AudioAnalysis/AudioAnalysisTools/Indices/IndexProperties.cs +++ b/AudioAnalysis/AudioAnalysisTools/Indices/IndexProperties.cs @@ -192,7 +192,7 @@ public string GetPlotAnnotation() /// See CLASS: DrawSummaryIndices /// METHOD: Bitmap ConstructVisualIndexImage(DataTable dt, string title, int timeScale, double[] order, bool doNormalise) /// - public Image GetPlotImage(double[] array, List errors = null) + public Image GetPlotImage(double[] array, List errors = null) { int dataLength = array.Length; string annotation = this.GetPlotAnnotation(); @@ -249,7 +249,7 @@ public Image GetPlotImage(double[] array, List errors = if ((errors != null) && (errors.Count > 0)) { bool verticalText = false; - foreach (ErroneousIndexSegments errorSegment in errors) + foreach (GapsAndJoins errorSegment in errors) { var errorBmp = errorSegment.DrawErrorPatch(trackHeight - 2, verticalText); if (errorBmp != null) diff --git a/AudioAnalysis/AudioAnalysisTools/Indices/SpectralIndexValues.cs b/AudioAnalysis/AudioAnalysisTools/Indices/SpectralIndexValues.cs new file mode 100644 index 000000000..c6a1c8d41 --- /dev/null +++ b/AudioAnalysis/AudioAnalysisTools/Indices/SpectralIndexValues.cs @@ -0,0 +1,243 @@ +// +// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group). +// + +namespace AudioAnalysisTools.Indices +{ + using System; + using System.Collections.Generic; + using System.Drawing; + using Acoustics.Shared; + using AnalysisBase.ResultBases; + using Fasterflect; + using TowseyLibrary; + + public class SpectralIndexValues : SpectralIndexBase + { + private static readonly Dictionary> CachedSelectorsInternal; + private static readonly Dictionary> CachedSettersInternal; + + static SpectralIndexValues() + { + var getters = ReflectionExtensions.GetGetters(); + + CachedSelectorsInternal = new Dictionary>(getters.Count); + foreach (var keyValuePair in getters) + { + // var key = keyValuePair.Key; + var selector = keyValuePair.Value; + + CachedSelectorsInternal.Add( + keyValuePair.Key, + spectrumBase => selector((SpectralIndexValues)spectrumBase)); + } + + var setters = ReflectionExtensions.GetSetters(); + + CachedSettersInternal = new Dictionary>(getters.Count); + foreach (var keyValuePair in setters) + { + // var key = keyValuePair.Key; + var setter = keyValuePair.Value; + + CachedSettersInternal.Add( + keyValuePair.Key, + (spectrumBase, value) => setter((SpectralIndexValues)spectrumBase, value)); + } + } + + public SpectralIndexValues() + { + // empty constructor important! + } + + public SpectralIndexValues(int spectrumLength, Dictionary indexProperties) + { + foreach (var cachedSetter in CachedSetters) + { + var defaultValue = 0.0; + + if (indexProperties.ContainsKey(cachedSetter.Key)) + { + var indexProperty = indexProperties[cachedSetter.Key]; + if (indexProperty.IsSpectralIndex) + { + defaultValue = indexProperty.DefaultValue; + } + } + + double[] initArray = (new double[spectrumLength]).FastFill(defaultValue); + + // WARNING: Potential throw site + // No need to give following warning because should call CheckExistenceOfSpectralIndexValues() method before entering loop. + // This prevents multiple warnings through loop. + this.SetPropertyValue(cachedSetter.Key, initArray); + } + } + + /// + /// Imports a dictionary of spectra. + /// Assumes `CheckExistenceOfSpectralIndexValues` has already been called. + /// Assumes frequency component is in fist index (i.e. frequency is rows) and time in second index (time is columns). + /// + /// + /// The dictionary to convert to spectral index base + /// + public static SpectralIndexValues[] ImportFromDictionary(Dictionary dictionaryOfSpectra) + { + return dictionaryOfSpectra.FromTwoDimensionalArray(CachedSetters, TwoDimensionalArray.ColumnMajor); + } + + /// + /// Used to check that the keys in the indexProperties dictionary correspond to Properties in the SpectralIndexValues class. + /// Call this method before entering a loop because do not want the error message at every iteration through loop. + /// + public static void CheckExistenceOfSpectralIndexValues(Dictionary indexProperties) + { + var siv = new SpectralIndexValues(); + double[] dummyArray = null; + + foreach (var kvp in indexProperties) + { + if (!kvp.Value.IsSpectralIndex) + { + continue; + } + + var success = siv.TrySetPropertyValue(kvp.Key, dummyArray); + if (!success) + { + LoggedConsole.WriteWarnLine( + "### WARNING: The PROPERTY <" + kvp.Key + "> does not exist in the SpectralIndexValues class!"); + } + } + } + + public static Dictionary> CachedSelectors + { + get + { + return CachedSelectorsInternal; + } + } + + public static Dictionary> CachedSetters + { + get + { + return CachedSettersInternal; + } + } + + public static Image CreateImageOfSpectralIndices(SpectralIndexValues spectralIndices) + { + string[] keys = { "ACI", "BGN", "CVR", "ENT", "EVN", "PMN", "POW", "RHZ", "RNG", "RPS", "RVT", "R3D", "SPT" }; + var images = new List(); + foreach (var key in keys) + { + double[] normalisedIndex = null; + + switch (key) + { + case "ACI": + normalisedIndex = DataTools.normalise(spectralIndices.ACI); + break; + case "BGN": + normalisedIndex = DataTools.normalise(spectralIndices.BGN); + break; + case "CVR": + normalisedIndex = DataTools.normalise(spectralIndices.CVR); + break; + case "ENT": + normalisedIndex = DataTools.normalise(spectralIndices.ENT); + break; + case "EVN": + normalisedIndex = DataTools.normalise(spectralIndices.EVN); + break; + case "PMN": + normalisedIndex = DataTools.normalise(spectralIndices.PMN); + break; + case "POW": + normalisedIndex = DataTools.normalise(spectralIndices.POW); + break; + case "RHZ": + normalisedIndex = DataTools.normalise(spectralIndices.RHZ); + break; + case "RNG": + normalisedIndex = DataTools.normalise(spectralIndices.RNG); + break; + case "RPS": + normalisedIndex = DataTools.normalise(spectralIndices.RPS); + break; + case "RVT": + normalisedIndex = DataTools.normalise(spectralIndices.RVT); + break; + case "R3D": + normalisedIndex = DataTools.normalise(spectralIndices.R3D); + break; + case "SPT": + normalisedIndex = DataTools.normalise(spectralIndices.SPT); + break; + default: + break; + } + + var image = GraphsAndCharts.DrawGraph(key, normalisedIndex, 100); + images.Add(image); + } + + var combinedImage = ImageTools.CombineImagesVertically(images.ToArray()); + return combinedImage; + } + + public double[] ACI { get; set; } + + public double[] BGN { get; set; } + + public double[] CVR { get; set; } + + public double[] DIF { get; set; } + + public double[] ENT { get; set; } + + public double[] EVN { get; set; } + + /// + /// PMN = Power Minus Noise. + /// PMN is measured in decibels but should replace POW as the average decibel spectrogram. + /// PMN calculates the average decibel spectrogram correctly. + /// + public double[] PMN { get; set; } + + /// + /// The POW spectral index should eventually be depracated. + /// It is derived from an incorrect way of averaging decibel values + /// + public double[] POW { get; set; } + + // Spectral Ridges Horizontal + public double[] RHZ { get; set; } + + // Spectral Ridges Vertical + public double[] RVT { get; set; } + + // Spectral Ridges Positive slope + public double[] RPS { get; set; } + + // Spectral Ridges Negative Slope + public double[] RNG { get; set; } + + // Sum of Spectral Ridges in Horizontal, postive and neg slope directions (RHZ+RPS+RNG) + public double[] R3D { get; set; } + + // Spectral Peak Tracks + public double[] SPT { get; set; } + + public double[] SUM { get; set; } + + public override Dictionary> GetSelectors() + { + return CachedSelectors; + } + } +} \ No newline at end of file diff --git a/AudioAnalysis/AudioAnalysisTools/Indices/SummaryIndexValues.cs b/AudioAnalysis/AudioAnalysisTools/Indices/SummaryIndexValues.cs index 4aa7e355f..537b5da8f 100644 --- a/AudioAnalysis/AudioAnalysisTools/Indices/SummaryIndexValues.cs +++ b/AudioAnalysis/AudioAnalysisTools/Indices/SummaryIndexValues.cs @@ -7,8 +7,6 @@ namespace AudioAnalysisTools.Indices { using System; using System.Collections.Generic; - using System.Drawing; - using Acoustics.Shared; using AnalysisBase.ResultBases; using AudioAnalysisTools.StandardSpectrograms; @@ -73,10 +71,10 @@ public IndexCalculateResult( /// public class SummaryIndexValues : SummaryIndexBase { - static SummaryIndexValues() - { - CachedSelectors = ReflectionExtensions.GetGetters(); - } + //static SummaryIndexValues() + //{ + // CachedSelectors = ReflectionExtensions.GetGetters(); + //} /// /// Initializes a new instance of the class. @@ -87,9 +85,15 @@ public SummaryIndexValues() // serialization entry this.BackgroundNoise = -100; this.AvgSignalAmplitude = -100; + + // Ideally the following two values should be booleans but + // the method IndexMatrices.GetDictionaryOfSummaryIndices() does not permit. This is a TODO sometime! + this.FileJoin = 0; // false + this.RecordingExists = 1; // true } public SummaryIndexValues(TimeSpan wavDuration, Dictionary indexProperties) + : this() { this.SegmentDurationSeconds = wavDuration.TotalSeconds; @@ -108,10 +112,16 @@ public SummaryIndexValues(TimeSpan wavDuration, Dictionary - /// Gets or sets the NoFile Index. - /// This is used only when concatenating index files. It helps to keep track of missing files. + /// Gets or sets a value indicating whether a Recording exists for this minute. + /// This is used only when concatenating index files. It helps to keep track of missing data. + /// + public double RecordingExists { get; set; } + + /// + /// Gets or sets a value indicating whether there is a File Join at this minute. + /// This is used only when concatenating index files. It helps to keep track of the source recordings. /// - public double NoFile { get; set; } + public double FileJoin { get; set; } public double ZeroSignal { get; set; } @@ -181,233 +191,4 @@ public SummaryIndexValues(TimeSpan wavDuration, Dictionary> CachedSelectors { get; set; } } - - public class SpectralIndexValues : SpectralIndexBase - { - private static readonly Dictionary> CachedSelectorsInternal; - private static readonly Dictionary> CachedSettersInternal; - - static SpectralIndexValues() - { - var getters = ReflectionExtensions.GetGetters(); - - CachedSelectorsInternal = new Dictionary>(getters.Count); - foreach (var keyValuePair in getters) - { - // var key = keyValuePair.Key; - var selector = keyValuePair.Value; - - CachedSelectorsInternal.Add( - keyValuePair.Key, - spectrumBase => selector((SpectralIndexValues)spectrumBase)); - } - - var setters = ReflectionExtensions.GetSetters(); - - CachedSettersInternal = new Dictionary>(getters.Count); - foreach (var keyValuePair in setters) - { - // var key = keyValuePair.Key; - var setter = keyValuePair.Value; - - CachedSettersInternal.Add( - keyValuePair.Key, - (spectrumBase, value) => setter((SpectralIndexValues)spectrumBase, value)); - } - } - - public SpectralIndexValues() - { - // empty constructor important! - } - - public SpectralIndexValues(int spectrumLength, Dictionary indexProperties) - { - foreach (var cachedSetter in CachedSetters) - { - var defaultValue = 0.0; - - if (indexProperties.ContainsKey(cachedSetter.Key)) - { - var indexProperty = indexProperties[cachedSetter.Key]; - if (indexProperty.IsSpectralIndex) - { - defaultValue = indexProperty.DefaultValue; - } - } - - double[] initArray = (new double[spectrumLength]).FastFill(defaultValue); - - // WARNING: Potential throw site - // No need to give following warning because should call CheckExistenceOfSpectralIndexValues() method before entering loop. - // This prevents multiple warnings through loop. - this.SetPropertyValue(cachedSetter.Key, initArray); - } - } - - /// - /// Imports a dictionary of spectra. - /// Assumes `CheckExistenceOfSpectralIndexValues` has already been called. - /// Assumes frequency component is in fist index (i.e. frequency is rows) and time in second index (time is columns). - /// - /// - /// The dictionary to convert to spectral index base - /// - public static SpectralIndexValues[] ImportFromDictionary(Dictionary dictionaryOfSpectra) - { - return dictionaryOfSpectra.FromTwoDimensionalArray(CachedSetters, TwoDimensionalArray.Transpose); - } - - /// - /// Used to check that the keys in the indexProperties dictionary correspond to Properties in the SpectralIndexValues class. - /// Call this method before entering a loop because do not want the error message at every iteration through loop. - /// - public static void CheckExistenceOfSpectralIndexValues(Dictionary indexProperties) - { - var siv = new SpectralIndexValues(); - double[] dummyArray = null; - - foreach (var kvp in indexProperties) - { - if (!kvp.Value.IsSpectralIndex) - { - continue; - } - - var success = siv.TrySetPropertyValue(kvp.Key, dummyArray); - if (!success) - { - LoggedConsole.WriteWarnLine( - "### WARNING: The PROPERTY <" + kvp.Key + "> does not exist in the SpectralIndexValues class!"); - } - } - } - - public static Dictionary> CachedSelectors - { - get - { - return CachedSelectorsInternal; - } - } - - public static Dictionary> CachedSetters - { - get - { - return CachedSettersInternal; - } - } - - public static Image CreateImageOfSpectralIndices(SpectralIndexValues spectralIndices) - { - string[] keys = { "ACI", "BGN", "CVR", "ENT", "EVN", "PMN", "POW", "RHZ", "RNG", "RPS", "RVT", "R3D", "SPT" }; - var images = new List(); - foreach (var key in keys) - { - double[] normalisedIndex = null; - - switch (key) - { - case "ACI": - normalisedIndex = DataTools.normalise(spectralIndices.ACI); - break; - case "BGN": - normalisedIndex = DataTools.normalise(spectralIndices.BGN); - break; - case "CVR": - normalisedIndex = DataTools.normalise(spectralIndices.CVR); - break; - case "ENT": - normalisedIndex = DataTools.normalise(spectralIndices.ENT); - break; - case "EVN": - normalisedIndex = DataTools.normalise(spectralIndices.EVN); - break; - case "PMN": - normalisedIndex = DataTools.normalise(spectralIndices.PMN); - break; - case "POW": - normalisedIndex = DataTools.normalise(spectralIndices.POW); - break; - case "RHZ": - normalisedIndex = DataTools.normalise(spectralIndices.RHZ); - break; - case "RNG": - normalisedIndex = DataTools.normalise(spectralIndices.RNG); - break; - case "RPS": - normalisedIndex = DataTools.normalise(spectralIndices.RPS); - break; - case "RVT": - normalisedIndex = DataTools.normalise(spectralIndices.RVT); - break; - case "R3D": - normalisedIndex = DataTools.normalise(spectralIndices.R3D); - break; - case "SPT": - normalisedIndex = DataTools.normalise(spectralIndices.SPT); - break; - default: - break; - } - - var image = GraphsAndCharts.DrawGraph(key, normalisedIndex, 100); - images.Add(image); - } - - var combinedImage = ImageTools.CombineImagesVertically(images.ToArray()); - return combinedImage; - } - - public double[] ACI { get; set; } - - public double[] BGN { get; set; } - - public double[] CVR { get; set; } - - public double[] DIF { get; set; } - - public double[] ENT { get; set; } - - public double[] EVN { get; set; } - - /// - /// PMN = Power Minus Noise. - /// PMN is measured in decibels but should replace POW as the average decibel spectrogram. - /// PMN calculates the average decibel spectrogram correctly. - /// - public double[] PMN { get; set; } - - /// - /// The POW spectral index should eventually be depracated. - /// It is derived from an incorrect way of averaging decibel values - /// - public double[] POW { get; set; } - - // Spectral Ridges Horizontal - public double[] RHZ { get; set; } - - // Spectral Ridges Vertical - public double[] RVT { get; set; } - - // Spectral Ridges Positive slope - public double[] RPS { get; set; } - - // Spectral Ridges Negative Slope - public double[] RNG { get; set; } - - // Sum of Spectral Ridges in Horizontal, postive and neg slope directions (RHZ+RPS+RNG) - public double[] R3D { get; set; } - - // Spectral Peak Tracks - public double[] SPT { get; set; } - - public double[] SUM { get; set; } - - public override Dictionary> GetSelectors() - { - return CachedSelectors; - } - } } diff --git a/AudioAnalysis/AudioAnalysisTools/LongDurationSpectrograms/LDSpectrogramRGB.cs b/AudioAnalysis/AudioAnalysisTools/LongDurationSpectrograms/LDSpectrogramRGB.cs index fda99bc09..89c8de7c0 100644 --- a/AudioAnalysis/AudioAnalysisTools/LongDurationSpectrograms/LDSpectrogramRGB.cs +++ b/AudioAnalysis/AudioAnalysisTools/LongDurationSpectrograms/LDSpectrogramRGB.cs @@ -295,7 +295,7 @@ public int YInterval // mark 1 kHz intervals /// public Dictionary IndexStats { get; private set; } - public List ErroneousSegments { get; private set; } + public List ErroneousSegments { get; private set; } /// /// Gets or sets a file from which can be obtained information about sunrise and sunset times for the recording site. @@ -629,7 +629,7 @@ public Image DrawFalseColourSpectrogramChromeless(string colorMode, string color bool errorsExist = (this.ErroneousSegments != null) && (this.ErroneousSegments.Count > 0); if (errorsExist) { - bmp = ErroneousIndexSegments.DrawErrorSegments(bmp, this.ErroneousSegments); + bmp = GapsAndJoins.DrawErrorSegments(bmp, this.ErroneousSegments); } return bmp; @@ -1382,7 +1382,7 @@ public static Tuple[] DrawSpectrogramsFromSpectralIndices( Dictionary indexStatistics = null, SiteDescription siteDescription = null, FileInfo sunriseDataFile = null, - List segmentErrors = null, + List segmentErrors = null, ImageChrome imageChrome = ImageChrome.With, bool verbose = false) { diff --git a/AudioAnalysis/AudioAnalysisTools/LongDurationSpectrograms/LDSpectrogramStitching.cs b/AudioAnalysis/AudioAnalysisTools/LongDurationSpectrograms/LDSpectrogramStitching.cs index dedabbaf1..0c06039e1 100644 --- a/AudioAnalysis/AudioAnalysisTools/LongDurationSpectrograms/LDSpectrogramStitching.cs +++ b/AudioAnalysis/AudioAnalysisTools/LongDurationSpectrograms/LDSpectrogramStitching.cs @@ -12,9 +12,7 @@ namespace AudioAnalysisTools.LongDurationSpectrograms using Acoustics.Shared; using Acoustics.Shared.Csv; using AnalysisBase.ResultBases; - using AudioAnalysisTools.StandardSpectrograms; - using Indices; using TowseyLibrary; @@ -84,7 +82,7 @@ public static void DrawSpectralIndexFiles( DirectoryInfo opDir, SiteDescription siteDescription, FileInfo sunriseDataFile = null, - List segmentErrors = null, + List segmentErrors = null, bool verbose = false) { // derive new indices such as sqrt(POW), NCDI etc -- main reason for this is to view what their distributions look like. @@ -99,7 +97,7 @@ public static void DrawSpectralIndexFiles( var indexDistributions = IndexDistributions.WriteSpectralIndexDistributionStatistics(dictionary, opDir, opFileStem); - SummaryIndexBase[] summaryIndices = null; + //SummaryIndexBase[] summaryIndices = null; string analysisType = "Towsey.Acoustic"; Tuple[] tuple = LDSpectrogramRGB.DrawSpectrogramsFromSpectralIndices( @@ -111,7 +109,7 @@ public static void DrawSpectralIndexFiles( opFileStem, analysisType, dictionary, - summaryIndices, + null, //summaryIndices, indexDistributions, siteDescription, sunriseDataFile, @@ -164,7 +162,7 @@ public static void DrawSummaryIndexFiles( DirectoryInfo opDir, SiteDescription siteDescription, FileInfo sunriseDatafile = null, - List erroneousSegments = null, // info if have fatal errors i.e. no signal + List erroneousSegments = null, // info if have fatal errors i.e. no signal bool verbose = false) { var dto = (DateTimeOffset)indexGenerationData.RecordingStartDate; @@ -247,7 +245,7 @@ public static Dictionary ConcatenateAllSummaryIndexFiles( // check length of data and make adjustments if required. // NOTHING done with this info at the moment. Could be used to truncate data to 24 hours. - int totalRowMinutes = (int)Math.Round(summaryIndices.Count() * indexResolution.TotalMinutes); + //int totalRowMinutes = (int)Math.Round(summaryIndices.Count() * indexResolution.TotalMinutes); // write out the list of data file names to JSON file. var arrayOfFileNames = summaryIndices.Select(x => x.FileName).ToArray(); @@ -264,7 +262,8 @@ public static Dictionary ConcatenateAllSummaryIndexFiles( var indicesCsvfile = new FileInfo(indicesFile); Csv.WriteToCsv(indicesCsvfile, summaryIndices); - // Put SUMMARY indices into dictionary. ################# WARNING: THIS METHOD ONLY GETS A "HARD CODED" LIST OF SUMMARY INDICES. See the method. + // Put SUMMARY indices into dictionary. TODO need to generalise the following method + // ################# WARNING: THIS METHOD ONLY GETS A "HARD CODED" LIST OF SUMMARY INDICES. See the method. var dictionaryOfSummaryIndices = IndexMatrices.GetDictionaryOfSummaryIndices(summaryIndices); // return the dictionary - it will be used later to produce an index tracks image. @@ -348,7 +347,7 @@ public static void StitchPartialSpectrograms() //###################################################### string[] fileEntries = Directory.GetFiles(inputDirectory.FullName); - List images = new List(); + var images = new List(); bool interpolateSpacer = true; var imagePair = new Image[2];