Browse Source

playing with DCT implementations

pull/322/head
Anton Firszov 9 years ago
parent
commit
d6d172025b
  1. 7
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  2. 36
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs
  3. 78
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs
  4. 30
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs
  5. 33
      tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegUtilityTestFixture.cs
  6. 26
      tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs
  7. 27
      tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.FastFloatingPointDCT.cs
  8. 2
      tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs

7
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs

@ -250,6 +250,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
}
}
public float[] ToArray()
{
float[] result = new float[Size];
this.CopyTo(result);
return result;
}
/// <summary>
/// Multiply all elements of the block.
/// </summary>

36
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.AccurateDCT.cs

@ -0,0 +1,36 @@
namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using Xunit;
using Xunit.Abstractions;
public partial class ReferenceImplementationsTests
{
public class AccurateDCT : JpegUtilityTestFixture
{
public AccurateDCT(ITestOutputHelper output)
: base(output)
{
}
[Theory]
[InlineData(42)]
[InlineData(1)]
[InlineData(2)]
public void ForwardThenInverse(int seed)
{
float[] data = JpegUtilityTestFixture.Create8x8RandomFloatData(-1000, 1000, seed);
var b0 = default(Block8x8F);
b0.LoadFrom(data);
Block8x8F b1 = ReferenceImplementations.AccurateDCT.TransformFDCT(ref b0);
Block8x8F b2 = ReferenceImplementations.AccurateDCT.TransformIDCT(ref b1);
this.CompareBlocks(b0, b2, 1e-4f);
}
}
}
}

78
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs

@ -3,21 +3,28 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
using System;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
using Xunit;
using Xunit.Abstractions;
public partial class ReferenceImplementationsTests
{
public class FastFloatingPointDCT
public class FastFloatingPointDCT : JpegUtilityTestFixture
{
public FastFloatingPointDCT(ITestOutputHelper output)
: base(output)
{
}
[Theory]
[InlineData(42, 0)]
[InlineData(1, 0)]
[InlineData(2, 0)]
public void ForwardThenInverse(int seed, int startAt)
{
int[] data = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed);
int[] data = JpegUtilityTestFixture.Create8x8RandomIntData(-1000, 1000, seed);
float[] src = data.ConvertAllToFloat();
float[] dest = new float[64];
float[] temp = new float[64];
@ -25,13 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
ReferenceImplementations.FastFloatingPointDCT.fDCT2D_llm(src, dest, temp, true);
ReferenceImplementations.FastFloatingPointDCT.iDCT2D_llm(dest, src, temp);
for (int i = startAt; i < 64; i++)
{
float expected = data[i];
float actual = (float)src[i];
Assert.Equal(expected, actual, new ApproximateFloatComparer(2f));
}
this.CompareBlocks(data.ConvertAllToFloat(), src, 2f);
}
[Theory]
@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)]
public void IDCT_IsEquivalentTo_StandardIntegerImplementation(int seed)
{
int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed);
int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-1000, 1000, seed);
Span<float> floatSrc = intData.ConvertAllToFloat();
ReferenceImplementations.StandardIntegerDCT.TransformIDCTInplace(intData);
@ -50,13 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
ReferenceImplementations.FastFloatingPointDCT.iDCT2D_llm(floatSrc, dest, temp);
for (int i = 0; i < 64; i++)
{
float expected = intData[i];
float actual = dest[i];
Assert.Equal(expected, actual, new ApproximateFloatComparer(1f));
}
this.CompareBlocks(intData.ConvertAllToFloat(), dest, 1f);
}
[Theory]
@ -65,7 +60,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)]
public void IDCT_IsEquivalentTo_AccurateImplementation(int seed)
{
int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed);
int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-1000, 1000, seed);
float[] floatSrc = intData.ConvertAllToFloat();
ReferenceImplementations.AccurateDCT.TransformIDCTInplace(intData);
@ -75,13 +70,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
ReferenceImplementations.FastFloatingPointDCT.iDCT2D_llm(floatSrc, dest, temp);
for (int i = 0; i < 64; i++)
{
float expected = intData[i];
float actual = dest[i];
Assert.Equal(expected, actual, new ApproximateFloatComparer(1f));
}
this.CompareBlocks(intData.ConvertAllToFloat(), dest, 1f);
}
[Theory]
@ -90,7 +79,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)]
public void FDCT_IsEquivalentTo_StandardIntegerImplementation(int seed)
{
int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed);
int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-1000, 1000, seed);
float[] floatSrc = intData.ConvertAllToFloat();
ReferenceImplementations.StandardIntegerDCT.TransformFDCTInplace(intData);
@ -100,14 +89,39 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
ReferenceImplementations.FastFloatingPointDCT.fDCT2D_llm(floatSrc, dest, temp, offsetSourceByNeg128: true);
for (int i = 0; i < 64; i++)
{
float expected = intData[i];
float actual = dest[i];
this.CompareBlocks(intData.ConvertAllToFloat(), dest, 2f);
}
[Theory]
[InlineData(42)]
[InlineData(1)]
[InlineData(2)]
public void FDCT_IsEquivalentTo_AccurateImplementation(int seed)
{
float[] floatData = JpegUtilityTestFixture.Create8x8RandomFloatData(-1000, 1000);
Block8x8F source = default(Block8x8F);
source.LoadFrom(floatData);
Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source);
Block8x8F actual = ReferenceImplementations.FastFloatingPointDCT.TransformFDCT(ref source);
this.CompareBlocks(expected, actual, 1f);
//int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-1000, 1000, seed);
//float[] floatSrc = intData.ConvertAllToFloat();
Assert.Equal(expected, actual, new ApproximateFloatComparer(1f));
}
//ReferenceImplementations.AccurateDCT.TransformFDCTInplace(intData);
//float[] dest = new float[64];
//float[] temp = new float[64];
//ReferenceImplementations.FastFloatingPointDCT.fDCT2D_llm(floatSrc, dest, temp, offsetSourceByNeg128: false);
//this.CompareBlocks(intData.ConvertAllToFloat(), dest, 1f);
}
}
}
}

30
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs

@ -10,7 +10,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public partial class ReferenceImplementationsTests
{
public class StandardIntegerDCT
{
public StandardIntegerDCT(ITestOutputHelper output)
@ -34,10 +33,39 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Block8x8 expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref source);
Block8x8 actual = ReferenceImplementations.StandardIntegerDCT.TransformIDCT(ref source);
Block8x8F sourceF = source.AsFloatBlock();
Block8x8F wut0 = ReferenceImplementations.FastFloatingPointDCT.TransformIDCT(ref sourceF);
Block8x8 wut1 = wut0.RoundAsInt16Block();
long diff = Block8x8.TotalDifference(ref expected, ref actual);
this.Output.WriteLine(expected.ToString());
this.Output.WriteLine(actual.ToString());
this.Output.WriteLine(wut1.ToString());
this.Output.WriteLine("DIFFERENCE: "+diff);
Assert.True(diff < 4);
}
[Theory]
[InlineData(42)]
[InlineData(1)]
[InlineData(2)]
public void FDCT_IsEquivalentTo_AccurateImplementation(int seed)
{
int[] data = Create8x8RandomIntData(-1000, 1000, seed);
Block8x8 source = default(Block8x8);
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);
Assert.True(diff < 4);
}

33
tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegUtilityTestFixture.cs

@ -11,6 +11,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
using System.Diagnostics;
using System.Text;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using Xunit;
using Xunit.Abstractions;
public class JpegUtilityTestFixture : MeasureFixture
@ -133,5 +136,35 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
Debug.WriteLine(msg);
this.Output.WriteLine(msg);
}
internal void CompareBlocks(Block8x8 a, Block8x8 b, float tolerance) =>
this.CompareBlocks(a.AsFloatBlock(), b.AsFloatBlock(), tolerance);
internal void CompareBlocks(Block8x8F a, Block8x8F b, float tolerance)
=> this.CompareBlocks(a.ToArray(), b.ToArray(), tolerance);
internal void CompareBlocks(Span<float> a, Span<float> b, float tolerance)
{
ApproximateFloatComparer comparer = new ApproximateFloatComparer(tolerance);
double totalDifference = 0.0;
bool failed = false;
for (int i = 0; i < 64; i++)
{
float expected = a[i];
float actual = b[i];
totalDifference += Math.Abs(expected - actual);
if (!comparer.Equals(expected, actual))
{
failed = true;
this.Output.WriteLine($"Difference too large at index {i}");
}
}
this.Output.WriteLine("TOTAL DIFF: "+totalDifference);
Assert.False(failed);
}
}
}

26
tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs

@ -100,20 +100,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
double tmp, tmp2;
Block8x8F res = default(Block8x8F);
for (v=0; v<8; v++) {
for (u=0; u<8; u++) {
tmp = 0.0;
for (y=0; y<8; y++) {
tmp2 = 0.0;
for (x=0; x<8; x++) {
tmp2 += (double) block[y, x] * CosLut[x, u];
for (v = 0; v < 8; v++)
{
for (u = 0; u < 8; u++)
{
tmp = 0.0;
for (y = 0; y < 8; y++)
{
tmp2 = 0.0;
for (x = 0; x < 8; x++)
{
tmp2 += (double)block[y,x] * CosLut[x,u];
}
tmp += CosLut[y, v] * tmp2;
tmp += CosLut[y,v] * tmp2;
}
res[v, u] = (float)tmp;
}
res[v,u] = (float) tmp;
}
}
return res;
}
}

27
tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.FastFloatingPointDCT.cs

@ -4,12 +4,39 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Jpeg.Common;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils;
internal static partial class ReferenceImplementations
{
internal static class FastFloatingPointDCT
{
public static Block8x8F TransformIDCT(ref Block8x8F source)
{
float[] s = new float[64];
source.CopyTo(s);
float[] d = new float[64];
float[] temp = new float[64];
iDCT2D_llm(s, d, temp);
Block8x8F result = default(Block8x8F);
result.LoadFrom(d);
return result;
}
public static Block8x8F TransformFDCT(ref Block8x8F source)
{
float[] s = new float[64];
source.CopyTo(s);
float[] d = new float[64];
float[] temp = new float[64];
fDCT2D_llm(s, d, temp);
Block8x8F result = default(Block8x8F);
result.LoadFrom(d);
return result;
}
/// <summary>
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L200
/// </summary>

2
tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs

@ -75,6 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
return result;
}
[Obsolete("Looks like this method produces really bad results for bigger values!")]
public static Block8x8 TransformIDCT(ref Block8x8 block)
{
int[] temp = new int[Block8x8.Size];
@ -231,6 +232,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
/// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984.
/// </summary>
/// <param name="src">The source block of coefficients</param>
[Obsolete("Looks like this method produces really bad results for bigger values!")]
public static void TransformIDCTInplace(Span<int> src)
{
// Horizontal 1-D IDCT.

Loading…
Cancel
Save