Browse Source

managed to figure out everything regarding IDCT reference implementations

af/merge-core
Anton Firszov 9 years ago
parent
commit
f672ae340b
  1. 53
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  2. 64
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  3. 9
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs
  4. 29
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs
  5. 51
      tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs
  6. 9
      tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.FastFloatingPointDCT.cs
  7. 7
      tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs

53
src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs

@ -60,6 +60,58 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return !left.Equals(right); 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;
}
/// <summary> /// <summary>
/// Pointer-based "Indexer" (getter part) /// Pointer-based "Indexer" (getter part)
/// </summary> /// </summary>
@ -195,5 +247,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
return result; return result;
} }
} }
} }

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

@ -89,6 +89,58 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
set => this[(y * 8) + x] = value; 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;
}
/// <summary> /// <summary>
/// Pointer-based "Indexer" (getter part) /// Pointer-based "Indexer" (getter part)
/// </summary> /// </summary>
@ -119,18 +171,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
fp[idx] = value; 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;
//}
/// <summary> /// <summary>
/// Fill the block with defaults (zeroes) /// Fill the block with defaults (zeroes)
/// </summary> /// </summary>

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

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
this.CompareBlocks(data.ConvertAllToFloat(), src, 2f); this.CompareBlocks(data.ConvertAllToFloat(), src, 2f);
} }
[Theory] [Theory(Skip = "Sandboxing only! (Incorrect reference implementation)")]
[InlineData(42)] [InlineData(42)]
[InlineData(1)] [InlineData(1)]
[InlineData(2)] [InlineData(2)]
@ -82,12 +82,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-1000, 1000, seed); int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-1000, 1000, seed);
float[] floatSrc = intData.ConvertAllToFloat(); float[] floatSrc = intData.ConvertAllToFloat();
ReferenceImplementations.StandardIntegerDCT.TransformFDCTInplace(intData); ReferenceImplementations.StandardIntegerDCT.Subtract128_TransformFDCT_Upscale8_Inplace(intData);
float[] dest = new float[64]; float[] dest = new float[64];
float[] temp = 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); this.CompareBlocks(intData.ConvertAllToFloat(), dest, 2f);
} }
@ -104,7 +104,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
source.LoadFrom(floatData); source.LoadFrom(floatData);
Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); 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); this.CompareBlocks(expected, actual, 1f);

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

@ -1,3 +1,4 @@
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Jpg namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ {
using System; using System;
@ -10,16 +11,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public partial class ReferenceImplementationsTests public partial class ReferenceImplementationsTests
{ {
public class StandardIntegerDCT public class StandardIntegerDCT : JpegUtilityTestFixture
{ {
public StandardIntegerDCT(ITestOutputHelper output) public StandardIntegerDCT(ITestOutputHelper output)
: base(output)
{ {
this.Output = output;
} }
private ITestOutputHelper Output { get; } [Theory(Skip = "Sandboxing only! (Incorrect reference implementation)")]
[Theory]
[InlineData(42)] [InlineData(42)]
[InlineData(1)] [InlineData(1)]
[InlineData(2)] [InlineData(2)]
@ -54,18 +53,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ {
int[] data = Create8x8RandomIntData(-1000, 1000, seed); int[] data = Create8x8RandomIntData(-1000, 1000, seed);
Block8x8 source = default(Block8x8); Block8x8F source = default(Block8x8F);
source.LoadFrom(data); source.LoadFrom(data);
Block8x8 expected = ReferenceImplementations.AccurateDCT.TransformFDCT(ref source); Block8x8F 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); 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<int> block = original.AddScalarToAllValues(128); Span<int> block = original.AddScalarToAllValues(128);
ReferenceImplementations.StandardIntegerDCT.TransformFDCTInplace(block); ReferenceImplementations.StandardIntegerDCT.Subtract128_TransformFDCT_Upscale8_Inplace(block);
for (int i = 0; i < 64; i++) for (int i = 0; i < 64; i++)
{ {

51
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 internal static partial class ReferenceImplementations
{ {
/// <summary> /// <summary>
/// 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 /// https://github.com/keithw/mympeg2enc/blob/master/idct.c#L222
/// Claiming: /// Claiming:
/// /* reference idct taken from "ieeetest.c" /// /* reference idct taken from "ieeetest.c"
@ -18,8 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
internal static class AccurateDCT internal static class AccurateDCT
{ {
private static double[,] CosLut = InitCosLut(); private static double[,] CosLut = InitCosLut();
public static Block8x8 TransformIDCT(ref Block8x8 block) public static Block8x8 TransformIDCT(ref Block8x8 block)
{ {
Block8x8F temp = block.AsFloatBlock(); Block8x8F temp = block.AsFloatBlock();
@ -50,27 +50,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
result.CopyTo(span); 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) public static Block8x8F TransformIDCT(ref Block8x8F block)
{ {
int x, y, u, v; int x, y, u, v;
@ -92,8 +71,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
} }
return res; return res;
} }
public static Block8x8F TransformFDCT(ref Block8x8F block) public static Block8x8F TransformFDCT(ref Block8x8F block)
{ {
int x, y, u, v; int x, y, u, v;
@ -119,7 +97,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
} }
return res; 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;
}
} }
} }
} }

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

@ -1,3 +1,4 @@
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
{ {
using System; using System;
@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
return result; return result;
} }
public static Block8x8F TransformFDCT(ref Block8x8F source) public static Block8x8F TransformFDCT_UpscaleBy8(ref Block8x8F source)
{ {
float[] s = new float[64]; float[] s = new float[64];
source.CopyTo(s); source.CopyTo(s);
@ -480,15 +481,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
y[1] = c0 + c3; y[1] = c0 + c3;
y[7] = c0 - c3; y[7] = c0 - c3;
} }
internal static void fDCT2D_llm( internal static void fDCT2D_llm(
Span<float> s, Span<float> s,
Span<float> d, Span<float> d,
Span<float> temp, Span<float> temp,
bool downscaleBy8 = false, bool downscaleBy8 = false,
bool offsetSourceByNeg128 = false) bool subtract128FromSource = false)
{ {
Span<float> sWorker = offsetSourceByNeg128 ? s.AddScalarToAllValues(-128f) : s; Span<float> sWorker = subtract128FromSource ? s.AddScalarToAllValues(-128f) : s;
for (int j = 0; j < 8; j++) for (int j = 0; j < 8; j++)
{ {

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

@ -1,3 +1,4 @@
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
{ {
using System; using System;
@ -65,11 +66,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
/// </summary> /// </summary>
private const int CenterJSample = 128; 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]; int[] temp = new int[Block8x8.Size];
block.CopyTo(temp); block.CopyTo(temp);
TransformFDCTInplace(temp); Subtract128_TransformFDCT_Upscale8_Inplace(temp);
var result = default(Block8x8); var result = default(Block8x8);
result.LoadFrom(temp); result.LoadFrom(temp);
return result; return result;
@ -91,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
/// Leave results scaled up by an overall factor of 8. /// Leave results scaled up by an overall factor of 8.
/// </summary> /// </summary>
/// <param name="block">The block of coefficients.</param> /// <param name="block">The block of coefficients.</param>
public static void TransformFDCTInplace(Span<int> block) public static void Subtract128_TransformFDCT_Upscale8_Inplace(Span<int> block)
{ {
// Pass 1: process rows. // Pass 1: process rows.
for (int y = 0; y < 8; y++) for (int y = 0; y < 8; y++)

Loading…
Cancel
Save