public override ImageSimilarityReport <TPixelA, TPixelB> CompareImagesOrFrames <TPixelA, TPixelB>(ImageFrame <TPixelA> expected, ImageFrame <TPixelB> actual)
        {
            if (expected.Size() != actual.Size())
            {
                throw new ImageDimensionsMismatchException(expected.Size(), actual.Size());
            }

            int width = actual.Width;

            // TODO: Comparing through Rgba64 may not robust enough because of the existence of super high precision pixel types.
            var aBuffer = new Rgba64[width];
            var bBuffer = new Rgba64[width];

            float totalDifference = 0F;

            var           differences   = new List <PixelDifference>();
            Configuration configuration = expected.GetConfiguration();

            for (int y = 0; y < actual.Height; y++)
            {
                Span <TPixelA> aSpan = expected.GetPixelRowSpan(y);
                Span <TPixelB> bSpan = actual.GetPixelRowSpan(y);

                PixelOperations <TPixelA> .Instance.ToRgba64(configuration, aSpan, aBuffer);

                PixelOperations <TPixelB> .Instance.ToRgba64(configuration, bSpan, bBuffer);

                for (int x = 0; x < width; x++)
                {
                    int d = GetManhattanDistanceInRgbaSpace(ref aBuffer[x], ref bBuffer[x]);

                    if (d > PerPixelManhattanThreshold)
                    {
                        var diff = new PixelDifference(new Point(x, y), aBuffer[x], bBuffer[x]);
                        differences.Add(diff);

                        totalDifference += d;
                    }
                }
            }

            float normalizedDifference = totalDifference / (actual.Width * (float)actual.Height);

            normalizedDifference /= 4F * 65535F;

            if (normalizedDifference > ImageThreshold)
            {
                return(new ImageSimilarityReport <TPixelA, TPixelB>(expected, actual, differences, normalizedDifference));
            }
            else
            {
                return(ImageSimilarityReport <TPixelA, TPixelB> .Empty);
            }
        }
        public void VerifySimilarity_WhenAnImageFrameIsDifferent_Reports <TPixel>(TestImageProvider <TPixel> provider)
            where TPixel : struct, IPixel <TPixel>
        {
            using (Image <TPixel> image = provider.GetImage())
            {
                using (Image <TPixel> clone = image.Clone())
                {
                    ImagingTestCaseUtility.ModifyPixel(clone.Frames[0], 42, 43, 1);

                    IEnumerable <ImageSimilarityReport> reports = ImageComparer.Exact.CompareImages(image, clone);

                    PixelDifference difference = reports.Single().Differences.Single();
                    Assert.Equal(new Point(42, 43), difference.Position);
                }
            }
        }
        public override ImageSimilarityReport <TPixelA, TPixelB> CompareImagesOrFrames <TPixelA, TPixelB>(
            ImageFrame <TPixelA> expected,
            ImageFrame <TPixelB> actual)
        {
            if (expected.Size() != actual.Size())
            {
                throw new ImageDimensionsMismatchException(expected.Size(), actual.Size());
            }

            int width = actual.Width;

            // TODO: Comparing through Rgba64 may not be robust enough because of the existence of super high precision pixel types.
            var aBuffer = new Rgba64[width];
            var bBuffer = new Rgba64[width];

            var           differences   = new List <PixelDifference>();
            Configuration configuration = expected.GetConfiguration();

            for (int y = 0; y < actual.Height; y++)
            {
                Span <TPixelA> aSpan = expected.GetPixelRowSpan(y);
                Span <TPixelB> bSpan = actual.GetPixelRowSpan(y);

                PixelOperations <TPixelA> .Instance.ToRgba64(configuration, aSpan, aBuffer);

                PixelOperations <TPixelB> .Instance.ToRgba64(configuration, bSpan, bBuffer);

                for (int x = 0; x < width; x++)
                {
                    Rgba64 aPixel = aBuffer[x];
                    Rgba64 bPixel = bBuffer[x];

                    if (aPixel != bPixel)
                    {
                        var diff = new PixelDifference(new Point(x, y), aPixel, bPixel);
                        differences.Add(diff);
                    }
                }
            }

            return(new ImageSimilarityReport <TPixelA, TPixelB>(expected, actual, differences));
        }
        public void TolerantImageComparer_DoesNotApproveSimilarityAboveTolerance <TPixel>(TestImageProvider <TPixel> provider)
            where TPixel : unmanaged, IPixel <TPixel>
        {
            using (Image <TPixel> image = provider.GetImage())
            {
                using (Image <TPixel> clone = image.Clone())
                {
                    byte perChannelChange = 20;
                    ImagingTestCaseUtility.ModifyPixel(clone, 3, 1, perChannelChange);

                    var comparer = ImageComparer.Tolerant();

                    ImageDifferenceIsOverThresholdException ex = Assert.ThrowsAny <ImageDifferenceIsOverThresholdException>(
                        () => comparer.VerifySimilarity(image, clone));

                    PixelDifference diff = ex.Reports.Single().Differences.Single();
                    Assert.Equal(new Point(3, 1), diff.Position);
                }
            }
        }