picture_match
v0.2.0
Published
基于 WASM 的图像偏移检测库,使用相位相关(Phase Correlation)算法,支持部分重叠、不同尺寸的匹配。
Readme
pic_match
基于 WASM 的图像偏移检测库,使用相位相关(Phase Correlation)算法,支持部分重叠、不同尺寸的匹配。
安装
npm install picture_matchAPI
import { findOffset } from "pic_match";
const result = await findOffset(imageA, imageB);
// result: { dx, dy, confidence, overlap, peak, psr }参数
| 参数 | 类型 | 说明 |
| -------- | ----------- | ------------------------------------------------------------------ |
| imageA | ImageData | 参考图像,包含 data(Uint8ClampedArray RGBA)、width、height |
| imageB | ImageData | 待匹配图像,格式同上 |
两张图尺寸可以不同;只要内容处于同一像素尺度,且重叠面积足够(约 15% 以上),就能求出偏移。
返回值
| 字段 | 类型 | 说明 |
| ------------ | -------- | --------------------------------------------------------------------- |
| dx | number | X 方向偏移量(正数表示 imageB 相对于 imageA 右移),带亚像素精度 |
| dy | number | Y 方向偏移量(正数表示 imageB 相对于 imageA 下移) |
| confidence | number | 匹配程度:重叠区归一化互相关(NCC),1 表示完全一致,<0.4 基本不可信 |
| overlap | number | 重叠面积占较小一张图的比例,0~1 |
| peak | number | 互功率谱峰值高度,调试用 |
| psr | number | 峰值旁瓣比,调试用 |
confidence 的含义是"重叠区域看起来有多像",因此被遮挡、亮度/局部内容差异都会让它下降;
当它很低时说明结果不可信(尺度不同、内容无关、重叠太小等),应当直接丢弃这次匹配。
const { dx, dy, confidence } = await findOffset(a, b);
if (confidence >= 0.8) {
// 可信:把 B 放到 (dx, dy) 处即可与 A 对齐
}性能
| 分辨率 | 耗时 | | --------- | ------- | | 64×64 | ~1ms | | 256×256 | ~14ms | | 512×512 | ~70ms | | 1920×1080 | ~74ms | | 3840×2160 | ~95ms | | 1920×1080 里找 120px 小图 | ~71ms | | 3840×2160 里找 120px 小图 | ~82ms |
测试环境:Linux x86_64, Node.js 26, release 构建。耗时主要由内部 FFT 画布尺寸决定(上限 1024×1024), 超过该尺寸的图像会先降采样;小图会保留至少约 32px,避免被大图"拖"到无法匹配。
算法说明
使用相位相关(Phase Correlation)算法:
- 将两张图像转为灰度图,按需降采样
- 各自去均值,并施加两端渐变的 Tukey 窗(比 Hanning 窗保留更多有效面积,对部分重叠更友好)
- 对两张图分别做 2D FFT
- 计算互功率谱并归一化(PHAT),丢掉幅度只保留相位
- 反 FFT 得到相关面,只在物理可达的偏移范围内取峰
- 对若干候选峰用重叠区 NCC 复核;再按小块统计每个偏移能解释的面积
- 画面里同时存在不动的部分和偏移的内容时,优先返回偏移内容的位移;有多个偏移时,取涉及面积更大的那个
- 对选中的峰做抛物线插值,得到亚像素偏移
该算法对部分重叠场景有良好支持,即使两张图只有部分内容相同也能正确检测偏移。 不动的背景不会把结果拉回零偏移;只有确实存在足够大的平移区域时,才会改选非零偏移。
适用与不适用
| 场景 | 结果 |
| -------------------------------------- | ---- |
| 同尺度平移(含部分重叠,重叠 ≥15%) | ✅ |
| 两张图尺寸/长宽比不同,但像素尺度相同 | ✅ |
| 大图里找小图(模板匹配) | ✅ |
| 亮度、对比度变化 | ✅ |
| 局部遮挡 | ✅ 位置正确,confidence 下降 |
| 部分内容不动、部分内容平移 | ✅ 返回平移部分的偏移;多个平移区域取面积更大的 |
| 旋转、透视 | ❌ |
| 同一内容的不同分辨率/缩放(需先统一尺度) | ❌ confidence 会很低 |
开发环境准备
1. 安装 Rust 工具链
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env添加 WASM 编译目标:
rustup target add wasm32-unknown-unknown2. 安装 wasm-pack
cargo install wasm-pack3. 安装 Node.js 环境
推荐使用 pnpm,否则把下面相关命令中pnpm换成npm
4. 安装项目依赖
pnpm install5. 环境检查
确认以下命令可用:
rustc --version # >= 1.70
cargo --version
wasm-pack --version # >= 0.12
node --version # >= 18构建
pnpm build该命令依次执行:
build:wasm- 使用 wasm-pack 编译 Rust 为 WASMbuild:ts- 使用 Vite 构建 TypeScript 库
测试
pnpm build:wasm # 测试直接加载 pkg/ 里的 WASM,需要先构建
pnpm test测试覆盖:
| 文件 | 内容 |
| ------------------- | ------------------------------------------------------------ |
| match.test.ts | 基础平移、负向偏移、不同画布尺寸、大偏移、无关/纯色图像 |
| overlap.test.ts | 部分重叠扫描(重叠 15%~98%)、两图互不包含、全景拼接 |
| template.test.ts | 大图找小图:居中/边缘/右半屏、亮度变化、遮挡、1080p 图标搜索 |
| moving.test.ts | 不动背景与平移物体共存、多个平移区域时按面积选择 |
| accuracy.test.ts | 打印各场景的误差、confidence、overlap,直观看到匹配程度 |
| benchmark.test.ts | 各分辨率的耗时,并检查不超过预算 |
Rust 侧另有单元测试:
cargo test