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