Рисование матрицы с градиентом цветов "Спектрограмма"

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

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

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

Как нарисовать матрицу вывода 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 до величин (пренебрегают отрицательными значениями):

  1. Это один из "До свидания" из ранее:

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

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

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

2 ответа

Решение

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

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

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

  • Шаг первый - выбрать градиент цветов. Посмотрите функцию interpolateColors здесь для примера этого!

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

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

Сначала создается несколько образцов данных и градиент 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<Color> baseColors = new List<Color>();  // 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<Color> 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<Color> interpolateColors(List<Color> stopColors, int count)
{
    SortedDictionary<float, Color> gradient = new SortedDictionary<float, Color>();
    for (int i = 0; i < stopColors.Count; i++)
        gradient.Add(1f * i / (stopColors.Count - 1), stopColors[i]);
    List<Color> ColorList = new List<Color>();

    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 до 20 Кб, второе выглядит линейным в диапазоне от 0 до 100.

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

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

Другие вопросы по тегам