@@ -188,10 +188,39 @@ if (result.diffClusters) {
188188| 2 | Default - filters single isolated pixels as rendering noise |
189189| 3+ | More permissive - only larger clusters detected |
190190
191- ### 6. Perceptual Similarity (SSIM)
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)
192220
193221Beyond pixel counting - measure structural similarity from a human perception perspective.
194222
223+ ** SSIM (Structural Similarity Index)** - Overall perceptual similarity:
195224``` javascript
196225const result = await compare (' img1.png' , ' img2.png' , {
197226 includeSSIM: true // Note: Can be slow on large images
@@ -200,13 +229,33 @@ const result = await compare('img1.png', 'img2.png', {
200229if (result .perceptualScore !== null ) {
201230 console .log (` SSIM: ${ result .perceptualScore .toFixed (3 )} ` );
202231 // 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)
203244
204- if (result .perceptualScore > 0.95 ) {
205- console .log (' Images are perceptually very similar' );
245+ if (result .gmsdScore < 0.05 ) {
246+ console .log (' Edges are very similar' );
206247 }
207248}
208249```
209250
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+
210259### 7. Tolerance & Color Spaces
211260
212261** RGB Mode (default)** - Exact matching with pixel tolerance:
@@ -558,22 +607,22 @@ All functions accept:
558607``` typescript
559608interface CompareOptions {
560609 // Basic options
561- pixelTolerance? : number ; // 0-255, ignore diffs below threshold (default: 0)
562- colorThreshold? : number ; // 0.0-1.0, YIQ mode threshold (default: 0.0 = RGB)
610+ threshold? : number ; // CIEDE2000 Delta E threshold (default: 2.0)
563611 antialiasing? : boolean ; // Ignore AA artifacts (default: true)
564- ignoreColors? : boolean ; // Brightness only (default: false)
565612 maxDiffs? : number ; // Stop after N diffs (default: unlimited)
566613
567614 // Analysis options
568615 includeDiffPixels? : boolean ; // List all diff pixels (memory intensive, default: false)
569616 includeClusters? : boolean ; // Spatial clustering (default: false)
570617 includeSSIM? : boolean ; // SSIM perceptual score (slow, default: false)
618+ includeGMSD? : boolean ; // GMSD edge similarity (fast, default: false)
571619 minClusterSize? : number ; // Filter clusters smaller than this (default: 2)
572-
573- // Accessibility options
574- includeAccessibilityData? : boolean ; // RGB, luminance, WCAG (default: false)
575- checkColorBlindness? : boolean ; // Color blindness simulation (default: false)
576- colorBlindnessThreshold? : number ; // Visibility threshold 0-255 (default: 30.0)
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+ };
577626
578627 // Output options
579628 diffPath? : string ; // Save diff image path
@@ -606,7 +655,8 @@ interface DiffResult {
606655 diffPixelsList: DiffPixel [] | null ; // Null unless includeDiffPixels enabled
607656 diffClusters: DiffCluster [] | null ; // Null unless includeClusters enabled
608657 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
610660}
611661```
612662
0 commit comments