Skip to content

Commit

Permalink
writing all feature vectors to one file
Browse files Browse the repository at this point in the history
(cherry picked from commit 126a327)
  • Loading branch information
mkholghi authored and atruskie committed Jun 12, 2018
1 parent 15888da commit 8b19583
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 36 deletions.
184 changes: 162 additions & 22 deletions src/AnalysisPrograms/MahnooshSandpit.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
using System;
using System.Threading.Tasks;
// <copyright file="UnsupervisedFeatureLearningTest.cs" company="QutEcoacoustics">
// 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).
// </copyright>

namespace AnalysisPrograms
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Accord.MachineLearning;
using Accord.Math;
using Acoustics.Shared.Csv;
Expand All @@ -27,7 +30,7 @@ public void Execute(Arguments arguments)
{
LoggedConsole.WriteLine("feature extraction process");

var inputDir = @"D:\Mahnoosh\Liz\"; //@"C:\Users\kholghim\Mahnoosh\UnsupervisedFeatureLearning\";
var inputDir = @"D:\Mahnoosh\Liz\"; //@"C:\Users\kholghim\Mahnoosh\UnsupervisedFeatureLearning\"; //
var resultDir = Path.Combine(inputDir, "FeatureLearning");
var inputPath = Path.Combine(inputDir, "PatchSamplingSegments");
var trainSetPath = Path.Combine(inputDir, "TrainSet");
Expand All @@ -38,7 +41,7 @@ public void Execute(Arguments arguments)
var outputReSpecImagePath = Path.Combine(resultDir, "ReconstrcutedSpectrogram.png");
var outputClusterImagePath = Path.Combine(resultDir, "Clusters.bmp");

// +++++++++++++++++++++++++++++++++++++++++++++++++patch sampling from 1000 random 1-min recordings from Gympie
// +++++++++++++++++++++++++++++++++++++++++++++++++patch sampling from 1-min recordings

// check whether there is any file in the folder/subfolders
if (Directory.GetFiles(inputPath, "*", SearchOption.AllDirectories).Length == 0)
Expand Down Expand Up @@ -67,18 +70,18 @@ public void Execute(Arguments arguments)
NoiseReductionType = NoiseReductionType.None,
};

// Define the minFreBin and MaxFreqBin to be able to work at arbitrary frequency bin bounds.
// MinFreBin and MaxFreqBin to work with arbitrary frequency bin bounds.
// The default value is minFreqBin = 1 and maxFreqBin = finalBinCount.
// To work with arbitrary frequency bin bounds we need to manually set these two parameters.
// For any other arbitrary frequency bin bounds, these two parameters need to be manually set .

// Black Rail call is between 1000 Hz and 3000 Hz, which is mapped to Mel value [1000, 1876]
// Hence, we only work with freq bins between [46, 88]
// Hence, we only work with freq bins between [40, 76]
int minFreqBin = 40;
int maxFreqBin = 76;
int maxFreqBin = 80; //76;
int numFreqBand = 1;
int patchWidth = (maxFreqBin - minFreqBin + 1) / numFreqBand; //finalBinCount / numFreqBand;
int patchHeight = 1; // 2; // 4; // 16; // 6; // Frame size
int numRandomPatches = 100; //80; // 40; // 20; // 30; // 500; //
int numRandomPatches = 80; //20; //2; //10; //100; // 40; // 30; // 500; //
// int fileCount = Directory.GetFiles(folderPath, "*.wav").Length;

// Define variable number of "randomPatch" lists based on "numFreqBand"
Expand Down Expand Up @@ -150,7 +153,7 @@ public void Execute(Arguments arguments)
}

// convert list of random patches matrices to one matrix
int numberOfClusters = 20; //500; //256; // 128; // 64; // 32; // 10; // 50;
int numberOfClusters = 16; //128; //20; // 256; //500; // 128; // 64; // 32; // 10; // 50;
List<double[][]> allBandsCentroids = new List<double[][]>();
List<KMeansClusterCollection> allClusteringOutput = new List<KMeansClusterCollection>();

Expand All @@ -162,10 +165,18 @@ public void Execute(Arguments arguments)
var whitenedSpectrogram = PcaWhitening.Whitening(patchMatrix);

// Do k-means clustering
string pathToClusterCsvFile = Path.Combine(resultDir, "ClusterCentroids" + i.ToString() + ".csv");
var clusteringOutput = KmeansClustering.Clustering(whitenedSpectrogram.Reversion, numberOfClusters, pathToClusterCsvFile);
var clusteringOutput = KmeansClustering.Clustering(whitenedSpectrogram.Reversion, numberOfClusters);
// var clusteringOutput = KmeansClustering.Clustering(patchMatrix, noOfClusters, pathToClusterCsvFile);

// writing centroids to a csv file
// note that Csv.WriteToCsv can't write data types like dictionary<int, double[]> (problems with arrays)
// I converted the dictionary values to a matrix and used the Csv.WriteMatrixToCsv
// it might be a better way to do this
string pathToClusterCsvFile = Path.Combine(resultDir, "ClusterCentroids" + i.ToString() + ".csv");
var clusterCentroids = clusteringOutput.ClusterIdCentroid.Values.ToArray();
Csv.WriteMatrixToCsv(pathToClusterCsvFile.ToFileInfo(), clusterCentroids.ToMatrix());
//Csv.WriteToCsv(pathToClusterCsvFile.ToFileInfo(), clusterCentroids);

// sorting clusters based on size and output it to a csv file
Dictionary<int, double> clusterIdSize = clusteringOutput.ClusterIdSize;
int[] sortOrder = KmeansClustering.SortClustersBasedOnSize(clusterIdSize);
Expand Down Expand Up @@ -229,6 +240,14 @@ public void Execute(Arguments arguments)
throw new ArgumentException("The folder of recordings is empty...");
}

//*****
// lists of features for all processing files
// the key is the file name, and the value is the features for different bands
Dictionary<string, List<double[,]>> allFilesMeanFeatureVectors = new Dictionary<string, List<double[,]>>();
Dictionary<string, List<double[,]>> allFilesMaxFeatureVectors = new Dictionary<string, List<double[,]>>();
Dictionary<string, List<double[,]>> allFilesStdFeatureVectors = new Dictionary<string, List<double[,]>>();


foreach (string filePath in Directory.GetFiles(testSetPath, "*.wav"))
{
FileInfo fileInfo = filePath.ToFileInfo();
Expand Down Expand Up @@ -294,7 +313,8 @@ public void Execute(Arguments arguments)
double[][] featureTransVectors = new double[allSequentialPatchMatrix.ToArray()[i].GetLength(0)][];
for (int j = 0; j < allSequentialPatchMatrix.ToArray()[i].GetLength(0); j++)
{
var normVector = ART_2A.NormaliseVector(allSequentialPatchMatrix.ToArray()[i].ToJagged()[j]); // normalize each patch to unit length
// normalize each patch to unit length
var normVector = ART_2A.NormaliseVector(allSequentialPatchMatrix.ToArray()[i].ToJagged()[j]);
featureTransVectors[j] = allNormCentroids.ToArray()[i].ToMatrix().Dot(normVector);
}

Expand All @@ -315,7 +335,7 @@ public void Execute(Arguments arguments)
List<double[,]> allStdFeatureVectors = new List<double[,]>();

// number of frames needs to be concatenated to form 1 second. Each 24 frames make 1 second.
int numFrames = 24 / patchHeight;
int numFrames = (24 / patchHeight) * 60; //24 / patchHeight; //

foreach (var freqBandFeature in allFeatureTransVectors)
{
Expand All @@ -325,7 +345,7 @@ public void Execute(Arguments arguments)
int c = 0;
while (c + numFrames < freqBandFeature.GetLength(0))
{
// First, make a list of patches that would be equal to 1 second
// First, make a list of patches that would be equal to the needed resolution (1 scond, 60 second, etc.)
List<double[]> sequencesOfFramesList = new List<double[]>();
for (int i = c; i < c + numFrames; i++)
{
Expand Down Expand Up @@ -363,8 +383,18 @@ public void Execute(Arguments arguments)
allStdFeatureVectors.Add(stdFeatureVectors.ToArray().ToMatrix());
}

//*****
// the keys of the following dictionaries contain file name
// and their values are a list<double[,]> which the list.count is
// equal to the number of freq bands defined as an user-defined parameter.
// the 2D-array is the feature vectors.
allFilesMeanFeatureVectors.Add(fileInfo.Name, allMeanFeatureVectors);
allFilesMaxFeatureVectors.Add(fileInfo.Name, allMaxFeatureVectors);
allFilesStdFeatureVectors.Add(fileInfo.Name, allStdFeatureVectors);

// +++++++++++++++++++++++++++++++++++Temporal Summarization

/*
// ++++++++++++++++++++++++++++++++++Writing features to file
// First, concatenate mean, max, std for each second.
// Then write to CSV file.
Expand Down Expand Up @@ -396,11 +426,11 @@ public void Execute(Arguments arguments)
for (int i = 0; i < allMeanFeatureVectors.ToArray()[j].ToJagged().GetLength(0); i++)
{
List<double[]> featureList = new List<double[]>
{
allMeanFeatureVectors.ToArray()[j].ToJagged()[i],
allMaxFeatureVectors.ToArray()[j].ToJagged()[i],
allStdFeatureVectors.ToArray()[j].ToJagged()[i],
};
{
allMeanFeatureVectors.ToArray()[j].ToJagged()[i],
allMaxFeatureVectors.ToArray()[j].ToJagged()[i],
allStdFeatureVectors.ToArray()[j].ToJagged()[i],
};
double[] featureVector = DataTools.ConcatenateVectors(featureList);
featureVectors.Add(featureVector);
}
Expand All @@ -427,7 +457,7 @@ public void Execute(Arguments arguments)
}
}
}

*/
/*
// Reconstructing the target spectrogram based on clusters' centroids
List<double[,]> convertedSpec = new List<double[,]>();
Expand All @@ -446,6 +476,116 @@ public void Execute(Arguments arguments)
*/
}
}

//*****
// ++++++++++++++++++++++++++++++++++Writing features to one file
// First, concatenate mean, max, std for each second.
// Then, write the features of each pre-defined frequency band into a separate CSV file.

var filesName = allFilesMeanFeatureVectors.Keys.ToArray();
var meanFeatures = allFilesMeanFeatureVectors.Values.ToArray();
var maxFeatures = allFilesMaxFeatureVectors.Values.ToArray();
var stdFeatures = allFilesStdFeatureVectors.Values.ToArray();

// The number of elements in the list shows the number of freq bands
// the size of each element in the list shows the number of files processed to generate feature for.
// the dimensions of the matrix shows the number of feature vectors generated for each file and the length of feature vector
var allMeans = new List<double[][,]>();
var allMaxs = new List<double[][,]>();
var allStds = new List<double[][,]>();

// looping over freq bands
for (int i = 0; i < meanFeatures[0].Count; i++)
{
var means = new List<double[,]>();
var maxs = new List<double[,]>();
var stds = new List<double[,]>();

// looping over all files
for (int k = 0; k < meanFeatures.Length; k++)
{
means.Add(meanFeatures[k].ToArray()[i]);
maxs.Add(maxFeatures[k].ToArray()[i]);
stds.Add(stdFeatures[k].ToArray()[i]);
}

allMeans.Add(means.ToArray());
allMaxs.Add(maxs.ToArray());
allStds.Add(stds.ToArray());
}

// each element of meanFeatures array is a list of features for different frequency bands.
// looping over the number of freq bands
for (int i = 0; i < allMeans.ToArray().GetLength(0); i++)
{
// creating output feature file based on the number of freq bands
var outputFeatureFile = Path.Combine(resultDir, "FeatureVectors-" + i.ToString() + ".csv");

// creating the header for CSV file
List<string> header = new List<string>();
header.Add("file name");
for (int j = 0; j < allMeans.ToArray()[i][0].GetLength(1); j++)
{
header.Add("mean" + j.ToString());
}

for (int j = 0; j < allStds.ToArray()[i][0].GetLength(1); j++)
{
header.Add("std" + j.ToString());
}

for (int j = 0; j < allMaxs.ToArray()[i][0].GetLength(1); j++)
{
header.Add("max" + j.ToString());
}

var csv = new StringBuilder();
string content = string.Empty;
foreach (var entry in header.ToArray())
{
content += entry.ToString() + ",";
}

csv.AppendLine(content);

var allFilesFeatureVectors = new Dictionary<string, double[,]>();

// looping over files
for (int j = 0; j < allMeans.ToArray()[i].GetLength(0); j++)
{
// concatenating mean, std, and max vector together for the pre-defined resolution
List<double[]> featureVectors = new List<double[]>();
for (int k = 0; k < allMeans.ToArray()[i][j].ToJagged().GetLength(0); k++)
{
List<double[]> featureList = new List<double[]>
{
allMeans.ToArray()[i][j].ToJagged()[k],
allMaxs.ToArray()[i][j].ToJagged()[k],
allStds.ToArray()[i][j].ToJagged()[k],
};
double[] featureVector = DataTools.ConcatenateVectors(featureList);
featureVectors.Add(featureVector);
}

allFilesFeatureVectors.Add(filesName[j], featureVectors.ToArray().ToMatrix());
}

// writing feature vectors to CSV file
foreach (var entry in allFilesFeatureVectors)
{
content = string.Empty;
content += entry.Key.ToString() + ",";
foreach (var cent in entry.Value)
{
content += cent.ToString() + ",";
}

csv.AppendLine(content);
}

File.WriteAllText(outputFeatureFile, csv.ToString());
}
//*****
}

[Command(
Expand Down
7 changes: 1 addition & 6 deletions src/AudioAnalysisTools/DSP/KmeansClustering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ namespace AudioAnalysisTools.DSP
using Accord.MachineLearning;
using Accord.Math;
using Accord.Math.Distances;
using Accord.Statistics.Kernels;
using Acoustics.Shared;
using Acoustics.Shared.Csv;
using CsvHelper;
using Zio;

Expand All @@ -29,7 +26,7 @@ public class Output
public KMeansClusterCollection Clusters { get; set; }
}

public static Output Clustering(double[,] patches, int numberOfClusters, string pathToCentroidFile)
public static Output Clustering(double[,] patches, int numberOfClusters)
{
// "Generator.Seed" sets a random seed for the framework's main internal number generator, which
// gets a reference to the random number generator used internally by the Accord.NET classes and methods.
Expand All @@ -55,8 +52,6 @@ public static Output Clustering(double[,] patches, int numberOfClusters, string
clusterIdCentroid.Add(clust.Index, clust.Centroid);
}

Csv.WriteToCsv(pathToCentroidFile.ToFileInfo(), clusterIdCentroid);

var output = new Output()
{
ClusterIdCentroid = clusterIdCentroid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public void TestKmeansClustering()

// Do k-means clustering
string pathToClusterCsvFile = Path.Combine(outputDir.FullName, "ClusterCentroids" + i.ToString() + ".csv");
var clusteringOutput = KmeansClustering.Clustering(patchMatrix, numberOfClusters, pathToClusterCsvFile);
var clusteringOutput = KmeansClustering.Clustering(patchMatrix, numberOfClusters);

// sorting clusters based on size and output it to a csv file
Dictionary<int, double> clusterIdSize = clusteringOutput.ClusterIdSize;
Expand Down
Loading

0 comments on commit 8b19583

Please sign in to comment.