当前位置: 首页 > news >正文

LMS/NLMS最小均值算法:双麦克风降噪

NLMS学习率不知道怎么调,效果没LMS好。

原理参考:
https://stc.fs.cvut.cz/pdf17/6563.pdf

测试内容:
noise-16k16bits.pcm是噪音音频文件
mixervoice-16k16bits.pcm 是由演讲录音与noise-16k16bits.pcm按0.5:0.5 比例混合的音频,语音+噪音。

// test-lms-denoiser.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#include <iostream>#include <stdio.h>
#include <stdlib.h>
#include <math.h>#define FILTER_LEN 32    // 滤波器长度
#define MU 0.01f         // 收敛因子(步长)// LMS滤波器结构体
typedef struct {float coeffs[FILTER_LEN];  // 滤波器系数float buffer[FILTER_LEN];   // 输入信号缓冲区int index;                 // 缓冲区当前索引
} LMSFilter;// 初始化滤波器
void LMS_Init(LMSFilter* filter) {for (int i = 0; i < FILTER_LEN; i++) {filter->coeffs[i] = 0.0f;  // 初始系数设为0filter->buffer[i] = 0.0f;   // 清空缓冲区}filter->index = 0;
}// LMS自适应滤波(单次迭代)
float LMS_Update(LMSFilter* filter, float input, float desired) {// 将新样本存入缓冲区filter->buffer[filter->index] = input;// 计算滤波器输出(卷积)float output = 0.0f;for (int i = 0; i < FILTER_LEN; i++) {int j = (filter->index - i + FILTER_LEN) % FILTER_LEN;output += filter->coeffs[i] * filter->buffer[j];}// 计算误差float error = desired - output;// 更新滤波器系数for (int i = 0; i < FILTER_LEN; i++) {int j = (filter->index - i + FILTER_LEN) % FILTER_LEN;filter->coeffs[i] += MU * error * filter->buffer[j];}// 更新缓冲区索引filter->index = (filter->index + 1) % FILTER_LEN;return output;
}// 生成测试信号(正弦波+噪声)
float generate_input(int n) {float signal = 0.3f * sinf(2 * 3.1415926f * n / 50.0f);float noise = 0.1f * ((float)rand() / RAND_MAX - 0.5f);return signal + noise;
}// 模拟未知系统(待辨识的FIR系统)
float unknown_system(float* buf) {float known_coeffs[FILTER_LEN] = { 1,/* 假设的未知系数 */ };for (int i = 0; i < FILTER_LEN; i++) {known_coeffs[i] = 1.0f;}float output = 0.0f;for (int i = 0; i < FILTER_LEN; i++) {output += known_coeffs[i] * buf[i];}return output;
}int main_() {LMSFilter filter;LMS_Init(&filter);float input_buf[FILTER_LEN] = { 0 };int buf_idx = 0;// 迭代1000次训练for (int n = 0; n < 1000; n++) {// 生成输入信号float x = generate_input(n);input_buf[buf_idx] = x;buf_idx = (buf_idx + 1) % FILTER_LEN;// 获取未知系统的期望输出float d = unknown_system(input_buf);// LMS更新float y = LMS_Update(&filter, x, d);// 打印误差(观察收敛)if (n % 100 == 0) {printf("Step %d: Error = %.4f\n", n, d - y);}}// 打印学习到的系数printf("\nLearned Coefficients:\n");for (int i = 0; i < FILTER_LEN; i++) {printf("%.4f ", filter.coeffs[i]);}return 0;
}/// LMS自适应滤波基本参数
#define LMS_FILTER_SIZE 32      //定义FIR滤波器的阶数。为保证高速运算须为8的倍数。
#define LMS_NIU         0.01    //LMS收敛因子
// NLMS归一化自适应滤波基本参数
#define NLMS_MU         0.01f  // NLMS 学习率
#define NLMS_EPS        0.001   // NLMS 防止除数为零的常量// LMS滤波采样数据类型
typedef float lms_node_t, nlms_node_t;/// <summary>
/// LMS对象结构
/// </summary>
typedef struct lms_{lms_node_t coeffs[LMS_FILTER_SIZE];lms_node_t buffer[LMS_FILTER_SIZE+1];lms_node_t error;
} lms_t, nlms_t;/// <summary>
/// 初始化LMS滤波对象
/// </summary>
/// <param name="lms"></param>
/// <returns></returns>
lms_t* lms_init(lms_t* lms)
{if (lms == NULL) {return NULL;}memset(lms->coeffs, 0, sizeof(lms_t::coeffs));memset(lms->buffer, 0, sizeof(lms_t::buffer));lms->error = 0;return lms;
}/// <summary>
/// 对一个信号采样进行LMS滤波自适应,估计滤波后的值。降噪时:假设mic1(desired)=语音+噪声,mic2(input)=参考噪声 mic2_noise, mic1_mixed
/// </summary>
/// <param name="lms">LMS滤波对象</param>
/// <param name="input">降噪时对应:input - 参考噪声 mic2_noise</param>
/// <param name="desired">降噪时对应:desired - mic1为语音+噪声</param>
/// <returns>降噪时对应:噪声估计;lms->error 为干净声音样本</returns>
lms_node_t lms_update(lms_t* lms, lms_node_t input, lms_node_t desired)
{lms_node_t* coeffs = NULL;lms_node_t* buffer = NULL;lms_node_t estimated_noise = 0;lms_node_t coeff = 0;int idx = 0;if (lms == NULL) {return NULL;}coeffs = lms->coeffs;buffer = lms->buffer;buffer[LMS_FILTER_SIZE - 1] = input;// 卷积for (idx = 0; idx < LMS_FILTER_SIZE; ++idx) {estimated_noise += coeffs[idx] * buffer[idx];}// 求误差lms->error = desired - estimated_noise;// 更新参数coeff = LMS_NIU * lms->error;for (idx = 0; idx < LMS_FILTER_SIZE; ++idx) {// 更新滤波参数coeffs[idx] += coeff * buffer[idx];// 缓存前移,挪出下一个采样的位置buffer[idx] = buffer[idx+1];}return estimated_noise;
}/// 归一化LMS滤波初始化
#define nlms_init lms_init/// <summary>
/// 对一个信号采样进行NLMS滤波自适应,估计滤波后的值。降噪时:假设mic1(desired)=语音+噪声,mic2(input)=参考噪声 mic2_noise, mic1_mixed
/// </summary>
/// <param name="nlms">NLMS滤波对象</param>
/// <param name="input">降噪时对应:input - 参考噪声 mic2_noise</param>
/// <param name="desired">降噪时对应:desired - mic1为语音+噪声</param>
/// <returns>降噪时对应:噪声估计,nlms->error 为干净声音采样</returns>
nlms_node_t nlms_update(nlms_t* nlms, nlms_node_t input, nlms_node_t desired)
{nlms_node_t* coeffs = NULL;nlms_node_t* buffer = NULL;nlms_node_t estimated_noise = 0;nlms_node_t power = NLMS_EPS;nlms_node_t coeff = 0;int idx = 0;if (nlms == NULL) {return NULL;}coeffs = nlms->coeffs;buffer = nlms->buffer;buffer[LMS_FILTER_SIZE - 1] = input;// 卷积for (idx = 0; idx < LMS_FILTER_SIZE; ++idx) {estimated_noise += coeffs[idx] * buffer[idx];// 求平方和,计算输入信号功率power += buffer[idx] * buffer[idx];}// 求误差nlms->error = desired - estimated_noise;// 更新参数if (power == NLMS_EPS)power = 0.0f;coeff = NLMS_MU * nlms->error / (NLMS_EPS + power);for (idx = 0; idx < LMS_FILTER_SIZE; ++idx) {// 更新滤波参数coeffs[idx] += coeff * buffer[idx];// 缓存前移,挪出放置下一个采样的位置buffer[idx] = buffer[idx + 1];}return estimated_noise;
}#include <fstream>
#include <vector>void mixerNoiseToCleanVoice();int main()
{std::cout << "Hello World!\n";// 生成混合噪音音频
#if 0{mixerNoiseToCleanVoice();return 0;}
#endif// c++ 版本 LMS 降噪
#if 0{std::ifstream mixervoice("mixervoice-16k16bits.pcm", std::ios::binary | std::ios::in);std::ifstream noise("noise-16k16bits.pcm", std::ios::binary | std::ios::in);std::vector<short> noiseBuffer(1024);std::vector<short> mixerBuffer(1024);std::ofstream clean("output-clean-16k16bits.pcm", std::ios::binary | std::ios::out);std::vector<short> cleanBuffer(1024);LMSFilter filter;LMS_Init(&filter);float input_buf[FILTER_LEN] = { 0 };int buf_idx = 0;while (!mixervoice.eof()) {noise.read((char*)noiseBuffer.data(), noiseBuffer.size() * sizeof(short));mixervoice.read((char*)mixerBuffer.data(), mixerBuffer.size() * sizeof(short));if (noise.eof() || mixervoice.eof()) {break;}for (int i = 0; i < 1024; i++) {float sn = noiseBuffer[i] / 32768.0f;float sm = mixerBuffer[i] / 32768.0f;float x = sn;float d = sm;input_buf[buf_idx] = x;buf_idx = (buf_idx + 1) % FILTER_LEN;// LMS更新float y = LMS_Update(&filter, x, d);cleanBuffer[i] = y * 32768.0f;}clean.write((const char*)cleanBuffer.data(), cleanBuffer.size() * sizeof(short));}return 0;}
#endif// LMS 降噪测试
#if 1{std::ifstream mixervoice("mixervoice-16k16bits.pcm", std::ios::binary | std::ios::in);std::ifstream noise("noise-16k16bits.pcm", std::ios::binary | std::ios::in);std::vector<short> noiseBuffer(1024);std::vector<short> mixerBuffer(1024);std::ofstream clean("output-1-clean-16k16bits.pcm", std::ios::binary | std::ios::out);std::vector<short> cleanBuffer(1024);lms_t lms;lms_init(&lms);while (!mixervoice.eof()) {noise.read((char*)noiseBuffer.data(), noiseBuffer.size() * sizeof(short));mixervoice.read((char*)mixerBuffer.data(), mixerBuffer.size() * sizeof(short));if (noise.eof() || mixervoice.eof()) {break;}for (int i = 0; i < 1024; i++) {float sn = noiseBuffer[i] / 32768.0f;float sm = mixerBuffer[i] / 32768.0f;float estimated_noise = lms_update(&lms, sn, sm);cleanBuffer[i] = lms.error * 32768.0f;}clean.write((const char*)cleanBuffer.data(), cleanBuffer.size() * sizeof(short));}}
#endif// NLMS 降噪测试
#if 1{std::ifstream mixervoice("mixervoice-16k16bits.pcm", std::ios::binary | std::ios::in);std::ifstream noise("noise-16k16bits.pcm", std::ios::binary | std::ios::in);std::vector<short> noiseBuffer(1024);std::vector<short> mixerBuffer(1024);std::ofstream clean("output-2-clean-16k16bits.pcm", std::ios::binary | std::ios::out);std::vector<short> cleanBuffer(1024);nlms_t nlms;nlms_init(&nlms);while (!mixervoice.eof()) {noise.read((char*)noiseBuffer.data(), noiseBuffer.size() * sizeof(short));mixervoice.read((char*)mixerBuffer.data(), mixerBuffer.size() * sizeof(short));if (noise.eof() || mixervoice.eof()) {break;}for (int i = 0; i < 1024; i++) {float sn = noiseBuffer[i] / 32768.0f;float sm = mixerBuffer[i] / 32768.0f;float estimated_noise = nlms_update(&nlms, sn, sm);cleanBuffer[i] = (sm - estimated_noise) * 32768.0f;}clean.write((const char*)cleanBuffer.data(), cleanBuffer.size() * sizeof(short));}}
#endifreturn 0;
}/// <summary>
/// 混合噪音与干净音频
/// </summary>
void mixerNoiseToCleanVoice()
{std::ifstream noise("noise-16k16bits.pcm", std::ios::binary | std::ios::in);std::ifstream clean("clean-16k16bits.pcm", std::ios::binary | std::ios::in);std::ofstream mixervoice("varvoice-16k16bits.pcm", std::ios::binary | std::ios::out);std::vector<short> noiseBuffer(1024);std::vector<short> cleanBuffer(1024);std::vector<short> mixerBuffer(1024);while (!clean.eof()) {noise.read((char*)noiseBuffer.data(), noiseBuffer.size() * sizeof(short));clean.read((char*)cleanBuffer.data(), cleanBuffer.size() * sizeof(short));if (clean.eof()) {break;}for (int i = 0; i < 1024; i++) {mixerBuffer[i] = noiseBuffer[i] * 0.5 + cleanBuffer[i] * 0.5;}mixervoice.write((const char*)mixerBuffer.data(), mixerBuffer.size() * sizeof(short));}
}
http://www.dtcms.com/a/318703.html

相关文章:

  • CentOS8.5安装19c单机告警及处理
  • 碳纳米管的原子精度制造——展望
  • 福彩双色球第2025090期篮球号码分析
  • docker启动出现Error response from daemon: Container的问题【已解决】
  • 容器化运维工具(2)Kubernetes 详细教程(含图解)
  • 开发避坑指南(18): SpringBoot环境变量配置错误:占位符解析失败解决方案
  • 【数据结构与算法-Day 12】深入浅出栈:从“后进先出”原理到数组与链表双实现
  • 奔图P2500NW打印机加碳粉方法
  • 《Transformer黑魔法Mask与Softmax、Attention的关系:一个-∞符号如何让AI学会“选择性失明“》
  • 深入理解 qRegisterMetaType<T>()
  • DAY32打卡
  • 字符输入流—read方法
  • Kotlin Native调用C curl
  • 内部类详解:Java中的嵌套艺术
  • WebView 中控制光标
  • Diamond基础1:认识Lattice器件
  • 数据结构 二叉树(1)二叉树简单了解
  • Linux学习-数据结构(栈和队列)
  • 8.6学习总结
  • Selenium在Pyhton应用
  • Java 大视界 -- Java 大数据机器学习模型在电商用户生命周期价值评估与客户关系精细化管理中的应用(383)
  • 应急响应排查(windows版)
  • Vue计算属性详解2
  • Python Pandas.lreshape函数解析与实战教程
  • 机器学习模型调优实战指南
  • 关于应急响应的那些事
  • 第14届蓝桥杯Scratch选拔赛初级及中级(STEMA)真题2023年3月12日
  • 人工智能-python-机器学习实战:特征降维、PCA与KNN的核心价值解析
  • Linux: NFS 服务部署与autofs自动挂载的配置
  • 分隔串处理方法