mirror of https://github.com/SixLabors/ImageSharp
13 changed files with 477 additions and 138 deletions
@ -0,0 +1,113 @@ |
|||
// ReSharper disable InconsistentNaming
|
|||
namespace SixLabors.ImageSharp.Tests.Formats.Jpg |
|||
{ |
|||
using System; |
|||
|
|||
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; |
|||
|
|||
using Xunit; |
|||
|
|||
public partial class ReferenceImplementationsTests |
|||
{ |
|||
public class FastFloatingPointDCT |
|||
{ |
|||
[Theory] |
|||
[InlineData(42, 0)] |
|||
[InlineData(1, 0)] |
|||
[InlineData(2, 0)] |
|||
public void ForwardThenInverse(int seed, int startAt) |
|||
{ |
|||
int[] data = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed); |
|||
float[] src = data.ConvertAllToFloat(); |
|||
float[] dest = new float[64]; |
|||
float[] temp = new float[64]; |
|||
|
|||
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)); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(42)] |
|||
[InlineData(1)] |
|||
[InlineData(2)] |
|||
public void IDCT_IsEquivalentTo_StandardIntegerImplementation(int seed) |
|||
{ |
|||
int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed); |
|||
Span<float> floatSrc = intData.ConvertAllToFloat(); |
|||
|
|||
ReferenceImplementations.StandardIntegerDCT.TransformIDCTInplace(intData); |
|||
|
|||
float[] dest = new float[64]; |
|||
float[] temp = new float[64]; |
|||
|
|||
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)); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(42)] |
|||
[InlineData(1)] |
|||
[InlineData(2)] |
|||
public void IDCT_IsEquivalentTo_AccurateImplementation(int seed) |
|||
{ |
|||
int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed); |
|||
float[] floatSrc = intData.ConvertAllToFloat(); |
|||
|
|||
ReferenceImplementations.AccurateDCT.TransformIDCTInplace(intData); |
|||
|
|||
float[] dest = new float[64]; |
|||
float[] temp = new float[64]; |
|||
|
|||
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)); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(42)] |
|||
[InlineData(1)] |
|||
[InlineData(2)] |
|||
public void FDCT_IsEquivalentTo_StandardIntegerImplementation(int seed) |
|||
{ |
|||
int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed); |
|||
float[] floatSrc = intData.ConvertAllToFloat(); |
|||
|
|||
ReferenceImplementations.StandardIntegerDCT.TransformFDCTInplace(intData); |
|||
|
|||
float[] dest = new float[64]; |
|||
float[] temp = new float[64]; |
|||
|
|||
ReferenceImplementations.FastFloatingPointDCT.fDCT2D_llm(floatSrc, dest, temp, offsetSourceByNeg128: true); |
|||
|
|||
for (int i = 0; i < 64; i++) |
|||
{ |
|||
float expected = intData[i]; |
|||
float actual = dest[i]; |
|||
|
|||
Assert.Equal(expected, actual, new ApproximateFloatComparer(1f)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
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 StandardIntegerDCT |
|||
{ |
|||
public StandardIntegerDCT(ITestOutputHelper output) |
|||
{ |
|||
this.Output = output; |
|||
} |
|||
|
|||
private ITestOutputHelper Output { get; } |
|||
|
|||
[Theory] |
|||
[InlineData(42)] |
|||
[InlineData(1)] |
|||
[InlineData(2)] |
|||
public void IDCT_IsEquivalentTo_AccurateImplementation(int seed) |
|||
{ |
|||
int[] data = Create8x8RandomIntData(-1000, 1000, seed); |
|||
|
|||
Block8x8 source = default(Block8x8); |
|||
source.LoadFrom(data); |
|||
|
|||
Block8x8 expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref source); |
|||
Block8x8 actual = ReferenceImplementations.StandardIntegerDCT.TransformIDCT(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); |
|||
} |
|||
|
|||
|
|||
[Theory] |
|||
[InlineData(42, 0)] |
|||
[InlineData(1, 0)] |
|||
[InlineData(2, 0)] |
|||
public void ForwardThenInverse(int seed, int startAt) |
|||
{ |
|||
Span<int> original = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed); |
|||
|
|||
Span<int> block = original.AddScalarToAllValues(128); |
|||
|
|||
ReferenceImplementations.StandardIntegerDCT.TransformFDCTInplace(block); |
|||
|
|||
for (int i = 0; i < 64; i++) |
|||
{ |
|||
block[i] /= 8; |
|||
} |
|||
|
|||
ReferenceImplementations.StandardIntegerDCT.TransformIDCTInplace(block); |
|||
|
|||
for (int i = startAt; i < 64; i++) |
|||
{ |
|||
float expected = original[i]; |
|||
float actual = (float)block[i]; |
|||
|
|||
Assert.Equal(expected, actual, new ApproximateFloatComparer(3f)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,121 @@ |
|||
namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils |
|||
{ |
|||
using System; |
|||
|
|||
using SixLabors.ImageSharp.Formats.Jpeg.Common; |
|||
|
|||
internal static partial class ReferenceImplementations |
|||
{ |
|||
/// <summary>
|
|||
/// Reference implementations based on:
|
|||
/// https://github.com/keithw/mympeg2enc/blob/master/idct.c#L222
|
|||
/// Claiming:
|
|||
/// /* reference idct taken from "ieeetest.c"
|
|||
/// * Written by Tom Lane (tgl@cs.cmu.edu).
|
|||
/// * Released to public domain 11/22/93.
|
|||
/// */
|
|||
/// </summary>
|
|||
internal static class AccurateDCT |
|||
{ |
|||
private static double[,] CosLut = InitCosLut(); |
|||
|
|||
|
|||
public static Block8x8 TransformIDCT(ref Block8x8 block) |
|||
{ |
|||
Block8x8F temp = block.AsFloatBlock(); |
|||
Block8x8F res0 = TransformIDCT(ref temp); |
|||
return res0.RoundAsInt16Block(); |
|||
} |
|||
|
|||
public static void TransformIDCTInplace(Span<int> span) |
|||
{ |
|||
var temp = new Block8x8(); |
|||
temp.LoadFrom(span); |
|||
Block8x8 result = TransformIDCT(ref temp); |
|||
result.CopyTo(span); |
|||
} |
|||
|
|||
public static Block8x8 TransformFDCT(ref Block8x8 block) |
|||
{ |
|||
Block8x8F temp = block.AsFloatBlock(); |
|||
Block8x8F res0 = TransformFDCT(ref temp); |
|||
return res0.RoundAsInt16Block(); |
|||
} |
|||
|
|||
public static void TransformFDCTInplace(Span<int> span) |
|||
{ |
|||
var temp = new Block8x8(); |
|||
temp.LoadFrom(span); |
|||
Block8x8 result = TransformFDCT(ref temp); |
|||
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; |
|||
double tmp, tmp2; |
|||
Block8x8F res = default(Block8x8F); |
|||
|
|||
for (y=0; y<8; y++) { |
|||
for (x=0; x<8; x++) { |
|||
tmp = 0.0; |
|||
for (v=0; v<8; v++) { |
|||
tmp2 = 0.0; |
|||
for (u=0; u<8; u++) { |
|||
tmp2 += (double) block[v * 8 + u] * CosLut[x, u]; |
|||
} |
|||
tmp += CosLut[y, v] * tmp2; |
|||
} |
|||
res[y, x] = (float)tmp; |
|||
} |
|||
} |
|||
return res; |
|||
} |
|||
|
|||
|
|||
public static Block8x8F TransformFDCT(ref Block8x8F block) |
|||
{ |
|||
int x, y, u, v; |
|||
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]; |
|||
} |
|||
tmp += CosLut[y, v] * tmp2; |
|||
} |
|||
res[v, u] = (float)tmp; |
|||
} |
|||
} |
|||
|
|||
return res; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue