Рисование матрицы с gradleиентом цветов «Спектрограмма»

После использования STFT (короткое временное преобразование Фурье) выход представляет собой матрицу, которая представляет собой 3D-график, как если бы (A[X, Y] = M) A – выходная matrix, X – время, Y – частота, а третье измерение M – это амплитуда, иллюстрируемая интенсивностью цвета пикселя, как на следующих изображениях:

### Спектрограмма 1

Спектрограмма 2

Как рисовать матрицу вывода A с gradleиентом цветов, как на изображениях на C #?
Есть ли библиотека, которая содержит элемент управления спектрограммой для C #?


Обновить:
После некоторых модификаций данного алгоритма я смог нарисовать спектрограмму, я не изменил цветовую палитру, кроме первого цвета, измененного на черный, но я не знаю, почему он очень поблек!

Это представляет собой звуковое высказывание

Пока-пока

Bye Bye Spectrogram

И эта 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 baseColors = new List(); // 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 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; } 

Я не очень разбираюсь в том, что вы говорите в своих ответах, я сожалею о своих маленьких знаниях.


Обновить:
Это результат после добавления log10 к значениям (отрицательные значения игнорируются):

  1. Это одно из «до свидания» от:

введите описание изображения здесь

  1. Взрыв дробовика:

введите описание изображения здесь

  1. Музыкальная шкатулка:

введите описание изображения здесь

Я думаю, что этот выход приемлем, он отличается от примеров, которые я привел в начале, но я думаю, что это лучше.

    Нет, нет контроля над ящиком, о котором я знаю. Конечно, могут быть и библиотеки, которые вы можете купить, но, конечно, но вы не можете спросить о них.

    В теории вы могли бы использовать, или я предполагаю, что я должен сказать, что для этого используется управление Chart . Но поскольку DataPoints являются довольно дорогостоящими объектами или, по крайней мере, более дорогими, чем они выглядят, это кажется нецелесообразным.

    Вместо этого вы можете просто нарисовать график в Bitmap самостоятельно.

    • Первый шаг – выбрать gradleиент цветов. См. Здесь функцию interpolateColors для примера!

    • Тогда вы просто сделаете двойной цикл над данными, используя floats для размеров шага и пикселя, и там сделайте Graphics.FillRectangle .

    Вот простой пример использования GDI+ для создания Bitmap и Winforms PictureBox для отображения. Он не добавляет осей в графику и полностью заполняет ее.

    Сначала он создает несколько выборочных данных и gradleиент с 1000 цветами. Затем он втягивается в Bitmap и отображает результат:

    введите описание изображения здесь

     private void button6_Click(object sender, EventArgs e) { // target size: Size sz = pictureBox1.ClientSize; 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.GetLength(1); // create a few stop colors: List baseColors = new List(); // create a color list baseColors.Add(Color.RoyalBlue); 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 colors = interpolateColors(baseColors, 1000); // 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); } // now draw the data: using (Graphics G = Graphics.FromImage(bmp)) for (int x = 0; x < data.GetLength(0); x++) for (int y = 0; y < data.GetLength(1); y++) { using (SolidBrush brush = new SolidBrush(colors[(int)data[x, y]])) G.FillRectangle(brush, x * stepX, y * stepY, stepX, stepY); } // and display the result pictureBox1.Image = bmp; } 

    Вот функция из ссылки:

     List interpolateColors(List stopColors, int count) { SortedDictionary gradient = new SortedDictionary(); for (int i = 0; i < stopColors.Count; i++) gradient.Add(1f * i / (stopColors.Count - 1), stopColors[i]); List ColorList = new List(); using (Bitmap bmp = new Bitmap(count, 1)) using (Graphics G = Graphics.FromImage(bmp)) { Rectangle bmpCRect = new Rectangle(Point.Empty, bmp.Size); LinearGradientBrush br = new LinearGradientBrush (bmpCRect, Color.Empty, Color.Empty, 0, false); ColorBlend cb = new ColorBlend(); cb.Positions = new float[gradient.Count]; for (int i = 0; i < gradient.Count; i++) cb.Positions[i] = gradient.ElementAt(i).Key; cb.Colors = gradient.Values.ToArray(); br.InterpolationColors = cb; G.FillRectangle(br, bmpCRect); for (int i = 0; i < count; i++) ColorList.Add(bmp.GetPixel(i, 0)); br.Dispose(); } return ColorList; } 

    Вы, вероятно, захотите нарисовать оси с метками и т. Д. Для этого вы можете использовать Graphics.DrawString или TextRenderer.DrawText . Просто оставь достаточно места вокруг области рисования!

    Я использовал значения данных, переданные int как прямые указатели в таблицу цветов.

    В зависимости от ваших данных вам необходимо масштабировать их или даже использовать преобразование журнала. Первое из ваших изображений показывает логарифмический масштаб от 100 до 20k, второй выглядит линейным, идущим от 0 до 100.

    Если вы покажете нам свою структуру данных, мы сможем дать вам дальнейшие советы по адаптации кода для его использования.

    Вы можете создать bitmap в соответствии с другим ответом. Также широко используется таблица цветового поиска, чтобы преобразовать величину журнала FFT в цвет, используемый для каждого пикселя или небольшого прямоугольника.