當工程師討論視覺回歸或網站變更監控時,像素層級的差異比對演算法(例如 pixelmatch 或歐基里德 RGB 距離)通常是預設的解決方案。

然而,在真實世界的網路環境中,逐像素比對在正常使用者互動與動態渲染條件下根本無法正常運作

  1. 彈性版面位移:單一 20px 的動態橫幅插入頁面頂端,會將後續所有 DOM 元素向下推移,導致下游 100% 的像素在 pixelmatch 測試中失敗,即使內容本身並未改變。
  2. 次像素抗鋸齒抖動:作業系統(macOS、Linux 與 Windows)會以細微的次像素抗鋸齒變化渲染字型字形,產生數千個偽陽性像素差異。
  3. 語義變更 vs. 外觀變更:段落中單字的修改應觸發局部警示,但英雄圖片中微小的色彩漸層變化則不應觸發緊急通知。

PageWatch.tech,我們結合傳統的 結構相似性指數(SSIM)ORB 特徵對齊 以及 孿生神經網路(SNN) 進行潛在空間語義比對,解決了上述問題。

本文將深入探討我們電腦視覺差異檢測管線的數學原理、神經網路架構以及 TypeScript 實作。


🧮 1. 超越像素比對:結構相似性指數(SSIM)

與原始的均方誤差(MSE)不同,SSIM 依據人類視覺感知,透過三個維度衡量視覺變化: 亮度對比度結構

數學上,兩張影像視窗 $x$ 與 $y$ 之間的 SSIM 定義為:

$$\text{SSIM}(x, y) = \frac{(2\mu_x\mu_y + C_1)(2\sigma_{xy} + C_2)}{(\mu_x^2 + \mu_y^2 + C_1)(\sigma_x^2 + \sigma_y^2 + C_2)}$$

其中:

  • $\mu_x, \mu_y$ 為局部像素平均強度。
  • $\sigma_x^2, \sigma_y^2$ 為局部變異數。
  • $\sigma_{xy}$ 為 $x$ 與 $y$ 之間的共變異數。
  • $C_1, C_2$ 為穩定常數。

SSIM 視窗滑動的 TypeScript 實作

以下程式碼片段示範如何在截圖 Canvas 緩衝區上實作 SSIM 局部視窗滑動:

/**
 * Calculates localized Structural Similarity Index (SSIM) map 
 * across two image buffers using an 8x8 Gaussian sliding window.
 */
export function calculateSSIMMap(
  img1: Float32Array,
  img2: Float32Array,
  width: number,
  height: number,
  windowSize = 8
): { meanSSIM: number; ssimMap: Float32Array } {
  const C1 = (0.01 * 255) ** 2;
  const C2 = (0.03 * 255) ** 2;

  const numWindowsX = Math.floor(width / windowSize);
  const numWindowsY = Math.floor(height / windowSize);
  const ssimMap = new Float32Array(numWindowsX * numWindowsY);

  let totalSSIM = 0;

  for (let wy = 0; wy < numWindowsY; wy++) {
    for (let wx = 0; wx < numWindowsX; wx++) {
      let sumX = 0, sumY = 0, sumX2 = 0, sumY2 = 0, sumXY = 0;
      const count = windowSize * windowSize;

      for (let dy = 0; dy < windowSize; dy++) {
        for (let dx = 0; dx < windowSize; dx++) {
          const px = wx * windowSize + dx;
          const py = wy * windowSize + dy;
          const idx = py * width + px;

          const v1 = img1[idx];
          const v2 = img2[idx];

          sumX += v1;
          sumY += v2;
          sumX2 += v1 * v1;
          sumY2 += v2 * v2;
          sumXY += v1 * v2;
        }
      }

      const muX = sumX / count;
      const muY = sumY / count;
      const varX = sumX2 / count - muX * muX;
      const varY = sumY2 / count - muY * muY;
      const covXY = sumXY / count - muX * muY;

      const num = (2 * muX * muY + C1) * (2 * covXY + C2);
      const den = (muX * muX + muY * muY + C1) * (varX + varY + C2);
      const ssim = num / den;

      const windowIdx = wy * numWindowsX + wx;
      ssimMap[windowIdx] = ssim;
      totalSSIM += ssim;
    }
  }

  const meanSSIM = totalSSIM / (numWindowsX * numWindowsY);
  return { meanSSIM, ssimMap };
}

Enter fullscreen mode Exit fullscreen mode


🎯 2. 透過特徵點對齊(ORB/SIFT)補償彈性版面位移

當網頁因新增頂端元素而向下位移時,單獨使用 SSIM 仍會將位移區域標記為差異。

為了解決此問題,我們應用 定向快速特徵與旋轉二進位描述子(ORB) 特徵匹配,計算單應性矩陣,以在比對前對齊動態版面偏移:

Baseline Image                 Shifted Image               Homography Corrected
┌──────────────┐              ┌──────────────┐              ┌──────────────┐
│  [ Header ]  │              │  (NEW BANNER)│              │  [ Header ]  │
│  [ Article ] │  ──Offset──> │  [ Header ]  │  ──Warp───>  │  [ Article ] │ (Aligned)
│  [ Footer ]  │              │  [ Article ] │              │  [ Footer ]  │
└──────────────┘              └──────────────┘              └──────────────┘

Enter fullscreen mode Exit fullscreen mode

  1. 提取關鍵點:辨識穩定的幾何興趣點(按鈕、商標角點、結構性文字邊界)。
  2. 計算邊界框平移向量:計算各版面區域的 $(dx, dy)$ 平移偏移量。
  3. 剛體轉換:在計算 SSIM 差異前,將候選影像扭曲回與基準影像對齊。

🧠 3. 透過孿生神經網路與 ONNX Runtime 產生語義嵌入

對於複雜的網頁元件(例如動態圖表、變更中的頭像或樣式化排版),像素或 SSIM 比對仍可能過於敏感。

我們透過在 ONNX Runtime 中執行輕量級的 孿生 ResNet-18 神經網路,將截圖區域投影至 128 維潛在特徵空間,解決此問題。

          ┌─────────────────────┐
          │ Baseline Image Patch│ ────► [ ResNet-18 ] ────► Embedding Vector A (128d)
          └─────────────────────┘                                │
                                                                 ▼
                                                        Cos-Similarity Loss
                                                                 ▲
          ┌─────────────────────┐                                │
          │ Candidate Image Patch│ ───► [ ResNet-18 ] ────► Embedding Vector B (128d)
          └─────────────────────┘

Enter fullscreen mode Exit fullscreen mode

若嵌入向量 A 與向量 B 之間的餘弦距離小於閾值 $\epsilon$,系統會將該變更視為 外觀/非語義變更(例如抗鋸齒字型渲染或輕微的色彩平衡調整)。

ONNX Runtime 邊緣推論程式碼片段

import * as orb from "onnxruntime-node";

let inferenceSession: orb.InferenceSession | null = null;

export async function getModelSession(): Promise<orb.InferenceSession> {
  if (!inferenceSession) {
    // Load quantized ResNet-18 model optimized for layout embedding
    inferenceSession = await orb.InferenceSession.create(
      "./models/visual_embedding_resnet18_quantized.onnx"
    );
  }
  return inferenceSession;
}

/**
 * Computes 128-dimensional latent space feature embeddings for a given visual patch.
 */
export async function computeSemanticEmbedding(
  patchFloat32Tensor: orb.Tensor
): Promise<Float32Array> {
  const session = await getModelSession();
  const feeds: Record<string, orb.Tensor> = { input: patchFloat32Tensor };

  const results = await session.run(feeds);
  const embedding = results.output.data as Float32Array;

  return embedding;
}

/**
 * Calculates Cosine Similarity between two 128d embeddings.
 */
export function cosineSimilarity(a: Float32Array, b: Float32Array): number {
  let dotProduct = 0;
  let normA = 0;
  let normB = 0;

  for (let i = 0; i < a.length; i++) {
    dotProduct += a[i] * b[i];
    normA += a[i] * a[i];
    normB += b[i] * b[i];
  }

  return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}

Enter fullscreen mode Exit fullscreen mode


⚡ 4. 多層級電腦視覺管線

以下是 PageWatch.tech 在比對兩張快照時,完整電腦視覺管線的執行流程:

  1. 步驟 1:結構雜湊檢查(邊緣端) — 即時 HTML AST 檢查。若完全相同,提前中止(成本 = 0ms)。
  2. 步驟 2:全域 SSIM 地圖產生 — 計算視窗 PNG 上的滑動視窗 SSIM 地圖。
  3. 步驟 3:ORB 版面對齊 — 若局部區域 SSIM 下降,執行 ORB 特徵匹配以偵測彈性頁面版面位移。
  4. 步驟 4:神經語義過濾(ONNX) — 針對剩餘低 SSIM 的視覺區塊,將張量輸入 ONNX ResNet-18 嵌入模型,以區分外觀雜訊與真實內容變更。
  5. 步驟 5:警示覆蓋層產生 — 僅以亮紅色高亮已驗證、高信心的語義變更。

🏁 結論

結合傳統 SSIM 演算法ORB 剛體特徵對齊孿生神經網路,我們成功消除了超過 99% 的視覺警示偽陽性,同時維持即時且可靠的監控。

若您有興趣嘗試智慧型、無雜訊的網站變更監控工具,歡迎造訪 PageWatch.tech

對於我們的 SSIM 實作或 ONNX 模型量化有任何問題?歡迎在下方留言! 🚀