Browse Source

ImageComparer refactors, ExactComparer

pull/298/head
Anton Firszov 9 years ago
parent
commit
a00aec7d13
  1. 4
      tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs
  2. 173
      tests/ImageSharp.Tests/ImageComparer.cs
  3. 59
      tests/ImageSharp.Tests/ImagesSimilarityException.cs
  4. 213
      tests/ImageSharp.Tests/PercentageImageComparer.cs
  5. 2
      tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs
  6. 4
      tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTest.cs
  7. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BlackWhiteTest.cs
  8. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ColorBlindnessTest.cs
  9. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs
  10. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/HueTest.cs
  11. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/KodachromeTest.cs
  12. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/LomographTest.cs
  13. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/PolaroidTest.cs
  14. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturationTest.cs
  15. 2
      tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SepiaTest.cs
  16. 2
      tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs
  17. 2
      tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs
  18. 2
      tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs
  19. 2
      tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs
  20. 2
      tests/ImageSharp.Tests/Processing/Processors/Effects/AlphaTest.cs
  21. 2
      tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs
  22. 2
      tests/ImageSharp.Tests/Processing/Processors/Effects/BrightnessTest.cs
  23. 2
      tests/ImageSharp.Tests/Processing/Processors/Effects/ContrastTest.cs
  24. 2
      tests/ImageSharp.Tests/Processing/Processors/Effects/InvertTest.cs
  25. 2
      tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs
  26. 2
      tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs
  27. 2
      tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs
  28. 2
      tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs
  29. 1
      tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs
  30. 14
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs
  31. 175
      tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs
  32. 0
      tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs

4
tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs

@ -35,7 +35,7 @@ namespace ImageSharp.Tests.Formats.Png
using (Image<Rgba32> img2 = Image.Load<Rgba32>(ms, new PngDecoder()))
{
// img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder());
ImageComparer.VerifySimilarity(image, img2);
PercentageImageComparer.VerifySimilarity(image, img2);
}
}
}
@ -121,7 +121,7 @@ namespace ImageSharp.Tests.Formats.Png
ms.Position = 0;
using (Image<Rgba32> img2 = Image.Load<Rgba32>(ms, new PngDecoder()))
{
ImageComparer.VerifySimilarity(image, img2);
PercentageImageComparer.VerifySimilarity(image, img2);
}
}
}

173
tests/ImageSharp.Tests/ImageComparer.cs

@ -1,173 +1,22 @@
// <copyright file="ImageComparer.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using ImageSharp;
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
using SixLabors.Primitives;
using Xunit;
/// <summary>
/// Class to perform simple image comparisons.
/// </summary>
public static class ImageComparer
{
internal const int DefaultScalingFactor = 32; // This is means the images get scaled into a 32x32 image to sample pixels
internal const int DefaultSegmentThreshold = 3; // The greyscale difference between 2 segements my be > 3 before it influences the overall difference
internal const float DefaultImageThreshold = 0.000F; // After segment thresholds the images must have no differences
/// <summary>
/// Fills the bounded area with a solid color and does a visual comparison between 2 images asserting the difference outwith
/// that area is less then a configurable threshold.
/// </summary>
/// <typeparam name="TPixelA">The color of the expected image</typeparam>
/// <typeparam name="TPixelB">The color type fo the the actual image</typeparam>
/// <param name="expected">The expected image</param>
/// <param name="actual">The actual image</param>
/// <param name="bounds">The bounds within the image has been altered</param>
/// <param name="imageTheshold">
/// The threshold for the percentage difference where the images are asumed to be the same.
/// The default/undefined value is <see cref="DefaultImageThreshold"/>
/// </param>
/// <param name="segmentThreshold">
/// The threshold of the individual segments before it acumulates towards the overall difference.
/// The default undefined value is <see cref="DefaultSegmentThreshold"/>
/// </param>
/// <param name="scalingFactor">
/// This is a sampling factor we sample a grid of average pixels <paramref name="scalingFactor"/> width by <paramref name="scalingFactor"/> high
/// The default undefined value is <see cref="DefaultScalingFactor"/>
/// </param>
public static void EnsureProcessorChangesAreConstrained<TPixelA, TPixelB>(
Image<TPixelA> expected,
Image<TPixelB> actual,
Rectangle bounds,
float imageTheshold = DefaultImageThreshold,
byte segmentThreshold = DefaultSegmentThreshold,
int scalingFactor = DefaultScalingFactor)
where TPixelA : struct, IPixel<TPixelA>
where TPixelB : struct, IPixel<TPixelB>
{
// Draw identical shapes over the bounded and compare to ensure changes are constrained.
expected.Mutate(x => x.Fill(NamedColors<TPixelA>.HotPink, bounds));
actual.Mutate(x => x.Fill(NamedColors<TPixelB>.HotPink, bounds));
VerifySimilarity(expected, actual, imageTheshold, segmentThreshold, scalingFactor);
}
/// <summary>
/// Does a visual comparison between 2 images and then asserts the difference is less then a configurable threshold
/// </summary>
/// <typeparam name="TPixelA">The color of the expected image</typeparam>
/// <typeparam name="TPixelB">The color type fo the the actual image</typeparam>
/// <param name="expected">The expected image</param>
/// <param name="actual">The actual image</param>
/// <param name="imageTheshold">
/// The threshold for the percentage difference where the images are asumed to be the same.
/// The default/undefined value is <see cref="DefaultImageThreshold"/>
/// </param>
/// <param name="segmentThreshold">
/// The threshold of the individual segments before it acumulates towards the overall difference.
/// The default undefined value is <see cref="DefaultSegmentThreshold"/>
/// </param>
/// <param name="scalingFactor">
/// This is a sampling factor we sample a grid of average pixels <paramref name="scalingFactor"/> width by <paramref name="scalingFactor"/> high
/// The default undefined value is <see cref="DefaultScalingFactor"/>
/// </param>
public static void VerifySimilarity<TPixelA, TPixelB>(
Image<TPixelA> expected,
Image<TPixelB> actual,
float imageTheshold = DefaultImageThreshold,
byte segmentThreshold = DefaultSegmentThreshold,
int scalingFactor = DefaultScalingFactor)
where TPixelA : struct, IPixel<TPixelA> where TPixelB : struct, IPixel<TPixelB>
{
Assert.Equal(expected.Width, actual.Width);
Assert.Equal(expected.Height, actual.Height);
float percentage = expected.PercentageDifference(actual, segmentThreshold, scalingFactor);
Assert.InRange(percentage, 0, imageTheshold);
}
/// <summary>
/// Does a visual comparison between 2 images and then and returns the percentage diffence between the 2
/// </summary>
/// <typeparam name="TPixelA">The color of the source image</typeparam>
/// <typeparam name="TPixelB">The color type for the target image</typeparam>
/// <param name="source">The source image</param>
/// <param name="target">The target image</param>
/// <param name="segmentThreshold">
/// The threshold of the individual segments before it acumulates towards the overall difference.
/// The default undefined value is <see cref="DefaultSegmentThreshold"/>
/// </param>
/// <param name="scalingFactor">
/// This is a sampling factor we sample a grid of average pixels <paramref name="scalingFactor"/> width by <paramref name="scalingFactor"/> high
/// The default undefined value is <see cref="ImageComparer.DefaultScalingFactor"/>
/// </param>
/// <returns>Returns a number from 0 - 1 which represents the difference focter between the images.</returns>
public static float PercentageDifference<TPixelA, TPixelB>(this Image<TPixelA> source, Image<TPixelB> target, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor)
where TPixelA : struct, IPixel<TPixelA>
where TPixelB : struct, IPixel<TPixelB>
{
// code adapted from https://www.codeproject.com/Articles/374386/Simple-image-comparison-in-NET
Fast2DArray<byte> differences = GetDifferences(source, target, scalingFactor);
int diffPixels = 0;
foreach (byte b in differences.Data)
{
if (b > segmentThreshold) { diffPixels++; }
}
return diffPixels / (float)(scalingFactor * scalingFactor);
}
private static Fast2DArray<byte> GetDifferences<TPixelA, TPixelB>(Image<TPixelA> source, Image<TPixelB> target, int scalingFactor)
where TPixelA : struct, IPixel<TPixelA>
where TPixelB : struct, IPixel<TPixelB>
{
var differences = new Fast2DArray<byte>(scalingFactor, scalingFactor);
Fast2DArray<byte> firstGray = source.GetGrayScaleValues(scalingFactor);
Fast2DArray<byte> secondGray = target.GetGrayScaleValues(scalingFactor);
using ImageSharp.PixelFormats;
for (int y = 0; y < scalingFactor; y++)
{
for (int x = 0; x < scalingFactor; x++)
{
int diff = firstGray[x, y] - secondGray[x, y];
differences[x, y] = (byte)Math.Abs(diff);
}
}
public abstract class ImageComparer
{
public abstract void Verify<TPixelA, TPixelB>(Image<TPixelA> expected, Image<TPixelB> actual)
where TPixelA : struct, IPixel<TPixelA> where TPixelB : struct, IPixel<TPixelB>;
}
return differences;
}
public class ExactComparer : ImageComparer
{
public static ExactComparer Instance { get; } = new ExactComparer();
private static Fast2DArray<byte> GetGrayScaleValues<TPixelA>(this Image<TPixelA> source, int scalingFactor)
where TPixelA : struct, IPixel<TPixelA>
public override void Verify<TPixelA, TPixelB>(Image<TPixelA> expected, Image<TPixelB> actual)
{
byte[] buffer = new byte[3];
using (Image<TPixelA> img = source.Clone(x => x.Resize(scalingFactor, scalingFactor).Grayscale()))
{
using (PixelAccessor<TPixelA> pixels = img.Lock())
{
var grayScale = new Fast2DArray<byte>(scalingFactor, scalingFactor);
for (int y = 0; y < scalingFactor; y++)
{
for (int x = 0; x < scalingFactor; x++)
{
pixels[x, y].ToXyzBytes(buffer, 0);
grayScale[x, y] = buffer[0];
}
}
return grayScale;
}
}
throw new NotImplementedException();
}
}
}

59
tests/ImageSharp.Tests/ImagesSimilarityException.cs

@ -0,0 +1,59 @@
namespace ImageSharp.Tests
{
using System;
using System.Text;
using SixLabors.Primitives;
public class ImagesSimilarityException : Exception
{
public ImagesSimilarityException(string message)
: base(message)
{
}
}
public class ImageDimensionsMismatchException : ImagesSimilarityException
{
public ImageDimensionsMismatchException(Size expectedSize, Size actualSize)
: base($"The image dimensions {actualSize} do not match the expected {expectedSize}!")
{
this.ExpectedSize = expectedSize;
this.ActualSize = actualSize;
}
public Size ExpectedSize { get; }
public Size ActualSize { get; }
}
public class ImagesAreNotEqualException : ImagesSimilarityException
{
public ImagesAreNotEqualException(Point[] differences)
: base("Images are not equal! Differences: " + StringifyDifferences(differences))
{
this.Differences = differences;
}
public Point[] Differences { get; }
private static string StringifyDifferences(Point[] differences)
{
var sb = new StringBuilder();
int max = Math.Min(5, differences.Length);
for (int i = 0; i < max; i++)
{
sb.Append(differences[i]);
if (i < max - 1)
{
sb.Append(';');
}
}
if (differences.Length >= 5)
{
sb.Append("...");
}
return sb.ToString();
}
}
}

213
tests/ImageSharp.Tests/PercentageImageComparer.cs

@ -0,0 +1,213 @@
// <copyright file="ImageComparer.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp.Tests
{
using System;
using ImageSharp;
using ImageSharp.Memory;
using ImageSharp.PixelFormats;
using SixLabors.Primitives;
using Xunit;
/// <summary>
/// Class to perform simple image comparisons.
/// </summary>
public class PercentageImageComparer : ImageComparer
{
public float ImageThreshold { get; }
public byte SegmentThreshold { get; }
public int ScaleIntoSize { get; }
public PercentageImageComparer(
float imageThreshold = DefaultImageThreshold,
byte segmentThreshold = DefaultSegmentThreshold,
int scaleIntoSize = DefaultScaleIntoSize)
{
this.ImageThreshold = imageThreshold;
this.SegmentThreshold = segmentThreshold;
this.ScaleIntoSize = scaleIntoSize;
}
/// <summary>
/// This is means the images get scaled into a 32x32 image to sample pixels
/// </summary>
public const int DefaultScaleIntoSize = 32;
/// <summary>
/// The greyscale difference between 2 segements my be > 3 before it influences the overall difference
/// </summary>
public const int DefaultSegmentThreshold = 3;
/// <summary>
/// After segment thresholds the images must have no differences
/// </summary>
public const float DefaultImageThreshold = 0.000F;
/// <summary>
/// Fills the bounded area with a solid color and does a visual comparison between 2 images asserting the difference outwith
/// that area is less then a configurable threshold.
/// </summary>
/// <typeparam name="TPixelA">The color of the expected image</typeparam>
/// <typeparam name="TPixelB">The color type fo the the actual image</typeparam>
/// <param name="expected">The expected image</param>
/// <param name="actual">The actual image</param>
/// <param name="bounds">The bounds within the image has been altered</param>
/// <param name="imageTheshold">
/// The threshold for the percentage difference where the images are asumed to be the same.
/// The default/undefined value is <see cref="DefaultImageThreshold"/>
/// </param>
/// <param name="segmentThreshold">
/// The threshold of the individual segments before it acumulates towards the overall difference.
/// The default undefined value is <see cref="DefaultSegmentThreshold"/>
/// </param>
/// <param name="scaleIntoSize">
/// This is a sampling factor we sample a grid of average pixels <paramref name="scaleIntoSize"/> width by <paramref name="scaleIntoSize"/> high
/// The default undefined value is <see cref="DefaultScaleIntoSize"/>
/// </param>
public static void EnsureProcessorChangesAreConstrained<TPixelA, TPixelB>(
Image<TPixelA> expected,
Image<TPixelB> actual,
Rectangle bounds,
float imageTheshold = DefaultImageThreshold,
byte segmentThreshold = DefaultSegmentThreshold,
int scaleIntoSize = DefaultScaleIntoSize)
where TPixelA : struct, IPixel<TPixelA>
where TPixelB : struct, IPixel<TPixelB>
{
// Draw identical shapes over the bounded and compare to ensure changes are constrained.
expected.Mutate(x => x.Fill(NamedColors<TPixelA>.HotPink, bounds));
actual.Mutate(x => x.Fill(NamedColors<TPixelB>.HotPink, bounds));
VerifySimilarity(expected, actual, imageTheshold, segmentThreshold, scaleIntoSize);
}
/// <summary>
/// Does a visual comparison between 2 images and then asserts the difference is less then a configurable threshold
/// </summary>
/// <typeparam name="TPixelA">The color of the expected image</typeparam>
/// <typeparam name="TPixelB">The color type fo the the actual image</typeparam>
/// <param name="expected">The expected image</param>
/// <param name="actual">The actual image</param>
/// <param name="imageTheshold">
/// The threshold for the percentage difference where the images are asumed to be the same.
/// The default/undefined value is <see cref="DefaultImageThreshold"/>
/// </param>
/// <param name="segmentThreshold">
/// The threshold of the individual segments before it acumulates towards the overall difference.
/// The default undefined value is <see cref="DefaultSegmentThreshold"/>
/// </param>
/// <param name="scaleIntoSize">
/// This is a sampling factor we sample a grid of average pixels <paramref name="scaleIntoSize"/> width by <paramref name="scaleIntoSize"/> high
/// The default undefined value is <see cref="DefaultScaleIntoSize"/>
/// </param>
public static void VerifySimilarity<TPixelA, TPixelB>(
Image<TPixelA> expected,
Image<TPixelB> actual,
float imageTheshold = DefaultImageThreshold,
byte segmentThreshold = DefaultSegmentThreshold,
int scaleIntoSize = DefaultScaleIntoSize)
where TPixelA : struct, IPixel<TPixelA> where TPixelB : struct, IPixel<TPixelB>
{
var comparer = new PercentageImageComparer(imageTheshold, segmentThreshold, scaleIntoSize);
comparer.Verify(expected, actual);
}
/// <summary>
/// Does a visual comparison between 2 images and then and returns the percentage diffence between the 2
/// </summary>
/// <typeparam name="TPixelA">The color of the source image</typeparam>
/// <typeparam name="TPixelB">The color type for the target image</typeparam>
/// <param name="source">The source image</param>
/// <param name="target">The target image</param>
/// <param name="segmentThreshold">
/// The threshold of the individual segments before it acumulates towards the overall difference.
/// The default undefined value is <see cref="DefaultSegmentThreshold"/>
/// </param>
/// <param name="scalingFactor">
/// This is a sampling factor we sample a grid of average pixels <paramref name="scalingFactor"/> width by <paramref name="scalingFactor"/> high
/// The default undefined value is <see cref="DefaultScaleIntoSize"/>
/// </param>
/// <returns>Returns a number from 0 - 1 which represents the difference focter between the images.</returns>
public static float PercentageDifference<TPixelA, TPixelB>(Image<TPixelA> source, Image<TPixelB> target, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScaleIntoSize)
where TPixelA : struct, IPixel<TPixelA>
where TPixelB : struct, IPixel<TPixelB>
{
// code adapted from https://www.codeproject.com/Articles/374386/Simple-image-comparison-in-NET
Fast2DArray<byte> differences = GetDifferences(source, target, scalingFactor);
int diffPixels = 0;
foreach (byte b in differences.Data)
{
if (b > segmentThreshold) { diffPixels++; }
}
return diffPixels / (float)(scalingFactor * scalingFactor);
}
private static Fast2DArray<byte> GetDifferences<TPixelA, TPixelB>(Image<TPixelA> source, Image<TPixelB> target, int scalingFactor)
where TPixelA : struct, IPixel<TPixelA>
where TPixelB : struct, IPixel<TPixelB>
{
var differences = new Fast2DArray<byte>(scalingFactor, scalingFactor);
Fast2DArray<byte> firstGray = GetGrayScaleValues(source, scalingFactor);
Fast2DArray<byte> secondGray = GetGrayScaleValues(target, scalingFactor);
for (int y = 0; y < scalingFactor; y++)
{
for (int x = 0; x < scalingFactor; x++)
{
int diff = firstGray[x, y] - secondGray[x, y];
differences[x, y] = (byte)Math.Abs(diff);
}
}
return differences;
}
private static Fast2DArray<byte> GetGrayScaleValues<TPixelA>(Image<TPixelA> source, int scalingFactor)
where TPixelA : struct, IPixel<TPixelA>
{
byte[] buffer = new byte[3];
using (Image<TPixelA> img = source.Clone(x => x.Resize(scalingFactor, scalingFactor).Grayscale()))
{
using (PixelAccessor<TPixelA> pixels = img.Lock())
{
var grayScale = new Fast2DArray<byte>(scalingFactor, scalingFactor);
for (int y = 0; y < scalingFactor; y++)
{
for (int x = 0; x < scalingFactor; x++)
{
pixels[x, y].ToXyzBytes(buffer, 0);
grayScale[x, y] = buffer[0];
}
}
return grayScale;
}
}
}
public override void Verify<TPixelA, TPixelB>(Image<TPixelA> expected, Image<TPixelB> actual)
{
if (expected.Size() != actual.Size())
{
throw new ImageDimensionsMismatchException(expected.Size(), actual.Size());
}
float percentage = PercentageDifference(expected, actual, this.SegmentThreshold, this.ScaleIntoSize);
if (percentage > this.ImageThreshold)
{
throw new ImagesSimilarityException(
$"The percentage difference of images {percentage} is larger than expected maximum {this.ImageThreshold}!");
}
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Binarization/BinaryThresholdTest.cs

@ -44,7 +44,7 @@ namespace ImageSharp.Tests.Processing.Processors.Binarization
image.Mutate(x => x.BinaryThreshold(value, bounds));
image.DebugSave(provider, value);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

4
tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTest.cs

@ -56,7 +56,7 @@ namespace ImageSharp.Tests.Processing.Processors.Binarization
image.Mutate(x => x.Dither(ditherer, bounds));
image.DebugSave(provider, name);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
@ -85,7 +85,7 @@ namespace ImageSharp.Tests.Processing.Processors.Binarization
image.Mutate(x => x.Dither(diffuser, .5F, bounds));
image.DebugSave(provider, name);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/BlackWhiteTest.cs

@ -36,7 +36,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix
image.Mutate(x => x.BlackWhite(bounds));
image.DebugSave(provider);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/ColorBlindnessTest.cs

@ -50,7 +50,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix
image.Mutate(x => x.ColorBlindness(colorBlindness, bounds));
image.DebugSave(provider, colorBlindness.ToString());
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/GrayscaleTest.cs

@ -54,7 +54,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix
image.Mutate(x => x.Grayscale(value, bounds));
image.DebugSave(provider, value.ToString());
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/HueTest.cs

@ -43,7 +43,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix
image.Mutate(x => x.Hue(value, bounds));
image.DebugSave(provider, value);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/KodachromeTest.cs

@ -36,7 +36,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix
image.Mutate(x => x.Kodachrome(bounds));
image.DebugSave(provider);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/LomographTest.cs

@ -38,7 +38,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix
image.Mutate(x => x.Lomograph(bounds));
image.DebugSave(provider);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/PolaroidTest.cs

@ -36,7 +36,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix
image.Mutate(x => x.Polaroid(bounds));
image.DebugSave(provider);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SaturationTest.cs

@ -43,7 +43,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix
image.Mutate(x => x.Saturation(value, bounds));
image.DebugSave(provider, value);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/ColorMatrix/SepiaTest.cs

@ -36,7 +36,7 @@ namespace ImageSharp.Tests.Processing.Processors.ColorMatrix
image.Mutate(x => x.Sepia(bounds));
image.DebugSave(provider);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs

@ -43,7 +43,7 @@ namespace ImageSharp.Tests.Processing.Processors.Convolution
image.Mutate(x => x.BoxBlur(value, bounds));
image.DebugSave(provider, value);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs

@ -56,7 +56,7 @@ namespace ImageSharp.Tests.Processing.Processors.Convolution
image.DebugSave(provider, grayscale: true);
// TODO: We don't need this any longer after switching to ReferenceImages
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs

@ -43,7 +43,7 @@ namespace ImageSharp.Tests.Processing.Processors.Convolution
image.Mutate(x => x.GaussianBlur(value, bounds));
image.DebugSave(provider, value);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs

@ -43,7 +43,7 @@ namespace ImageSharp.Tests.Processing.Processors.Convolution
image.Mutate(x => x.GaussianSharpen(value, bounds));
image.DebugSave(provider, value);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Effects/AlphaTest.cs

@ -43,7 +43,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects
image.Mutate(x => x.Alpha(value, bounds));
image.DebugSave(provider, value);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Effects/BackgroundColorTest.cs

@ -36,7 +36,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects
image.Mutate(x => x.BackgroundColor(NamedColors<TPixel>.HotPink, bounds));
image.DebugSave(provider);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Effects/BrightnessTest.cs

@ -43,7 +43,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects
image.Mutate(x => x.Brightness(value, bounds));
image.DebugSave(provider, value);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); ;
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds); ;
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Effects/ContrastTest.cs

@ -43,7 +43,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects
image.Mutate(x => x.Contrast(value, bounds));
image.DebugSave(provider, value);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Effects/InvertTest.cs

@ -36,7 +36,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects
image.Mutate(x => x.Invert(bounds));
image.DebugSave(provider);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Effects/OilPaintTest.cs

@ -43,7 +43,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects
image.Mutate(x => x.OilPaint(levels, brushSize, bounds));
image.DebugSave(provider, string.Join("-", levels, brushSize));
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds, 0.001F);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds, 0.001F);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Effects/PixelateTest.cs

@ -77,7 +77,7 @@ namespace ImageSharp.Tests.Processing.Processors.Effects
}
}
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Overlays/GlowTest.cs

@ -60,7 +60,7 @@ namespace ImageSharp.Tests.Processing.Processors.Overlays
image.Mutate(x => x.Glow(bounds));
image.DebugSave(provider);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Overlays/VignetteTest.cs

@ -60,7 +60,7 @@ namespace ImageSharp.Tests.Processing.Processors.Overlays
image.Mutate(x => x.Vignette(bounds));
image.DebugSave(provider);
ImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
PercentageImageComparer.EnsureProcessorChangesAreConstrained(source, image, bounds);
}
}
}

1
tests/ImageSharp.Tests/TestUtilities/PixelTypes.cs

@ -60,6 +60,7 @@ namespace ImageSharp.Tests
// TODO: Add multi-flag entries by rules defined in PackedPixelConverterHelper
Default = Rgba32,
// "All" is handled as a separate, individual case instead of using bitwise OR
All = 30
}

14
tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

@ -59,15 +59,15 @@ namespace ImageSharp.Tests
/// <param name="grayscale">A boolean indicating whether we should debug save + compare against a grayscale image, smaller in size.</param>
/// <param name="imageTheshold">
/// The threshold for the percentage difference where the images are asumed to be the same.
/// The default/undefined value is <see cref="ImageComparer.DefaultImageThreshold"/>
/// The default/undefined value is <see cref="PercentageImageComparer.DefaultImageThreshold"/>
/// </param>
/// <param name="segmentThreshold">
/// The threshold of the individual segments before it acumulates towards the overall difference.
/// The default undefined value is <see cref="ImageComparer.DefaultSegmentThreshold"/>
/// The default undefined value is <see cref="PercentageImageComparer.DefaultSegmentThreshold"/>
/// </param>
/// <param name="scalingFactor">
/// This is a sampling factor we sample a grid of average pixels <paramref name="scalingFactor"/> width by <paramref name="scalingFactor"/> high
/// The default undefined value is <see cref="ImageComparer.DefaultScalingFactor"/>
/// The default undefined value is <see cref="PercentageImageComparer.DefaultScaleIntoSize"/>
/// </param>
/// <returns></returns>
public static Image<TPixel> CompareToReferenceOutput<TPixel>(
@ -76,9 +76,9 @@ namespace ImageSharp.Tests
object testOutputDetails = null,
string extension = "png",
bool grayscale = false,
float imageTheshold = ImageComparer.DefaultImageThreshold,
byte segmentThreshold = ImageComparer.DefaultSegmentThreshold,
int scalingFactor = ImageComparer.DefaultScalingFactor)
float imageTheshold = PercentageImageComparer.DefaultImageThreshold,
byte segmentThreshold = PercentageImageComparer.DefaultSegmentThreshold,
int scalingFactor = PercentageImageComparer.DefaultScaleIntoSize)
where TPixel : struct, IPixel<TPixel>
{
string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(extension, testOutputDetails);
@ -99,7 +99,7 @@ namespace ImageSharp.Tests
using (Image<Rgba32> referenceImage = Image.Load<Rgba32>(referenceOutputFile, ReferenceDecoder.Instance))
{
ImageComparer.VerifySimilarity(referenceImage, image, imageTheshold, segmentThreshold, scalingFactor);
PercentageImageComparer.VerifySimilarity(referenceImage, image, imageTheshold, segmentThreshold, scalingFactor);
}
return image;

175
tests/ImageSharp.Tests/TestUtilities/Tests/ImageComparerTests.cs

@ -0,0 +1,175 @@
// ReSharper disable InconsistentNaming
namespace ImageSharp.Tests
{
using ImageSharp.PixelFormats;
using SixLabors.Primitives;
using Xunit;
using Xunit.Abstractions;
public class ImageComparerTests
{
public ImageComparerTests(ITestOutputHelper output)
{
this.Output = output;
}
private ITestOutputHelper Output { get; }
[Theory]
[WithTestPatternImages(
100,
100,
PixelTypes.Rgba32,
PercentageImageComparer.DefaultImageThreshold,
PercentageImageComparer.DefaultSegmentThreshold,
PercentageImageComparer.DefaultScaleIntoSize)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0, 0, 100)]
public void PercentageComparer_ApprovesPerfectSimilarity<TPixel>(
TestImageProvider<TPixel> provider,
float imageTheshold,
byte segmentThreshold,
int scaleIntoSize)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
using (Image<TPixel> clone = image.Clone())
{
PercentageImageComparer.VerifySimilarity(
image,
clone,
imageTheshold,
segmentThreshold,
scaleIntoSize);
}
}
}
private static void ModifyPixel<TPixel>(Image<TPixel> img, int x, int y, byte value)
where TPixel : struct, IPixel<TPixel>
{
TPixel pixel = img[x, y];
var rgbaPixel = default(Rgba32);
pixel.ToRgba32(ref rgbaPixel);
rgbaPixel.R += value;
pixel.PackFromRgba32(rgbaPixel);
img[x, y] = pixel;
}
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
public void PercentageComparer_ApprovesImperfectSimilarity<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
using (Image<TPixel> clone = image.Clone())
{
ModifyPixel(clone, 0, 0, 2);
PercentageImageComparer.VerifySimilarity(image, clone, scaleIntoSize: 100);
}
}
}
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, 99, 100)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, 100, 99)]
public void PercentageComparer_ThrowsOnSizeMismatch<TPixel>(TestImageProvider<TPixel> provider, int w, int h)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
using (Image<TPixel> clone = image.Clone(ctx => ctx.Resize(w, h)))
{
ImageDimensionsMismatchException ex = Assert.ThrowsAny<ImageDimensionsMismatchException>(
() =>
{
PercentageImageComparer.VerifySimilarity(image, clone);
});
this.Output.WriteLine(ex.Message);
}
}
}
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
public void PercentageComparer_WhenDifferenceIsTooLarge_Throws<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
using (Image<TPixel> clone = image.Clone())
{
ModifyPixel(clone, 0, 0, 42);
ModifyPixel(clone, 1, 0, 42);
ModifyPixel(clone, 2, 0, 42);
Assert.ThrowsAny<ImagesSimilarityException>(
() => { PercentageImageComparer.VerifySimilarity(image, clone, scaleIntoSize: 100); });
}
}
}
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
public void ExactComparer_ApprovesExactEquality<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
using (Image<TPixel> clone = image.Clone())
{
ExactComparer.Instance.Verify(image, clone);
}
}
}
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, 99, 100)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, 100, 99)]
public void ExactComparer_ThrowsOnSizeMismatch<TPixel>(TestImageProvider<TPixel> provider, int w, int h)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
using (Image<TPixel> clone = image.Clone(ctx => ctx.Resize(w, h)))
{
ImageDimensionsMismatchException ex = Assert.ThrowsAny<ImageDimensionsMismatchException>(
() =>
{
ExactComparer.Instance.Verify(image, clone);
});
this.Output.WriteLine(ex.Message);
}
}
}
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32)]
public void ExactComparer_ThrowsOnSmallestPixelDifference<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
using (Image<TPixel> clone = image.Clone())
{
ModifyPixel(clone, 42, 42, 1);
ModifyPixel(clone, 7, 93, 1);
ImagesAreNotEqualException ex = Assert.ThrowsAny<ImagesAreNotEqualException>(
() =>
{
ExactComparer.Instance.Verify(image, clone);
});
this.Output.WriteLine(ex.Message);
Assert.Equal(2, ex.Differences.Length);
Assert.Contains(new Point(42, 24), ex.Differences);
Assert.Contains(new Point(7, 93), ex.Differences);
}
}
}
}
}

0
tests/ImageSharp.Tests/TestUtilities/Tests/IntegrationTestUtilsTests.cs → tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs

Loading…
Cancel
Save