siamese 图像相似度
约 1448 字大约 5 分钟
siamese 图像相似度
siamese 是 ScriptX 里的图像相似度模块。它不是通用 OCR,也不是目标检测,而是专门回答一个问题:
“这两张图,或者同一张图里的两个区域,到底像不像?”
适合的场景通常是:
- 比较两张按钮图是否是同一套素材
- 从大图里裁两块区域,看它们是不是同一个头像、图标、验证码块
- 做轻量的“相似度打分”,而不是严格的像素级完全相等
先记住这 8 条
- 全局对象名是
siamese,兼容别名是$siamese。 siamese自己不是模型实例,必须先siamese.init(modelPath)才能拿到会话对象。- 每次
init()返回的都是一个新的模型会话,带自己的id。 detect(...)支持两种用法:两张图直接比,或者一张图里给两块矩形区域来比。- 矩形区域格式必须是
[x1, y1, x2, y2]。 - 返回值是
0..1的相似度分数,越接近1表示越像。 - 模型文件必须是可读的 ONNX 文件,而且要正好暴露两个输入。
close()、release()、destroy()是同一个释放动作的不同名字。
siamese.init(path)
加载一份外部 Siamese ONNX 模型,并返回一个模型会话对象。
const model = siamese.init("/sdcard/models/siamese.onnx");
参数
| 参数 | 类型 | 可填值 | 说明 |
|---|---|---|---|
path | string | 非空模型路径 | Siamese ONNX 模型文件路径 |
返回值
SiameseModelSession
也就是后面会频繁出现的那个“模型实例对象”。
模型要求
当前源码对模型有这些明确要求:
- 必须是可读的外部 ONNX 文件
- 必须正好暴露 2 个输入
- 每个输入都必须是
NCHW形状 - 形状必须长这样:
[1, 3, height, width]
height和width必须是固定正数,不能是未定维度
返回会话对象后最常用的字段
| 字段 | 类型 | 说明 |
|---|---|---|
id | string | 会话 id,例如 siamese-1 |
detect(...) | function | 做相似度计算 |
close() | function | 释放会话 |
isClosed() | function | 判断会话是否已关闭 |
示例
const model = siamese.init("/sdcard/models/siamese.onnx");
log(model.id);
siamese.closeAll()
关闭当前脚本里所有尚未释放的 Siamese 会话。
返回值
number
表示这次实际关闭了多少个会话。
什么时候适合用它
- 一个脚本里开了多份 Siamese 模型
- 想在脚本结束前统一清理
示例
const count = siamese.closeAll();
log(`closed=${count}`);
model.detect(image1, image2)
直接比较两张图片的相似度。
const score = model.detect(img1, img2);
参数
| 参数 | 类型 | 可填值 | 说明 |
|---|---|---|---|
image1 | Image / Bitmap / 可解析图片值 | ScriptX 当前支持的图片输入类型 | 第一张图 |
image2 | Image / Bitmap / 可解析图片值 | ScriptX 当前支持的图片输入类型 | 第二张图 |
返回值
number
范围大致在 0..1。
怎么理解这个分数
| 分数区间 | 常见理解 |
|---|---|
0.9 以上 | 非常像,通常就是同类图 |
0.7 ~ 0.9 | 比较像,可能是同一元素不同压缩或轻微变形 |
0.4 ~ 0.7 | 有相似性,但不够稳定 |
0.4 以下 | 大概率不是同一个目标 |
这不是源码里硬编码的阈值,而是脚本侧常见的经验用法。真正阈值还是要按你的模型和素材来定。
真实行为
- 底层会先把图片转成 BGR Mat
- 再按模型输入尺寸做 letterbox 缩放
- 背景补成中灰
128 - 最后把模型输出的首个 logit 做 sigmoid,变成
0..1分数
示例:比较两张头像
const model = siamese.init("/sdcard/models/siamese.onnx");
const a = images.read("./assets/avatar-a.png");
const b = images.read("./assets/avatar-b.png");
try {
const score = model.detect(a, b);
log(`score=${score}`);
} finally {
a.recycle();
b.recycle();
model.close();
}
model.detect(image, rect1, rect2)
从同一张图里裁两块区域,再比较这两块区域的相似度。
const score = model.detect(image, [10, 20, 90, 100], [120, 20, 200, 100]);
参数
| 参数 | 类型 | 可填值 | 说明 |
|---|---|---|---|
image | Image / Bitmap / 可解析图片值 | ScriptX 当前支持的图片输入类型 | 原始大图 |
rect1 | number[] | [x1, y1, x2, y2] | 第一块区域 |
rect2 | number[] | [x1, y1, x2, y2] | 第二块区域 |
返回值
number
同样是 0..1 分数。
区域参数规则
rect1 和 rect2 都必须是长度为 4 的数组:
[x1, y1, x2, y2]
并且必须满足:
x1 < x2y1 < y2- 区域和原图有实际重叠
源码会先把区域裁进图片边界,再判断是不是空区域。空区域会直接报错。
什么时候更适合这种写法
- 你已经只有一张大图
- 两个目标都在同一张图里
- 不想自己先
images.clip(...)两次
示例:比较两张卡片上的图标
const model = siamese.init("/sdcard/models/siamese.onnx");
const image = images.captureScreen();
try {
const score = model.detect(
image,
[80, 300, 180, 400],
[80, 500, 180, 600]
);
log(`score=${score}`);
} finally {
image.recycle();
model.close();
}
model.close() / model.release() / model.destroy()
关闭当前 Siamese 模型会话。
返回值
boolean
| 返回值 | 含义 |
|---|---|
true | 这次确实完成了关闭 |
false | 之前已经关过了 |
关掉之后会怎样
isClosed()会变成true- 再调
detect(...)会直接报错
示例
const model = siamese.init("/sdcard/models/siamese.onnx");
model.close();
model.isClosed()
判断当前会话是否已经关闭。
返回值
boolean
示例
const model = siamese.init("/sdcard/models/siamese.onnx");
log(model.isClosed()); // false
model.close();
log(model.isClosed()); // true
model.id
当前模型会话的字符串 id。
类型
string
典型值
siamese-1siamese-2
什么时候有用
- 打日志区分多个会话
- 做自己的会话池管理
示例
const model = siamese.init("/sdcard/models/siamese.onnx");
log(model.id);
model.close();
siamese.api
siamese 自己的别名引用。
类型
object
示例
const api = siamese.api;
log(api === siamese);
一段完整的新手示例
这个例子演示的是:先在一张截图里比较两个头像区域,如果足够相似,再继续做后续动作。
const model = siamese.init("/sdcard/models/siamese.onnx");
const image = images.captureScreen();
try {
const score = model.detect(
image,
[90, 240, 170, 320],
[90, 420, 170, 500]
);
log(`similarity=${score}`);
if (score >= 0.85) {
log("两个区域很可能是同类头像");
} else {
log("两个区域差异比较大");
}
} finally {
image.recycle();
model.close();
}
