fix: 修复DOCX独立封面视觉门禁
统一 Chromium 与 DOCX 的受限高度封面布局,补齐可编辑边框、装饰边和 Office 页面舍入适配。 按四套独立封面主题、纵横方向及五组页边距执行 40 个 Word/WPS 真实视觉场景,严格失败为 0,并保留正文诊断供 D4 修复。
This commit is contained in:
@@ -67,6 +67,80 @@ function calculateBinaryIou(
|
||||
return union === 0 ? 1 : intersection / union;
|
||||
}
|
||||
|
||||
function dilateBinaryMask(
|
||||
input: Uint8Array,
|
||||
width: number,
|
||||
height: number,
|
||||
radius: number,
|
||||
): Uint8Array {
|
||||
if (radius === 0) {
|
||||
return input;
|
||||
}
|
||||
const horizontal = new Uint8Array(input.length);
|
||||
const output = new Uint8Array(input.length);
|
||||
for (let y = 0; y < height; y += 1) {
|
||||
const rowOffset = y * width;
|
||||
for (let x = 0; x < width; x += 1) {
|
||||
const start = Math.max(0, x - radius);
|
||||
const end = Math.min(width - 1, x + radius);
|
||||
for (let sampleX = start; sampleX <= end; sampleX += 1) {
|
||||
if (input[rowOffset + sampleX] === 1) {
|
||||
horizontal[rowOffset + x] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let y = 0; y < height; y += 1) {
|
||||
for (let x = 0; x < width; x += 1) {
|
||||
const start = Math.max(0, y - radius);
|
||||
const end = Math.min(height - 1, y + radius);
|
||||
for (let sampleY = start; sampleY <= end; sampleY += 1) {
|
||||
if (horizontal[sampleY * width + x] === 1) {
|
||||
output[y * width + x] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function calculateSpatiallyTolerantIou(
|
||||
left: Uint8Array,
|
||||
right: Uint8Array,
|
||||
width: number,
|
||||
height: number,
|
||||
radius: number,
|
||||
): number {
|
||||
if (radius === 0) {
|
||||
return calculateBinaryIou(left, right);
|
||||
}
|
||||
const dilatedLeft = dilateBinaryMask(left, width, height, radius);
|
||||
const dilatedRight = dilateBinaryMask(right, width, height, radius);
|
||||
let leftCount = 0;
|
||||
let rightCount = 0;
|
||||
let matchedLeft = 0;
|
||||
let matchedRight = 0;
|
||||
for (let index = 0; index < left.length; index += 1) {
|
||||
if (left[index] === 1) {
|
||||
leftCount += 1;
|
||||
if (dilatedRight[index] === 1) {
|
||||
matchedLeft += 1;
|
||||
}
|
||||
}
|
||||
if (right[index] === 1) {
|
||||
rightCount += 1;
|
||||
if (dilatedLeft[index] === 1) {
|
||||
matchedRight += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
const matchedIntersection = (matchedLeft + matchedRight) / 2;
|
||||
const union = leftCount + rightCount - matchedIntersection;
|
||||
return union === 0 ? 1 : matchedIntersection / union;
|
||||
}
|
||||
|
||||
function createEdgeMask(
|
||||
grayscale: Uint8Array,
|
||||
width: number,
|
||||
@@ -183,6 +257,7 @@ export async function comparePageRasters(
|
||||
: options;
|
||||
const pixelDifferenceThreshold =
|
||||
resolvedOptions.pixelDifferenceThreshold ?? 8;
|
||||
const spatialTolerancePx = resolvedOptions.spatialTolerancePx ?? 4;
|
||||
if (
|
||||
!Number.isInteger(pixelDifferenceThreshold) ||
|
||||
pixelDifferenceThreshold < 0 ||
|
||||
@@ -190,6 +265,13 @@ export async function comparePageRasters(
|
||||
) {
|
||||
throw new Error("像素变化阈值必须是 0 到 255 之间的整数");
|
||||
}
|
||||
if (
|
||||
!Number.isInteger(spatialTolerancePx) ||
|
||||
spatialTolerancePx < 0 ||
|
||||
spatialTolerancePx > 8
|
||||
) {
|
||||
throw new Error("空间容差必须是 0 到 8 之间的整数像素");
|
||||
}
|
||||
const width =
|
||||
resolvedOptions.targetWidthPx ??
|
||||
Math.max(baseline.widthPx, candidate.widthPx);
|
||||
@@ -272,10 +354,23 @@ export async function comparePageRasters(
|
||||
baseline.widthPx === candidate.widthPx &&
|
||||
baseline.heightPx === candidate.heightPx,
|
||||
geometryNormalized: resolvedOptions.geometryNormalized ?? false,
|
||||
spatialTolerancePx,
|
||||
meanAbsoluteError: absoluteError / (pixelCount * 3),
|
||||
changedPixelRatio: changedPixels / pixelCount,
|
||||
inkIou: calculateBinaryIou(baselineInk, candidateInk),
|
||||
edgeIou: calculateBinaryIou(baselineEdges, candidateEdges),
|
||||
inkIou: calculateSpatiallyTolerantIou(
|
||||
baselineInk,
|
||||
candidateInk,
|
||||
width,
|
||||
height,
|
||||
spatialTolerancePx,
|
||||
),
|
||||
edgeIou: calculateSpatiallyTolerantIou(
|
||||
baselineEdges,
|
||||
candidateEdges,
|
||||
width,
|
||||
height,
|
||||
spatialTolerancePx,
|
||||
),
|
||||
};
|
||||
return {
|
||||
metrics,
|
||||
|
||||
Reference in New Issue
Block a user