Как растянуть ось X спектрограммы Matplotlib? - PullRequest
0 голосов
/ 07 января 2020

Извините, если это действительно очевидный вопрос. Я использую matplotlib для генерации некоторых спектрограмм для использования в качестве обучающих данных в модели машинного обучения. Спектрограммы представляют собой короткие клипы с музыкой c, и я хочу смоделировать ускорение или замедление песни на случайную величину для создания изменений в данных. Я показал мой код ниже для генерации каждой спектрограммы. Я временно изменил его, чтобы получить 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()

Я думал изменяя параметр Fs, заданный функции specgram, это растягивает данные вдоль оси x, но вместо этого кажется, что он изменяет размер всего графика и вводит пустое пространство вверху изображения странными и непредсказуемыми способами. Я уверен, что что-то упустил, но я не вижу, что это такое. Ниже изображение, чтобы проиллюстрировать, что я получаю.

Spectrogram Examples

Ответы [ 2 ]

0 голосов
/ 07 января 2020

Решением моей проблемы было установить 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()
0 голосов
/ 07 января 2020

Частота кадров - это фиксированное число, которое зависит только от ваших данных. Если вы измените его, вы эффективно «растянете» ось X, но не в ту сторону. Например, если у вас есть 1000 точек данных, которые соответствуют 1 секунде, ваша частота кадров (или лучшая частота дискретизации) будет 1000. Если ваш сигнал представляет собой простой синус 200 Гц, который немного увеличивает частоту во времени, 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

Сюжет очень похож, но на этот раз вы ошибаетесь: у вас есть почти 2 секунды на оси x, в то время как у вас должно быть только 1, более того, начальная частота, которую вы читаете, составляет 100 Гц, а не 200 Гц.


В заключение, частота выборки, которую вы установите, должна быть правильный. Если вы хотите растянуть сюжет, вы можете использовать что-то вроде plt.xlim(0.2, 0.4). Если вы хотите избежать белой полосы в верхней части графика, вы можете вручную установить ylim равным половине частоты кадров:

plt.ylim(0, frame_rate/2)

Это работает из-за простых свойств преобразования Фурье и Теорема Найквиста-Шеннона .

...