/// <summary> /// Joins summary indices csv files together. /// This method merges ALL the passed files of acoustic indices /// It is assumed you are concatenating a sequence of consecutive short recordings. /// </summary> public static List <SummaryIndexValues> ConcatenateAllSummaryIndexFiles( FileInfo[] summaryIndexFiles, DirectoryInfo opDir, IndexGenerationData indexGenerationData, string outputFileBaseName) { var indexResolution = indexGenerationData.IndexCalculationDuration; var summaryIndices = IndexMatrices.ConcatenateSummaryIndexFilesWithTimeCheck(summaryIndexFiles, indexResolution); if (summaryIndices.Count == 0) { LoggedConsole.WriteErrorLine("WARNING: LDSpectrogramStitching.ConcatenateAllSummaryIndexFiles(): Empty List of SUMMARY indices returned!"); return(null); } // 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); // write out the list of data file names to JSON file. var arrayOfFileNames = summaryIndices.Select(x => x.FileName).ToArray(); var path = FilenameHelpers.AnalysisResultPath(opDir, outputFileBaseName, "FileNames", "json"); Json.Serialise(new FileInfo(path), arrayOfFileNames); return(summaryIndices); }
public void CompressIndexSpectrogramsAcceptsRoundingFuncTest() { var spectra = new Dictionary <string, double[, ]> { { "Test", new double[, ] { { 1, 2, 3, 4, 5 } } }, }; // the default rounding func (when param omitted) is floor var result = IndexMatrices.CompressIndexSpectrograms( spectra, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(30)); Assert.AreEqual(2, result.First().Value.Length); result = IndexMatrices.CompressIndexSpectrograms( spectra, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(30), Math.Ceiling); Assert.AreEqual(3, result.First().Value.Length); result = IndexMatrices.CompressIndexSpectrograms( spectra, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(30), d => Math.Round(d, MidpointRounding.AwayFromZero)); Assert.AreEqual(3, result.First().Value.Length); }
public void CompressIndexSpectrogramsBasicAverageTest() { // testing an average that occurs with three windows, each of 5 values, // with the last window being two values too long var someSpectra = new double[256, 13].ForEach((i, j) => i); var spectra = new Dictionary <string, double[, ]> { { "I_DO_NOT_EXIST", someSpectra }, }; var compressed = IndexMatrices.CompressIndexSpectrograms( spectra, 5.Seconds(), 1.Seconds(), d => Math.Round(d, MidpointRounding.AwayFromZero)); var compressedSpectra = compressed["I_DO_NOT_EXIST"]; var average = compressedSpectra.Average(); Assert.AreEqual(127.5, average, $"Expected total average to be -100 but it was {average}"); // and make sure every value is as we expect for (int j = 0; j < compressedSpectra.LastColumnIndex(); j++) { Assert.AreEqual(127.5, compressedSpectra.GetColumn(j).Average()); } }
public void CompressIndexSpectrogramsFillsAllValuesTest(double renderScale, int dataSize, string key) { var someSpectra = new double[256, dataSize].Fill(-100); var spectra = new Dictionary <string, double[, ]> { { key, someSpectra }, }; var compressed = IndexMatrices.CompressIndexSpectrograms( spectra, renderScale.Seconds(), 0.1.Seconds(), d => Math.Round(d, MidpointRounding.AwayFromZero)); // ReSharper disable once CompareOfFloatsByEqualityOperator (We are testing exact values) if (renderScale == 0.1) { // in cases where the scales are equal, the method should short circuit and // just return the same matrix. Assert.AreEqual(spectra, compressed); } var compressedSpectra = compressed[key]; var average = compressedSpectra.Average(); // this test is specifically testing whether the last column has the correct value var lastColumn = MatrixTools.GetColumn(compressedSpectra, compressedSpectra.LastColumnIndex()); var lastColumnAverage = lastColumn.Average(); Assert.AreEqual(-100, lastColumnAverage, 0.0000000001, $"Expected last column to have value -100 but it was {lastColumnAverage:R}"); Assert.AreEqual(-100, average, 0.0000000001, $"Expected total average to be -100 but it was {average:R}"); }
public void TestReadSpectralIndices() { var testSpectra = PathHelper.ResolveAssetPath("20160725_203006_continuous1__Towsey.Acoustic.ACI.csv"); var dir = PathHelper.TestResources.ToDirectoryEntry(); var matrix = IndexMatrices.ReadSpectralIndices( dir, "20160725_203006_continuous1", "Towsey.Acoustic", new[] { "ACI" }) .Single() .Value; Assert.AreEqual(256, matrix.GetLength(0)); Assert.AreEqual(30, matrix.GetLength(1)); Assert.AreEqual(0.462924678189025, matrix[255, 0]); Assert.AreEqual(0.42779069684277, matrix[254, 0]); Assert.AreEqual(0.46412042529103, matrix[255, 1]); Assert.AreEqual(0.444650614488611, matrix[254, 1]); var matrix2 = IndexMatrices.ReadSpectrogram(testSpectra.ToFileInfo(), out var binCount); matrix2 = MatrixTools.MatrixRotate90Anticlockwise(matrix2); var actualEnumerator = matrix2.GetEnumerator(); foreach (var expected in matrix) { actualEnumerator.MoveNext(); Assert.AreEqual(expected, (double)actualEnumerator.Current, 1E-14, $"delta: {expected - (double)actualEnumerator.Current}"); } }
public static Dictionary <string, double[]> ConvertToDictionaryOfSummaryIndices(List <SummaryIndexValues> summaryIndices) { // Now add in derived indices i.e. NCDI etc // Decided NOT to do this anymore // dictionaryOfSummaryIndices = IndexMatrices.AddDerivedIndices(dictionaryOfSummaryIndices); // 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. return(dictionaryOfSummaryIndices); }
public void CompressIndexSpectrogramsTest() { var spectra = new Dictionary <string, double[, ]> { { "Test", new double[, ] { { 1, 2, 3, 4 } } }, }; Assert.ThrowsException <ArgumentException>( () => IndexMatrices.CompressIndexSpectrograms( spectra, TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(80))); }
/// <summary> /// Compress high resolution indices - intended to be used when summarizing results. /// Summarize method not yet written. /// </summary> /// <param name="analysisResults"></param> /// <param name="indexResults"></param> /// <param name="highResolutionParsedConfiguration"></param> private void SummarizeHighResolutionIndices( AnalysisResult2 analysisResults, IndexCalculateResult[] indexResults, AcousticIndices.AcousticIndicesConfig highResolutionParsedConfiguration) { // NOW COMPRESS THE HI-RESOLUTION SPECTRAL INDICES TO LOW RES double lowResolution = highResolutionParsedConfiguration.GetDoubleOrNull("LowResolution") ?? 60.0; TimeSpan imageScale = TimeSpan.FromSeconds(lowResolution); TimeSpan dataScale = highResolutionParsedConfiguration.IndexCalculationDuration.Seconds(); var dictionaryOfSpectra = indexResults.Select(icr => icr.SpectralIndexValues).ToArray().ToTwoDimensionalArray(SpectralIndexValues.CachedSelectors, TwoDimensionalArray.Rotate90ClockWise); var spectralSelection = IndexMatrices.CompressIndexSpectrograms(dictionaryOfSpectra, imageScale, dataScale); // check that have not compressed matrices to zero length double[,] matrix = spectralSelection.First().Value; if (matrix.GetLength(0) == 0 || matrix.GetLength(1) == 0) { LoggedConsole.WriteErrorLine("WARNING: SPECTRAL INDEX MATRICES compressed to zero length!!!!!!!!!!!!!!!!!!!!!!!!"); } // Place LOW RESOLUTION SPECTRAL INDICES INTO analysisResults before returning. //int windowLength = (int?)highResolutionConfig[AnalysisKeys.FrameLength] ?? IndexCalculate.DefaultWindowSize; var indexProperties = highResolutionParsedConfiguration.IndexProperties; SpectralIndexValues.CheckExistenceOfSpectralIndexValues(indexProperties); // Init a new spectral indices class and populate it with spectral indices var spectrums = SpectralIndexValues.ImportFromDictionary(spectralSelection); for (int i = 0; i < spectrums.Length; i++) { spectrums[i].ResultStartSeconds = (analysisResults.SegmentStartOffset + TimeSpan.FromSeconds(i * lowResolution)).TotalSeconds; spectrums[i].SegmentDurationSeconds = imageScale.TotalSeconds; spectrums[i].FileName = ((SegmentSettings <object>)analysisResults.SegmentSettings).Segment.SourceMetadata.Identifier; } // assign to the analysis result analysisResults.SpectralIndices = spectrums; // TODO TODO TODO // ALSO NEED TO COMPRESS THE analysisResults.SummaryIndices To LOW RESOLUTION //var summaryIndexValues = new SummaryIndexValues(); //summaryIndexValues.BackgroundNoise = ETC; // ETC //var summaryiv = new SummaryIndexValues[1]; //summaryiv[0] = summaryIndexValues; //analysisResults.SummaryIndices = summaryiv; }
public void TestReadSpectrogram() { var testSpectra = PathHelper.ResolveAssetPath("20160725_203006_continuous1__Towsey.Acoustic.ACI.csv"); var matrix = IndexMatrices.ReadSpectrogram(testSpectra.ToFileInfo(), out var binCount); Assert.AreEqual(30, matrix.GetLength(0)); Assert.AreEqual(256, matrix.GetLength(1)); Assert.AreEqual(256, binCount); Assert.AreEqual(0.462924678189025, matrix[0, 0]); Assert.AreEqual(0.42779069684277, matrix[0, 1]); Assert.AreEqual(0.46412042529103, matrix[1, 0]); Assert.AreEqual(0.444650614488611, matrix[1, 1]); }
// ############################################################################################################## // ######################### METHODS FOR STITCHING TNC - EDDIE GAME's DATA // ######################### CONCATENATE EVERYTHING /// <summary> /// RECENT METHOD TO CONCATENATE Spectral INDEX.CSV FILES - August 2015. Revised Septermber 2016 /// Was written to deal with EDDIE GAME PNG data where the files to be concatenated are all in one top level directory. /// This method merges all files of spectral indices in the passed directories. /// The total length of the concatenated files can exceed 24 hours - limited by memory! /// </summary> public static Dictionary <string, double[, ]> ConcatenateAllSpectralIndexFiles(DirectoryInfo[] directories, string[] keys, IndexGenerationData indexGenerationData) { string analysisType = "Towsey.Acoustic"; var dictionaryOfSpectralIndices = IndexMatrices.GetSpectralIndexFilesAndConcatenate(directories, analysisType, keys, indexGenerationData, true); if (dictionaryOfSpectralIndices.Count == 0) { LoggedConsole.WriteErrorLine("WARNING from method LDSpectrogramStitching.ConcatenateSpectralIndexFiles() !!!"); LoggedConsole.WriteErrorLine(" An empty dictionary of spectral indices was returned !!! "); return(null); } // now add in derived indices i.e. POW, NCDI etc // dictionaryOfSpectralIndices = IndexMatrices.AddDerivedIndices(dictionaryOfSpectralIndices); return(dictionaryOfSpectralIndices); }
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> /// 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 (Dictionary <string, double[, ]>, Dictionary <string, IndexProperties>) LoadSpectra( AnalysisIoInputDirectory io, string analysisTag, string fileStem, LdSpectrogramConfig config, Dictionary <string, IndexProperties> indexProperties) { var keys = config.GetKeys().Distinct(); // add two necessary keys for zooming keys = keys.ToList().Append("SUM"); keys = keys.ToList().Append("DIF"); //add following matrix for possible subsequent BNG combination matrix. string comboIndexID = "RHZ"; keys = keys.ToList().Append(comboIndexID); var relevantIndexProperties = keys.ToDictionary(x => x, x => indexProperties[x]); Dictionary <string, double[, ]> spectra = IndexMatrices.ReadSpectralIndices( io.InputBase, fileStem, analysisTag, keys.ToArray()); /* * // THE FOLLOWING IDEA TO MAKE A COMBINED MATRIX OF BGN and RHZ was rejected. * // Anthony was concerned that the BGN matrix alone was not conveying much information at high resolutions. * // The idea was to combine another matrix with the BGN matrix. * // I tried three combinations, BGN-RHZ, BGN-OSC and BGN-SPT. None of them appeard to provide additional useful information at high resolution. * // The problem is that at high resolution, i.e. approaching 0.1s for an analysis unit, there are not many orthogonal features in a single frequency bin. * // Make a BNG COMBINATION Spectral matrix. * //var comboMatrix = MatrixTools.MaxOfTwoMatrices(spectra["BNG"], spectra["RHZ"]); * var comboMatrix = MatrixTools.AddMatricesWeightedSum(spectra["BGN"], 1.0, spectra[comboIndexID], 10.0); * spectra["BGN"] = comboMatrix; */ return(spectra, relevantIndexProperties); }
public void CompressIndexSpectrogramsFillsAllValuesTest(double renderScale, int dataSize) { var bgnSpectra = new double[256, dataSize].Fill(-100); var spectra = new Dictionary <string, double[, ]> { { "BGN", bgnSpectra }, }; var compressed = IndexMatrices.CompressIndexSpectrograms( spectra, renderScale.Seconds(), 0.1.Seconds(), d => Math.Round(d, MidpointRounding.AwayFromZero)); var bgn = compressed["BGN"]; var average = bgn.Average(); // this test is specifically testing whether the last column has the correct value var lastColumn = MatrixTools.GetColumn(bgn, bgn.LastColumnIndex()); Assert.AreEqual(-100, lastColumn.Average()); Assert.AreEqual(-100, average); }
public void TestWriteReadSpectrogram() { var random = TestHelpers.Random.GetRandom(); var testSpectra = random.NextMatrix(100, 50); var testFile = this.outputDirectory.CombineFile("test.matrix.csv"); Csv.WriteMatrixToCsv(testFile, testSpectra); var matrix = IndexMatrices.ReadSpectrogram(testFile, out var binCount); Assert.AreEqual(100, matrix.GetLength(0)); Assert.AreEqual(50, matrix.GetLength(1)); Assert.AreEqual(50, binCount); var actualEnumerator = matrix.GetEnumerator(); foreach (var expected in testSpectra) { actualEnumerator.MoveNext(); Assert.AreEqual(expected, (double)actualEnumerator.Current, 1E-14, $"delta: {expected - (double)actualEnumerator.Current}"); } }
} // method DrawRidgeSpectrograms() public static Image DrawRidgeSpectrograms(DirectoryInfo inputDirectory, FileInfo ipConfig, string fileStem, double scale, Dictionary <string, double[, ]> spectra = null) { string analysisType = AcousticIndices.TowseyAcoustic; //double backgroundFilter = 0.0; // 0.0 means small values are removed. double backgroundFilter = 0.75; // 0.75 means small values are accentuated. var dataScale = TimeSpan.FromSeconds(scale); Dictionary <string, IndexProperties> indexProperties = IndexProperties.GetIndexProperties(ipConfig); string[] keys = SpectralPeakTracks.GetDefaultRidgeKeys(); // read the csv files of the indices in keys array if (spectra == null) { //C:\SensorNetworks\Output\BIRD50\Training\ID0001\Towsey.Acoustic\ID0001__Towsey.Acoustic.ACI spectra = IndexMatrices.ReadSpectralIndices(inputDirectory, fileStem, analysisType, keys); } var cs1 = new LDSpectrogramRGB(minuteOffset: TimeSpan.Zero, xScale: dataScale, sampleRate: 22050, frameWidth: 512, colorMap: null) { FileName = fileStem, BackgroundFilter = backgroundFilter, IndexCalculationDuration = dataScale, }; // set the relevant dictionary of index properties cs1.SetSpectralIndexProperties(indexProperties); cs1.SpectrogramMatrices = spectra; if (cs1.GetCountOfSpectrogramMatrices() == 0) { LoggedConsole.WriteLine("WARNING: " + fileStem + ": No spectrogram matrices in the dictionary. Spectrogram files do not exist?"); return(null); } else if (cs1.GetCountOfSpectrogramMatrices() < keys.Length) { LoggedConsole.WriteLine("WARNING: " + fileStem + ": Missing indices in the dictionary. Some files do not exist?"); return(null); } var stringFont = Drawing.Tahoma8; // constants for labels Color[] color = { Color.Blue, Color.Green, Color.Red, Color.Orange, Color.Purple }; int labelYvalue = 3; int labelIndex = 0; Image <Rgb24> ridges = null; foreach (string key in keys) { Image <Rgb24> greyScaleImage = (Image <Rgb24>)cs1.DrawGreyscaleSpectrogramOfIndex(key); var pixelWidth = greyScaleImage.Width; int height = greyScaleImage.Height; ridges.Mutate(g2 => { if (ridges == null) { ridges = new Image <Rgb24>(pixelWidth, height); g2.Clear(Color.White); } g2.DrawText(key, stringFont, color[labelIndex], new PointF(0, labelYvalue)); }); labelYvalue += 10; //g1.DrawLine(new Pen(Color.Black, 1), 0, 0, width, 0);//draw upper boundary //g1.DrawLine(new Pen(Color.Black, 1), 0, 1, width, 1);//draw upper boundary // transfer greyscale image to colour image for (int y = 0; y < height; y++) { for (int x = 0; x < pixelWidth; x++) { var col = greyScaleImage[x, y]; if (col.G < 150) { ridges[x, y] = color[labelIndex]; } } } labelIndex += 1; } //foreach key return(ridges); } // method DrawRidgeSpectrograms()
} // method DrawAggregatedSpectrograms() public static Image <Rgb24> DrawGrayScaleSpectrograms(Arguments arguments, string fileStem, TimeSpan dataScale, Dictionary <string, double[, ]> spectra = null) { // default values int sampleRate = 22050; int frameWidth = 512; //double backgroundFilter = 0.0; // 0.0 means small values are removed. double backgroundFilter = 0.75; // 0.75 means small values are accentuated. string analysisType = AcousticIndices.TowseyAcoustic; string[] keys = LDSpectrogramRGB.GetArrayOfAvailableKeys(); var inputDirectory = arguments.InputDataDirectory; Dictionary <string, IndexProperties> indexProperties = IndexProperties.GetIndexProperties(arguments.IndexPropertiesConfig.ToFileInfo()); if (spectra == null) { spectra = IndexMatrices.ReadSpectralIndices(inputDirectory.ToDirectoryInfo(), fileStem, analysisType, keys); } // note: the spectra are oriented as per visual orientation, i.e. xAxis = time frames //int frameCount = spectra[keys[0]].GetLength(1); var cs1 = new LDSpectrogramRGB(minuteOffset: TimeSpan.Zero, xScale: dataScale, sampleRate: sampleRate, frameWidth: frameWidth, colorMap: null) { FileName = fileStem, BackgroundFilter = backgroundFilter, IndexCalculationDuration = dataScale, }; cs1.SetSpectralIndexProperties(indexProperties); // set the relevant dictionary of index properties cs1.SpectrogramMatrices = spectra; if (cs1.GetCountOfSpectrogramMatrices() == 0) { LoggedConsole.WriteLine("WARNING: " + fileStem + ": No spectrogram matrices in the dictionary. Spectrogram files do not exist?"); return(null); } var list = new List <Image <Rgb24> >(); var stringFont = Drawing.Arial14; foreach (string key in keys) { var image = cs1.DrawGreyscaleSpectrogramOfIndex(key); int width = 70; int height = image.Height; var label = new Image <Rgb24>(width, height); label.Mutate(g1 => { g1.Clear(Color.Gray); g1.DrawText(key, stringFont, Color.Black, new PointF(4, 30)); g1.DrawLine(new Pen(Color.Black, 1), 0, 0, width, 0); //draw upper boundary g1.DrawLine(new Pen(Color.Black, 1), 0, 1, width, 1); //draw upper boundary }); var imagearray = new[] { label, image }; var labelledImage = ImageTools.CombineImagesInLine(imagearray); list.Add(labelledImage); } //foreach key var combinedImage = ImageTools.CombineImagesVertically(list); return(combinedImage); } // method DrawGrayScaleSpectrograms()
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)); }
public void TestAnalyzeSr64000Recording() { int sampleRate = 64000; double duration = 420; // signal duration in seconds = 7 minutes int[] harmonics = { 500, 1000, 2000, 4000, 8000 }; var recording = DspFilters.GenerateTestRecording(sampleRate, duration, harmonics, WaveType.Cosine); string recordingName = "TemporaryRecording2"; var recordingPath = this.outputDirectory.CombineFile(recordingName + ".wav"); WavWriter.WriteWavFileViaFfmpeg(recordingPath, recording.WavReader); var fst = FreqScaleType.Linear125Octaves7Tones28Nyquist32000; var freqScale = new FrequencyScale(fst); /* * // draw the signal as spectrogram just for debugging purposes * // but can only draw a two minute spectrogram when sr=64000 - change duration above. * duration = 120; // if drawing sonogram, then set signal duration = 2 minutes * var sonogram = OctaveFreqScale.ConvertRecordingToOctaveScaleSonogram(recording, fst); * var sonogramImage = sonogram.GetImageFullyAnnotated(sonogram.GetImage(), "SPECTROGRAM", freqScale.GridLineLocations); * var outputImagePath = this.outputDirectory.CombineFile("SignalSpectrogram_OctaveFreqScale.png"); * sonogramImage.Save(outputImagePath.FullName); */ // Now need to rewrite the config file with new parameter settings var configPath = PathHelper.ResolveConfigFile("Towsey.Acoustic.yml"); // Convert the Config config to IndexCalculateConfig class and merge in the unnecesary parameters. //Config configuration = Yaml.Deserialise(configPath); //IndexCalculateConfig config = IndexCalculateConfig.GetConfig(configuration, false); // because of difficulties in dealing with Config config files, just edit the text file!!!!! var configLines = File.ReadAllLines(configPath.FullName); configLines[configLines.IndexOf(x => x.StartsWith("IndexCalculationDuration: "))] = "IndexCalculationDuration: 15.0"; //configLines[configLines.IndexOf(x => x.StartsWith("BgNoiseBuffer: "))] = "BgNoiseBuffer: 5.0"; configLines[configLines.IndexOf(x => x.StartsWith("FrequencyScale: Linear"))] = "FrequencyScale: " + fst; // the is the only octave scale currently functioning for IndexCalculate class configLines[configLines.IndexOf(x => x.StartsWith("FrameLength"))] = $"FrameLength: {freqScale.WindowSize}"; configLines[configLines.IndexOf(x => x.StartsWith("ResampleRate: "))] = "ResampleRate: 64000"; // write the edited Config file to temporary output directory var newConfigPath = this.outputDirectory.CombineFile("Towsey.Acoustic.yml"); File.WriteAllLines(newConfigPath.FullName, configLines); PathHelper.ResolveConfigFile("IndexPropertiesConfig.yml").CopyTo(this.outputDirectory.CombineFile("IndexPropertiesConfig.yml").FullName); var arguments = new AnalyseLongRecording.Arguments { Source = recordingPath, Config = newConfigPath.FullName, Output = this.outputDirectory, MixDownToMono = true, Parallel = !Debugger.IsAttached, }; AnalyseLongRecording.Execute(arguments); var resultsDirectory = this.outputDirectory.Combine("Towsey.Acoustic"); var listOfFiles = resultsDirectory.EnumerateFiles().ToArray(); Assert.AreEqual(19, listOfFiles.Length); var csvCount = listOfFiles.Count(f => f.Name.EndsWith(".csv")); Assert.AreEqual(15, csvCount); var jsonCount = listOfFiles.Count(f => f.Name.EndsWith(".json")); Assert.AreEqual(2, jsonCount); var pngCount = listOfFiles.Count(f => f.Name.EndsWith(".png")); Assert.AreEqual(2, pngCount); var bgnFile = resultsDirectory.CombineFile(recordingName + "__Towsey.Acoustic.BGN.csv"); double[,] actualBgn = Csv.ReadMatrixFromCsv <double>(bgnFile, TwoDimensionalArray.None); var expectedSpectrumFile = PathHelper.ResolveAsset("LongDuration", "BgnMatrix.OctaveScale.csv"); // uncomment the following line when first produce the array // bgnFile.CopyTo(expectedSpectrumFile.FullName); // compare actual BGN file with expected file. var expectedBgn = Csv.ReadMatrixFromCsv <double>(expectedSpectrumFile, TwoDimensionalArray.None); CollectionAssert.That.AreEqual(expectedBgn, actualBgn, 0.000_000_001); var array = MatrixTools.GetRow(actualBgn, 0); Assert.AreEqual(28, actualBgn.RowLength()); Assert.AreEqual(256, array.Length); // draw array just to check peaks are in correct places - just for debugging purposes var ldsBgnSpectrumFile = this.outputDirectory.CombineFile("Spectrum2.png"); GraphsAndCharts.DrawGraph(array, "LD BGN SPECTRUM Octave", ldsBgnSpectrumFile); // ########################################## // SECOND part of test is to create the LD spectrograms because they are not created when IndexCalcDuration < 60 seconds // first read in the index generation data var icdPath = resultsDirectory.CombineFile(recordingName + "__IndexGenerationData.json"); var indexConfigData = Json.Deserialize <IndexGenerationData>(icdPath); var indexPropertiesConfig = PathHelper.ResolveConfigFile("IndexPropertiesConfig.yml"); var ldSpectrogramConfigFile = PathHelper.ResolveConfigFile("SpectrogramFalseColourConfig.yml"); var ldSpectrogramConfig = LdSpectrogramConfig.ReadYamlToConfig(ldSpectrogramConfigFile); ldSpectrogramConfig.FreqScale = fst.ToString(); // finally read in the dictionary of spectra string analysisType = "Towsey.Acoustic"; var keys = LDSpectrogramRGB.GetArrayOfAvailableKeys(); var dictionaryOfSpectra = IndexMatrices.ReadSpectralIndices(resultsDirectory, recordingName, analysisType, keys); LDSpectrogramRGB.DrawSpectrogramsFromSpectralIndices( inputDirectory: resultsDirectory, outputDirectory: resultsDirectory, ldSpectrogramConfig: ldSpectrogramConfig, indexPropertiesConfigPath: indexPropertiesConfig, indexGenerationData: indexConfigData, basename: recordingName, analysisType: analysisType, indexSpectrograms: dictionaryOfSpectra); // test number of images - should now be 23 listOfFiles = resultsDirectory.EnumerateFiles().ToArray(); pngCount = listOfFiles.Count(f => f.Name.EndsWith(".png")); Assert.AreEqual(22, pngCount); var twoMapsImagePath = resultsDirectory.CombineFile(recordingName + "__2Maps.png"); var twoMapsImage = Image.Load <Rgb24>(twoMapsImagePath.FullName); // image is (7*4) * 652 Assert.AreEqual(28, twoMapsImage.Width); Assert.AreEqual(652, twoMapsImage.Height); }
} // method DrawAggregatedSpectrograms() public static Image DrawGrayScaleSpectrograms(Arguments arguments, string fileStem, TimeSpan dataScale, Dictionary <string, double[, ]> spectra = null) { int sampleRate = 22050; int frameWidth = 512; //double backgroundFilter = 0.0; // 0.0 means small values are removed. double backgroundFilter = 0.75; // 0.75 means small values are accentuated. string analysisType = AcousticIndices.TowseyAcoustic; string[] keys = LDSpectrogramRGB.GetArrayOfAvailableKeys(); //LoggedConsole.WriteLine("# Spectrogram Config file: " + arguments.SpectrogramConfigPath); //LoggedConsole.WriteLine("# Index Properties Config file: " + arguments.IndexPropertiesConfig); var inputDirectory = arguments.InputDataDirectory; Dictionary <string, IndexProperties> indexProperties = IndexProperties.GetIndexProperties(arguments.IndexPropertiesConfig.ToFileInfo()); if (spectra == null) { //C:\SensorNetworks\Output\BIRD50\Training\ID0001\Towsey.Acoustic\ID0001__Towsey.Acoustic.ACI spectra = IndexMatrices.ReadSpectralIndices(inputDirectory.ToDirectoryInfo(), fileStem, analysisType, keys); } // note: the spectra are oriented as per visual orientation, i.e. xAxis = time frames //int frameCount = spectra[keys[0]].GetLength(1); var cs1 = new LDSpectrogramRGB(minuteOffset: TimeSpan.Zero, xScale: dataScale, sampleRate: sampleRate, frameWidth: frameWidth, colourMap: null) { FileName = fileStem, BackgroundFilter = backgroundFilter, IndexCalculationDuration = dataScale, }; cs1.SetSpectralIndexProperties(indexProperties); // set the relevant dictionary of index properties cs1.SpectrogramMatrices = spectra; if (cs1.GetCountOfSpectrogramMatrices() == 0) { LoggedConsole.WriteLine("WARNING: " + fileStem + ": No spectrogram matrices in the dictionary. Spectrogram files do not exist?"); return(null); } List <Image> list = new List <Image>(); Font stringFont = new Font("Arial", 14); foreach (string key in keys) { var image = cs1.DrawGreyscaleSpectrogramOfIndex(key); int width = 70; int height = image.Height; var label = new Bitmap(width, height); var g1 = Graphics.FromImage(label); g1.Clear(Color.Gray); g1.DrawString(key, stringFont, Brushes.Black, new PointF(4, 30)); g1.DrawLine(new Pen(Color.Black), 0, 0, width, 0); //draw upper boundary g1.DrawLine(new Pen(Color.Black), 0, 1, width, 1); //draw upper boundary Image[] imagearray = { label, image }; var labelledImage = ImageTools.CombineImagesInLine(imagearray); list.Add(labelledImage); } //foreach key var combinedImage = ImageTools.CombineImagesVertically(list.ToArray()); return(combinedImage); } // method DrawGrayScaleSpectrograms()
public static void ConcatenateDays() { DirectoryInfo parentDir = new DirectoryInfo(@"C:\SensorNetworks\Output\Frommolt"); DirectoryInfo dataDir = new DirectoryInfo(parentDir + @"\AnalysisOutput\mono"); var imageDirectory = new DirectoryInfo(parentDir + @"\ConcatImageOutput"); //string indexPropertiesConfig = @"C:\Work\GitHub\audio-analysis\AudioAnalysis\AnalysisConfigFiles\IndexPropertiesConfigHiRes.yml"; DateTimeOffset?startDate = new DateTimeOffset(2012, 03, 29, 0, 0, 0, TimeSpan.Zero); DateTimeOffset?endDate = new DateTimeOffset(2012, 06, 20, 0, 0, 0, TimeSpan.Zero); var timeSpanOffsetHint = new TimeSpan(01, 0, 0); //string fileSuffix = @"2Maps.png"; //string fileSuffix = @"ACI-ENT-EVN.png"; // WARNING: POW was removed in December 2018 string fileSuffix = @"BGN-POW-EVN.png"; TimeSpan totalTimespan = (DateTimeOffset)endDate - (DateTimeOffset)startDate; int dayCount = totalTimespan.Days + 1; // assume last day has full 24 hours of recording available. bool verbose = true; if (verbose) { LoggedConsole.WriteLine("\n# Start date = " + startDate.ToString()); LoggedConsole.WriteLine("# End date = " + endDate.ToString()); LoggedConsole.WriteLine(string.Format("# Elapsed time = {0:f1} hours", dayCount * 24)); LoggedConsole.WriteLine("# Day count = " + dayCount + " (inclusive of start and end days)"); LoggedConsole.WriteLine("# Time Zone = " + timeSpanOffsetHint.ToString()); } //string dirMatch = "Monitoring_Rosin_2012*T*+0200_.merged.wav.channel_0.wav"; string stem = "Monitoring_Rosin_2012????T??0000+0200_.merged.wav.channel_"; string dirMatch = stem + "?.wav"; DirectoryInfo[] subDirectories = dataDir.GetDirectories(dirMatch, SearchOption.AllDirectories); string format = "yyyyMMdd"; string startDay = ((DateTimeOffset)startDate).ToString(format); //string fileMatch = stem + "?__" + fileSuffix; //FileInfo[] files = IndexMatrices.GetFilesInDirectories(subDirectories, fileMatch); // Sort the files by date and return as a dictionary: sortedDictionaryOfDatesAndFiles<DateTimeOffset, FileInfo> //var sortedDictionaryOfDatesAndFiles = FileDateHelpers.FilterFilesForDates(files, timeSpanOffsetHint); //following needed if a day is missing. int defaultDayWidth = 20; int defaultDayHeight = 300; Brush brush = Brushes.White; Font stringFont = new Font("Tahoma", 12); var list = new List <Image>(); // loop over days for (int d = 0; d < dayCount; d++) { Console.WriteLine(string.Format("Day {0} of {1} days", d, dayCount)); var thisday = ((DateTimeOffset)startDate).AddDays(d); string date = thisday.ToString(format); stem = "Monitoring_Rosin_" + date + "T??0000+0200_.merged.wav.channel_"; string fileMatch = stem + "?__" + fileSuffix; FileInfo[] files = IndexMatrices.GetFilesInDirectories(subDirectories, fileMatch); if (files.Length == 0) { Bitmap gapImage = new Bitmap(defaultDayWidth, defaultDayHeight); Graphics g5 = Graphics.FromImage(gapImage); g5.Clear(Color.Gray); g5.DrawString("Day", stringFont, brush, new PointF(2, 5)); g5.DrawString("missing", stringFont, brush, new PointF(2, 35)); list.Add(gapImage); continue; } // Sort the files by date and return as a dictionary: sortedDictionaryOfDatesAndFiles<DateTimeOffset, FileInfo> //var sortedDictionaryOfDatesAndFiles = FileDateHelpers.FilterFilesForDates(files, timeSpanOffsetHint); Image image = ConcatenateFourChannelImages(files, imageDirectory, fileSuffix, date); defaultDayHeight = image.Height; list.Add(image); } Image combinedImage = ImageTools.CombineImagesInLine(list); Bitmap labelImage1 = new Bitmap(combinedImage.Width, 24); Graphics g1 = Graphics.FromImage(labelImage1); g1.Clear(Color.Black); g1.DrawString(fileSuffix, stringFont, brush, new PointF(2, 2)); //labelImage1.Save(Path.Combine(imageDirectory.FullName, suffix1)); Graphics g = Graphics.FromImage(combinedImage); g.DrawImage(labelImage1, 0, 0); string fileName = string.Format(startDay + "." + fileSuffix); combinedImage.Save(Path.Combine(imageDirectory.FullName, fileName)); }
public static Image DrawIndexSpectrogramCommon( LdSpectrogramConfig config, IndexGenerationData indexGenerationData, Dictionary <string, IndexProperties> indexProperties, TimeSpan startTime, TimeSpan endTime, TimeSpan dataScale, TimeSpan imageScale, int imageWidth, Dictionary <string, double[, ]> spectra, string basename) { double scalingFactor = Math.Round(imageScale.TotalMilliseconds / dataScale.TotalMilliseconds); Contract.Requires(scalingFactor >= 1.0, $"Compression scale `{scalingFactor}`is invalid"); // calculate data duration from column count of abitrary matrix //TimeSpan dataDuration = TimeSpan.FromSeconds(matrix.GetLength(1) * dataScale.TotalSeconds); int columnCount = spectra.FirstValue().GetLength(1); var startIndex = (int)(startTime.Ticks / dataScale.Ticks); var endIndex = (int)(endTime.Ticks / dataScale.Ticks); Contract.Ensures(endIndex <= columnCount); // extract subset of target data var spectralSelection = new Dictionary <string, double[, ]>(); foreach (string key in spectra.Keys) { var matrix = spectra[key]; int rowCount = matrix.GetLength(0); spectralSelection[key] = MatrixTools.Submatrix(matrix, 0, startIndex, rowCount - 1, endIndex - 1); Contract.Ensures( spectralSelection[key].GetLength(1) == (endTime - startTime).Ticks / dataScale.Ticks, "The expected number of frames should be extracted."); } // compress spectrograms to correct scale if (scalingFactor > 1) { // we add rounding to the compression so that fractional pixels get rendered spectralSelection = IndexMatrices.CompressIndexSpectrograms( spectralSelection, imageScale, dataScale, d => Math.Round(d, MidpointRounding.AwayFromZero)); } else { // this else is unnecessary - completely defensive code Contract.Ensures(scalingFactor == 1); } // check that have not compressed matrices to zero length if (spectralSelection.FirstValue().GetLength(0) == 0 || spectralSelection.FirstValue().GetLength(1) == 0) { throw new InvalidOperationException("Spectral matrices compressed to zero size"); } // DEFINE the DEFAULT colour maps for the false-colour spectrograms // Then obtain values from spectrogramDrawingConfig. NOTE: WE REQUIRE LENGTH = 11 chars. string colorMap1 = "ACI-ENT-EVN"; if (config.ColorMap1 != null && config.ColorMap1.Length == 11) { colorMap1 = config.ColorMap1; } string colorMap2 = "BGN-PMN-EVN"; if (config.ColorMap2 != null && config.ColorMap2.Length == 11) { colorMap2 = config.ColorMap2; } double backgroundFilterCoeff = indexGenerationData.BackgroundFilterCoeff; // double colourGain = (double?)configuration.ColourGain ?? SpectrogramConstants.COLOUR_GAIN; // determines colour saturation var cs1 = new LDSpectrogramRGB(config, indexGenerationData, colorMap1) { FileName = basename, BackgroundFilter = backgroundFilterCoeff, }; cs1.SetSpectralIndexProperties(indexProperties); // set the relevant dictionary of index properties cs1.LoadSpectrogramDictionary(spectralSelection); // set up piecewise linear function to determine colour weights var logResolution = Math.Log(imageScale.TotalMilliseconds, 2); double upperResolution = Math.Log(32768, 2); double lowerResolution = Math.Log(256, 2); double range = upperResolution - lowerResolution; double blendWeight1; if (logResolution >= upperResolution) { blendWeight1 = 1.0; } else if (logResolution <= lowerResolution) { blendWeight1 = 0.0; } else { blendWeight1 = (logResolution - lowerResolution) / range; } double blendWeight2 = 1 - blendWeight1; //else if (imageScaleInMsPerPixel > 2000) //{ // blendWeight1 = 0.7; // blendWeight2 = 0.3; //} //else if (imageScaleInMsPerPixel > 1000) //{ // blendWeight1 = 0.3; // blendWeight2 = 0.7; //} //else if (imageScaleInMsPerPixel > 500) //{ // // > 0.5 seconds // blendWeight1 = 0.2; // blendWeight2 = 0.8; //} //else if (imageScaleInMsPerPixel > 300) //{ // // > 0.5 seconds // blendWeight1 = 0.1; // blendWeight2 = 0.9; //} var ldfcSpectrogram = cs1.DrawBlendedFalseColourSpectrogram(colorMap1, colorMap2, blendWeight1, blendWeight2); if (ldfcSpectrogram == null) { throw new InvalidOperationException("Null Image returned from DrawBlendedFalseColourSpectrogram"); } return(ldfcSpectrogram); }
public static void Execute(Arguments arguments) { var inputDirs = arguments.InputDataDirectories.Select(FileInfoExtensions.ToDirectoryInfo); var output = arguments.OutputDirectory.ToDirectoryInfo(); string date = "# DATE AND TIME: " + DateTime.Now; LoggedConsole.WriteLine("\n# DRAW an EASY IMAGE from consecutive days of SUMMARY INDICES in CSV files."); LoggedConsole.WriteLine("# IT IS ASSUMED THAT THE CSV files are already concatenated into 24 hour files."); LoggedConsole.WriteLine(date); LoggedConsole.WriteLine("# Summary Index.csv files are in directories:"); foreach (DirectoryInfo dir in inputDirs) { LoggedConsole.WriteLine(" {0}", dir.FullName); } LoggedConsole.WriteLine("# Output directory: " + output); if (arguments.StartDate == null) { LoggedConsole.WriteLine("# Start date = NULL (No argument provided). Will revise start date ...."); } else { LoggedConsole.WriteLine("# Start date = " + arguments.StartDate.ToString()); } if (arguments.EndDate == null) { LoggedConsole.WriteLine("# End date = NULL (No argument provided). Will revise end date ...."); } else { LoggedConsole.WriteLine("# End date = " + arguments.EndDate.ToString()); } LoggedConsole.WriteLine("# FILE FILTER = " + arguments.FileFilter); LoggedConsole.WriteLine(); // PATTERN SEARCH FOR SUMMARY INDEX FILES. //string pattern = "*__Towsey.Acoustic.Indices.csv"; FileInfo[] csvFiles = IndexMatrices.GetFilesInDirectories(inputDirs.ToArray(), arguments.FileFilter); //LoggedConsole.WriteLine("# Subdirectories Count = " + subDirectories.Length); LoggedConsole.WriteLine("# SummaryIndexFiles.csv Count = " + csvFiles.Length); if (csvFiles.Length == 0) { LoggedConsole.WriteErrorLine("\n\nWARNING from method DrawEasyImage.Execute():"); LoggedConsole.WriteErrorLine(" No SUMMARY index files were found."); LoggedConsole.WriteErrorLine(" RETURNING EMPTY HANDED!"); return; } // Sort the files by date and return as a dictionary: sortedDictionaryOfDatesAndFiles<DateTimeOffset, FileInfo> //var sortedDictionaryOfDatesAndFiles = LDSpectrogramStitching.FilterFilesForDates(csvFiles, arguments.TimeSpanOffsetHint); // calculate new start date if passed value = null. DateTimeOffset?startDate = arguments.StartDate; DateTimeOffset?endDate = arguments.EndDate; TimeSpan totalTimespan = (DateTimeOffset)endDate - (DateTimeOffset)startDate; int dayCount = totalTimespan.Days + 1; // assume last day has full 24 hours of recording available. LoggedConsole.WriteLine("\n# Start date = " + startDate.ToString()); LoggedConsole.WriteLine("# End date = " + endDate.ToString()); LoggedConsole.WriteLine(string.Format("# Elapsed time = {0:f1} hours", dayCount * 24)); LoggedConsole.WriteLine("# Day count = " + dayCount + " (inclusive of start and end days)"); LoggedConsole.WriteLine("# Time Zone = " + arguments.TimeSpanOffsetHint.ToString()); // create top level output directory if it does not exist. DirectoryInfo opDir = output; if (!opDir.Exists) { opDir.Create(); } // SET UP DEFAULT SITE LOCATION INFO -- DISCUSS IWTH ANTHONY // The following location data is used only to draw the sunrise/sunset tracks on images. double?latitude = null; double?longitude = null; var siteDescription = new SiteDescription(); siteDescription.SiteName = arguments.FileStemName; siteDescription.Latitude = latitude; siteDescription.Longitude = longitude; // the following required if drawing the index images FileInfo indexPropertiesConfig = null; // require IndexGenerationData and indexPropertiesConfig for drawing //indexGenerationData = IndexGenerationData.GetIndexGenerationData(csvFiles[0].Directory); indexPropertiesConfig = arguments.IndexPropertiesConfig.ToFileInfo(); Dictionary <string, IndexProperties> listOfIndexProperties = IndexProperties.GetIndexProperties(indexPropertiesConfig); Tuple <List <string>, List <double[]> > tuple = CsvTools.ReadCSVFile(csvFiles[0].FullName); var names = tuple.Item1; // default EASY indices int redID = 3; // backgroundNoise int grnID = 5; // avSNROfActiveframes int bluID = 7; // events per second string rep = @"bgn-avsnr-evn"; // ACI Ht Hpeaks EASY indices if (false) { redID = 11; // ACI grnID = 12; // Ht //bluID = 13; // HavgSp //bluID = 14; // Hvariance //bluID = 15; // Hpeaks bluID = 16; // Hcov //bluID = 7; // SPT rep = @"aci-ht-hcov"; //rep = @"aci-ht-spt"; } // LF, MF, HF if (true) { redID = 10; // LF grnID = 9; // MF bluID = 8; // HF rep = @"lf-mf-hf"; } IndexProperties redIndexProps = listOfIndexProperties[names[redID]]; IndexProperties grnIndexProps = listOfIndexProperties[names[grnID]]; IndexProperties bluIndexProps = listOfIndexProperties[names[bluID]]; int dayPixelHeight = 4; int rowCount = (dayPixelHeight * dayCount) + 35; // +30 for grid lines int colCount = 1440; var bitmap = new Image <Rgb24>(colCount, rowCount); var colour = Color.Yellow; int currentRow = 0; var oneDay = TimeSpan.FromHours(24); int graphWidth = colCount; int trackHeight = 20; var stringFont = Drawing.Arial8; string[] monthNames = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; // for drawing the y-axis scale int scaleWidth = trackHeight + 7; var yAxisScale = new Image <Rgb24>(scaleWidth, rowCount + (2 * trackHeight)); yAxisScale.Mutate(g => { g.Clear(Color.Black); // loop over days for (int d = 0; d < dayCount; d++) { var thisday = ((DateTimeOffset)startDate).AddDays(d); if (thisday.Day == 1) { int nextRow = currentRow + 1; for (int c = 0; c < colCount; c++) { bitmap[c, currentRow] = Color.Gray; bitmap[c, nextRow] = Color.Gray; } for (int c = 0; c < scaleWidth; c++) { yAxisScale[c, currentRow + trackHeight] = Color.Gray; yAxisScale[c, nextRow + trackHeight] = Color.Gray; } string month = monthNames[thisday.Month - 1]; if (thisday.Month == 1) // January { g.DrawText(thisday.Year.ToString(), stringFont, Color.White, new PointF(0, nextRow + trackHeight + 1)); //draw time g.DrawText(month, stringFont, Color.White, new PointF(1, nextRow + trackHeight + 11)); //draw time } else { g.DrawText(month, stringFont, Color.White, new PointF(1, nextRow + trackHeight + 1)); //draw time } currentRow += 2; } // get the exact date and time LoggedConsole.WriteLine($"READING DAY {d + 1} of {dayCount}: {thisday.ToString()}"); // CREATE DAY LEVEL OUTPUT DIRECTORY for this day string dateString = $"{thisday.Year}{thisday.Month:D2}{thisday.Day:D2}"; tuple = CsvTools.ReadCSVFile(csvFiles[d].FullName); var arrays = tuple.Item2; var redArray = arrays[redID]; var grnArray = arrays[grnID]; var bluArray = arrays[bluID]; // NormaliseMatrixValues the indices redArray = DataTools.NormaliseInZeroOne(redArray, redIndexProps.NormMin, redIndexProps.NormMax); grnArray = DataTools.NormaliseInZeroOne(grnArray, grnIndexProps.NormMin, grnIndexProps.NormMax); bluArray = DataTools.NormaliseInZeroOne(bluArray, bluIndexProps.NormMin, bluIndexProps.NormMax); for (int c = 0; c < colCount; c++) { for (int r = 0; r < dayPixelHeight; r++) { //transformedValue = Math.Sqrt(redArray[c]); var transformedValue = redArray[c] * redArray[c]; int redVal = (int)Math.Round(transformedValue * 255); if (redVal < 0) { redVal = 0; } else if (redVal > 255) { redVal = 255; } //transformedValue = Math.Sqrt(grnArray[c]); transformedValue = grnArray[c] * grnArray[c]; // square the value int grnVal = (int)Math.Round(transformedValue * 255); if (grnVal < 0) { grnVal = 0; } else if (grnVal > 255) { grnVal = 255; } //transformedValue = Math.Sqrt(bluArray[c]); transformedValue = bluArray[c] * bluArray[c]; // square the value int bluVal = (int)Math.Round(transformedValue * 255); if (bluVal < 0) { bluVal = 0; } else if (bluVal > 255) { bluVal = 255; } bitmap[c, currentRow + r] = Color.FromRgb((byte)redVal, (byte)grnVal, (byte)bluVal); } } // over all columns currentRow += dayPixelHeight; if (thisday.Day % 7 == 0) { for (int c = 0; c < colCount; c++) { bitmap[c, currentRow] = Color.Gray; } currentRow++; } } // over days }); // draw on civil dawn and dusk lines int startdayOfYear = ((DateTimeOffset)startDate).DayOfYear; int endDayOfYear = ((DateTimeOffset)endDate).DayOfYear; SunAndMoon.AddSunRiseSetLinesToImage(bitmap, arguments.BrisbaneSunriseDatafile.ToFileInfo(), startdayOfYear, endDayOfYear, dayPixelHeight); // add the time scales Image <Rgb24> timeBmp1 = ImageTrack.DrawTimeRelativeTrack(oneDay, graphWidth, trackHeight); var imageList = new [] { timeBmp1, bitmap, timeBmp1 }; Image <Rgb24> compositeBmp1 = (Image <Rgb24>)ImageTools.CombineImagesVertically(imageList); imageList = new [] { yAxisScale, compositeBmp1 }; Image <Rgb24> compositeBmp2 = (Image <Rgb24>)ImageTools.CombineImagesInLine(imageList); // indices used for image string indicesDescription = $"{redIndexProps.Name}|{grnIndexProps.Name}|{bluIndexProps.Name}"; string startString = $"{startDate.Value.Year}/{startDate.Value.Month}/{startDate.Value.Day}"; string endString = $"{endDate.Value.Year}/{endDate.Value.Month}/{endDate.Value.Day}"; string title = $"EASY: {arguments.FileStemName} From {startString} to {endString} Indices: {indicesDescription}"; Image <Rgb24> titleBar = ImageTrack.DrawTitleTrack(compositeBmp2.Width, trackHeight, title); imageList = new [] { titleBar, compositeBmp2 }; compositeBmp2 = (Image <Rgb24>)ImageTools.CombineImagesVertically(imageList); var outputFileName = Path.Combine(opDir.FullName, arguments.FileStemName + "." + rep + ".EASY.png"); compositeBmp2.Save(outputFileName); } // Execute()
} //Execute() public static Output GetInstanceRepresentations(Arguments arguments) { LoggedConsole.WriteLine("1. Read in all Instances and do feature extraction"); //################################### FEATURE WEIGHTS //TRY DIFFERENT WEIGHTINGS assuming following "SPT,RHZ,RVT,RPS,RNG"; bool doDeltaFeatures = false; double[] weights = { 1.0, 1.0, 0.8, 0.7, 0.7 }; double[] deltaWeights = { 1.0, 1.0, 0.8, 0.7, 0.7, 0.5, 0.4, 0.4, 0.2, 0.2 }; if (doDeltaFeatures) { weights = deltaWeights; } //MAX-POOLING for SPECTRAL REDUCTION // frequency bins used to reduce dimensionality of the 256 spectral values. int startBin = 8; int maxOf2Bin = 117; int maxOf3Bin = 160; int endBin = 200; double[] testArray = new double[256]; for (int i = 0; i < testArray.Length; i++) { testArray[i] = i; } double[] reducedArray = MaxPoolingLimited(testArray, startBin, maxOf2Bin, maxOf3Bin, endBin); int reducedSpectralLength = reducedArray.Length; LoggedConsole.WriteLine(" Reduced spectral length = " + reducedSpectralLength); int instanceCount = arguments.InstanceCount; int speciesCount = arguments.SpeciesCount; // READ IN THE SPECIES LABELS FILE AND SET UP THE DATA string[] fileID = new string[instanceCount]; int[] speciesID = new int[speciesCount]; ReadGlotinsSpeciesLabelFile(arguments.SpeciesLabelsFile, instanceCount, out fileID, out speciesID); // INIT array of species counts int[] instanceNumbersPerSpecies = new int[speciesCount]; // INIT array of frame counts int[] frameNumbersPerInstance = new int[instanceCount]; // initialise species description matrix var keyArray = FEATURE_KEYS.Split(','); int totalFeatureCount = keyArray.Length * reducedArray.Length; Console.WriteLine(" Total Feature Count = " + totalFeatureCount); if (doDeltaFeatures) { totalFeatureCount *= 2; LoggedConsole.WriteLine(" Total Delta Feature Count = " + totalFeatureCount); } // one matrix row per species double[,] instanceFeatureMatrix = new double[instanceCount, totalFeatureCount]; // loop through all all instances for (int j = 0; j < instanceCount; j++) { LoggedConsole.Write("."); int frameCount = 0; // get the spectral index files int speciesLabel = speciesID[j]; // dictionary to store feature spectra for instance. var aggreDictionary = new Dictionary <string, double[]>(); // dictionary to store delta spectra for instance. var deltaDictionary = new Dictionary <string, double[]>(); foreach (string key in keyArray) { string name = string.Format("{0}_Species{1:d2}.{2}.csv", fileID[j], speciesLabel, key); FileInfo file = new FileInfo(Path.Combine(arguments.InputDataDirectory.FullName, name)); if (file.Exists) { int binCount; double[,] matrix = IndexMatrices.ReadSpectrogram(file, out binCount); // create or get the array of spectral values. double[] aggregateArray = new double[reducedSpectralLength]; double[] deltaArray = new double[reducedSpectralLength]; double[] ipVector = MatrixTools.GetRow(matrix, 0); ipVector = DataTools.SubtractValueAndTruncateToZero(ipVector, arguments.BgnThreshold); reducedArray = MaxPoolingLimited(ipVector, startBin, maxOf2Bin, maxOf3Bin, endBin); double[] previousArray = reducedArray; // transfer spectral values to array. int rowCount = matrix.GetLength(0); //rowCount = (int)Math.Round(rowCount * 0.99); // ###################### USE ONLY 99% of instance //if (rowCount > 1200) rowCount = 1200; for (int r = 1; r < rowCount; r++) { ipVector = MatrixTools.GetRow(matrix, r); ipVector = DataTools.SubtractValueAndTruncateToZero(ipVector, arguments.BgnThreshold); reducedArray = MaxPoolingLimited(ipVector, startBin, maxOf2Bin, maxOf3Bin, endBin); for (int c = 0; c < reducedSpectralLength; c++) { aggregateArray[c] += reducedArray[c]; // Calculate the DELTA values TWO OPTIONS ################################################## double delta = Math.Abs(reducedArray[c] - previousArray[c]); //double delta = reducedArray[c] - previousArray[c]; //if (delta < 0.0) delta = 0.0; //double delta = previousArray[c]; //previous array - i.e. do not calculate delta deltaArray[c] += delta; } previousArray = reducedArray; } aggreDictionary[key] = aggregateArray; deltaDictionary[key] = deltaArray; frameCount = rowCount; } //if (file.Exists) } //foreach (string key in keyArray) instanceNumbersPerSpecies[speciesLabel - 1]++; frameNumbersPerInstance[j] += frameCount; // create the matrix of instance descriptions which consists of concatenated vectors // j = index of instance ID = row number int featureID = 0; foreach (string key in keyArray) { int featureOffset = featureID * reducedSpectralLength; for (int c = 0; c < reducedSpectralLength; c++) { // TWO OPTIONS: SUM OR AVERAGE ###################################### //instanceFeatureMatrix[j, featureOffset + c] = dictionary[key][c]; instanceFeatureMatrix[j, featureOffset + c] = aggreDictionary[key][c] / frameCount; } featureID++; } if (doDeltaFeatures) { foreach (string key in keyArray) { int featureOffset = featureID * reducedSpectralLength; for (int c = 0; c < reducedSpectralLength; c++) { // TWO OPTIONS: SUM OR AVERAGE ###################################### //instanceFeatureMatrix[j, featureOffset + c] = dictionary[key][c]; instanceFeatureMatrix[j, featureOffset + c] = deltaDictionary[key][c] / frameCount; } featureID++; } } // if doDeltaFeatures } // end for loop j over all instances LoggedConsole.WriteLine("Done!"); LoggedConsole.WriteLine("\nSum of species number array = " + instanceNumbersPerSpecies.Sum()); LoggedConsole.WriteLine("Sum of frame number array = " + frameNumbersPerInstance.Sum()); bool addLineNumbers = true; string countsArrayOutputFilePath = Path.Combine(arguments.OutputDirectory.FullName, "BirdClef50_training_Counts.txt"); FileTools.WriteArray2File(instanceNumbersPerSpecies, addLineNumbers, countsArrayOutputFilePath); // Initialise output data arrays Output output = new Output(); output.FileID = fileID; output.SpeciesID = speciesID; output.InstanceNumbersPerSpecies = instanceNumbersPerSpecies; output.ReducedSpectralLength = reducedSpectralLength; // INIT array of frame counts output.FrameNumbersPerInstance = frameNumbersPerInstance; // matrix: each row= one instance; each column = one feature output.InstanceFeatureMatrix = instanceFeatureMatrix; output.Weights = weights; return(output); } // GetInstanceRepresentations()