使用归一化互相关来匹配音频信号中的模式

问题描述 投票:0回答:1

该图显示了 16Hz 波形文件的音频信号。我们必须寻找绿色中的橙色图案。 结果应该是正确的索引,它是绿色的第七个索引,其中橙色重复,并且是相似因子。尽管模式随着时间的推移而变化,但我认为仍然有可能使用相似性因素来识别它。 我尝试了许多解决方案,例如余弦相似度、欧氏距离、fft等,但仍然无法找到可以识别模式的解决方案。这是使用归一化互相关的代码:

import numpy as np
import matplotlib.pyplot as plt
import scipy.signal

orange = np.array([-0.67788696,  0.35198975,  0.69580078, -0.02264404, -0.24002075,
        0.33798218,  0.40097046, -0.03103638,  0.22860718, -0.21273804,
       -0.3649292 , -0.19015503, -0.2906189 , -0.01010132,  0.03689575,
       -0.15289307, -0.14328003, -0.50906372, -0.57232666])
       
green = np.array([-0.69995117, -0.58444214, -0.07501221,  0.1862793 , -0.03338623,
        0.05426025, -0.40628052, -0.543396  ,  0.17922974,  0.52752686,
        0.61419678,  0.00531006, -0.027771  ,  0.12228394,  0.13223267,
        0.69995117, -0.13153076,  0.05752563, -0.32174683, -0.19555664,
       -0.52252197, -0.59085083, -0.16491699,  0.1791687 , -0.05090332,
       -0.03591919, -0.45037842, -0.5687561 ])       
       
def normalized_cross_correlation(signal1:np.ndarray, signal2:np.ndarray):
    """
    Calculate the normalized cross-correlation between two signals.

    Parameters:
        signal1 (ndarray): First signal.
        signal2 (ndarray): Second signal.

    Returns:
        ndarray: Normalized cross-correlation between the two signals.
    """
    # Check if the signals have zero variance
    if np.std(signal1) == 0 or np.std(signal2) == 0:
        return np.nan

    # Normalize both signals
    signal1_normalized = (signal1 - np.mean(signal1)) / np.std(signal1)
    signal2_normalized = (signal2 - np.mean(signal2)) / np.std(signal2)
    
    # Calculate the cross-correlation between the two normalized signals
    cross_correlation = np.correlate(signal1_normalized, signal2_normalized, mode='full')
    
    # Normalize the cross-correlation
    cross_correlation /= np.sqrt(np.sum(signal1_normalized**2) * np.sum(signal2_normalized**2))
    
    return cross_correlation

def find_best_match(signal1:np.ndarray, signal2:np.ndarray):
    """
    Find the best match between two signals using normalized cross-correlation.

    Parameters:
        signal1 (ndarray): First signal (pattern to search for).
        signal2 (ndarray): Second signal (the signal being searched).

    Returns:
        float: Similarity score between 0.0 and 1.0 (1.0 indicating a perfect match).
        int: Index of the best match in the second signal.
    """

    # Calculate normalized cross-correlation
    cross_correlation = normalized_cross_correlation(signal1, signal2)
    
    # Return zero similarity score and invalid index
    if np.any(np.isnan(cross_correlation)):
        return 0.0, -1 
    
    # Find the index of the peak in the cross-correlation
    best_match_index = np.argmax(cross_correlation)
    
    # Calculate the similarity score
    similarity_score = cross_correlation[best_match_index]
    
    return similarity_score, best_match_index
    
fig = plt.figure(figsize=(12, 6), dpi=80)
gs = fig.add_gridspec(2, hspace=0)
axs = gs.subplots()    
axs[0].axhline(y = 0.0, color = 'lightgray', linestyle = '-', linewidth=1.0) 

# Get the difference
diff = len(green) - len(orange)

ss = []
bmi = []
for loop in range(0, diff):
    # keep shifting orange on green index by index
    similarity_score, best_match_index = find_best_match(orange, green[loop:len(orange)+loop])
    if best_match_index != -1:
        ss.append(similarity_score)
        bmi.append(best_match_index)
    
# Find the max score
score = max(ss)
best_match_index = bmi[ss.index(score)]
print (score, ss.index(score), best_match_index)

axs[0].plot(orange, color='orange')
axs[1].plot(green, color='green')
axs[1].plot(7, green[7], '.', color='black', markersize=15)

fig.tight_layout()
plt.show()
plt.close() 
exit()

代码没有生成正确的索引,即 7。这里的模式匹配的理想解决方案是什么?

signal-processing cross-correlation
1个回答
0
投票

更新 find_best_match 函数以使用更合适的模式(非完整)计算互相关,并解释结果以找到绿色信号中模式的正确起始索引,而无需冗余循环。这是搜索波形并在索引 7 处找到它的更新版本

import numpy as np
import matplotlib.pyplot as plt
import scipy.signal

orange = np.array([-0.67788696,  0.35198975,  0.69580078, -0.02264404, -0.24002075,
        0.33798218,  0.40097046, -0.03103638,  0.22860718, -0.21273804,
       -0.3649292 , -0.19015503, -0.2906189 , -0.01010132,  0.03689575,
       -0.15289307, -0.14328003, -0.50906372, -0.57232666])
       
green = np.array([-0.69995117, -0.58444214, -0.07501221,  0.1862793 , -0.03338623,
        0.05426025, -0.40628052, -0.543396  ,  0.17922974,  0.52752686,
        0.61419678,  0.00531006, -0.027771  ,  0.12228394,  0.13223267,
        0.69995117, -0.13153076,  0.05752563, -0.32174683, -0.19555664,
       -0.52252197, -0.59085083, -0.16491699,  0.1791687 , -0.05090332,
       -0.03591919, -0.45037842, -0.5687561 ])       
       

def normalized_cross_correlation(signal1:np.ndarray, signal2:np.ndarray):
    """
    Calculate the normalized cross-correlation between two signals.
    """
    if np.std(signal1) == 0 or np.std(signal2) == 0:
        return np.nan

    signal1_normalized = (signal1 - np.mean(signal1)) / np.std(signal1)
    signal2_normalized = (signal2 - np.mean(signal2)) / np.std(signal2)
    
    cross_correlation = np.correlate(signal2_normalized, signal1_normalized, mode='valid')
    
    cross_correlation /= np.sqrt(np.sum(signal1_normalized**2) * np.sum(signal2_normalized**2))
    
    return cross_correlation

cross_correlation = normalized_cross_correlation(orange, green)

# Find the index of the peak in the cross-correlation
best_match_index = np.argmax(cross_correlation)

# Calculate the similarity score
similarity_score = cross_correlation[best_match_index]
similarity_score, best_match_index

输出:

(0.3894741869827266, 7)
© www.soinside.com 2019 - 2024. All rights reserved.