如何拉伸matplotlib谱图的xaxis?

2024-05-15 21:55:46 发布

您现在位置:Python中文网/ 问答频道 /正文

抱歉,如果这是一个非常明显的问题。我正在使用matplotlib生成一些光谱图,用作机器学习模型中的训练数据。频谱图是音乐的短片段,我想模拟随机加速或减慢歌曲,以在数据中产生变化。我在下面展示了生成每个光谱图的代码。我临时修改了它,从歌曲中的同一点开始生成两个图像,一个有变化,一个没有变化,以便比较它们,看看它是否按预期工作

from pydub import AudioSegment
import matplotlib.pyplot as plt
import numpy as np

BPM_VARIATION_AMOUNT = 0.2
FRAME_RATE = 22050
CHUNK_SIZE = 2
BUFFER = FRAME_RATE * 5

def generate_random_specgram(track):
    # Read audio data from file
    audio = AudioSegment.from_file(track.location)
    audio = audio.set_channels(1).set_frame_rate(FRAME_RATE)
    samples = audio.get_array_of_samples()
    start = np.random.randint(BUFFER, len(samples) - BUFFER)
    chunk = samples[start:start + int(CHUNK_SIZE * FRAME_RATE)]

    # Plot specgram and save to file
    filename = ('specgrams/%s-%s-%s.png' % (track.trackid, start, track.bpm))
    plt.figure(figsize=(2.56, 0.64), frameon=False).add_axes([0, 0, 1, 1])
    plt.axis('off')
    plt.specgram(chunk, Fs = FRAME_RATE)
    plt.savefig(filename)
    plt.close()

    # Perform random variations to the BPM
    frame_rate = FRAME_RATE
    bpm = track.bpm
    variation = 1 - BPM_VARIATION_AMOUNT + (
        np.random.random() * BPM_VARIATION_AMOUNT * 2)
    bpm *= variation
    bpm = round(bpm, 2)
    # I thought this next line should have been /= but that stretched the wrong way?
    frame_rate *= (bpm / track.bpm) 

    # Read audio data from file
    chunk = samples[start:start + int(CHUNK_SIZE * frame_rate)]

    # Plot specgram and save to file
    filename = ('specgrams/%s-%s-%s.png' % (track.trackid, start, bpm))
    plt.figure(figsize=(2.56, 0.64), frameon=False).add_axes([0, 0, 1, 1])
    plt.axis('off')
    plt.specgram(chunk, Fs = frame_rate)
    plt.savefig(filename)
    plt.close()

我认为,通过更改指定给specgram函数的Fs参数,这将沿x轴拉伸数据,但它似乎在调整整个图形的大小,并以奇怪且不可预测的方式在图像顶部引入空白。我肯定我遗漏了什么,但我看不出是什么。下面是一张图片来说明我得到了什么

Spectrogram Examples


Tags: fromratepltrandomtrackframestartaudio
2条回答

帧率是一个固定的数字,仅取决于您的数据,如果您更改它,将有效地“拉伸”x轴,但方式错误。例如,如果有1000个对应于1秒的数据点,则帧速率(或更好的采样频率)将为1000。如果您的信号是一个简单的200Hz正弦信号,它会在时间上略微增加频率,那么specgram将是:

t = np.linspace(0, 1, 1000)
signal = np.sin((200*2*np.pi + 200*t) * t)

frame_rate = 1000
plt.specgram(signal, Fs=frame_rate);

enter image description here

如果更改帧率,则x轴和y轴比例将错误。如果将帧速率设置为500,则将具有:

t = np.linspace(0, 1, 1000)
signal = np.sin((200*2*np.pi + 200*t) * t)

frame_rate = 500
plt.specgram(signal, Fs=frame_rate);

enter image description here

曲线图非常相似,但这次是错误的:x轴上几乎有2秒,而你应该只有1秒,而且,你读取的起始频率是100Hz,而不是200Hz


总之,您设置的采样频率必须是正确的。如果要拉伸绘图,可以使用类似plt.xlim(0.2, 0.4)的方法。如果要避免在绘图顶部出现白带,可以手动将ylim设置为帧速率的一半:

plt.ylim(0, frame_rate/2)

这是因为傅里叶变换和Nyquist-Shannon theorem的简单特性

我的问题的解决方案是设置绘图的xlim和ylim。以下是我的测试文件中的代码,在该文件中,我最终消除了所有奇怪的空白:

from pydub import AudioSegment
import numpy as np
import matplotlib.pyplot as plt

BUFFER = 5
FRAME_RATE = 22050
SAMPLE_LENGTH = 2

def plot(audio_file, bpm, variation=1):
    audio = AudioSegment.from_file(audio_file)
    audio = audio.set_channels(1).set_frame_rate(FRAME_RATE)
    samples = audio.get_array_of_samples()
    chunk_length = int(FRAME_RATE * SAMPLE_LENGTH * variation)
    start = np.random.randint(
        BUFFER * FRAME_RATE,
        len(samples) - (BUFFER * FRAME_RATE) - chunk_length)
    chunk = samples[start:start + chunk_length]

    plt.figure(figsize=(5.12, 2.56)).add_axes([0, 0, 1, 1])
    plt.specgram(chunk, Fs=FRAME_RATE * variation)
    plt.xlim(0, SAMPLE_LENGTH)
    plt.ylim(0, FRAME_RATE / 2 * variation)
    plt.savefig('specgram-%f.png' % (bpm * variation))
    plt.close()

相关问题 更多 >