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 | いいえ | 1 つのクラスタとして報告する連結した差分ピクセルの最小数です。孤立したピクセルノイズを除外します。デフォルトは 4 です。 |
詳細
image-diff MCP サーバー
プログラムによる画像比較と視覚的な差分の生成を行う Model Context Protocol(MCP)サーバーです。2 つの画像をピクセル単位で比較し、視覚的な差異のまとまりを特定して、ヒートマップを生成します。
デザインモックのスクリーンショットと、実際の UI 実装を比較するために設計されています。
仕組み
- ピクセル比較:YIQ NTSC 知覚色距離を使用します(人間が色の違いをどのように認識するかを考慮します)
- アンチエイリアス検出:フォントのスムージングや角丸による誤検出を除外します
- 連結成分ラベリング:隣接する差分ピクセルを空間的なクラスターにまとめます
- 自然な区切りによるクラスタリング:最近傍距離の分析を使用して最適な
cluster_gapを自動計算し、近くのクラスターを意味のあるグループにまとめます - ヒートマップ生成:差異の重大度を示す黄色から赤色への強度グラデーションを使用します
- 自動位置合わせ:OpenCV の ZNCC テンプレートマッチングを使用して、サイズの異なる画像を位置合わせします
ツール
get_diff_of_images
2 つの画像を比較し、構造化された差分結果を取得します。
パラメーター:
| パラメーター | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
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:画像が一致するかどうかを示す booleansummary:差分の全体的な統計情報(ピクセル数、割合、クラスター数)clusters:バウンディングボックス、重大度評価、強度指標を含む差分領域の配列clustering:使用した間隔、自動計算されたかどうか、代替として推奨される間隔の値alignment(画像サイズが異なる場合):位置座標、信頼度スコア、使用した方式、所要時間dimensions:比較対象領域の幅と高さheatmapPath:生成されたヒートマップ画像のファイルパスcompositePath:ソース画像にヒートマップを重ねた画像のファイルパス
要件:
- 画像にローカルファイルパスからアクセスできる必要があります
- PNG、JPEG、WebP、TIFF 形式の入力に対応しています
- 画像の寸法が異なる場合、小さい方の画像は大きい方の画像内に自動的に位置合わせされます
ツール呼び出しの形式
MCP ツール呼び出しの入力(最小限の入力 — 必須のパス 2 つのみ):
{
"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:同一の画像 — 差分なし
2つの画像がピクセル単位で同一の場合、エンジンは処理を短絡し、すぐに結果を返します。
| ソース | ターゲット | 合成画像 |
|---|---|---|
![]() | ![]() | ![]() |
合成オーバーレイには、ヒートマップのハイライトがないソース画像が表示されます。差分がないためです。
構造化出力
{
"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:フォントの変更(sans-serif -> serif)
ページ全体がセリフフォント(Georgia)で再レンダリングされます。すべてのテキスト要素がわずかにずれるため、グリフ単位の差分が多数発生します。デフォルトの自動ギャップでは、エンジンが最適な cluster_gap=10 を計算し、これらをテキストブロックや UI 領域を表す 40 個のクラスターに統合します。
| ソース | ターゲット |
|---|---|
![]() | ![]() |
| 単独のヒートマップ | 合成オーバーレイ |
|---|---|
![]() | ![]() |
ヒートマップは、見出し、統計値、表の内容、サイドバーのラベルなど、すべてのテキスト領域を明るく表示します。自動ギャップによって、近くにあるグリフの断片が意味のあるグループに統合されます。
構造化出力 — デフォルトの自動ギャップ(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 | クラスター数 | 動作 |
|---|---|---|
| auto(デフォルト) | 40 | 自動計算された gap=10 により、断片がテキストブロックにマージされます |
| 0(マージなし) | 331 | グリフの断片がすべて個別のクラスターになります |
cluster_gap=0 にすると、意味のある40個のグループが331個の未処理クラスターに分割されます。個々の文字の差分ごとに1つのクラスターができます。これを防ぐのが自動ギャップです。
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(Zero-mean Normalized Cross-Correlation)テンプレートマッチングを縮小解像度で実行し、候補をすばやく検出します
- 詳細な調整 — 粗検索で得られた一致箇所の周辺で、フル解像度のピクセル単位 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) |
|---|---|
![]() | ![]() |
エンジンは89msで99.3% の信頼度をもって、サイドバーの位置**(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 |
クラスターの重大度評価
各差分クラスターは、ピクセル数と強度に基づいて重大度が分類されます。
| 重大度 | 意味 | 典型的な原因 |
|---|---|---|
| 軽微 | 孤立したピクセルの差異 | サブピクセルレンダリング、圧縮アーティファクト |
| 小 | 小規模で局所的な差異 | わずかな色の変化、フォントの太さの変化 |
| 中程度 | 目立つ領域ごとの差異 | テキストの変更、バッジの変更 |
| 大 | 大規模または強度の高い差異 | ボタンの色の変更、要素の欠落/追加、レイアウトのずれ |
エージェントスキルの例
example-skills/ ディレクトリには、image-diff MCP サーバー上にワークフローを構築する方法を示す、すぐに使える エージェントスキルが含まれています。
implement-figma-design
Figma のデザインに一致する UI コンポーネントを構築するため、3 つの MCP サーバー(Figma、Playwright、image-diff)を連携させる、反復的なデザイン実装スキルです。エージェントはコードを記述し、実行中の開発サーバーのスクリーンショットを撮影して、Figma のリファレンスとの差分を比較します。各差分クラスターの根本原因(色の不一致、タイポグラフィ、間隔、要素の欠落など)を診断し、修正を適用して、差分が 0.01%以下に収束するまで繰り返します。
前提条件: Figma MCP サーバー、Playwright MCP サーバー、image-diff MCP サーバー、稼働中の開発サーバー。
使用方法: example-skills/implement-figma-design/ を、プロジェクトの .claude/skills/ ディレクトリ(またはエージェントプラットフォームに対応するスキルの保存場所)にコピーします。
クレジット
- ピクセル比較アルゴリズムは、Mapbox による pixelmatch をフォークして使用(ISC ライセンス)
- 色の距離は、Y. Kotsarenko と F. Ramos による「Measuring perceived color difference using YIQ NTSC transmission color space in mobile applications」に基づいています
- アンチエイリアス検出は、V. Vysniauskas による「Anti-aliased Pixel and Intensity Slope Detector」(2009 年)に基づいています
- 連結成分ラベリングには、Union-Find を使用する古典的な 2 パスアルゴリズムを使用しています
- 画像の位置合わせには opencv-wasm(Apache 2.0 ライセンス)を使用しています
- 画像の入出力には sharp(Apache 2.0 ライセンス)を使用しています
- クラスタリング手法は、Gemini Testing による looks-same(MIT ライセンス)に着想を得ています
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。




















