资讯动态

PESQ语音质量评测实战:从mos-pesq.zip解压到MOS分数批量计算

发布时间:2026/10/20 15:37:32 来源:尧图企业网站定制
简介mos-pesq.zip是一份面向音频开发与语音质量评测人员的完整PESQ工具包提供基于ITU-T P.862标准的感知语音质量评估实现。它通过命令行可对PCM音频文件进行MOS打分适用于验证编解码算法对语音质量的影响尤其适合在嵌入式或实时通信项目中快速定位音质损耗。包内共12个文件以C语言源码为主5个.c源文件实现核心算法与主程序3个.h头文件定义接口配合Makefile可一键编译生成可执行文件另含shell测试脚本、README说明文档以及一个内含测试音频的zip数据包整体约207KB轻量易部署。目前已有963人学习过该资源。使用者能获得从源码编译到命令行运行的全套指引通过对比原始语音与带噪语音的PESQ分值可量化评估PCM编解码前后的语音质量为音频链路质量的调优提供客观依据。1. 这个压缩包到底装了什么为什么 MOS 评测都绕不开它做语音质量评测的人电脑里几乎都会躺着一个叫mos-pesq.zip的压缩包。它可能来自同事的网盘分享可能是某次项目交付的附件也可能是你从某个技术论坛里捞到的资源。这个文件名指向一个明确的用途用 PESQ 算法给语音质量打分输出一个可量化的 MOS 值用来判断你的编解码器、网络传输、降噪算法到底有没有把声音搞坏。很多人第一次拿到这个压缩包解压之后对着里面的可执行文件、源码目录和一大堆.wav文件发懵不知道先跑哪个命令、参数怎么填、结果怎么看。这篇笔记就沿着「是什么 → 怎么跑 → 参数怎么设 → 坑在哪 → 怎么批量用」这条线把mos-pesq.zip里的东西讲透。无论你是做音频算法的开发者、通信设备的测试工程师还是刚入门语音质量评测的学生照着这篇文章能把一条 PESQ 评测流程从零跑通并且能看懂每一个分数背后的意义。PESQ 本质上是 ITU-T P.862 标准定义的一种客观语音质量评估算法。它通过比较一个原始参考信号和一个经过传输链路后受损的退化信号计算出一个分数范围通常在 1.0 到 4.5 之间理论极限是 -0.5 到 4.5实际语音场景很少跌到 1.0 以下。分数越接近 4.5说明退化信号跟原始信号越接近传输链路对语音的损伤越小。这个算法在 VoIP 音质评测、编解码器质量对比、降噪算法效果验证里是事实上的标配所以mos-pesq.zip里装的无非是 PESQ 算法的现成实现外加配套的脚本和样例文件。接下来我们把它的结构、原理和用法一层层拆开。2. 从 MOS 到 PESQ先搞懂分数是怎么算出来的2.1 MOS 的主观出身与客观替代MOSMean Opinion Score平均意见分最初是个纯粹的主观指标。评测机构找一批听音人让他们听一段经过被测系统的语音然后按 1 到 5 分打分5 分是“完美听不出任何损伤”4 分是“能察觉损伤但不影响理解”3 分是“有明显损伤需要集中注意力才能听懂”2 分是“几乎听不懂”1 分是“完全不可用”。把所有听音人的打分取平均就是这段语音的 MOS 分。主观评测的问题显而易见费时费力费钱而且结果不可复现。今天找 A 组人打分和明天找 B 组人打分结果可能差出 0.5 分同一组人换个环境重听一遍也会波动。工程上需要的是一个确定性的、可自动化的替代指标于是 PESQ 这类客观评估算法应运而生。PESQ 的思想是既然人耳对语音的感知可以通过心理声学模型近似模拟那就把参考信号和退化信号都映射到一个“内部表示”空间在这个空间里计算两者的差异再映射回 MOS 尺度。理解这一点很重要因为 PESQ 输出的分数虽然有 MOS 之名但它更适合叫 MOS-LQOListening Quality Objective客观听音质量分。它不是真的找了一群人来打分而是用一个数学模型去“猜”这些人会打多少分。在实际使用中PESQ 分数和主观 MOS 分大体趋势一致但在边缘场景极端丢包、强背景噪声、非线性失真下会有偏差这一点在后面讲避坑时要反复提到。2.2 PESQ 的核心计算流程时频映射与扰动密度PESQ 的计算流程可以拆成几个阶段理解它有助于你判断“这个分数可不可信”以及“分数异常时问题出在哪”。电平对齐与时间对齐PESQ 第一步会对参考信号和退化信号做增益补偿消除音量差异带来的影响。同时它会用互相关方法估算两个信号之间的时延把退化信号的时间轴对齐到参考信号上。这一步非常关键因为如果信号没对齐后面算出来的差异会非常大分数会极其难看。听觉变换把对齐后的两个信号分别通过一个模拟人耳频率响应的滤波器组转换到 Bark 域一种模仿人耳听觉特性的频率尺度。每个频带上的信号再经过响度变换映射成“响度谱”。这个响度谱模拟的是人耳对各个频率成分的感知强度单位是 sone。扰动密度计算在响度谱上逐帧计算参考信号和退化信号的差异得到扰动密度disturbance density。扰动密度分为正值退化信号比参考信号能量大比如新增了噪声和负值退化信号比参考信号能量小比如丢帧导致语音缺失。PESQ 会对正负扰动分别做不对称处理因为人耳对不同类型损伤的敏感度不一样——对新增噪声的容忍度比对丢帧的容忍度略高。聚合映射把所有帧的扰动密度聚合先按频带和帧做时间-频率平均再映射到 PESQ 得分原始分最后通过一个逻辑函数转换成 MOS-LQO 分数。这个流程决定了 PESQ 的一个本质特性它是一个全参考full-reference算法必须同时有原始参考音频和退化音频才能评测。如果你的场景里拿不到干净参考信号比如实时网络流里的任意一段语音PESQ 就不适用需要换无参考non-intrusive算法这是后话。2.3mos-pesq.zip里可能装了什么代码、二进制、脚本与样例虽然不同渠道拿到的mos-pesq.zip内容细节会不一样但常见的包内结构有很强的规律性。解压之后你应该先看一下根目录确认里面有没有可执行文件Linux 下通常叫pesq或pesq.exeWindows 下可能带.exe后缀有没有src源码目录有没有Makefile或编译脚本有没有存着.wav样例文件的audio或samples目录以及有没有说明文档README、doc目录。包内结构不固定取决于打包的人用的是哪个版本、什么平台有的包只放编译好的可执行文件加几个样例音频有的包把完整源码也塞进去了方便你在目标平台上重新编译。我这里说一句经验之谈拿到压缩包先别急着跑先把包内文件列一遍。用unzip -l mos-pesq.zip就能在不解压的情况下看清单判断里面是二进制发行包还是源码包。如果看到Makefile和一堆.c/.m文件说明你要走编译这条路如果看到现成的pesq可执行文件那就直接授权加运行能省不少事。3. 用 mos-pesq.zip 在本地跑通一条最小评测流程3.1 解压与文件清单核对拿到mos-pesq.zip第一步自然是解压。假设你把它放在了~/mos目录下在 Linux 环境里执行mkdir -p ~/mos cd ~/mos unzip mos-pesq.zip -d mos-pesq cd mos-pesq ls -la命令解释unzip mos-pesq.zip -d mos-pesq解压到mos-pesq目录里不直接解压到当前目录是为了避免压缩包内部文件结构凌乱时污染现有目录。ls -la看全部文件包括隐藏文件和权限位。如果ls列出的文件里有一个绿色可执行的pesq说明包里带现成二进制如果只看到.c、.h、Makefile你就需要编译。参数说明-d指定解压目标目录目录不存在时会自动创建ls -la里的-a显示隐藏文件-l查看权限和大小。这步的核心目的是确认你到底拿到的是哪种包决定后续流程。3.2 Linux 下现成二进制直接跑第一条命令与第一个分数如果解压后有可执行的pesq文件在终端里先给它加执行权限然后看一眼它的帮助信息chmod x pesq ./pesq命令解释chmod x pesq给pesq添加执行权限因为通过unzip解压出来的文件通常不带可执行位。如果包里的二进制是在别的 Linux 发行版上编译的还可能遇到动态链接库不匹配的问题这时直接运行./pesq会报error while loading shared libraries后面讲排毒时会处理。运行之后通常会打印一段帮助文字告诉你用法。不同版本的 PESQ 程序用法略有差异但主流形式有两种# 形式一参数带采样率 ./pesq 8000 ref.wav deg.wav # 形式二不指定采样率自动识别 ./pesq ref.wav deg.wav这里8000表示窄带 8kHz 采样率。为什么采样率这个参数这么重要因为 PESQ 的算法模型是按两个采样率体系设计的8kHz 对应窄带语音频率范围 300-3400Hz就是传统电话线路的带宽16kHz 对应宽带语音频率到 7kHz对应现代 VoIP 和高清语音。选错采样率不会报错但分数会失真因为算法内部滤波器的频率响应是按目标采样率设计的。执行后会看到类似这样的输出PESQ Score (MOS-LQO) : 3.214 Raw MOS : 3.412第一行是最终分数第二行是中间计算值。发布评测报告、对比算法性能时看 MOS-LQO不看 Raw MOS。3.3 源码包编译Makefile 与依赖检查如果mos-pesq.zip里装的是源码那就需要先编译。PESQ 官方参考实现是用 C 语言写的标准情况下只需要一个 C 编译器和 make 工具不需要第三方库。编译步骤通常非常简单cd ~/mos/mos-pesq make如果make成功会在当前目录生成pesq可执行文件。如果失败最常见的报错是gcc: command not found说明系统没装编译器。Debian/Ubuntu 系列执行sudo apt-get install build-essentialCentOS/RHEL 系列执行sudo yum install gcc make。装完再跑make。make的细节它会读取Makefile里的规则调用gcc编译源码目录下的多个.c文件生成目标文件后再链接成pesq。你不需要理解每一个编译选项只需要知道如果编译过程中出现error:开头的行基本都是源码本身对环境的要求没满足或者编译器版本太旧。这个项目年代比较早有些代码拿到新的 GCC 环境会遇到隐式声明之类的编译告警甚至有报错。我的建议编译报错先查是不是-lm没链接数学库在 Makefile 的链接行末尾加上-lm再试这是老 C 项目最常见的编译翻车点。3.4 准备一对测试音频参考与退化跑第一条 PESQ 命令之前需要一对文件一个是干净参考信号一个是待测退化信号。样例目录里通常自带一对示例你可以先跑样例熟悉流程。假如样例文件不完整或者你想用自己的音频记住三个硬性要求WAV 格式、PCM 编码、位深 16bit 或 32bit。PESQ 参考实现对输入格式有限制不是任何 WAV 都能跑。如果是 8kHz 窄带评测音频必须是 8kHz 采样率、16bit 线性 PCM 的单声道 WAV如果是 16kHz 宽带评测音频必须是 16kHz 采样率、16bit 线性 PCM。用 ffmpeg 转换的时候命令如下ffmpeg -i input.mp3 -ar 8000 -ac 1 -sample_fmt s16 ref.wav ffmpeg -i degraded.mp3 -ar 8000 -ac 1 -sample_fmt s16 deg.wav命令解释-ar 8000重采样到 8kHz-ac 1转成单声道-sample_fmt s16强制输出 16bit 有符号整数 PCM。为什么强制这些参数因为 PESQ 对波形做逐样本对比输入格式不统一会造成程序拒绝处理或静默跑出错误结果。关于参考信号和退化信号的对应关系在生成退化信号之前就要想清楚参考信号是“端到端链路输入”退化信号是“你从链路另一头采集到的输出”。如果你在做编解码器评测参考信号是编码器输入的原始音频退化信号是解码器输出的音频如果你在做网络丢包模拟评测参考信号是发送端的原始音频退化信号是丢包补偿后的接收端音频。两者的内容必须同源同内容只是经过了被测系统的处理时延和损伤是允许存在的。跑完第一条命令后你还要知道一个关键点PESQ 参考实现处理音频文件时对时长有内部限制。一般 8 到 20 秒的语音片段是最稳的太短少于 3 秒会导致统计意义不足分数忽高忽低太长超过 60 秒可能导致程序内部内存溢出或处理时间过长某些老实现甚至会直接崩溃。所以做评测前把音频截取成 10 秒左右的片段是标准操作。4. 参数与边界采样率、响度、时长与分数解读4.1 采样率参数是整个评测流程最容易被忽略的玄学PESQ 有两种运行模式按采样率区分这个参数直接影响算法内部的滤波器组设计和扰动计算方式。如果你用的是8000参数程序会把输入音频按 8kHz 处理但不会做重采样——它默认你的音频就是 8kHz输入 16kHz 的音频不会自动降采样而是当成 8kHz 音频直接解析后果是频谱上原本 4-7kHz 的能量被错误映射分数失真。目前主流的mos-pesq包已经能做到pesq ref.wav deg.wav这种不指定采样率的用法程序会读取 WAV 头里的采样率字段自行决定模式。但老版本的程序必须手动指定模式。怎么判断你手里的包支持哪种用法解压后直接不带参数运行./pesq看帮助文本usage: pesq 8000 ref deg说明要手填模式usage: pesq ref deg说明自动识别。16kHz 宽带的评测程序参数在某些版本里写作16000。这里有一个从业者们常踩的坑8kHz 模式下给 16kHz 音频或者 16kHz 模式下给 8kHz 音频程序不报错分数看起来也正常但和主观听感差得很远。这属于那种“错得很安静”的故障比报错更危险。4.2 输入文件格式约束位深、编码、头信息PESQ 对输入 WAV 文件的要求可以列成一张表建议贴在工位旁边对照项目窄带模式宽带模式采样率8000 Hz16000 Hz位深16 bit PCM16 bit PCM声道数单声道1 ch单声道1 ch编码PCM无压缩PCM无压缩文件头WAV / WaveWAV / Wave不符合要求的文件在运行时会出现两种典型表现一种是程序直接报Unsupported format或Error: unknown format告诉你文件头解析不了另一种是程序正常执行、正常输出分数但分数意义不明——比如你把一个 48kHz 的音频喂进去程序只读取前 8k 个采样点对应的秒数导致实际评测的只是音频的一小段分数完全没代表性。处理非标准文件的方法比较固定通过 ffmpeg 或 sox 统一转格式。前面已经给了 ffmpeg 的转换命令思路是重采样、转单声道、指定位深一气呵成。日常评测流程里我习惯先做一个preprocess.sh脚本把所有待测音频统一转成程序要求的格式再进入评测环节避免手动转换漏掉某些文件。4.3 读对、读懂分数MOS-LQO 与置信区间PESQ 程序输出的 MOS-LQO 是一个点估计值它没有给你置信区间。实际工作中同一个退化音频你可能要评测多次用不同参考片段或多次重放采集退化信号最后看到的分数是一个分布。我一般会跑至少 3 到 5 个不同的语音片段比如男女声各两段内容不同把 MOS-LQO 分数取平均才作为这一组对比条件的最终结果。分数解读还要结合具体场景。做编解码器对比时MOS-LQO 在 4.0 以上算良好3.5 到 4.0 是普通3.5 以下用户容易感知到明显音质损伤。但这是经验值不同测试语音集的结果不能直接比较——这就是为什么 PESQ 评测报告里必须写清楚用了哪些测试音频、什么采样率、什么对比条件。同一套算法在用男声朗读新闻的片段里得 4.2在吵闹背景下的女声片段里得 3.1两个分数离散度很大只报一个平均值反而掩盖了问题。5. 避坑与排查PESQ 评测里最常见的 4 个翻车现场5.1 分数高得离谱4.3 以上但听感明显有问题现象退化信号里明明有可感知的底噪或失真人耳打分大概 3 分左右PESQ 却给出 4.3 以上。原因最常见的是参考信号和退化信号没对齐好程序自动对齐功能失效后把信号里的某一段和参考信号的另一段匹配上了。还有一种可能你拿错了参考信号退化信号的波形既然和参考信号同源程序算不出差异分数自然虚高。第三种可能参考信号本身已经带了损伤程序对比的是“退化信号”和“本来就有点脏的参考”两者差异小分高。解决停下手里的活先检查测试音频的原始素材。用 Audacity 或 Python 脚本把参考信号和退化信号的波形叠在一起看确认内容对齐、幅度近似。同时确认参考信号是链路输入端的干净原始信号不是从链路中间某个节点偷录的二次信号。5.2 采样率不符程序直接退出现象运行./pesq 8000 ref.wav deg.wav时程序报Error: sample frequency is not matching或者更隐晦的File has not the expected format。原因mos-pesq里的程序实现往往同时支持 8kHz 和 16kHz 两套内部模型但它在处理之前会检查 WAV 文件头里的采样率字段。如果你指定8000而 WAV 文件里写的是 16000程序判断模式与文件不一致直接拒绝执行。解决确认评测目标窄带评测就统一把音频转成 8000 Hz宽带评测就转成 16000 Hz。用 ffmpeg 批量转换后再执行。不要试图用8000去评测一个 16kHz 的音频文件那没有任何意义。5.3 程序正常跑完但一段音频的分数比另一段低了 1.5 分以上现象同一个退化信号用不同参考片段评测分数离散度极大。原因某一段参考片段的起始位置和退化信号有几百毫秒的静音前缀PESQ 做时间对齐时把这部分静音当成了时延的一部分对齐偏差拉到 300-500ms 后语音内容根本对不上出了个极低分。还有一个常见原因是片段内容本身太短评测窗口不足半秒有效语音扰动统计没有意义。解决处理音频时统一做首尾静音切除保证参考和退化信号从第一个有效语音帧开始内容完全一一对应。如果需要在长录音中截取片段保持参考和退化信号的截取起始点一致。做完静音处理后重新跑一遍看分数是否回到合理区间。5.4 波形幅度差异大分数不合格现象退化信号听起来只是比参考信号音量大了一些或者音量小了一些PESQ 分数掉到 2 分以下。原因PESQ 的电平对齐模块会尝试做增益补偿但这个能力是有限度的。如果链路里增益差超过约 30dB或者出现了削波失真数字削波会产生大量非线性谐波对齐失效分数被严重拉低。人耳对纯音量大小变化不敏感但 PESQ 对幅度异常会很敏感。解决评测流程里加一个响度归一化步骤用ffmpeg loudnorm或先测有效值再统一乘一个增益系数把参考信号和退化信号的平均响度对齐到大致相同的水平。注意不要对退化信号做任何动态范围压缩那会改变它本身的损伤特征评测结果就不再反映真实链路了。这里我个人的习惯是退化信号只做幅度缩放不做 EQ 和动态处理。5.5 多平台移植Windows 与 Linux 的路径与换行符坑现象在 Windows 上解压mos-pesq.zip双击想运行的.exe有执行权限但运行时报缺少某个.dll或直接闪退而在 Linux 上解压脚本里的换行符变成了\r导致$\r: command not found。原因mos-pesq.zip里的可执行文件是针对特定平台编译的Windows 和 Linux 的二进制不通用。配套的.sh脚本或Makefile如果在 Windows 下用记事本编辑过再传回 Linux行尾的 CRLF 会让 bash 和 make 解析出错。解决Windows 上先看包里有没有.exe或.bat文件没有就说明这个包只支持 Linux需要换成对应平台版本Linux 上如果出现command not found但文件名明明存在先执行sed -i s/\r$// 脚本名.sh去掉 CRLF 再跑。这里多说一句这只是文本编码层面的问题和任何网络连接工具无关属于纯粹的文件格式兼容问题。6. 批量评测与回归门禁把一条命令变成一套自动化流程单条./pesq跑通只是起点实际工程里的需求永远是批量你要评测 10 组编解码器参数、20 条网络损伤条件、5 种降噪算法每组条件对应几十个语音片段。手动一条条跑不现实也违背了 PESQ 的自动化初衷。我一般会把它组织成一个带目录参数和结果归档脚本的批处理流程。下面给你一个可以直接抄走的 Python 脚本框架import os import subprocess import csv ref_dir refs # 参考信号目录 deg_dir degs # 退化信号目录子目录名代表测试条件 output_csv pesq_results.csv pesq_bin ./pesq conditions [d for d in os.listdir(deg_dir) if os.path.isdir(os.path.join(deg_dir, d))] with open(output_csv, w, newline) as f: writer csv.writer(f) writer.writerow([condition, ref_file, deg_file, mos_lqo]) for cond in conditions: deg_files [f for f in os.listdir(os.path.join(deg_dir, cond)) if f.endswith(.wav)] for deg_file in sorted(deg_files): ref_file deg_file # 假设参考文件和退化文件同文件名 ref_path os.path.join(ref_dir, ref_file) deg_path os.path.join(deg_dir, cond, deg_file) if not os.path.exists(ref_path): print(f[跳过] 缺少参考文件: {ref_file}) continue result subprocess.run([pesq_bin, 8000, ref_path, deg_path], capture_outputTrue, textTrue) # 从输出中解析 MOS-LQO 分数 for line in result.stdout.splitlines(): if MOS-LQO in line: score line.split(:)[-1].strip() writer.writerow([cond, ref_file, deg_file, score]) print(f{cond} / {deg_file} - {score})脚本逻辑说明先遍历degs目录下的所有子目录每个子目录名代表一种测试条件比如codec_a_24kbps、loss_5%对这个子目录里的每一个 WAV 文件在refs目录下找同名参考文件构造命令行调用pesq解析输出里的MOS-LQO分数写进 CSV。用subprocess.run而不是os.system是为了能稳定捕获命令输出解析分数时有更好的容错性。参数说明deg_dir下的子目录结构是这个脚本约定的参考文件名和退化文件名必须一致因为脚本用同一个文件名去两处找文件。如果你的文件命名习惯不同可以改成遍历时记录下标或者读取一个映射表核心逻辑不变。capture_outputTrue会把程序的标准输出捕获到内存里我们在里面搜MOS-LQO这一行取分数。程序出错了怎么办cust输出为空脚本不会崩CSV 里这一行不会写后续检查 CSV 时哪些条件缺行就一目了然。这种设计比中途抛异常更符合批量评测的容错需求。有了这个脚本评测结果就变成一张 CSV 表。我习惯在表上加一个汇总步骤每个条件取所有片段分数的平均值和标准差平均值横向对比不同条件的音质差距标准差反映分数稳定性。这并不是算完就完——把结果贴到一个简单的对比表里做算法调优和回归验证时能一眼看出哪个改动是有效的哪个改动只是玄学波动。我现在做编解码器优化时会把每次改动后的评测 CSV 归档成带时间戳的文件防止“这次结果比上次高 0.2 分”这种结论没有可追溯的数据支撑。说一个我自己的教训早期做评测时偷懒只跑一个 8 秒片段就得结论某次调整算法参数后这一个片段的分数提高到 4.2我就以为改动有效结果换了一个片段重测分数反而跌了 0.3。从那以后我再也不敢拿单个片段的分说事了批处理脚本和至少 5 个片段的平均值成了我的习惯底线。PESQ 是一个稳定的算法但语音内容本身是多变的评测流程的稳定性比单次分数的变化更能说明问题。希望这篇从解压到批处理的笔记能帮到你让你手里的mos-pesq.zip真正成为评测流程里可信赖的标尺而不是一个跑完就忘的黑匣子。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑