镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Expose an image comparison API in the core library - #3276

Open
ndavat wants to merge 1 commit into
SixLabors:mainfrom
ndavat:feature/1129-expose-image-comparer
Open

ndavat wants to merge 1 commit into
SixLabors:mainfrom
ndavat:feature/1129-expose-image-comparer

Conversation

@ndavat

@ndavat ndavat commented Nov 14, 2026

Copy link
Copy Markdown

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Description

Fixes #1129. Implements the API direction agreed in discussion #1358.

New public namespace SixLabors.ImageSharp.Comparison:

  • ImageComparer - abstract comparer with Exact, Tolerant(...) and TolerantPercentage(...) factories. Non-generic entry points: CompareImagesOrFrames(Image, Image), CompareImagesOrFrames(int, ImageFrame, ImageFrame), CompareImages(Image, Image) and VerifySimilarity(Image, Image).
  • TolerantImageComparer - configurable thresholds with the same semantics as the internal test comparers: manhattan metric in sixteen-bit units, same normalization, NaN-safe comparisons.
  • ImageSimilarityReport - non-generic report: frame index, compared frames, TotalNormalizedDifference, DifferencePercentageString, IsEmpty, and CreateDifferenceImage().
  • Exceptions: ImagesSimilarityException, ImageDimensionsMismatchException, ImageFramesMismatchException, ImageDifferenceIsOverThresholdException.

Design notes (from discussion #1358):

  • Pixel-agnostic: entry points take non-generic Image / ImageFrame. A pair of nested IImageFrameVisitor implementations double-dispatches into the pixel-specific comparison.
  • PixelDifference is not public. The report carries aggregates and frames only; the difference image shows where and how pixels differ. This also fixes the nullable TotalNormalizedDifference TODO from the test utilities.
  • Difference image: mid-gray where equal; per RGB channel white or black where the actual component is above or below the expected one; fully opaque. Alpha differences are reflected in the total difference only. I kept the rendering RGB-only on purpose and am open to changing the encoding.
  • Reports under threshold keep their frames, so CreateDifferenceImage() also works for matching frames and renders uniform mid-gray.

Implementation notes:

  • ImageComparer.Exact is TolerantImageComparer(0, 0), matching the internal ImageComparer.Exact.
  • The shared comparison and difference-image math lives in an internal ImageComparisonEngine, so both paths use one row conversion and one metric.
  • AOT: Seed<TPixel>() seeds the comparison for same-type pairs through a new AotCompileImageComparison<TPixel>(), following the compile-only seeding pattern used by the encoders. Cross-type pairs follow the same discovery limits as other cross-type generic APIs.

Test coverage:

tests/ImageSharp.Tests/Comparison/ImageComparerTests.cs - 15 tests covering exact and tolerant thresholds, per-pixel threshold behavior, percentage semantics, dimension and frame-count mismatches, VerifySimilarity, multi-frame indices, difference-image rendering, the non-generic double-dispatch path, and mixed pixel formats (Rgba32 vs Rgb24).

The internal test comparers are unchanged (104 test files use them). Re-pointing them to the public API can be a follow-up.

@CLAassistant

CLAassistant commented Nov 14, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose ImageComparer API

2 participants