@vizzly-testing/honeydiff 0.9.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/README.md +62 -12
- package/index.d.ts +94 -0
- package/package.json +1 -1
- package/platforms/index-aarch64-apple-darwin.node +0 -0
- package/platforms/index-aarch64-unknown-linux-gnu.node +0 -0
- package/platforms/index-x86_64-pc-windows-msvc.node +0 -0
- package/platforms/index-x86_64-unknown-linux-gnu.node +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,46 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.10.1] - 2026-03-11
|
|
9
|
+
|
|
10
|
+
## What's Changed
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Variable-height image comparisons now correctly return diff clusters for the height-difference region. Previously, when a baseline and current screenshot had different heights, `diffClusters` was empty even though `isDifferent` was `true` and `diffPixels` was non-zero — meaning callers had no bounding box to highlight. A synthetic cluster covering the full-width region at the height boundary is now included in `diffClusters`.
|
|
14
|
+
|
|
15
|
+
**Full Changelog**: https://github.com/vizzly-testing/honeydiff/compare/v0.10.0...v0.10.1
|
|
16
|
+
|
|
17
|
+
## [0.10.0] - 2026-02-03
|
|
18
|
+
|
|
19
|
+
## What's Changed
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- **GMSD (Gradient Magnitude Similarity Deviation)** - New `includeGMSD` option for fast, edge-sensitive perceptual comparison
|
|
23
|
+
- Returns `gmsdScore` in results (0.0 = identical, higher = more different)
|
|
24
|
+
- Very fast compared to SSIM, ideal for detecting border/outline changes
|
|
25
|
+
- Based on Xue et al. 2014 research paper
|
|
26
|
+
- **Note:** GMSD requires images with identical dimensions. For variable-height comparisons, `gmsdScore` will be `null`
|
|
27
|
+
- **Cluster Merging** - New `clusterMerge` option to consolidate fragmented text regions
|
|
28
|
+
- Simple API: `clusterMerge: true` enables with sensible defaults
|
|
29
|
+
- Advanced API: Pass an object with `horizontalDistance`, `yBandTolerance`, `maxHeightRatio`, `maxWidthRatio`
|
|
30
|
+
- Solves the "59 clusters for one date string" problem by intelligently merging nearby character-level changes into logical text regions
|
|
31
|
+
- Uses SWT-inspired heuristics (Epshtein et al. 2010) for horizontal-biased text detection
|
|
32
|
+
|
|
33
|
+
**Full Changelog**: https://github.com/vizzly-testing/honeydiff/compare/v0.9.0...v0.10.0
|
|
34
|
+
|
|
35
|
+
## [Unreleased]
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
- **GMSD (Gradient Magnitude Similarity Deviation)** - New `includeGMSD` option for fast, edge-sensitive perceptual comparison
|
|
39
|
+
- Returns `gmsdScore` in results (0.0 = identical, higher = more different)
|
|
40
|
+
- Very fast compared to SSIM, ideal for detecting border/outline changes
|
|
41
|
+
- Based on Xue et al. 2014 research paper
|
|
42
|
+
- **Cluster Merging** - New `clusterMerge` option to consolidate fragmented text regions
|
|
43
|
+
- Simple API: `clusterMerge: true` enables with sensible defaults
|
|
44
|
+
- Advanced API: Pass an object with `horizontalDistance`, `yBandTolerance`, `maxHeightRatio`, `maxWidthRatio`
|
|
45
|
+
- Solves the "59 clusters for one date string" problem
|
|
46
|
+
- Uses SWT-inspired heuristics (Epshtein et al. 2010)
|
|
47
|
+
|
|
8
48
|
## [0.9.0] - 2026-01-26
|
|
9
49
|
|
|
10
50
|
## What's Changed
|
package/README.md
CHANGED
|
@@ -188,10 +188,39 @@ if (result.diffClusters) {
|
|
|
188
188
|
| 2 | Default - filters single isolated pixels as rendering noise |
|
|
189
189
|
| 3+ | More permissive - only larger clusters detected |
|
|
190
190
|
|
|
191
|
-
|
|
191
|
+
**Cluster Merging for Text Regions:**
|
|
192
|
+
|
|
193
|
+
Text changes often fragment into many small clusters (one per character). Enable cluster merging to consolidate them into logical regions:
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
// Simple: enable with sensible defaults
|
|
197
|
+
const result = await compare('img1.png', 'img2.png', {
|
|
198
|
+
includeClusters: true,
|
|
199
|
+
clusterMerge: true // Merge nearby clusters (great for text)
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Advanced: tune the merging behavior
|
|
203
|
+
const result = await compare('img1.png', 'img2.png', {
|
|
204
|
+
includeClusters: true,
|
|
205
|
+
clusterMerge: {
|
|
206
|
+
horizontalDistance: 15, // Max gap between clusters to merge (pixels)
|
|
207
|
+
yBandTolerance: 5, // Vertical tolerance for "same line"
|
|
208
|
+
maxHeightRatio: 2.0, // Prevent merging very different sized clusters
|
|
209
|
+
maxWidthRatio: 3.0
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Before: "2024-01-01" detected as 59 clusters (one per character/gap)
|
|
214
|
+
// After: "2024-01-01" detected as 1-2 logical regions
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Uses SWT-inspired heuristics from text detection research (Epshtein et al. 2010).
|
|
218
|
+
|
|
219
|
+
### 6. Perceptual Similarity (SSIM & GMSD)
|
|
192
220
|
|
|
193
221
|
Beyond pixel counting - measure structural similarity from a human perception perspective.
|
|
194
222
|
|
|
223
|
+
**SSIM (Structural Similarity Index)** - Overall perceptual similarity:
|
|
195
224
|
```javascript
|
|
196
225
|
const result = await compare('img1.png', 'img2.png', {
|
|
197
226
|
includeSSIM: true // Note: Can be slow on large images
|
|
@@ -200,13 +229,33 @@ const result = await compare('img1.png', 'img2.png', {
|
|
|
200
229
|
if (result.perceptualScore !== null) {
|
|
201
230
|
console.log(`SSIM: ${result.perceptualScore.toFixed(3)}`);
|
|
202
231
|
// Output: SSIM: 0.923 (0.0 = different, 1.0 = identical)
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**GMSD (Gradient Magnitude Similarity Deviation)** - Fast edge-sensitive metric:
|
|
236
|
+
```javascript
|
|
237
|
+
const result = await compare('img1.png', 'img2.png', {
|
|
238
|
+
includeGMSD: true // Very fast, great for detecting structural changes
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
if (result.gmsdScore !== null) {
|
|
242
|
+
console.log(`GMSD: ${result.gmsdScore.toFixed(4)}`);
|
|
243
|
+
// Output: GMSD: 0.0234 (0.0 = identical, higher = more different)
|
|
203
244
|
|
|
204
|
-
if (result.
|
|
205
|
-
console.log('
|
|
245
|
+
if (result.gmsdScore < 0.05) {
|
|
246
|
+
console.log('Edges are very similar');
|
|
206
247
|
}
|
|
207
248
|
}
|
|
208
249
|
```
|
|
209
250
|
|
|
251
|
+
GMSD is ideal for catching:
|
|
252
|
+
- Border thickness changes
|
|
253
|
+
- Font weight shifts
|
|
254
|
+
- Icon updates
|
|
255
|
+
- Any edge/outline regressions
|
|
256
|
+
|
|
257
|
+
**Reference:** Xue et al. 2014 - "Gradient Magnitude Similarity Deviation: A Highly Efficient Perceptual Image Quality Index"
|
|
258
|
+
|
|
210
259
|
### 7. Tolerance & Color Spaces
|
|
211
260
|
|
|
212
261
|
**RGB Mode (default)** - Exact matching with pixel tolerance:
|
|
@@ -558,22 +607,22 @@ All functions accept:
|
|
|
558
607
|
```typescript
|
|
559
608
|
interface CompareOptions {
|
|
560
609
|
// Basic options
|
|
561
|
-
|
|
562
|
-
colorThreshold?: number; // 0.0-1.0, YIQ mode threshold (default: 0.0 = RGB)
|
|
610
|
+
threshold?: number; // CIEDE2000 Delta E threshold (default: 2.0)
|
|
563
611
|
antialiasing?: boolean; // Ignore AA artifacts (default: true)
|
|
564
|
-
ignoreColors?: boolean; // Brightness only (default: false)
|
|
565
612
|
maxDiffs?: number; // Stop after N diffs (default: unlimited)
|
|
566
613
|
|
|
567
614
|
// Analysis options
|
|
568
615
|
includeDiffPixels?: boolean; // List all diff pixels (memory intensive, default: false)
|
|
569
616
|
includeClusters?: boolean; // Spatial clustering (default: false)
|
|
570
617
|
includeSSIM?: boolean; // SSIM perceptual score (slow, default: false)
|
|
618
|
+
includeGMSD?: boolean; // GMSD edge similarity (fast, default: false)
|
|
571
619
|
minClusterSize?: number; // Filter clusters smaller than this (default: 2)
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
620
|
+
clusterMerge?: boolean | { // Merge nearby clusters (default: false)
|
|
621
|
+
horizontalDistance?: number; // Max horizontal gap to merge (default: 15)
|
|
622
|
+
yBandTolerance?: number; // Vertical "same line" tolerance (default: 5)
|
|
623
|
+
maxHeightRatio?: number; // Max height ratio to merge (default: 2.0)
|
|
624
|
+
maxWidthRatio?: number; // Max width ratio to merge (default: 3.0)
|
|
625
|
+
};
|
|
577
626
|
|
|
578
627
|
// Output options
|
|
579
628
|
diffPath?: string; // Save diff image path
|
|
@@ -606,7 +655,8 @@ interface DiffResult {
|
|
|
606
655
|
diffPixelsList: DiffPixel[] | null; // Null unless includeDiffPixels enabled
|
|
607
656
|
diffClusters: DiffCluster[] | null; // Null unless includeClusters enabled
|
|
608
657
|
intensityStats: IntensityStats | null; // Null unless includeDiffPixels enabled
|
|
609
|
-
perceptualScore: number | null; // 0.0-1.0, null unless includeSSIM enabled
|
|
658
|
+
perceptualScore: number | null; // SSIM 0.0-1.0, null unless includeSSIM enabled
|
|
659
|
+
gmsdScore: number | null; // GMSD 0.0+, null unless includeGMSD enabled
|
|
610
660
|
}
|
|
611
661
|
```
|
|
612
662
|
|
package/index.d.ts
CHANGED
|
@@ -91,12 +91,67 @@ export interface DiffResult {
|
|
|
91
91
|
intensityStats: IntensityStats | null;
|
|
92
92
|
/** SSIM (Structural Similarity Index) perceptual score 0.0-1.0 (null unless includeSSIM is enabled) */
|
|
93
93
|
perceptualScore: number | null;
|
|
94
|
+
/**
|
|
95
|
+
* GMSD (Gradient Magnitude Similarity Deviation) score (null unless includeGMSD is enabled)
|
|
96
|
+
* Lower values (closer to 0.0) indicate more similar images
|
|
97
|
+
* Typical range: 0.0 to ~0.3 for natural images
|
|
98
|
+
*/
|
|
99
|
+
gmsdScore: number | null;
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
// ============================================================================
|
|
97
103
|
// Options
|
|
98
104
|
// ============================================================================
|
|
99
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Options for merging nearby clusters
|
|
108
|
+
*
|
|
109
|
+
* These heuristics are designed to merge fragmented text regions (like individual
|
|
110
|
+
* characters in a date string) into logical regions while avoiding merging
|
|
111
|
+
* unrelated visual changes.
|
|
112
|
+
*
|
|
113
|
+
* Inspired by the Stroke Width Transform (SWT) text detection algorithm:
|
|
114
|
+
* Epshtein, B., Ofek, E., & Wexler, Y. (2010). "Detecting Text in Natural Scenes
|
|
115
|
+
* with Stroke Width Transform." CVPR 2010.
|
|
116
|
+
*/
|
|
117
|
+
export interface ClusterMergeOptions {
|
|
118
|
+
/**
|
|
119
|
+
* Maximum horizontal distance to merge clusters in same Y-band (pixels)
|
|
120
|
+
*
|
|
121
|
+
* Clusters within this horizontal distance and overlapping Y-ranges
|
|
122
|
+
* will be merged. Suitable for merging characters in text.
|
|
123
|
+
* @default 15
|
|
124
|
+
*/
|
|
125
|
+
horizontalDistance?: number;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Maximum vertical tolerance for "same Y-band" (pixels)
|
|
129
|
+
*
|
|
130
|
+
* Clusters with Y-ranges within this tolerance of each other are
|
|
131
|
+
* considered to be on the same line.
|
|
132
|
+
* @default 5
|
|
133
|
+
*/
|
|
134
|
+
yBandTolerance?: number;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Maximum height ratio between clusters to allow merging
|
|
138
|
+
*
|
|
139
|
+
* Prevents merging clusters of very different sizes (e.g., a word
|
|
140
|
+
* with a large image). Based on SWT heuristic.
|
|
141
|
+
* @default 2.0
|
|
142
|
+
*/
|
|
143
|
+
maxHeightRatio?: number;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Maximum width ratio between clusters to allow merging
|
|
147
|
+
*
|
|
148
|
+
* Additional SWT-inspired heuristic to prevent merging dissimilar
|
|
149
|
+
* regions.
|
|
150
|
+
* @default 3.0
|
|
151
|
+
*/
|
|
152
|
+
maxWidthRatio?: number;
|
|
153
|
+
}
|
|
154
|
+
|
|
100
155
|
export interface CompareOptions {
|
|
101
156
|
/**
|
|
102
157
|
* Perceptual color difference threshold using CIEDE2000 (Delta E units)
|
|
@@ -145,6 +200,23 @@ export interface CompareOptions {
|
|
|
145
200
|
*/
|
|
146
201
|
includeSSIM?: boolean;
|
|
147
202
|
|
|
203
|
+
/**
|
|
204
|
+
* Calculate GMSD (Gradient Magnitude Similarity Deviation) score
|
|
205
|
+
*
|
|
206
|
+
* GMSD is very fast and highly sensitive to edge/structural changes.
|
|
207
|
+
* Useful for detecting border thickness changes, font weight shifts,
|
|
208
|
+
* and icon updates.
|
|
209
|
+
*
|
|
210
|
+
* **Note:** GMSD requires images with identical dimensions. For variable-height
|
|
211
|
+
* comparisons, `gmsdScore` will be `null`. Use SSIM for variable-height images.
|
|
212
|
+
*
|
|
213
|
+
* Reference: Xue et al. 2014 - "Gradient Magnitude Similarity Deviation:
|
|
214
|
+
* A Highly Efficient Perceptual Image Quality Index"
|
|
215
|
+
*
|
|
216
|
+
* @default false
|
|
217
|
+
*/
|
|
218
|
+
includeGMSD?: boolean;
|
|
219
|
+
|
|
148
220
|
/**
|
|
149
221
|
* Minimum cluster size to count as a real difference
|
|
150
222
|
*
|
|
@@ -161,6 +233,28 @@ export interface CompareOptions {
|
|
|
161
233
|
*/
|
|
162
234
|
minClusterSize?: number;
|
|
163
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Merge nearby clusters into logical regions
|
|
238
|
+
*
|
|
239
|
+
* When enabled, nearby clusters are merged using horizontal-biased heuristics
|
|
240
|
+
* that work well for text regions. This helps consolidate fragmented text
|
|
241
|
+
* changes (e.g., "2024-01-01" showing as 59 clusters) into logical regions.
|
|
242
|
+
*
|
|
243
|
+
* Automatically enables clustering when set.
|
|
244
|
+
*
|
|
245
|
+
* @default undefined (no merging)
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* // Simple: enable merging with sensible defaults
|
|
250
|
+
* { clusterMerge: true }
|
|
251
|
+
*
|
|
252
|
+
* // Advanced: tune the merging behavior
|
|
253
|
+
* { clusterMerge: { horizontalDistance: 20, yBandTolerance: 10 } }
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
clusterMerge?: boolean | ClusterMergeOptions;
|
|
257
|
+
|
|
164
258
|
/**
|
|
165
259
|
* Path to save the diff image (highlighted differences)
|
|
166
260
|
* @default undefined
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|