简体   繁体   English

Matplotlib-从WAV文件绘制波形

[英]Matplotlib - plotting waveform from a wav file

I'm working on a program that aims on hiding user-specified data in wav files (steganographical program, but only for educational use, nothing extremely sophisticated). 我正在开发一个旨在将用户指定的数据隐藏在wav文件中的程序(隐秘程序,但仅用于教育用途,没有什么非常复杂的功能)。 Aside from doing the steganographic operations, I also need to visualize content of the original and output wav files, however I don't know how to do it in a feasible way. 除了进行隐写操作外,我还需要可视化原始和输出的wav文件的内容,但是我不知道如何以可行的方式进行操作。

At first, I thought I'd just use the canvas widget from tkinter , but it's hardly usable since the input wav files can be quite large and it would be unfeasible to draw such amounts of data, not to mention that I'd need to handle zooming, scrolling etc. 起初,我以为我只会使用tkintercanvas小部件,但是它几乎不可用,因为输入的wav文件可能非常大,并且绘制如此大量的数据是不可行的,更不用说我需要处理缩放,滚动等

I found matplotlib which I thought could solve my problem. 我发现matplotlib可以解决我的问题。 I loaded a 10 MB wav file (16 bit, stereo), separated the samples for the two channels and converted them to signed 16 bit integers. 我加载了一个10 MB的wav文件(16位,立体声),分离了两个通道的样本并将其转换为带符号的16位整数。 Then I tried to plot the data for the first channel but it seems that matplotlib cannot handle such a large amount of points to plot - at first I can see the waveform plot (but still it takes quite a while) but when I resize the window (which causes redrawing of the plot), the following exception occurs: 然后,我尝试绘制第一个通道的数据,但似乎matplotlib无法处理如此大量的绘制点-最初,我可以看到波形图(但仍然需要一段时间),但是当我调整窗口大小时(这将导致重绘绘图),发生以下异常:

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python33\lib\tkinter\__i`enter code here`nit__.py", line 1475, in __call__
    return self.func(*args)
  File "C:\Python33\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 276, in resize
    self.show()
  File "C:\Python33\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 348, in  draw
    FigureCanvasAgg.draw(self)
  File "C:\Python33\lib\site-packages\matplotlib\backends\backend_agg.py", line 451, in draw
    self.figure.draw(self.renderer)
  File "C:\Python33\lib\site-packages\matplotlib\artist.py", line 56, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "C:\Python33\lib\site-packages\matplotlib\figure.py", line 1035, in draw
    func(*args)
  File "C:\Python33\lib\site-packages\matplotlib\artist.py", line 56, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "C:\Python33\lib\site-packages\matplotlib\axes.py", line 2088, in draw
    a.draw(renderer)
  File "C:\Python33\lib\site-packages\matplotlib\artist.py", line 56, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "C:\Python33\lib\site-packages\matplotlib\lines.py", line 563, in draw
    drawFunc(renderer, gc, tpath, affine.frozen())
  File "C:\Python33\lib\site-packages\matplotlib\lines.py", line 939, in _draw_lines
    self._lineFunc(renderer, gc, path, trans)
  File "C:\Python33\lib\site-packages\matplotlib\lines.py", line 979, in _draw_solid
    renderer.draw_path(gc, path, trans)
  File "C:\Python33\lib\site-packages\matplotlib\backends\backend_agg.py", line 145, in   draw_path
    self._renderer.draw_path(gc, path, transform, rgbFace)
  OverflowError: Allocated too many blocks

The same error occurred when I tried to load a bigger WAV file (50 MB), even without plotting the waveform. 当我尝试加载更大的WAV文件(50 MB)时,即使没有绘制波形,也会发生相同的错误。 So I'd need to take a different approach but don't quite know how to do it. 因此,我需要采用其他方法,但不太了解该怎么做。 When I load the samples first, I could probably plot averages of subsets of the input samples, which should probably be bearable for matplotlib. 当我首先加载样本时,我可能会绘制输入样本子集的平均值,这对于matplotlib应该是可以承受的。 But I don't know how to deal with situation when I zoom/scroll through the plot, which would mean recomputing the averages based on the actual zoomlevel and the actual view position ("window"), which would probably be very poor performance-wise. 但是我不知道如何缩放/滚动图,这意味着要根据实际的缩放级别和实际的视图位置(“窗口”)重新计算平均值,这可能会导致性能很差-明智的。

And this was only a sample plot, so I can't imagine plotting four times this amount of data (2 channels, original and output data) without facing performance issues or even failures/exceptions as mentioned. 而且这只是一个示例图,因此我无法想象绘制出四倍于此数量的数据(2通道,原始数据和输出数据)而不会遇到性能问题甚至提到的故障/异常。 On smaller files (a few hundreds kB) it works well (but it's still somewhat slow). 在较小的文件(几百kB)上,它可以很好地工作(但仍然有些慢)。

Do you have any suggestions on this issue, please? 请问您对此有何建议?

EDIT: I found out I had a bad interpretation of the input data in struct.pack() for the 16-bit samples (I used a string <H instead of <h ) and somehow I don't have problems with the 10 MB WAV and it seems like there's some speed-up, however plotting the waveform is still much slower than what would be appropriate. 编辑:我发现我对struct.pack()中的16位样本的输入数据有错误的解释(我使用字符串<H而不是<h ),并且以某种方式我对10 MB没有任何问题WAV,似乎有一定的提速,但是绘制波形仍然比合适的要慢得多。 The 50 MB WAV seems to plot well, but when I resize the window (and therefore the matplotlib canvas), the aforementioned exception occurs and the replotting doesn't take place anymore when I try to zoom to a certain area or resize the window to the previous size. 50 MB WAV似乎绘制得很好,但是当我调整窗口大小(并因此调整matplotlib画布)时,发生上述异常,并且当我尝试缩放到某个区域或调整窗口大小时不再进行重绘以前的尺寸。

Here's the code I used just for getting to know matplotlib a little bit (it's based on the simple matplotlib demo ): 这是我用来了解matplotlib的代码(它基于简单的matplotlib演示 ):

( EDIT2: I changed the code so that it behaves right in the same way, but now it is much simpler, I hope.) EDIT2:我更改了代码,以使其以相同的方式正常工作,但现在希望它更简单。)

import matplotlib
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg
from tkinter import tix
from tkinter.tix import *
from random import randrange

matplotlib.use('TkAgg')

samples = [randrange(-32768, 32768) for i in range(int(1e7))]
fig = Figure(figsize=(20,8), dpi=50)
subplot1 = fig.add_subplot(111)
subplot1.plot(samples, "r")

root = tix.Tk()

canvas = FigureCanvasTkAgg(fig, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)

toolbar = NavigationToolbar2TkAgg(canvas, root)
toolbar.update()
canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1)

root.mainloop()

Any suggestions how to solve this issue and how to deal with plotting the WAV data with a reasonable performance and memory consumption (this example uses >800 MB of memory before the exception occurs, which means my approach to this matter is not good at all). 关于如何解决此问题以及如何以合理的性能和内存消耗来处理WAV数据的任何建议(此示例在异常发生之前使用了800 MB以上的内存,这意味着我的解决方法根本不好) 。

You can simplify it even more to be run in an interactive prompt, but I digress 您可以进一步简化它以在交互式提示中运行,但是我离题了

import matplotlib
from matplotlib import pyplot as plt
from random import randrange


samples = [randrange(-32768, 32768) for i in range(int(1e7))]
fig, ax = plt.subplots(1, 1)
ax.plot(samples, "r-")

The problem is that your are trying to draw a of line segments which is more than the Agg library can deal with (I am not sure what limit is, and there should be some path simplification done before the path is passed off to Agg so it probably is not a point count limit anyway). 问题是您正在尝试绘制一个比Agg库可以处理的线段更多的线段(我不确定该限制是多少,并且在将路径传递给Agg之前应该进行一些路径简化)可能不是点数限制)。

To some degree this is not a huge problem, your screen is only ~1k pixels across, if you plotted all of the points you have, there would be 1e4 points per pixel, which is kind of silly, so you need to down sample. 从某种程度上讲,这不是一个大问题,您的屏幕只有大约1k像素,如果绘制所有点,则每个像素会有1e4个点,这很愚蠢,因此您需要降低采样率。

You can do this in a number of ways (and which way is right will depend on why you are plotting this) including: blindly down sampling ( x = x[::1000] ), averaging sections ( x = np.mean(x[::n * (len(x)//n)].reshape(-1, n), axis=1) ) or doing something exotic (take a fft and filter it to keep only low frequencies). 您可以通过多种方式做到这一点(和方法是正确的,将取决于你为什么要绘制这样),包括:一味降采样( x = x[::1000]平均部( x = np.mean(x[::n * (len(x)//n)].reshape(-1, n), axis=1) )或做一些奇怪的事情(进行fft并将其过滤以仅保持低频)。

If you need to be able to zoom in and see all the points in the zoomed region, you may need to do something fancier to replace the data with a non-down sampled version as you zoom. 如果需要放大并查看缩放区域中的所有点,则可能需要做一些更奇特的操作,以便在缩放时将数据替换为非向下采样版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM