更新基准测试

This commit is contained in:
崮生(子虚) 2026-04-10 20:57:37 +08:00
parent 161bafc02a
commit 4ada92b6c5

View File

@ -5,9 +5,12 @@
* :
* 1. backend/font_util/font.ts fontSubset API
* 2. HTTP HTML
* 3. @font-face + HTTP URL
* 4. @font-face + HTTP URL
* 5. canvas SSIM
* 3. full使
* 4. fontSubset @font-face + HTTP URL
* 5. DOM SSIM
*
* 注意: full full
* subset SSIM
*
* :
* 1. fontSubset
@ -139,24 +142,90 @@ async function renderTextViaBrowser(
return { pixels, screenshot: Buffer.from(screenshot), inkPixels };
}
/** 计算两张图片的结构相似度(简化版 SSIM返回 0~1 */
/**
* SSIM (Wang et al. 2004)
* 使 11x11 + 0~1
*/
function calculateSSIM(a: Uint8Array, b: Uint8Array): number {
if (a.length !== b.length) return 0;
const pixelCount = a.length / 4;
let sumA = 0, sumB = 0, sumA2 = 0, sumB2 = 0, sumAB = 0;
for (let i = 0; i < pixelCount; i++) {
const width = Math.sqrt(a.length / 4) | 0;
const height = (a.length / 4 / width) | 0;
if (width === 0 || height === 0) return 0;
/** 转灰度并提取到独立数组 */
const N = width * height;
const grayA = new Float64Array(N);
const grayB = new Float64Array(N);
for (let i = 0; i < N; i++) {
const idx = i * 4;
const ga = 0.299 * a[idx] + 0.587 * a[idx + 1] + 0.114 * a[idx + 2];
const gb = 0.299 * b[idx] + 0.587 * b[idx + 1] + 0.114 * b[idx + 2];
sumA += ga; sumB += gb;
sumA2 += ga * ga; sumB2 += gb * gb; sumAB += ga * gb;
grayA[i] = 0.299 * a[idx] + 0.587 * a[idx + 1] + 0.114 * a[idx + 2];
grayB[i] = 0.299 * b[idx] + 0.587 * b[idx + 1] + 0.114 * b[idx + 2];
}
const meanA = sumA / pixelCount, meanB = sumB / pixelCount;
const varA = sumA2 / pixelCount - meanA * meanA;
const varB = sumB2 / pixelCount - meanB * meanB;
const covAB = sumAB / pixelCount - meanA * meanB;
const C1 = 6.5025, C2 = 58.5225;
return (2 * meanA * meanB + C1) * (2 * covAB + C2) / ((meanA * meanA + meanB * meanB + C1) * (varA + varB + C2));
/** 构建积分图: S(x,y) = sum of gray[0..x-1, 0..y-1] */
const w1 = width + 1;
const intA = new Float64Array(w1 * (height + 1));
const intA2 = new Float64Array(w1 * (height + 1));
const intB = new Float64Array(w1 * (height + 1));
const intB2 = new Float64Array(w1 * (height + 1));
const intAB = new Float64Array(w1 * (height + 1));
for (let y = 0; y < height; y++) {
const rowOff = y * width;
const irowOff = (y + 1) * w1;
for (let x = 0; x < width; x++) {
const va = grayA[rowOff + x];
const vb = grayB[rowOff + x];
const ip = irowOff + x + 1;
intA[ip] = va + intA[ip - 1] + intA[ip - w1] - intA[ip - w1 - 1];
intA2[ip] = va * va + intA2[ip - 1] + intA2[ip - w1] - intA2[ip - w1 - 1];
intB[ip] = vb + intB[ip - 1] + intB[ip - w1] - intB[ip - w1 - 1];
intB2[ip] = vb * vb + intB2[ip - 1] + intB2[ip - w1] - intB2[ip - w1 - 1];
intAB[ip] = va * vb + intAB[ip - 1] + intAB[ip - w1] - intAB[ip - w1 - 1];
}
}
/**
* [x1, x2) x [y1, y2)
* (x2-x1) * (y2-y1)
*/
const rectSum = (img: Float64Array, x1: number, y1: number, x2: number, y2: number) =>
img[y2 * w1 + x2] - img[y1 * w1 + x2] - img[y2 * w1 + x1] + img[y1 * w1 + x1];
/** 11x11 窗口, 半径=5 */
const R = 5;
const C1 = 6.5025; // (0.01 * 255)^2
const C2 = 58.5225; // (0.03 * 255)^2
let ssimSum = 0;
let windowCount = 0;
for (let y = R; y < height - R; y++) {
for (let x = R; x < width - R; x++) {
const x1 = x - R, x2 = x + R + 1;
const y1 = y - R, y2 = y + R + 1;
const n = (2 * R + 1) * (2 * R + 1);
const sA = rectSum(intA, x1, y1, x2, y2);
const sA2 = rectSum(intA2, x1, y1, x2, y2);
const sB = rectSum(intB, x1, y1, x2, y2);
const sB2 = rectSum(intB2, x1, y1, x2, y2);
const sAB = rectSum(intAB, x1, y1, x2, y2);
const muA = sA / n;
const muB = sB / n;
const sigmaA2 = sA2 / n - muA * muA;
const sigmaB2 = sB2 / n - muB * muB;
const sigmaAB = sAB / n - muA * muB;
const num = (2 * muA * muB + C1) * (2 * sigmaAB + C2);
const den = (muA * muA + muB * muB + C1) * (sigmaA2 + sigmaB2 + C2);
ssimSum += num / den;
windowCount++;
}
}
return windowCount > 0 ? ssimSum / windowCount : 0;
}
// ======== 测试配置 ========
@ -171,6 +240,7 @@ const testCases = [
{ label: "千字文前段", fontPath: "font/令东齐伋复刻体.ttf", fontName: "令东齐伋复刻体", text: "天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔", sourceType: "ttf" as const, outType: "woff2" as const, fullFormat: "truetype" },
/** OTF 字体 */
{ label: "otf-五个汉字", fontPath: "font/temp/BaiHuOTFJiaoYuHanZi-2.otf", fontName: "白狐教育汉字", text: "天地黄宇宙", sourceType: "otf" as const, outType: "ttf" as const, fullFormat: "opentype" },
{ label: "otf-思源黑体", fontPath: "font/temp/SourceHanSans-Regular.otf", fontName: "思源黑体", text: "天地玄黄宇宙", sourceType: "otf" as const, outType: "ttf" as const, fullFormat: "opentype" },
];
// ======== 主测试 ========