Различие между локальным максимумом как частью пика и абсолютным максимумом пика

Я взял данные амплитуды из 10-секундного клипа mp3. Затем я выполнил быстрое преобразование Фурье, чтобы получить данные для клипа в частотной области (показано на первом рисунке). Теперь я хотел бы определить, на каких частотах расположены пики.

Отношение амплитуды к частоте

Я начал со сглаживания данных, что можно увидеть ниже на синем и красном графиках. Я создал порог, по которому пики должны быть превышены, чтобы их можно было учитывать. Это горизонтальная синяя линия на третьем графике ниже. Как видно, мой код обнаружения пиков работал до некоторой степени.

Сглаживание и обнаружение пиков

Проблема, с которой я столкнулся сейчас, очевидна на последнем графике, показанном ниже. Мой код находит максимумы, которые являются локальными максимумами как часть общего пика. Мне нужен способ отфильтровать эти локальные максимумы, чтобы для каждого пика я получал только один маркер. то есть для пика, показанного ниже, мне нужен маркер только на абсолютном пике, а не на каждом второстепенном пике на этом пути.

Увеличенный вид обнаружения пиков

Мой код обнаружения пика показан ниже:

for i, item in enumerate(xavg): #xavg contains all the smoothed data points
    if xavg[i] > threshold: #points must be above the threshold
        #if not the first or last point (so index isn't out of range)            
        if (i > 0) and (i < (len(xavg)-1)): 
            #greater than points on either side                
            if (xavg[i] > xavg[i-1]) and (xavg[i] > xavg[i+1]):  
                max_locations.append(i)

РЕДАКТИРОВАТЬ: я думаю, что недостаточно ясно изложил свою проблему. Я хочу найти расположение 5 или около того самых высоких пиков на графике, а не только самую высокую точку в целом. По сути, я пытаюсь дать клипу звуковой отпечаток, отмечая его доминирующие частоты.

EDIT2: Еще немного кода, чтобы показать, что я делаю в отношении БПФ и сглаживания:

def movingaverage(interval, window_size):
    window = np.ones(int(window_size))/float(window_size)
    return np.convolve(interval, window, 'same')

fft = np.fft.rfft(song)
xavg = movingaverage(abs(fft), 21)

person Nick Camps    schedule 31.07.2013    source источник
comment
Я не знаю, сколько полос вы использовали в своем БПФ, во всяком случае, мне кажется, что вы используете слишком узкую полосу для определения пиков. Возможно, попробуйте посмотреть на большее количество точек по обе стороны от вашей целевой точки. Число действительно зависит от вашего «разрешения» БПФ. Кроме того, вы можете попытаться уменьшить количество полос, используемых в вашем БПФ, это может сделать работу намного проще (и быстрее).   -  person Blubber    schedule 31.07.2013
comment
Я не уверен, как бы я изменил количество полос и т. Д. Я просто использовал numpy. Код fft = np.fft.rfft(song)   -  person Nick Camps    schedule 31.07.2013
comment
Проще просто попытаться расширить область поиска, взглянув на точки xargs[i-d:i+d], где d — некоторое число › 1 (у вас сейчас d=1). Это должно, по крайней мере, сделать его менее чувствительным к локальным максимумам.   -  person Blubber    schedule 31.07.2013


Ответы (4)


Поиск пика довольно сложен, я бы не стал пытаться реализовать свой собственный код, если это возможно. Попробуйте использовать scipy.signal.find_peaks_cwt, там несколько параметров, с которыми вы можете поиграться. Я думаю, что с помощью этой функции вам не нужно заранее сглаживать данные, поскольку один из параметров представляет собой список длин, по которым сглаживаются данные. Грубо говоря, алгоритм сглаживает данные по одной шкале длины, ищет пики, сглаживает по другой шкале длины, ищет пики и т. д., затем он ищет пики, которые появляются на всех или большинстве шкал длины.

person gggg    schedule 31.07.2013

Ваши значения могут быть разделены на чередующиеся надпороговые и подпороговые области. Когда вы найдете локальные максимумы, следите за тем, какой из них больше, пока значения снова не упадут ниже порога. Отложите эти «региональные» максимумы как истинный пик, затем продолжите работу со следующей надпороговой областью. Что-то типа:

# Store the true peaks
peaks = []

# If you consider the first value a possible local maxima.
# Otherwise, just initialize max_location to (None, 0)
if xavg[0] > xavg[1]:
    max_location = (0, xavg[0])
else:
    max_location = (None,0) # position and value

# Use a slice to skip the first and last items.
for i, item in enumerate(xavg[1:-1]):
    if xavg[i] > threshold:
        if ((xavg[i] > xavg[i-1]) and
            (xavg[i] > xavg[i+1]) and
            xavg[i] > max_location[1]):
            max_location = (i, xavg[i])
    else:
        # If we found a previous largest local maxima, save it as a true
        # peak, then reset the values until the next time we exceed the threshold
        if max_location[0] is not None:
            peaks.append(max_location[0])
        max_location = None
        max_location_value = 0

# Do you consider the last value a possible maximum?
if xavg[i+1] > xavg[i] and xavg[i+1] > max_location[1]:
    max_location = (i+1, xavg[i+1])

# Check one last time if the last point was over threshold.
if max_location[0] is not None:
    peaks.append(max_location[0])
person chepner    schedule 31.07.2013
comment
В этом коде есть ошибка "один за другим". вы перечисляете (xavg[1:-1]) как i, item, но затем ссылаетесь на xavg[i], который на самом деле является xavg[i+1]. - person Blubber; 31.07.2013
comment
О да. Я перепутал это со стилем C для циклов, где индекс увеличивается перед фактическим выходом из цикла. Исправлено, я думаю. - person chepner; 31.07.2013

В настоящее время вы составляете список (max_locations) любой точки, в которой предыдущая и следующая точки находятся ниже текущей точки. Если вас интересует только абсолютный максимум, вы можете сделать это так:

xavg.index (max (xavg [startPosition: endPosition]))

В качестве альтернативы, если вы хотите сохранить свой код нетронутым, вы можете проверить, больше ли текущая точка, чем любая другая точка, прежде чем поместить себя в позицию max_location:

если xavg[i] > xavg[max_location]: max_location = i

person Riz    schedule 31.07.2013
comment
Этот метод работает, если мне нужен только один пик. Возможно, я недостаточно ясно выразился: я хотел бы найти 5 или около того самых высоких пиков в образце. Дело в том, что если я просто возьму 5 самых высоких точек в выборке, все они могут быть частью одного и того же пика. Мне нужны 5 самых высоких шипов. - person Nick Camps; 31.07.2013

Об этом ОЧЕНЬ много литературы. На мой взгляд, у вас есть как минимум четыре варианта. Особенно рекомендую варианты 2 и 3.

  • как предложил Чепнер, считайте, что ваш пик такой же, пока он не упадет ниже вашего порога.
  • в качестве альтернативы, если вы считаете, что у вас может быть пик, который не совсем опускается ниже порога, а затем снова поднимается (двойной пик), вы можете вычислить производную вашего БПФ. Таким образом, когда пик падает ниже порога ИЛИ производная становится равной нулю (долина между пиками), вы называете это отчетливым пиком.
  • вы всегда можете подогнать функции к своим пикам, скажем, гауссовым или лоренцевским. Это, вероятно, самый надежный подход из тех, которые я упоминаю.
  • Вы определяете самый высокий пик, сохраняете его и удаляете N точек вокруг него (N=3,4,5,... вы выбираете), затем находите второй пик и удаляете такое же количество точек вокруг него...
person Jblasco    schedule 31.07.2013