关于 image-diff
image-diff 是用于比较本地图片的 MCP 服务,适合核对设计稿与 UI 截图。它会归并视觉差异并生成热图,还能自动对齐尺寸不同的图片。
主要功能
- 01逐像素颜色比较
- 02过滤抗锯齿差异
- 03归并差异区域
- 04自动计算聚类间距
- 05生成热图与合成图
- 06对齐尺寸不同的图片
适用场景
- 01核对设计稿与 UI 截图
- 02定位文本、颜色或布局变化
- 03将组件图与整页截图比较
MCP 相关
它向 MCP 客户端提供 `get_diff_of_images` 工具。该工具接收本地图片路径,并返回差异统计、聚类区域及热图文件路径。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
1收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| threshold | number | 否 | 颜色匹配灵敏度,范围为 0 到 1。值越低,越容易检测到细微差异。UI 比较的良好默认值为 0.1。严格比较使用 0.05,宽松比较使用 0.2。 |
| include_aa | boolean | 否 | 是否将抗锯齿像素(字体平滑、圆角)计为差异。默认值 false 表示排除这些像素以减少噪声。 |
| cluster_gap | integer | 否 | 合并为单个分组时,分组边界框之间的最大像素距离。省略时,将使用分组距离的自然断点自动计算最佳间距。设为 0 可完全禁用合并,或设为 5–20 进行手动控制。 |
| source_image | string | 是 | 源图像/参考图像的绝对文件路径(例如,设计稿)。支持 PNG、JPEG、WebP、TIFF。 |
| target_image | string | 是 | 目标图像/比较图像的绝对文件路径(例如,实际 UI 截图)。支持 PNG、JPEG、WebP、TIFF。 |
| min_cluster_size | integer | 否 | 报告为一个簇的连通差异像素的最小数量。过滤孤立的像素噪声。默认值为 4。 |
详细介绍
image-diff MCP 服务
一个用于以编程方式比较图像并生成视觉差异的 Model Context Protocol(MCP)服务。它逐像素比较两张图像,识别视觉差异簇,并生成热力图可视化结果。
专为比较设计稿截图与实际 UI 实现而设计。
工作原理
- 像素比较:使用 YIQ NTSC 感知色差(考虑人类对颜色差异的感知方式)
- 抗锯齿检测:过滤字体平滑和圆角造成的误报
- 连通分量标记:将相邻的差异像素归为同一空间簇
- 自然断点聚类:使用最近邻距离分析自动计算最佳
cluster_gap,将相邻簇合并为有意义的分组 - 热力图生成:使用从黄色到红色的渐变显示差异程度
- 自动对齐:使用 OpenCV ZNCC 模板匹配处理尺寸不同的图像
工具
get_diff_of_images
比较两张图像并获取结构化差异结果。
参数:
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
source_image | string | 是 | - | 参考图像的绝对路径 |
target_image | string | 是 | - | 对比图像的绝对路径 |
threshold | number | 否 | 0.1 | 颜色匹配灵敏度(0-1,值越低越灵敏) |
include_aa | boolean | 否 | false | 是否将抗锯齿像素计为差异 |
min_cluster_size | number | 否 | 4 | 构成一个簇所需的最少连通像素数 |
cluster_gap | number | 否 | auto | 合并相邻簇的最大像素间距。省略时,会使用自然断点的最近邻距离分析自动计算最佳间距。设为 0 表示不合并,也可设为任意正整数进行手动控制。 |
返回值:
identical:布尔值,表示图像是否一致summary:总体差异统计(像素数、百分比、簇数量)clusters:差异区域数组,包含边界框、严重程度评级和强度指标clustering:所用间距、是否为自动计算,以及建议的其他间距值alignment(图像尺寸不同时):位置坐标、置信度分数、所用策略和耗时dimensions:对比区域的宽度和高度heatmapPath:生成的热力图文件路径compositePath:叠加在源图像上的热力图文件路径
要求:
- 图像必须可通过本地文件路径访问
- 支持 PNG、JPEG、WebP、TIFF 输入格式
- 图像尺寸可以不同——较小的图像会自动与较大的图像对齐
工具调用格式
MCP 工具调用输入(最精简形式——仅包含两个必填路径):
{
"name": "get_diff_of_images",
"arguments": {
"source_image": "/path/to/design-mock.png",
"target_image": "/path/to/screenshot.png"
}
}
MCP 工具调用输入(包含所有可选参数):
{
"name": "get_diff_of_images",
"arguments": {
"source_image": "/path/to/design-mock.png",
"target_image": "/path/to/screenshot.png",
"threshold": 0.05,
"include_aa": true,
"min_cluster_size": 10,
"cluster_gap": 15
}
}
MCP 响应(尺寸相同且存在差异的图像):
{
"content": [
{
"type": "text",
"text": "{\n \"identical\": false,\n \"summary\": {\n \"totalPixels\": 480000,\n \"diffPixels\": 737,\n \"diffPercentage\": 0.154,\n \"antiAliasedPixels\": 1034,\n \"matchingPixels\": 478229,\n \"clusterCount\": 11,\n \"description\": \"0.15% of pixels differ across 11 cluster(s). Severity breakdown: 5 major, 5 minor, 1 trivial.\"\n },\n \"clustering\": {\n \"gapUsed\": 4,\n \"autoGap\": true,\n \"suggestedSmallerGap\": 3,\n \"suggestedLargerGap\": 5\n },\n \"clusters\": [ ... ],\n \"heatmapPath\": \"/tmp/image-diff-output/heatmap-1234567890.png\",\n \"compositePath\": \"/tmp/image-diff-output/composite-1234567890.png\",\n \"dimensions\": { \"width\": 800, \"height\": 600 }\n}"
}
]
}
text 字段包含完整 JSON 结果字符串。解析后,完整响应结构如下:
完整解析后的响应结构(尺寸相同且存在差异的图像)
{
"identical": false,
"summary": {
"totalPixels": 480000,
"diffPixels": 737,
"diffPercentage": 0.154,
"antiAliasedPixels": 1034,
"matchingPixels": 478229,
"clusterCount": 11,
"description": "0.15% of pixels differ across 11 cluster(s). Severity breakdown: 5 major, 5 minor, 1 trivial."
},
"clustering": {
"gapUsed": 4,
"autoGap": true,
"suggestedSmallerGap": 3,
"suggestedLargerGap": 5
},
"clusters": [
{
"id": 1,
"left": 444,
"top": 157,
"right": 504,
"bottom": 165,
"width": 61,
"height": 9,
"pixelCount": 170,
"totalImagePixels": 480000,
"areaPercentage": 0.035,
"meanIntensity": 0.111,
"maxIntensity": 0.343,
"severity": "minor"
}
],
"heatmapPath": "/tmp/image-diff-output/heatmap-1234567890.png",
"compositePath": "/tmp/image-diff-output/composite-1234567890.png",
"dimensions": {
"width": 800,
"height": 600
}
}
完整解析后的响应结构(相同图像)
{
"identical": true,
"summary": {
"totalPixels": 480000,
"diffPixels": 0,
"diffPercentage": 0,
"antiAliasedPixels": 0,
"matchingPixels": 480000,
"clusterCount": 0,
"description": "Images are identical (no differences detected)."
},
"clustering": {
"gapUsed": 0,
"autoGap": true,
"suggestedSmallerGap": null,
"suggestedLargerGap": null
},
"clusters": [],
"heatmapPath": "/tmp/image-diff-output/heatmap-1234567890.png",
"compositePath": "/tmp/image-diff-output/composite-1234567890.png",
"dimensions": {
"width": 800,
"height": 600
}
}
完整解析后的响应结构(尺寸不同的图像——自动对齐)
{
"identical": false,
"summary": {
"totalPixels": 108800,
"diffPixels": 9031,
"diffPercentage": 8.301,
"antiAliasedPixels": 329,
"matchingPixels": 99440,
"clusterCount": 7,
"description": "8.30% of pixels differ across 7 cluster(s). Severity breakdown: 1 major, 3 moderate, 3 minor."
},
"clustering": {
"gapUsed": 4,
"autoGap": true,
"suggestedSmallerGap": 1,
"suggestedLargerGap": 6
},
"clusters": [
{
"id": 1,
"left": 20,
"top": 185,
"right": 299,
"bottom": 215,
"width": 280,
"height": 31,
"pixelCount": 8372,
"totalImagePixels": 108800,
"areaPercentage": 7.695,
"meanIntensity": 0.242,
"maxIntensity": 0.244,
"severity": "major"
}
],
"alignment": {
"x": 680,
"y": 212,
"confidence": 0.993,
"strategy": "opencv-zncc-hybrid",
"alignmentTimeMs": 89,
"templateImage": "target",
"originalDimensions": {
"source": { "width": 1024, "height": 768 },
"target": { "width": 320, "height": 340 }
}
},
"heatmapPath": "/tmp/image-diff-output/heatmap-1234567890.png",
"compositePath": "/tmp/image-diff-output/composite-1234567890.png",
"dimensions": {
"width": 320,
"height": 340
}
}
注意:只有图像尺寸不同时才会出现 alignment 字段。dimensions 字段反映的是对比区域的尺寸(对齐后的模板尺寸,而非完整场景尺寸)。该接口中的 compositePath 字段为可选项,但通过 MCP 工具调用时始终会提供。
示例
以下示例均使用此仪表板作为源图像(800x600):

示例 1:图像完全相同 — 没有差异
当两张图像的像素完全一致时,引擎会提前结束处理并立即返回。
| 源图像 | 目标图像 | 合成图像 |
|---|---|---|
![]() | ![]() | ![]() |
合成叠加图显示的是源图像,没有热图高亮——因为不存在差异。
结构化输出
{
"identical": true,
"summary": {
"totalPixels": 480000,
"diffPixels": 0,
"diffPercentage": 0,
"antiAliasedPixels": 0,
"matchingPixels": 480000,
"clusterCount": 0,
"description": "Images are identical (no differences detected)."
},
"clustering": {
"gapUsed": 0,
"autoGap": true,
"suggestedSmallerGap": null,
"suggestedLargerGap": null
}
}
示例 2:颜色和数值变化
目标图像更新了统计数值(“12,847” -> “13,205”、“$48,290” -> “$52,180”),百分比文本也有所不同。引擎在变化的文本和数字中找到 11 个簇(自动间距 = 4)。
| 源图像 | 目标图像 |
|---|---|
![]() | ![]() |
| 独立热图 | 合成叠加图 |
|---|---|
![]() | ![]() |
独立热图仅在透明背景上显示差异像素——黄色表示强度较低,红色表示强度较高。合成叠加图将热图叠加到源图像上,让你能在上下文中准确看到差异所在位置。
结构化输出(11 个簇,显示前 3 个)
{
"identical": false,
"summary": {
"totalPixels": 480000,
"diffPixels": 737,
"diffPercentage": 0.154,
"antiAliasedPixels": 1034,
"matchingPixels": 478229,
"clusterCount": 11,
"description": "0.15% of pixels differ across 11 cluster(s). Severity breakdown: 5 major, 5 minor, 1 trivial."
},
"clustering": {
"gapUsed": 4,
"autoGap": true,
"suggestedSmallerGap": 3,
"suggestedLargerGap": 5
},
"clusters": [
{
"id": 1,
"left": 444,
"top": 157,
"right": 504,
"bottom": 165,
"width": 61,
"height": 9,
"pixelCount": 170,
"areaPercentage": 0.035,
"meanIntensity": 0.111,
"maxIntensity": 0.343,
"severity": "minor"
},
{
"id": 2,
"left": 450,
"top": 125,
"right": 478,
"bottom": 142,
"width": 29,
"height": 18,
"pixelCount": 112,
"areaPercentage": 0.023,
"meanIntensity": 0.677,
"maxIntensity": 0.738,
"severity": "major"
},
{
"id": 3,
"left": 284,
"top": 126,
"right": 308,
"bottom": 142,
"width": 25,
"height": 17,
"pixelCount": 91,
"areaPercentage": 0.019,
"meanIntensity": 0.682,
"maxIntensity": 0.738,
"severity": "major"
}
]
}
省略另外 8 个簇。
示例 3:字体变化(无衬线 -> 衬线)
整个页面使用衬线字体(Georgia)重新渲染。每个文本元素都略有偏移,产生许多字形级别的差异。使用默认自动间距时,引擎会计算出最佳的 cluster_gap=10,并将这些差异合并为 40 个簇,分别代表不同的文本块和 UI 区域。
| 源图像 | 目标图像 |
|---|---|
![]() | ![]() |
| 独立热图 | 合成叠加图 |
|---|---|
![]() | ![]() |
热图会在所有文本区域亮起——标题、统计数值、表格内容、侧边栏标签。自动间距会将附近的字形片段合并成有意义的组。
结构化输出 — 默认自动间距(40 个簇,显示前 3 个)
{
"identical": false,
"summary": {
"totalPixels": 480000,
"diffPixels": 11671,
"diffPercentage": 2.431,
"antiAliasedPixels": 8326,
"matchingPixels": 460003,
"clusterCount": 40,
"description": "2.43% of pixels differ across 40 cluster(s). Severity breakdown: 26 moderate, 14 minor."
},
"clustering": {
"gapUsed": 10,
"autoGap": true,
"suggestedSmallerGap": 9,
"suggestedLargerGap": 11
},
"clusters": [
{
"id": 1,
"left": 434,
"top": 125,
"right": 550,
"bottom": 166,
"width": 117,
"height": 42,
"pixelCount": 912,
"severity": "moderate"
},
{
"id": 2,
"left": 246,
"top": 125,
"right": 372,
"bottom": 166,
"width": 127,
"height": 42,
"pixelCount": 883,
"severity": "moderate"
},
{
"id": 3,
"left": 625,
"top": 125,
"right": 733,
"bottom": 166,
"width": 109,
"height": 42,
"pixelCount": 716,
"severity": "moderate"
}
]
}
省略另外 37 个簇。注意:差异像素相同,百分比相同——只有分组方式发生变化。
没有自动间距时,每个字形片段都会成为一个独立簇。显式设置 cluster_gap=0 会禁用合并:
cluster_gap | 簇 | 结果 |
|---|---|---|
| 自动(默认) | 40 | 自动计算的间距为 10,会将片段合并为文本块 |
| 0(不合并) | 331 | 每个字形片段都是一个独立簇 |
使用 cluster_gap=0 时,40 个有意义的分组会激增为 331 个原始簇——每个单独的字符差异都会形成一个簇。这正是自动间距帮你避免的问题。
cluster_gap=0 时的结构化输出(共 331 个簇,显示前 3 个)
{
"identical": false,
"summary": {
"totalPixels": 480000,
"diffPixels": 11671,
"diffPercentage": 2.431,
"antiAliasedPixels": 8326,
"matchingPixels": 460003,
"clusterCount": 331,
"description": "2.43% of pixels differ across 331 cluster(s). Severity breakdown: 61 major, 161 moderate, 105 minor, 4 trivial."
},
"clustering": {
"gapUsed": 0,
"autoGap": false,
"suggestedSmallerGap": null,
"suggestedLargerGap": 1
},
"clusters": [
{
"id": 1,
"left": 65,
"top": 88,
"right": 110,
"bottom": 98,
"width": 46,
"height": 11,
"pixelCount": 221,
"severity": "moderate"
},
{
"id": 2,
"left": 555,
"top": 317,
"right": 597,
"bottom": 329,
"width": 43,
"height": 13,
"pixelCount": 213,
"severity": "moderate"
},
{
"id": 3,
"left": 555,
"top": 391,
"right": 597,
"bottom": 403,
"width": 43,
"height": 13,
"pixelCount": 213,
"severity": "moderate"
}
]
}
省略了另外 328 个簇。注意:差异像素和占比相同,变化的只有分组方式。
示例 4:元素缺失(徽章被移除)
目标图像中的表格状态徽章(“Complete”“Active”)被移除,替换成了纯文本。引擎检测到 19 个簇(自动间距为 4),精准定位每个被移除的徽章。
| 源图像 | 目标图像 |
|---|---|
![]() | ![]() |
| 独立热力图 | 叠加图像 |
|---|---|
![]() | ![]() |
热力图会突出显示每个徽章的位置,这些位置原本的彩色胶囊已被纯文本替代。
结构化输出(共 19 个簇,显示前 3 个)
{
"identical": false,
"summary": {
"totalPixels": 480000,
"diffPixels": 4506,
"diffPercentage": 0.939,
"antiAliasedPixels": 2283,
"matchingPixels": 473211,
"clusterCount": 19,
"description": "0.94% of pixels differ across 19 cluster(s). Severity breakdown: 18 moderate, 1 minor."
},
"clustering": {
"gapUsed": 4,
"autoGap": true,
"suggestedSmallerGap": 3,
"suggestedLargerGap": 5
},
"clusters": [
{
"id": 1,
"left": 369,
"top": 426,
"right": 487,
"bottom": 439,
"width": 119,
"height": 14,
"pixelCount": 516,
"areaPercentage": 0.108,
"severity": "moderate"
},
{
"id": 2,
"left": 369,
"top": 352,
"right": 462,
"bottom": 365,
"width": 94,
"height": 14,
"pixelCount": 442,
"areaPercentage": 0.092,
"severity": "moderate"
},
{
"id": 3,
"left": 657,
"top": 427,
"right": 723,
"bottom": 439,
"width": 67,
"height": 13,
"pixelCount": 311,
"areaPercentage": 0.065,
"severity": "moderate"
}
]
}
省略了另外 16 个簇。
示例 5:布局偏移(内边距变化)
目标图像中的统计卡片增加了内边距和间距,导致所有内容都略微向下偏移。这会产生 18 个簇(自动间距为 18),因为自动间距会合并内容重新定位所造成的大量边缘级片段。
| 源图像 | 目标图像 |
|---|---|
![]() | ![]() |
| 独立热力图 | 叠加图像 |
|---|---|
![]() | ![]() |
凡是内容发生重新定位的地方,热力图都会亮起。自动间距会将连锁影响合并为连贯区域,分别代表每个偏移的元素。
结构化输出(共 18 个簇,显示前 3 个)
{
"identical": false,
"summary": {
"totalPixels": 480000,
"diffPixels": 11136,
"diffPercentage": 2.32,
"antiAliasedPixels": 10938,
"matchingPixels": 457926,
"clusterCount": 18,
"description": "2.32% of pixels differ across 18 cluster(s). Severity breakdown: 18 moderate."
},
"clustering": {
"gapUsed": 18,
"autoGap": true,
"suggestedSmallerGap": 17,
"suggestedLargerGap": 19
},
"clusters": [
{
"id": 1,
"left": 434,
"top": 105,
"right": 550,
"bottom": 195,
"width": 117,
"height": 91,
"pixelCount": 1531,
"areaPercentage": 0.319,
"severity": "moderate"
},
{
"id": 2,
"left": 369,
"top": 315,
"right": 490,
"bottom": 448,
"width": 122,
"height": 134,
"pixelCount": 1490,
"areaPercentage": 0.31,
"severity": "moderate"
},
{
"id": 3,
"left": 245,
"top": 103,
"right": 372,
"bottom": 195,
"width": 128,
"height": 93,
"pixelCount": 1473,
"areaPercentage": 0.307,
"severity": "moderate"
}
]
}
省略了另外 15 个簇。
自动对齐:比较尺寸不同的图像
当源图像和目标图像的尺寸不同时,引擎会自动查找较小图像(“模板”)在较大图像(“场景”)中的最佳匹配位置,裁剪该区域,然后运行标准的像素级比较。无需配置。
工作原理:
- 粗略搜索 — 在缩小的分辨率下使用 OpenCV ZNCC(零均值归一化互相关)进行模板匹配,以快速检测候选位置
- 精细校准 — 在粗略匹配位置附近,以全分辨率进行像素级 ZNCC,以达到亚像素精度
- 裁剪并比较 — 从场景中裁剪匹配区域,并与模板逐像素比较
这样就能直接将 Figma 组件模型与整页截图进行比较,无需手动裁剪。
以下所有对齐示例均使用此仪表板作为**源(场景)**图像(1024x768):

自动对齐示例 1:统计卡片模型与整页比较
将统计行的 Figma 模型(780x100)与整页(1024x768)进行比较。模型中的数字已更新(“12,847” -> “14,205”,“$48,290” -> “$52,180”,“3,847” -> “4,102”),百分比也有所不同。
| 整页(1024x768) | 统计卡片模型(780x100) |
|---|---|
![]() | ![]() |
引擎会自动定位到位置 (238, 80) 的统计行,从整页中裁剪出该 780x100 区域,并与模型进行比较。
| 合成叠加图(裁剪区域) |
|---|
![]() |
热力图会在对齐区域内准确标出发生变化的统计值和百分比。
结构化输出(共 15 个簇,显示前 3 个)
{
"identical": false,
"summary": {
"totalPixels": 78000,
"diffPixels": 2570,
"diffPercentage": 3.295,
"antiAliasedPixels": 2503,
"matchingPixels": 72927,
"clusterCount": 15,
"description": "3.29% of pixels differ across 15 cluster(s). Severity breakdown: 5 major, 4 moderate, 6 minor."
},
"clustering": {
"gapUsed": 5,
"autoGap": true,
"suggestedSmallerGap": 4,
"suggestedLargerGap": 6
},
"alignment": {
"x": 238,
"y": 80,
"confidence": 0.743,
"strategy": "opencv-zncc-hybrid",
"alignmentTimeMs": 146,
"templateImage": "target",
"originalDimensions": {
"source": { "width": 1024, "height": 768 },
"target": { "width": 780, "height": 100 }
}
},
"clusters": [
{
"id": 1,
"left": 607,
"top": 45,
"right": 679,
"bottom": 66,
"width": 73,
"height": 22,
"pixelCount": 421,
"areaPercentage": 0.54,
"severity": "major"
},
{
"id": 2,
"left": 413,
"top": 45,
"right": 480,
"bottom": 66,
"width": 68,
"height": 22,
"pixelCount": 412,
"areaPercentage": 0.528,
"severity": "major"
},
{
"id": 3,
"left": 22,
"top": 45,
"right": 103,
"bottom": 66,
"width": 82,
"height": 22,
"pixelCount": 369,
"areaPercentage": 0.473,
"severity": "major"
}
]
}
省略另外 12 个簇。
alignment 对象中的关键字段:
x、y— 在场景中找到模板的位置(像素坐标)confidence— 匹配质量(0-1,越高越好)strategy— 使用的算法(opencv-zncc-hybrid)alignmentTimeMs— 对齐耗时templateImage— 哪张图像是模板("target"表示目标图像较小)originalDimensions— 对齐前的原始尺寸
自动对齐示例 2:侧边栏面板模型与整页比较
右侧边栏的 Figma 模型(320x340),进度值已修改(78->85%、92->95%、45->62%),按钮颜色也不同(蓝色 -> 红色)。与整页进行比较。
| 整页(1024x768) | 侧边栏模型(320x340) |
|---|---|
![]() | ![]() |
引擎会以 99.3% 的置信度在 89ms 内找到位置 (680, 212) 的侧边栏,然后识别出发生变化的进度条和按钮。
| 合成叠加图(裁剪区域) | 独立热力图 |
|---|---|
![]() | ![]() |
底部的主要簇表示按钮颜色变化(蓝色 -> 红色)。中等大小的簇表示进度条填充部分的变化。
结构化输出(共 7 个簇,显示前 3 个)
{
"identical": false,
"summary": {
"totalPixels": 108800,
"diffPixels": 9031,
"diffPercentage": 8.301,
"antiAliasedPixels": 329,
"matchingPixels": 99440,
"clusterCount": 7,
"description": "8.30% of pixels differ across 7 cluster(s). Severity breakdown: 1 major, 3 moderate, 3 minor."
},
"clustering": {
"gapUsed": 4,
"autoGap": true,
"suggestedSmallerGap": 1,
"suggestedLargerGap": 6
},
"alignment": {
"x": 680,
"y": 212,
"confidence": 0.993,
"strategy": "opencv-zncc-hybrid",
"alignmentTimeMs": 89,
"templateImage": "target",
"originalDimensions": {
"source": { "width": 1024, "height": 768 },
"target": { "width": 320, "height": 340 }
}
},
"clusters": [
{
"id": 1,
"left": 20,
"top": 185,
"right": 299,
"bottom": 215,
"width": 280,
"height": 31,
"pixelCount": 8372,
"areaPercentage": 7.695,
"severity": "major"
},
{
"id": 2,
"left": 146,
"top": 157,
"right": 192,
"bottom": 164,
"width": 47,
"height": 8,
"pixelCount": 370,
"areaPercentage": 0.34,
"severity": "moderate"
},
{
"id": 3,
"left": 238,
"top": 71,
"right": 256,
"bottom": 78,
"width": 19,
"height": 8,
"pixelCount": 146,
"areaPercentage": 0.134,
"severity": "moderate"
}
]
}
省略另外 4 个簇。
自动对齐示例 3:完全相同的裁剪 — 0% 差异
从同一张完整页面截图中精确裁剪出的活动表格(420x496)。引擎在精确位置找到该表格,并确认差异为 0%,证明了对齐精度。
| 完整页面(1024x768) | 表格裁剪(420x496) |
|---|---|
![]() | ![]() |
| 合成叠加图(干净——无差异) |
|---|
![]() |
结构化输出
{
"identical": true,
"summary": {
"totalPixels": 208320,
"diffPixels": 0,
"diffPercentage": 0,
"antiAliasedPixels": 0,
"matchingPixels": 208320,
"clusterCount": 0,
"description": "Images are identical (no differences detected)."
},
"clustering": {
"gapUsed": 0,
"autoGap": true,
"suggestedSmallerGap": null,
"suggestedLargerGap": null
},
"alignment": {
"x": 244,
"y": 212,
"confidence": 1,
"strategy": "opencv-zncc-hybrid",
"alignmentTimeMs": 99,
"templateImage": "target",
"originalDimensions": {
"source": { "width": 1024, "height": 768 },
"target": { "width": 420, "height": 496 }
}
}
}
置信度 100%,差异像素为 0。对齐操作找到了精确位置,裁剪区域完全匹配。
对齐性能
| 场景 | 模板 | 场景尺寸 | 找到的位置 | 置信度 | 耗时 |
|---|---|---|---|---|---|
| 统计卡片 | 780x100 | 1024x768 | (238, 80) | 74.3% | 146ms |
| 侧边栏面板 | 320x340 | 1024x768 | (680, 212) | 99.3% | 89ms |
| 表格(完全相同) | 420x496 | 1024x768 | (244, 212) | 100% | 99ms |
差异簇严重程度评级
每个差异簇都会根据像素数量和强度进行严重程度分类:
| 严重程度 | 含义 | 典型原因 |
|---|---|---|
| trivial | 零散的像素差异 | 亚像素渲染、压缩伪影 |
| minor | 小范围的局部差异 | 轻微的颜色偏移、字体粗细变化 |
| moderate | 明显的区域差异 | 文本变化、徽标修改 |
| major | 大范围或强烈的差异 | 按钮颜色变化、元素缺失或新增、布局偏移 |
Agent Skills 示例
example-skills/ 目录包含可直接使用的 Agent Skills,展示如何基于 image-diff MCP 服务构建工作流。
implement-figma-design
一种迭代式设计实现技能,可协调三个 MCP 服务——Figma、Playwright 和 image-diff——构建与 Figma 设计匹配的 UI 组件。Agent 编写代码、截取运行中的开发服务的屏幕截图、与 Figma 参考图进行差异比较、诊断每个差异簇的根本原因(颜色不匹配、排版、间距、元素缺失等)、应用修复,并不断循环,直到差异收敛到 <= 0.01%。
前置条件: Figma MCP 服务、Playwright MCP 服务、image-diff MCP 服务,以及可正常运行的开发服务。
使用方法: 将 example-skills/implement-figma-design/ 复制到项目的 .claude/skills/ 目录(或你的 Agent 平台对应的技能目录)。
致谢
- 像素比较算法分叉自 Mapbox 的 pixelmatch(ISC 许可证)
- 颜色距离基于 Y. Kotsarenko 和 F. Ramos 的论文“Measuring perceived color difference using YIQ NTSC transmission color space in mobile applications”
- 抗锯齿检测基于 V. Vysniauskas 于 2009 年发表的论文“Anti-aliased Pixel and Intensity Slope Detector”
- 连通分量标记采用经典的两遍算法和并查集
- 图像对齐由 opencv-wasm 提供支持(Apache 2.0 许可证)
- 图像 I/O 由 sharp 提供支持(Apache 2.0 许可证)
- 聚类方法的灵感来自 Gemini Testing 的 looks-same(MIT 许可证)
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。




















