使用渐变颜色绘制矩阵“Spectrogram”

在使用STFT(短时傅立叶变换)之后,输出是表示3d图的矩阵,好像(A[X, Y] = M) A是输出矩阵,X是时间,Y是频率,并且第三维M是像素颜色强度所示的幅度,如下图所示:

###频谱图1

谱图2

如何使用C#中的图片中的渐变颜色绘制输出矩阵A?
是否有一个包含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); } 

这是我修改后的绘图function:

 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; } 

我真的不明白你在答案中谈论的log事情,对不起我的小知识。


更新:
这是将log10添加到幅度(忽略负值)后的输出:

  1. 这是之前的“再见”之一:

在此处输入图像描述

  1. 霰弹枪爆炸:

在此处输入图像描述

  1. 音乐盒:

在此处输入图像描述

我认为这个输出是可以接受的,它与我在开始时带来的例子不同,但我认为它更好。

不,我知道没有开箱即用的控制。 当然,你可以购买外面的图书馆,但是嘘,你不能问他们这些…

理论上你可以使用,或者我想我应该为此滥用 Chart控件。 但由于DataPoints是相当昂贵的对象,或者至少比它们看起来更昂贵,所以这似乎不可取。

相反,您可以自己简单地将图形绘制Bitmap

  • 第一步是决定颜色的渐变。 有关此示例,请参见interpolateColors函数 !

  • 然后,您只需使用floats对步长和像素大小进行数据双循环,然后在那里执行Graphics.FillRectangle

下面是一个使用GDI+创建BitmapWinforms 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 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; } 

这是链接中的function:

 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.DrawStringTextRenderer.DrawText来执行此操作。 只需在绘图区域周围留出足够的空间!

我使用转换为int的数据值作为颜色表的直接指针。

根据您的数据,您需要缩小它们甚至使用日志转换。 第一个图像显示从100到20k的对数标度,第二个看起来线性从0到100。

如果您向我们展示您的数据结构,我们可以为您提供进一步的提示,以便如何调整代码以使用它。

您可以根据其他答案创建位图。 使用颜色查找表将FFT对数幅度转换为用于每个像素或小矩形的颜色也很常见。