簡體   English   中英

如何拉伸 matplotlib 頻譜圖的 x 軸?

[英]How do I stretch the x-axis of a matplotlib spectrogram?

對不起,如果這是一個非常明顯的問題。 我正在使用 matplotlib 生成一些頻譜圖,用作機器學習模型中的訓練數據。 頻譜圖是音樂的短片,我想模擬以隨機量加速或減慢歌曲以在數據中產生變化。 我在下面展示了我的代碼來生成每個頻譜圖。 我臨時修改了它以在歌曲的同一點開始生成 2 個圖像,一個有變化,一個沒有,以便比較它們並看看它是否按預期工作。

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 軸拉伸數據,但它似乎正在調整整個圖形的大小並以奇怪且不可預測的方式在圖像頂部引入空白。 我確定我遺漏了一些東西,但我看不到它是什么。 下面是一張圖片來說明我得到了什么。

頻譜圖示例

幀率是一個固定數字,僅取決於您的數據,如果您更改它,您將有效地“拉伸”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);

在此處輸入圖片說明

如果您更改幀速率,您將獲得錯誤的 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);

在此處輸入圖片說明

情節非常相似,但這次是錯誤的:您在x軸上有將近2秒,而您應該只有1秒,而且您讀取的起始頻率是100Hz而不是200Hz。


總而言之,您設置的采樣頻率必須是正確的。 如果你想拉伸情節,你可以使用類似plt.xlim(0.2, 0.4) 如果您想避免繪圖頂部的白色帶,您可以手動將ylim設置為幀速率的一半:

plt.ylim(0, frame_rate/2)

這是因為傅里葉變換和Nyquist-Shannon 定理的簡單屬性。

我的問題的解決方案是設置情節的 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()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM