|
|
@ -12,7 +12,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison |
|
|
public class TolerantImageComparer : ImageComparer |
|
|
public class TolerantImageComparer : ImageComparer |
|
|
{ |
|
|
{ |
|
|
// 1% of all pixels in a 100*100 pixel area are allowed to have a difference of 1 unit
|
|
|
// 1% of all pixels in a 100*100 pixel area are allowed to have a difference of 1 unit
|
|
|
public const float DefaultImageThreshold = 1.0f / (100 * 100 * 255); |
|
|
// 257 = (1 / 255) * 65535.
|
|
|
|
|
|
public const float DefaultImageThreshold = 257F / (100 * 100 * 65535); |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Individual manhattan pixel difference is only added to total image difference when the individual difference is over 'perPixelManhattanThreshold'.
|
|
|
/// Individual manhattan pixel difference is only added to total image difference when the individual difference is over 'perPixelManhattanThreshold'.
|
|
|
@ -28,23 +29,23 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison |
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// The maximal tolerated difference represented by a value between 0.0 and 1.0.
|
|
|
/// The maximal tolerated difference represented by a value between 0.0 and 1.0.
|
|
|
/// Examples of percentage differences on a single pixel:
|
|
|
/// Examples of percentage differences on a single pixel:
|
|
|
/// 1. PixelA = (255,255,255,0) PixelB =(0,0,0,255) leads to 100% difference on a single pixel
|
|
|
/// 1. PixelA = (65535,65535,65535,0) PixelB =(0,0,0,65535) leads to 100% difference on a single pixel
|
|
|
/// 2. PixelA = (255,255,255,0) PixelB =(255,255,255,255) leads to 25% difference on a single pixel
|
|
|
/// 2. PixelA = (65535,65535,65535,0) PixelB =(65535,65535,65535,65535) leads to 25% difference on a single pixel
|
|
|
/// 3. PixelA = (255,255,255,0) PixelB =(128,128,128,128) leads to 50% difference on a single pixel
|
|
|
/// 3. PixelA = (65535,65535,65535,0) PixelB =(128,128,128,128) leads to 50% difference on a single pixel
|
|
|
///
|
|
|
///
|
|
|
/// The total differences is the sum of all pixel differences normalized by image dimensions!
|
|
|
/// The total differences is the sum of all pixel differences normalized by image dimensions!
|
|
|
/// The individual distances are calculated using the Manhattan function:
|
|
|
/// The individual distances are calculated using the Manhattan function:
|
|
|
/// <see>
|
|
|
/// <see>
|
|
|
/// <cref>https://en.wikipedia.org/wiki/Taxicab_geometry</cref>
|
|
|
/// <cref>https://en.wikipedia.org/wiki/Taxicab_geometry</cref>
|
|
|
/// </see>
|
|
|
/// </see>
|
|
|
/// ImageThresholdInPercents = 1.0/255 means that we allow one byte difference per channel on a 1x1 image
|
|
|
/// ImageThresholdInPercents = 1.0/65535 means that we allow one unit difference per channel on a 1x1 image
|
|
|
/// ImageThresholdInPercents = 1.0/(100*100*255) means that we allow only one byte difference per channel on a 100x100 image
|
|
|
/// ImageThresholdInPercents = 1.0/(100*100*65535) means that we allow only one unit difference per channel on a 100x100 image
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
public float ImageThreshold { get; } |
|
|
public float ImageThreshold { get; } |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// The threshold of the individual pixels before they acumulate towards the overall difference.
|
|
|
/// The threshold of the individual pixels before they acumulate towards the overall difference.
|
|
|
/// For an individual <see cref="Rgba32"/> pixel pair the value is the Manhattan distance of pixels:
|
|
|
/// For an individual <see cref="Rgba64"/> pixel pair the value is the Manhattan distance of pixels:
|
|
|
/// <see>
|
|
|
/// <see>
|
|
|
/// <cref>https://en.wikipedia.org/wiki/Taxicab_geometry</cref>
|
|
|
/// <cref>https://en.wikipedia.org/wiki/Taxicab_geometry</cref>
|
|
|
/// </see>
|
|
|
/// </see>
|
|
|
@ -60,12 +61,12 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison |
|
|
|
|
|
|
|
|
int width = actual.Width; |
|
|
int width = actual.Width; |
|
|
|
|
|
|
|
|
// TODO: Comparing through Rgba32 is not robust enough because of the existance of super high precision pixel types.
|
|
|
// TODO: Comparing through Rgba64 may not robust enough because of the existance of super high precision pixel types.
|
|
|
|
|
|
|
|
|
var aBuffer = new Rgba32[width]; |
|
|
var aBuffer = new Rgba64[width]; |
|
|
var bBuffer = new Rgba32[width]; |
|
|
var bBuffer = new Rgba64[width]; |
|
|
|
|
|
|
|
|
float totalDifference = 0.0f; |
|
|
float totalDifference = 0F; |
|
|
|
|
|
|
|
|
var differences = new List<PixelDifference>(); |
|
|
var differences = new List<PixelDifference>(); |
|
|
|
|
|
|
|
|
@ -74,8 +75,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison |
|
|
Span<TPixelA> aSpan = expected.GetPixelRowSpan(y); |
|
|
Span<TPixelA> aSpan = expected.GetPixelRowSpan(y); |
|
|
Span<TPixelB> bSpan = actual.GetPixelRowSpan(y); |
|
|
Span<TPixelB> bSpan = actual.GetPixelRowSpan(y); |
|
|
|
|
|
|
|
|
PixelOperations<TPixelA>.Instance.ToRgba32(aSpan, aBuffer, width); |
|
|
PixelOperations<TPixelA>.Instance.ToRgba64(aSpan, aBuffer, width); |
|
|
PixelOperations<TPixelB>.Instance.ToRgba32(bSpan, bBuffer, width); |
|
|
PixelOperations<TPixelB>.Instance.ToRgba64(bSpan, bBuffer, width); |
|
|
|
|
|
|
|
|
for (int x = 0; x < width; x++) |
|
|
for (int x = 0; x < width; x++) |
|
|
{ |
|
|
{ |
|
|
@ -91,8 +92,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
float normalizedDifference = totalDifference / ((float)actual.Width * (float)actual.Height); |
|
|
float normalizedDifference = totalDifference / (actual.Width * (float)actual.Height); |
|
|
normalizedDifference /= 4.0f * 255.0f; |
|
|
normalizedDifference /= 4F * 65535F; |
|
|
|
|
|
|
|
|
if (normalizedDifference > this.ImageThreshold) |
|
|
if (normalizedDifference > this.ImageThreshold) |
|
|
{ |
|
|
{ |
|
|
@ -105,12 +106,12 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
|
private static int GetManhattanDistanceInRgbaSpace(ref Rgba32 a, ref Rgba32 b) |
|
|
private static int GetManhattanDistanceInRgbaSpace(ref Rgba64 a, ref Rgba64 b) |
|
|
{ |
|
|
{ |
|
|
return Diff(a.R, b.R) + Diff(a.G, b.G) + Diff(a.B, b.B) + Diff(a.A, b.A); |
|
|
return Diff(a.R, b.R) + Diff(a.G, b.G) + Diff(a.B, b.B) + Diff(a.A, b.A); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
|
private static int Diff(byte a, byte b) => Math.Abs(a - b); |
|
|
private static int Diff(ushort a, ushort b) => Math.Abs(a - b); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |