From f672ae340bf039efb2871574bec68cf4419d4d6b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 26 Aug 2017 19:38:17 +0200 Subject: [PATCH] managed to figure out everything regarding IDCT reference implementations --- .../Formats/Jpeg/Common/Block8x8.cs | 53 +++++++++++++++ .../Formats/Jpeg/Common/Block8x8F.cs | 64 +++++++++++++++---- ...plementationsTests.FastFloatingPointDCT.cs | 9 +-- ...ImplementationsTests.StandardIntegerDCT.cs | 29 ++++----- .../ReferenceImplementations.AccurateDCT.cs | 51 +++++++-------- ...nceImplementations.FastFloatingPointDCT.cs | 9 +-- ...renceImplementations.StandardIntegerDCT.cs | 7 +- 7 files changed, 157 insertions(+), 65 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs index 0a15c37f01..c24bab9b84 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs @@ -60,6 +60,58 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return !left.Equals(right); } + public static Block8x8 operator *(Block8x8 block, int value) + { + Block8x8 result = block; + for (int i = 0; i < Size; i++) + { + int val = result[i]; + val *= value; + result[i] = (short)val; + } + + return result; + } + + public static Block8x8 operator /(Block8x8 block, int value) + { + Block8x8 result = block; + for (int i = 0; i < Size; i++) + { + int val = result[i]; + val /= value; + result[i] = (short)val; + } + + return result; + } + + public static Block8x8 operator +(Block8x8 block, int value) + { + Block8x8 result = block; + for (int i = 0; i < Size; i++) + { + int val = result[i]; + val += value; + result[i] = (short)val; + } + + return result; + } + + public static Block8x8 operator -(Block8x8 block, int value) + { + Block8x8 result = block; + for (int i = 0; i < Size; i++) + { + int val = result[i]; + val -= value; + result[i] = (short)val; + } + + return result; + } + /// /// Pointer-based "Indexer" (getter part) /// @@ -195,5 +247,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common return result; } + } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs index 9a1acecfa2..233cc0580b 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs @@ -89,6 +89,58 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common set => this[(y * 8) + x] = value; } + public static Block8x8F operator *(Block8x8F block, float value) + { + Block8x8F result = block; + for (int i = 0; i < Size; i++) + { + float val = result[i]; + val *= value; + result[i] = val; + } + + return result; + } + + public static Block8x8F operator /(Block8x8F block, float value) + { + Block8x8F result = block; + for (int i = 0; i < Size; i++) + { + float val = result[i]; + val /= value; + result[i] = (float)val; + } + + return result; + } + + public static Block8x8F operator +(Block8x8F block, float value) + { + Block8x8F result = block; + for (int i = 0; i < Size; i++) + { + float val = result[i]; + val += value; + result[i] = (float)val; + } + + return result; + } + + public static Block8x8F operator -(Block8x8F block, float value) + { + Block8x8F result = block; + for (int i = 0; i < Size; i++) + { + float val = result[i]; + val -= value; + result[i] = (float)val; + } + + return result; + } + /// /// Pointer-based "Indexer" (getter part) /// @@ -119,18 +171,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common fp[idx] = value; } - //public Block8x8 AsInt16Block() - //{ - // // TODO: Optimize this - // var result = default(Block8x8); - // for (int i = 0; i < Size; i++) - // { - // result[i] = (short)this[i]; - // } - - // return result; - //} - /// /// Fill the block with defaults (zeroes) /// diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs index 05fe178cb3..66d9e8d0c9 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg this.CompareBlocks(data.ConvertAllToFloat(), src, 2f); } - [Theory] + [Theory(Skip = "Sandboxing only! (Incorrect reference implementation)")] [InlineData(42)] [InlineData(1)] [InlineData(2)] @@ -82,12 +82,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-1000, 1000, seed); float[] floatSrc = intData.ConvertAllToFloat(); - ReferenceImplementations.StandardIntegerDCT.TransformFDCTInplace(intData); + ReferenceImplementations.StandardIntegerDCT.Subtract128_TransformFDCT_Upscale8_Inplace(intData); float[] dest = new float[64]; float[] temp = new float[64]; - ReferenceImplementations.FastFloatingPointDCT.fDCT2D_llm(floatSrc, dest, temp, offsetSourceByNeg128: true); + ReferenceImplementations.FastFloatingPointDCT.fDCT2D_llm(floatSrc, dest, temp, subtract128FromSource: true); this.CompareBlocks(intData.ConvertAllToFloat(), dest, 2f); } @@ -104,7 +104,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg source.LoadFrom(floatData); Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); - Block8x8F actual = ReferenceImplementations.FastFloatingPointDCT.TransformFDCT(ref source); + Block8x8F actual = ReferenceImplementations.FastFloatingPointDCT.TransformFDCT_UpscaleBy8(ref source); + actual /= 8; this.CompareBlocks(expected, actual, 1f); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs index 4fdfd62f33..1ea9609bb4 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs @@ -1,3 +1,4 @@ +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Jpg { using System; @@ -10,16 +11,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public partial class ReferenceImplementationsTests { - public class StandardIntegerDCT + public class StandardIntegerDCT : JpegUtilityTestFixture { public StandardIntegerDCT(ITestOutputHelper output) + : base(output) { - this.Output = output; } - private ITestOutputHelper Output { get; } - - [Theory] + [Theory(Skip = "Sandboxing only! (Incorrect reference implementation)")] [InlineData(42)] [InlineData(1)] [InlineData(2)] @@ -54,18 +53,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { int[] data = Create8x8RandomIntData(-1000, 1000, seed); - Block8x8 source = default(Block8x8); + Block8x8F source = default(Block8x8F); source.LoadFrom(data); - Block8x8 expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); - Block8x8 actual = ReferenceImplementations.StandardIntegerDCT.TransformFDCT(ref source); - - long diff = Block8x8.TotalDifference(ref expected, ref actual); - this.Output.WriteLine(expected.ToString()); - this.Output.WriteLine(actual.ToString()); - this.Output.WriteLine("DIFFERENCE: " + diff); + Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); - Assert.True(diff < 4); + source += 128; + Block8x8 temp = source.RoundAsInt16Block(); + Block8x8 actual8 = ReferenceImplementations.StandardIntegerDCT.Subtract128_TransformFDCT_Upscale8(ref temp); + Block8x8F actual = actual8.AsFloatBlock(); + actual /= 8; + + this.CompareBlocks(expected, actual, 1f); } @@ -79,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Span block = original.AddScalarToAllValues(128); - ReferenceImplementations.StandardIntegerDCT.TransformFDCTInplace(block); + ReferenceImplementations.StandardIntegerDCT.Subtract128_TransformFDCT_Upscale8_Inplace(block); for (int i = 0; i < 64; i++) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs index 7f80b4f980..f46155ac42 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs @@ -7,7 +7,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils internal static partial class ReferenceImplementations { /// - /// Reference implementations based on: + /// True accurate FDCT/IDCT implementations. We should test everything against them! + /// Based on: /// https://github.com/keithw/mympeg2enc/blob/master/idct.c#L222 /// Claiming: /// /* reference idct taken from "ieeetest.c" @@ -18,8 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils internal static class AccurateDCT { private static double[,] CosLut = InitCosLut(); - - + public static Block8x8 TransformIDCT(ref Block8x8 block) { Block8x8F temp = block.AsFloatBlock(); @@ -50,27 +50,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils result.CopyTo(span); } - - - private static double[,] InitCosLut() - { - double[,] coslu = new double[8,8]; - int a, b; - double tmp; - - for (a = 0; a < 8; a++) - for (b = 0; b < 8; b++) - { - tmp = Math.Cos((double)((a + a + 1) * b) * (3.14159265358979323846 / 16.0)); - if (b == 0) - { - tmp /= Math.Sqrt(2.0); - } - coslu[a, b] = tmp * 0.5; - } - return coslu; - } - public static Block8x8F TransformIDCT(ref Block8x8F block) { int x, y, u, v; @@ -92,8 +71,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils } return res; } - - + public static Block8x8F TransformFDCT(ref Block8x8F block) { int x, y, u, v; @@ -119,7 +97,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils } return res; - } + } + + private static double[,] InitCosLut() + { + double[,] coslu = new double[8, 8]; + int a, b; + double tmp; + + for (a = 0; a < 8; a++) + for (b = 0; b < 8; b++) + { + tmp = Math.Cos((double)((a + a + 1) * b) * (3.14159265358979323846 / 16.0)); + if (b == 0) + { + tmp /= Math.Sqrt(2.0); + } + coslu[a, b] = tmp * 0.5; + } + return coslu; + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.FastFloatingPointDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.FastFloatingPointDCT.cs index 6aea87330f..c4157f6947 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.FastFloatingPointDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.FastFloatingPointDCT.cs @@ -1,3 +1,4 @@ +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils { using System; @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils return result; } - public static Block8x8F TransformFDCT(ref Block8x8F source) + public static Block8x8F TransformFDCT_UpscaleBy8(ref Block8x8F source) { float[] s = new float[64]; source.CopyTo(s); @@ -480,15 +481,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils y[1] = c0 + c3; y[7] = c0 - c3; } - + internal static void fDCT2D_llm( Span s, Span d, Span temp, bool downscaleBy8 = false, - bool offsetSourceByNeg128 = false) + bool subtract128FromSource = false) { - Span sWorker = offsetSourceByNeg128 ? s.AddScalarToAllValues(-128f) : s; + Span sWorker = subtract128FromSource ? s.AddScalarToAllValues(-128f) : s; for (int j = 0; j < 8; j++) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs index a1f70bcdd6..4d2a1e44ff 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs @@ -1,3 +1,4 @@ +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils { using System; @@ -65,11 +66,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils /// private const int CenterJSample = 128; - public static Block8x8 TransformFDCT(ref Block8x8 block) + public static Block8x8 Subtract128_TransformFDCT_Upscale8(ref Block8x8 block) { int[] temp = new int[Block8x8.Size]; block.CopyTo(temp); - TransformFDCTInplace(temp); + Subtract128_TransformFDCT_Upscale8_Inplace(temp); var result = default(Block8x8); result.LoadFrom(temp); return result; @@ -91,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils /// Leave results scaled up by an overall factor of 8. /// /// The block of coefficients. - public static void TransformFDCTInplace(Span block) + public static void Subtract128_TransformFDCT_Upscale8_Inplace(Span block) { // Pass 1: process rows. for (int y = 0; y < 8; y++)