После использования STFT (кратковременное преобразование Фурье) на выходе получается матрица, представляющая трехмерный график, как если бы (A[X, Y] = M)
A - это выходная матрица, X - время, Y - частота, а третье измерение M - амплитуда, проиллюстрированная интенсивность цвета пикселя как на следующих рисунках:
Как нарисовать выходную матрицу A с градиентом цветов, как на изображениях в C #?
Есть ли библиотека, содержащая элемент управления спектрограммой для C #?
Обновление:
После некоторых модификаций данного алгоритма я смог нарисовать спектрограмму, я не изменил цветовую палитру, за исключением того, что первый цвет изменился на черный, но я не знаю, почему он очень блеклый!
Этот представляет собой звуковое высказывание
Пока-пока
И этот из pure sine wave
, так что частота почти всегда одинакова.
Спектрограмма чисто синусоидальной волны
Результат принят, он представляет частоты входного сигнала, как и ожидалось, но я думаю, что есть способ сделать спектрограмму так же хорошо проиллюстрированной, как и в примерах, не могли бы вы взглянуть на мой код и предложить модификации?
Это обработчик события:
private void SpectrogramButton_Click(object sender, EventArgs e)
{
Complex[][] SpectrogramData = Fourier_Transform.STFT(/*signal:*/ samples, /*windowSize:*/ 512, /*hopSize:*/ 512);
SpectrogramBox.Image = Spectrogram.DrawSpectrogram(SpectrogramData, /*Interpolation Factor:*/ 1000, /*Height:*/ 256);
}
А это функция рисования после моих изменений:
public static Bitmap DrawSpectrogram(Complex[][] Data, int InterpolationFactor, int Height)
{
// target size:
Size sz = new Size(Data.GetLength(0), Height);
Bitmap bmp = new Bitmap(sz.Width, sz.Height);
// the data array:
//double[,] data = new double[222, 222];
// step sizes:
float stepX = 1f * sz.Width / Data.GetLength(0);
float stepY = 1f * sz.Height / Data[0].GetLength(0);
// create a few stop colors:
List<Color> baseColors = new List<Color>(); // create a color list
baseColors.Add(Color.Black);
baseColors.Add(Color.LightSkyBlue);
baseColors.Add(Color.LightGreen);
baseColors.Add(Color.Yellow);
baseColors.Add(Color.Orange);
baseColors.Add(Color.Red);
// and the interpolate a larger number of grdient colors:
List<Color> colors = interpolateColors(baseColors, InterpolationFactor);
// a few boring test data
//Random rnd = new Random(1);
//for (int x = 0; x < data.GetLength(0); x++)
// for (int y = 0; y < data.GetLength(1); y++)
// {
// //data[x, y] = rnd.Next((int)(300 + Math.Sin(x * y / 999) * 200)) +
// // rnd.Next(x + y + 111);
// data[x, y] = 0;
// }
// now draw the data:
float Max = Complex.Max(Data);
using (Graphics G = Graphics.FromImage(bmp))
for (int x = 0; x < Data.GetLength(0); x++)
for (int y = 0; y < Data[0].GetLength(0); y++)
{
int Val = (int)Math.Ceiling((Data[x][y].Magnitude / Max) * (InterpolationFactor - 1));
using (SolidBrush brush = new SolidBrush(colors[(int)Val]))
G.FillRectangle(brush, x * stepX, (Data[0].GetLength(0) - y) * stepY, stepX, stepY);
}
// and display the result
return bmp;
}
Я действительно не понимаю того log
, о котором вы говорите в своих ответах, извините за мои небольшие знания.
Обновление:
Это результат после добавления взятия log10
к величине (отрицательные значения не учитываются):
Я думаю, что этот вывод приемлем, он отличается от примеров, которые я привел в начале, но я думаю, что он лучше.
Magnitude
напрямую, я сделал этоVal = 20d * Math.Log10(Data[x][y].Magnitude)
. Я не знаю, это ли вы имеете в виду, но мне пришлось пренебречь всеми отрицательными значениями, которые возникают в результатеMagnitude values < 1
, который выглядит очень темными областями в исходном изображении, поэтому я подумал отображения всего диапазона значений послеlog
на имеющийся у меня диапазон цветов, поэтому я удостоверяюсь, что отображаются все значения. - person Mohamed Hosnie   schedule 28.12.2015baseColors.Add(Color.Black); baseColors.Add(Color.DarkBlue); baseColors.Add(Color.MediumBlue); baseColors.Add(Color.LightSkyBlue); baseColors.Add(Color.LightGreen); baseColors.Add(Color.Yellow); baseColors.Add(Color.Orange); baseColors.Add(Color.Red);
- person Mohamed Hosnie   schedule 28.12.2015fr = sampleRate / # samples
, а затем значение частотыf
для индекса или строкиn
из уравнениеf = n * fr
, как вы видите, связано с частотой дискретизации и количеством образцов, которое меняется от файла к файлу, я не знаю, правильно ли оно, но я не пробовал, - person Mohamed Hosnie   schedule 29.12.2015Complex[][] SpectrogramData
) или вы всегда их снова вычисляете? Сериализация их должна быть скорее ... Содержат ли они линейные данные, например, одну строку на Гц или данные уже представлены в логарифмической схеме, что означает такое же количество строк / частот на октаву? Внимательно посмотрите на 1-е изображение, а также на то, что в моей ссылке: метки не линейные, а логарифмические .. - person TaW   schedule 29.12.2015SpectrogramData
с самого начала, потому что это всегда разные данные для каждого аудиофайла, что я делаю в своем STFT заключается в том, что я разделяю (сегментирую) выборки на сегменты известной длины, и для каждого сегмента я вычисляю данные БПФ, каждый индекс вSpectrogramData
- это БПФ данные для одного сегмента, напримерSpectrogramData[0]
- это данные БПФ первого сегмента и так далее ... это все, что я делал до сих пор. - person Mohamed Hosnie   schedule 29.12.2015SpectrogramData
является столбцом в растровом изображении. - person Mohamed Hosnie   schedule 29.12.2015sqrt(real*real + imaginary*imaginary)
, поэтому я использую как реальную, так и мнимую части. - person Mohamed Hosnie   schedule 30.12.2015Complex[,] fftData
, это сохранит данные:void saveKs(string dataFile) { using (BinaryWriter writer = new BinaryWriter(File.Open(dataFile, FileMode.Create))) { for (int y = 0; y < fftData.GetLength(0); y++) for (int x = 0; x < fftData.GetLength(1); x++) { writer.Write((double)fftData[x, y].Real); writer.Write((double)fftData[x, y].Imaginary); } } }
Сохранение зубчатого массива, конечно, только немного сложнее .., но все строки имеют одинаковую длину, верно ? - person TaW   schedule 30.12.2015Complex[][] fftDataJ
: - person TaW   schedule 30.12.2015Complex[][] loadJs(string dataFile) { Complex[][] fftJ = null; using (BinaryReader reader = new BinaryReader(File.Open(dataFile, FileMode.Open))) { int l0 = reader.ReadInt32(); fftJ = new Complex[l0][]; for (int y = 0; y < fftJ.GetLength(0); y++) { int l1 = reader.ReadInt32(); fftJ[y] = new Complex[l1]; for (int x = 0; x < fftJ[y].GetLength(0); x++) { double r = reader.ReadDouble(); double i = reader.ReadDouble(); fftJ[y][x] = new Complex(r, i); } } } return fftJ; }
(Простите за форматирование, но комментарий может быть только таким длинным ..) - person TaW   schedule 30.12.2015