mcpField
I
image-diff

image-diff

本地服务
github.com/pulsemcp开发工具818 周前更新

关于 image-diff

image-diff 是用于比较本地图片的 MCP 服务,适合核对设计稿与 UI 截图。它会归并视觉差异并生成热图,还能自动对齐尺寸不同的图片。

主要功能

  1. 01逐像素颜色比较
  2. 02过滤抗锯齿差异
  3. 03归并差异区域
  4. 04自动计算聚类间距
  5. 05生成热图与合成图
  6. 06对齐尺寸不同的图片

适用场景

  1. 01核对设计稿与 UI 截图
  2. 02定位文本、颜色或布局变化
  3. 03将组件图与整页截图比较

MCP 相关

它向 MCP 客户端提供 `get_diff_of_images` 工具。该工具接收本地图片路径,并返回差异统计、聚类区域及热图文件路径。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"com-pulsemcp-image-diff": {
"command": "npx",
"args": [
"-y",
"@pulsemcp/image-diff-mcp-server"
]
}
}
}
启动命令npx -y @pulsemcp/image-diff-mcp-server

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@pulsemcp/image-diff-mcp-server0.1.3

工具

1

收录的工具列表 · 更新于 2 周前

参数类型必填说明
thresholdnumber否颜色匹配灵敏度,范围为 0 到 1。值越低,越容易检测到细微差异。UI 比较的良好默认值为 0.1。严格比较使用 0.05,宽松比较使用 0.2。
include_aaboolean否是否将抗锯齿像素(字体平滑、圆角)计为差异。默认值 false 表示排除这些像素以减少噪声。
cluster_gapinteger否合并为单个分组时,分组边界框之间的最大像素距离。省略时,将使用分组距离的自然断点自动计算最佳间距。设为 0 可完全禁用合并,或设为 5–20 进行手动控制。
source_imagestring是源图像/参考图像的绝对文件路径(例如,设计稿)。支持 PNG、JPEG、WebP、TIFF。
target_imagestring是目标图像/比较图像的绝对文件路径(例如,实际 UI 截图)。支持 PNG、JPEG、WebP、TIFF。
min_cluster_sizeinteger否报告为一个簇的连通差异像素的最小数量。过滤孤立的像素噪声。默认值为 4。

详细介绍

image-diff MCP 服务

一个用于以编程方式比较图像并生成视觉差异的 Model Context Protocol(MCP)服务。它逐像素比较两张图像,识别视觉差异簇,并生成热力图可视化结果。

专为比较设计稿截图与实际 UI 实现而设计。

工作原理

  1. 像素比较:使用 YIQ NTSC 感知色差(考虑人类对颜色差异的感知方式)
  2. 抗锯齿检测:过滤字体平滑和圆角造成的误报
  3. 连通分量标记:将相邻的差异像素归为同一空间簇
  4. 自然断点聚类:使用最近邻距离分析自动计算最佳 cluster_gap,将相邻簇合并为有意义的分组
  5. 热力图生成:使用从黄色到红色的渐变显示差异程度
  6. 自动对齐:使用 OpenCV ZNCC 模板匹配处理尺寸不同的图像

工具

get_diff_of_images

比较两张图像并获取结构化差异结果。

参数:

参数类型必填默认值描述
source_imagestring是-参考图像的绝对路径
target_imagestring是-对比图像的绝对路径
thresholdnumber否0.1颜色匹配灵敏度(0-1,值越低越灵敏)
include_aaboolean否false是否将抗锯齿像素计为差异
min_cluster_sizenumber否4构成一个簇所需的最少连通像素数
cluster_gapnumber否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=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 个簇。


自动对齐:比较尺寸不同的图像

当源图像和目标图像的尺寸不同时,引擎会自动查找较小图像(“模板”)在较大图像(“场景”)中的最佳匹配位置,裁剪该区域,然后运行标准的像素级比较。无需配置。

工作原理:

  1. 粗略搜索 — 在缩小的分辨率下使用 OpenCV ZNCC(零均值归一化互相关)进行模板匹配,以快速检测候选位置
  2. 精细校准 — 在粗略匹配位置附近,以全分辨率进行像素级 ZNCC,以达到亚像素精度
  3. 裁剪并比较 — 从场景中裁剪匹配区域,并与模板逐像素比较

这样就能直接将 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)
fullmock
合成叠加图(干净——无差异)
composite
结构化输出
{
  "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。对齐操作找到了精确位置,裁剪区域完全匹配。


对齐性能
场景模板场景尺寸找到的位置置信度耗时
统计卡片780x1001024x768(238, 80)74.3%146ms
侧边栏面板320x3401024x768(680, 212)99.3%89ms
表格(完全相同)420x4961024x768(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 许可证)

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。