平均意见得分是体验质量和电信工程领域中使用的一种度量,表示刺激或系统的整体质量。它是所有单个“预定义范围内的值的算术平均值,主体根据该值对系统质量的表现给出自己的意见”。此类评级通常在主观质量评估测试中收集,但也可以通过算法估算。其是视频、音频和视听质量评估的常用衡量标准,但不限于这些方式。

平均意见得分用一个有理数表示,通常在 1-5 的范围内,其中 1 表示感知质量最低,5 表示感知质量最高。其他平均意见得分范围也是可能的,具体取决于基础测试中使用的评分标准。绝对类别评分标准非常常用,它将差和优秀之间的评分映射到 1 到 5 之间的数字,如下表所示。

$$ \begin{array}{c|c} \hline \text { 评分 } & \text { 标签 } \\ \hline 5 & \text { 优秀 } \\ \hline 4 & \text { 良好 } \\ \hline 3 & \text { 一般 } \\ \hline 2 & \text { 差 } \\ \hline 1 & \text { 糟糕 } \\ \hline \end{array} $$

平均意见得分计算为人类受试者在主观质量评估测试中对给定刺激进行的单个评分的算术平均值。因此:

$$ 平均意见得分=\frac{\sum_{n=1}^N R_n}{N} $$

其中 R 是 N 受试者对给定刺激的个人评分。

平均意见得分过去源于主观测量,即听众坐在“安静的房间”中,根据自己对电话通话质量的感受进行评分。这种测试方法已在电话行业使用了数十年,并在 ITU-T P.800 建议书中进行了标准化。它规定“讲话者应坐在安静的房间中,房间容积在 30 到 120 立方米之间,混响时间小于 500 毫秒(最好在 200-300 毫秒范围内)。房间噪音水平必须低于 30 dBA,且频谱中没有主要峰值。”后来的 ITU-T 建议书中也同样规定了对其他模式的要求。

获取平均意见得分评分可能既耗时又费钱,因为需要招募人工评估员。对于各种用例,例如编解码器开发或服务质量监控目的(需要反复自动评估质量),平均意见得分分数也可以通过客观质量模型来预测,这些模型通常是使用人工平均意见得分评分来开发和训练的。使用此类模型产生的一个问题是,产生的平均意见得分差异是否对用户来说是显而易见的。例如,当按五分制平均意见得分量表对图像进行评级时,平均意见得分等于 5 的图像质量预计会明显优于平均意见得分等于 1 的图像。与此相反,平均意见得分等于 3.8 的图像质量是否明显优于平均意见得分等于 3.6 的图像则并不明显。针对确定用户可感知的数码照片最小平均意见得分差异进行的研究表明,平均意见得分差异约为 0.46 才能让 75% 的用户能够检测到更高质量的图像。然而,随着用户期望的变化,图像质量期望也会随时间而变化。因此,使用等分析方法确定的最小可察觉平均意见得分差异可能会随时间而变化。

JavaScript音频视频实时通信5分制平均意见得分

生成音频和视频分数

 function score(stats) {
   const scores = {};
   const { audio, video } = normalize(stats);
   if (audio) {
     const delay = 20 + audio.bufferDelay + audio.roundTripTime / 2;
     const pl = audio.packetLoss;
     const R0 = 100;
     const Ie = audio.dtx
       ? 8
       : audio.bitrate
       ? clamp(55 - 4.6 * Math.log(audio.bitrate), 0, 30)
       : 6;
     const Bpl = audio.fec ? 20 : 10;
     const Ipl = Ie + (100 - Ie) * (pl / (pl + Bpl));
 ​
     const Id = delay * 0.03 + (delay > 150 ? 0.1 * (delay - 150) : 0);
     const R = clamp(R0 - Ipl - Id, 0, 100);
     const MOS = 1 + 0.035 * R + (R * (R - 60) * (100 - R) * 7) / 1000000;
 ​
     scores.audio = clamp(Math.round(MOS * 100) / 100, 1, 5);
   }
   if (video) {
     const pixels = video.expectedWidth * video.expectedHeight;
     const codecFactor = video.codec === 'vp9' ? 1.2 : 1.0;
     const delay = video.bufferDelay + video.roundTripTime / 2;
     if (video.frameRate !== 0) {
       const bPPPF = (codecFactor * video.bitrate) / pixels / video.frameRate;
       const base = clamp(0.56 * Math.log(bPPPF) + 5.36, 1, 5);
       const MOS =
         base -
         1.9 * Math.log(video.expectedFrameRate / video.frameRate) -
         delay * 0.002;
       scores.video = clamp(Math.round(MOS * 100) / 100, 1, 5);
     } else {
       scores.video = 1;
     }
   }
   return scores;
 }
 ​
 function normalize(stats) {
   return {
     audio: stats.audio
       ? {
           packetLoss: 0,
           bufferDelay: 50,
           roundTripTime: 50,
           fec: true,
           ...stats.audio,
         }
       : undefined,
     video: stats.video
       ? {
           packetLoss: 0,
           bufferDelay: 0,
           roundTripTime: 50,
           fec: false,
           expectedHeight: stats.video.height || 640,
           expectedWidth: stats.video.width || 480,
           frameRate: stats.video.expectedFrameRate || 30,
           expectedFrameRate: stats.video.frameRate || 30,
           ...stats.video,
         }
 ​
   };
 }
 ​
 function clamp(value, min, max) {
   return Math.max(min, Math.min(value, max));
 }
 ​
 module.exports = { score };

https://embed.notionlytics.com/wt/ZXlKM2IzSnJjM0JoWTJWVWNtRmphMlZ5U1dRaU9pSlhiRWhvWlV4VVQxbHNjMlZYV2tKbU9URndaU0lzSW5CaFoyVkpaQ0k2SWpaaE5qTmpOakZqTTJOaE5UUm1PV0poT0dSak1qRTNPVEZsWVdabFpUUmlJbjA9