简单音频比较

简单的音频比较

频谱算法

import os
import numpy as np
import matplotlib.pyplot as plt
from pydub import AudioSegment
from imagehash import phash
from PIL import Image
import io

import random
import time

def audio_to_spectrogram(audio_path, output_path=None, return_bytes=False):
    """
    将音频文件转换为频谱图
    :param audio_path: 音频文件的路径
    :param output_path: 频谱图的输出路径,如果为None则不保存到文件
    :param return_bytes: 是否返回字节形式的频谱图
    :return: 如果return_bytes为True,返回内存中的图像字节;否则返回None
    """
    audio = AudioSegment.from_file(audio_path)
    samples = np.array(audio.get_array_of_samples())
    if audio.channels == 2:
        samples = samples.reshape((-1, 2))
        samples = samples.mean(axis=1)

    print(f'处理音频: {audio_path}')
    plt.figure(figsize=(10, 4))
    plt.specgram(samples, Fs=audio.frame_rate, NFFT=1024, noverlap=512)
    plt.axis('off')
    
    # 保存到内存
    if return_bytes or not output_path:
        buf = io.BytesIO()
        plt.savefig(buf, format='png', bbox_inches='tight', pad_inches=0)
        buf.seek(0)
        
        # 如果同时需要保存到文件
        if output_path:
            plt.savefig(output_path, bbox_inches='tight', pad_inches=0)
            print(f'频谱图已保存至: {output_path}')
            
        plt.close()
        return buf.getvalue() if return_bytes else buf
    
    # 只保存到文件
    if output_path:
        plt.savefig(output_path, bbox_inches='tight', pad_inches=0)
        print(f'频谱图已保存至: {output_path}')
    
    plt.close()
    return None


def get_audio_perceptual_hash(audio_path):
    """
    获取音频的感知哈希值
    :param audio_path: 音频文件的路径
    :return: 音频的感知哈希值
    """
    # 直接获取内存中的频谱图数据
    spectrogram_buffer = audio_to_spectrogram(audio_path, return_bytes=False)

    # 从内存数据创建PIL图像对象
    image = Image.open(spectrogram_buffer)
    
    # 计算感知哈希
    hash_value = phash(image)
    
    return hash_value


def compare_audio_similarity(audio_path1, audio_path2):
    """
    对比两个音频文件的相似性
    :param audio_path1: 第一个音频文件的路径
    :param audio_path2: 第二个音频文件的路径
    :return: 汉明距离,值越小表示越相似
    """
    hash1 = get_audio_perceptual_hash(audio_path1)
    hash2 = get_audio_perceptual_hash(audio_path2)
    hamming_distance = hash1 - hash2
    return hamming_distance

if __name__ == "__main__":
    audio_path1 = "audio1.wav"
    audio_path2 = "audio2.wav"
    print(compare_audio_similarity(audio_path1, audio_path2))

dtw比较

import librosa
import numpy as np
from librosa.sequence import dtw
import matplotlib.pyplot as plt
import matplotlib
# 设置matplotlib支持中文显示
matplotlib.rcParams['font.sans-serif'] = ['SimHei']  # 用黑体显示中文
matplotlib.rcParams['axes.unicode_minus'] = False  # 正常显示负号

from pathlib import Path
import time
import logging

logger = logging.getLogger(__name__)

def compare_audio_dtw(audio1_path, audio2_path, chunk_duration=1.0, visualize=False, save_dir=None):
    """
    使用DTW算法分析audio1的每个片段在audio2中的位置和相似度
    
    参数:
        audio1_path: 第一个音频文件路径(会被分段)
        audio2_path: 第二个音频文件路径(完整搜索空间)
        chunk_duration: 每个分段的时长(秒)
        visualize: 是否生成MFCC可视化图像
        save_dir: 可视化图像保存目录,如果为None则为'images'
        
    返回:
        list: 包含每个片段的匹配信息的列表,每项为(片段索引, 匹配时间点, 相似度得分)
    """
    start_time = time.time()
    
    # 创建可视化保存目录
    if visualize:
        save_dir = save_dir or 'images'
        img_dir = Path(save_dir)
        if not img_dir.exists():
            img_dir.mkdir(parents=True, exist_ok=True)
    
    # === 加载音频 ===
    logger.info(f"加载音频文件: {audio1_path}, {audio2_path}")
    y1, sr = librosa.load(audio1_path, sr=None, res_type='kaiser_fast', offset=0.0, duration=None, mono=True)
    y2, _ = librosa.load(audio2_path, sr=sr, res_type='kaiser_fast', mono=True)  # 保持同采样率

    logger.info(f"{audio1_path} 采样率: {sr}")
    logger.info(f"{audio2_path} 采样率: {sr}")
    
    # === 参数设置 ===
    chunk_samples = int(chunk_duration * sr)
    num_chunks = len(y1) // chunk_samples
    
    logger.info(f"音频1 总共 {num_chunks} 段,每段 {chunk_duration} 秒")
    
    # 结果列表
    results = []
    
    # === 对每段做 DTW 匹配 audio2 ===
    for i in range(num_chunks):
        chunk = y1[i * chunk_samples : (i + 1) * chunk_samples]
        mfcc_chunk = librosa.feature.mfcc(y=chunk, sr=sr, n_mfcc=13)
        mfcc2 = librosa.feature.mfcc(y=y2, sr=sr, n_mfcc=13)
        
        # 可视化 mfcc_chunk 和 mfcc2
        if visualize:
            plt.figure(figsize=(12, 6))
            
            # 显示第一个音频片段的MFCC
            plt.subplot(1, 2, 1)
            plt.imshow(mfcc_chunk, aspect='auto', origin='lower')
            plt.title(f'MFCC - audio1 segment {i}')
            plt.ylabel('MFCC coefficients')
            plt.xlabel('Frames')
            plt.colorbar(format='%+2.0f dB')
            
            # 显示第二个音频文件的MFCC
            plt.subplot(1, 2, 2)
            plt.imshow(mfcc2, aspect='auto', origin='lower')
            plt.title('MFCC - audio2 full file')
            plt.ylabel('MFCC coefficients')
            plt.xlabel('Frames')
            plt.colorbar(format='%+2.0f dB')
            
            plt.tight_layout()
            plt.savefig(f'{img_dir}/mfcc_comparison_{i}.png')  # 保存图像到文件
            plt.close()  # 关闭图形以避免内存问题
        
        # 计算DTW
        D, wp = dtw(mfcc_chunk, mfcc2, subseq=True, metric='euclidean')
        
        # 找到最佳匹配位置
        best_idx = np.argmin(D[-1])
        best_time = librosa.frames_to_time(best_idx, sr=sr)
        
        # 获取最小代价和归一化相似度
        min_cost = D[-1, best_idx]
        max_possible_cost = np.max(D)
        similarity_score = 1 - (min_cost / max_possible_cost) if max_possible_cost > 0 else 1
        
        # 添加到结果列表
        results.append((i, best_time, similarity_score))
        
        logger.info(f"第{i}段 匹配位置: {best_time:.2f} 秒,相似度: {similarity_score:.2f}")
    
    # 计算耗时
    elapsed_time = time.time() - start_time
    logger.info(f"分析完成,耗时: {elapsed_time:.2f} 秒")
    
    return results

def format_results(results):
    """格式化结果为易读的字符串"""
    output = []
    for idx, time_pos, score in results:
        output.append(f"片段 {idx}: 在 {time_pos:.2f} 秒处匹配,相似度 {score:.2f}")
    return "\n".join(output)

if __name__ == "__main__":
    # 设置基本日志配置
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    
    # 示例用法
    audio1_path = "audio1.wav"
    audio2_path = "audio2.wav"
    
    results = compare_audio_dtw(audio1_path, audio2_path, visualize=True)
    print("\n匹配结果:")
    print(format_results(results))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值