Browse Source

Moved quantization table initialization to component post processors

pull/2076/head
Dmitry Pentin 4 years ago
parent
commit
6eceb6c0eb
  1. 10
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DirectComponentProcessor.cs
  2. 10
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs
  3. 10
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs
  4. 6
      src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs
  5. 31
      src/ImageSharp/Formats/Jpeg/Components/ScaledFloatingPointDCT.cs
  6. 3
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  7. 135
      tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs

10
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DirectComponentProcessor.cs

@ -8,11 +8,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
internal sealed class DirectComponentProcessor : ComponentProcessor
{
private readonly IRawJpegData rawJpeg;
private Block8x8F dequantizationTable;
public DirectComponentProcessor(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
: base(memoryAllocator, frame, postProcessorBufferSize, component, blockSize: 8)
=> this.rawJpeg = rawJpeg;
{
this.dequantizationTable = rawJpeg.QuantizationTables[component.QuantizationTableIndex];
FloatingPointDCT.AdjustToIDCT(ref this.dequantizationTable);
}
public override void CopyBlocksToColorBuffer(int spectralStep)
{
@ -28,7 +31,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
Size subSamplingDivisors = this.Component.SubSamplingDivisors;
Block8x8F dequantTable = this.rawJpeg.QuantizationTables[this.Component.QuantizationTableIndex];
Block8x8F workspaceBlock = default;
for (int y = 0; y < blocksRowsPerStep; y++)
@ -44,7 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
workspaceBlock.LoadFrom(ref blockRow[xBlock]);
// Dequantize
workspaceBlock.MultiplyInPlace(ref dequantTable);
workspaceBlock.MultiplyInPlace(ref this.dequantizationTable);
// Convert from spectral to color
FloatingPointDCT.TransformIDCT(ref workspaceBlock);

10
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor2.cs

@ -9,11 +9,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
internal sealed class DownScalingComponentProcessor2 : ComponentProcessor
{
private readonly IRawJpegData rawJpeg;
private Block8x8F dequantizationTable;
public DownScalingComponentProcessor2(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
: base(memoryAllocator, frame, postProcessorBufferSize, component, 4)
=> this.rawJpeg = rawJpeg;
{
this.dequantizationTable = rawJpeg.QuantizationTables[component.QuantizationTableIndex];
ScaledFloatingPointDCT.AdjustToIDCT(ref this.dequantizationTable);
}
public override void CopyBlocksToColorBuffer(int spectralStep)
{
@ -27,7 +30,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int blocksRowsPerStep = this.Component.SamplingFactors.Height;
Size subSamplingDivisors = this.Component.SubSamplingDivisors;
Block8x8F dequantTable = this.rawJpeg.QuantizationTables[this.Component.QuantizationTableIndex];
Block8x8F workspaceBlock = default;
int yBlockStart = spectralStep * blocksRowsPerStep;
@ -45,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
workspaceBlock.LoadFrom(ref blockRow[xBlock]);
// IDCT/Normalization/Range
ScaledFloatingPointDCT.TransformIDCT_4x4(ref workspaceBlock, ref dequantTable, normalizationValue, maximumValue);
ScaledFloatingPointDCT.TransformIDCT_4x4(ref workspaceBlock, ref this.dequantizationTable, normalizationValue, maximumValue);
// Save to the intermediate buffer
int xColorBufferStart = xBlock * this.BlockAreaSize.Width;

10
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor4.cs

@ -9,11 +9,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
internal sealed class DownScalingComponentProcessor4 : ComponentProcessor
{
private readonly IRawJpegData rawJpeg;
private Block8x8F dequantizationTable;
public DownScalingComponentProcessor4(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
: base(memoryAllocator, frame, postProcessorBufferSize, component, 2)
=> this.rawJpeg = rawJpeg;
{
this.dequantizationTable = rawJpeg.QuantizationTables[component.QuantizationTableIndex];
ScaledFloatingPointDCT.AdjustToIDCT(ref this.dequantizationTable);
}
public override void CopyBlocksToColorBuffer(int spectralStep)
{
@ -27,7 +30,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
int blocksRowsPerStep = this.Component.SamplingFactors.Height;
Size subSamplingDivisors = this.Component.SubSamplingDivisors;
Block8x8F dequantTable = this.rawJpeg.QuantizationTables[this.Component.QuantizationTableIndex];
Block8x8F workspaceBlock = default;
int yBlockStart = spectralStep * blocksRowsPerStep;
@ -45,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
workspaceBlock.LoadFrom(ref blockRow[xBlock]);
// IDCT/Normalization/Range
ScaledFloatingPointDCT.TransformIDCT_2x2(ref workspaceBlock, ref dequantTable, normalizationValue, maximumValue);
ScaledFloatingPointDCT.TransformIDCT_2x2(ref workspaceBlock, ref this.dequantizationTable, normalizationValue, maximumValue);
// Save to the intermediate buffer
int xColorBufferStart = xBlock * this.BlockAreaSize.Width;

6
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs

@ -9,11 +9,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
internal sealed class DownScalingComponentProcessor8 : ComponentProcessor
{
private readonly float dcDequantizer;
private readonly float dcDequantizatizer;
public DownScalingComponentProcessor8(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component)
: base(memoryAllocator, frame, postProcessorBufferSize, component, 1)
=> this.dcDequantizer = rawJpeg.QuantizationTables[component.QuantizationTableIndex][0];
=> this.dcDequantizatizer = 0.125f * rawJpeg.QuantizationTables[component.QuantizationTableIndex][0];
public override void CopyBlocksToColorBuffer(int spectralStep)
{
@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++)
{
float dc = ScaledFloatingPointDCT.TransformIDCT_1x1(blockRow[xBlock][0], this.dcDequantizer, normalizationValue, maximumValue);
float dc = ScaledFloatingPointDCT.TransformIDCT_1x1(blockRow[xBlock][0], this.dcDequantizatizer, normalizationValue, maximumValue);
// Save to the intermediate buffer
int xColorBufferStart = xBlock * this.BlockAreaSize.Width;

31
src/ImageSharp/Formats/Jpeg/Components/ScaledFloatingPointDCT.cs

@ -14,6 +14,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
/// </remarks>
internal static class ScaledFloatingPointDCT
{
#pragma warning disable SA1310 // naming rules violation warnings
private const float F32_0_541196100 = 0.541196100f;
private const float F32_0_765366865 = 0.765366865f;
private const float F32_1_847759065 = 1.847759065f;
#pragma warning restore SA1310
/// <summary>
/// Adjusts given quantization table for usage with IDCT algorithms
/// from <see cref="ScaledFloatingPointDCT"/>.
@ -56,27 +62,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
public static void TransformIDCT_4x4(ref Block8x8F block, ref Block8x8F dequantTable, float normalizationValue, float maxValue)
{
const int DCTSIZE = 8;
const float FIX_0_541196100 = 0.541196100f;
const float FIX_0_765366865 = 0.765366865f;
const float FIX_1_847759065 = 1.847759065f;
for (int ctr = 0; ctr < 4; ctr++)
{
// Even part
float tmp0 = block[ctr * DCTSIZE] * dequantTable[ctr * DCTSIZE];
float tmp2 = block[(ctr * DCTSIZE) + 2] * dequantTable[(ctr * DCTSIZE) + 2];
float tmp0 = block[ctr * 8] * dequantTable[ctr * 8];
float tmp2 = block[(ctr * 8) + 2] * dequantTable[(ctr * 8) + 2];
float tmp10 = tmp0 + tmp2;
float tmp12 = tmp0 - tmp2;
// Odd part
float z2 = block[(ctr * DCTSIZE) + 1] * dequantTable[(ctr * DCTSIZE) + 1];
float z3 = block[(ctr * DCTSIZE) + 3] * dequantTable[(ctr * DCTSIZE) + 3];
float z2 = block[(ctr * 8) + 1] * dequantTable[(ctr * 8) + 1];
float z3 = block[(ctr * 8) + 3] * dequantTable[(ctr * 8) + 3];
float z1 = (z2 + z3) * FIX_0_541196100;
tmp0 = z1 + (z2 * FIX_0_765366865);
tmp2 = z1 - (z3 * FIX_1_847759065);
float z1 = (z2 + z3) * F32_0_541196100;
tmp0 = z1 + (z2 * F32_0_765366865);
tmp2 = z1 - (z3 * F32_1_847759065);
/* Final output stage */
block[ctr + 4] = tmp10 + tmp0;
@ -98,9 +99,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
float z2 = block[(ctr * 8) + 1 + 4];
float z3 = block[(ctr * 8) + 3 + 4];
float z1 = (z2 + z3) * FIX_0_541196100;
tmp0 = z1 + (z2 * FIX_0_765366865);
tmp2 = z1 - (z3 * FIX_1_847759065);
float z1 = (z2 + z3) * F32_0_541196100;
tmp0 = z1 + (z2 * F32_0_765366865);
tmp2 = z1 - (z3 * F32_1_847759065);
/* Final output stage */
block[(ctr * 8) + 0] = MathF.Round(Numerics.Clamp(tmp10 + tmp0 + normalizationValue, 0, maxValue));

3
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -1025,9 +1025,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
break;
}
}
// Adjusting table for IDCT step during decompression
FloatingPointDCT.AdjustToIDCT(ref table);
}
}

135
tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs

@ -62,14 +62,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Block8x8F dequantMatrix = CreateBlockFromScalar(1);
// This step is needed to apply adjusting multipliers to the input block
FastFloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
FloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
// IDCT implementation tranforms blocks after transposition
srcBlock.TransposeInplace();
srcBlock.MultiplyInPlace(ref dequantMatrix);
// IDCT calculation
FastFloatingPointDCT.TransformIDCT(ref srcBlock);
FloatingPointDCT.TransformIDCT(ref srcBlock);
this.CompareBlocks(expected, srcBlock, 1f);
}
@ -95,14 +95,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Block8x8F dequantMatrix = CreateBlockFromScalar(1);
// This step is needed to apply adjusting multipliers to the input block
FastFloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
FloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
// IDCT implementation tranforms blocks after transposition
srcBlock.TransposeInplace();
srcBlock.MultiplyInPlace(ref dequantMatrix);
// IDCT calculation
FastFloatingPointDCT.TransformIDCT(ref srcBlock);
FloatingPointDCT.TransformIDCT(ref srcBlock);
this.CompareBlocks(expected, srcBlock, 1f);
}
@ -138,13 +138,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// Dequantization using unit matrix - no values are upscaled
// as quant matrix is all 1's
// This step is needed to apply adjusting multipliers to the input block
FastFloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
FloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
srcBlock.MultiplyInPlace(ref dequantMatrix);
// testee
// IDCT implementation tranforms blocks after transposition
srcBlock.TransposeInplace();
FastFloatingPointDCT.TransformIDCT(ref srcBlock);
FloatingPointDCT.TransformIDCT(ref srcBlock);
float[] actualDest = srcBlock.ToArray();
@ -162,56 +162,65 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
HwIntrinsics.AllowAll | HwIntrinsics.DisableFMA | HwIntrinsics.DisableAVX | HwIntrinsics.DisableSIMD);
}
//[Theory]
//[InlineData(1)]
//[InlineData(2)]
//public void TranformIDCT_4x4(int seed)
//{
// Span<float> src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed, 4, 4);
// var srcBlock = default(Block8x8F);
// srcBlock.LoadFrom(src);
// float[] expectedDest = new float[64];
// float[] temp = new float[64];
// // reference
// ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D_llm(src, expectedDest, temp);
// // testee
// // Part of the IDCT calculations is fused into the quantization step
// // We must multiply input block with adjusted no-quantization matrix
// // before applying IDCT
// Block8x8F dequantMatrix = CreateBlockFromScalar(1);
// // Dequantization using unit matrix - no values are upscaled
// // as quant matrix is all 1's
// // This step is needed to apply adjusting multipliers to the input block
// FastFloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
// // testee
// // IDCT implementation tranforms blocks after transposition
// srcBlock.TransposeInplace();
// DownScalingComponentProcessor2.TransformIDCT_4x4(ref srcBlock, ref dequantMatrix, NormalizationValue, MaxOutputValue);
// var comparer = new ApproximateFloatComparer(1f);
// Span<float> expectedSpan = expectedDest.AsSpan();
// Span<float> actualSpan = srcBlock.ToArray().AsSpan();
// AssertEquality(expectedSpan, actualSpan, comparer);
// AssertEquality(expectedSpan.Slice(8), actualSpan.Slice(8), comparer);
// AssertEquality(expectedSpan.Slice(16), actualSpan.Slice(16), comparer);
// AssertEquality(expectedSpan.Slice(24), actualSpan.Slice(24), comparer);
// static void AssertEquality(Span<float> expected, Span<float> actual, ApproximateFloatComparer comparer)
// {
// for (int x = 0; x < 4; x++)
// {
// float expectedValue = (float)Math.Round(Numerics.Clamp(expected[x] + NormalizationValue, 0, MaxOutputValue));
// Assert.Equal(expectedValue, actual[x], comparer);
// }
// }
//}
[Theory]
[InlineData(1)]
[InlineData(2)]
public void TranformIDCT_4x4(int seed)
{
Span<float> src = Create8x8RandomFloatData(MinInputValue, MaxInputValue, seed, 4, 4);
var srcBlock = default(Block8x8F);
srcBlock.LoadFrom(src);
float[] expectedDest = new float[64];
float[] temp = new float[64];
// reference
ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D_llm(src, expectedDest, temp);
// testee
// Part of the IDCT calculations is fused into the quantization step
// We must multiply input block with adjusted no-quantization matrix
// before applying IDCT
Block8x8F dequantMatrix = CreateBlockFromScalar(1);
// Dequantization using unit matrix - no values are upscaled
// as quant matrix is all 1's
// This step is needed to apply adjusting multipliers to the input block
ScaledFloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
// testee
// IDCT implementation tranforms blocks after transposition
srcBlock.TransposeInplace();
ScaledFloatingPointDCT.TransformIDCT_4x4(ref srcBlock, ref dequantMatrix, NormalizationValue, MaxOutputValue);
Block8x8F DEBUG_VARIABLE = Block8x8F.Load(expectedDest);
var comparer = new ApproximateFloatComparer(1f);
Span<float> expectedSpan = expectedDest.AsSpan();
Span<float> actualSpan = srcBlock.ToArray().AsSpan();
AssertEquality_4x4(expectedSpan, actualSpan, comparer);
AssertEquality_4x4(expectedSpan.Slice(2), actualSpan.Slice(8), comparer);
AssertEquality_4x4(expectedSpan.Slice(4), actualSpan.Slice(16), comparer);
AssertEquality_4x4(expectedSpan.Slice(6), actualSpan.Slice(24), comparer);
static void AssertEquality_4x4(Span<float> expected, Span<float> actual, ApproximateFloatComparer comparer)
{
float average_4x4 = 0f;
for (int y = 0; y < 2; y++)
{
int y8 = y * 8;
for (int x = 0; x < 2; x++)
{
float clamped = Numerics.Clamp(expected[y8 + x] + NormalizationValue, 0, MaxOutputValue);
average_4x4 += clamped;
}
}
average_4x4 /= 4f;
}
}
[Theory]
[InlineData(1)]
@ -237,12 +246,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// Dequantization using unit matrix - no values are upscaled
// as quant matrix is all 1's
// This step is needed to apply adjusting multipliers to the input block
FastFloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
ScaledFloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
// testee
// IDCT implementation tranforms blocks after transposition
srcBlock.TransposeInplace();
DownScalingComponentProcessor4.TransformIDCT_2x2(ref srcBlock, ref dequantMatrix, NormalizationValue, MaxOutputValue);
ScaledFloatingPointDCT.TransformIDCT_2x2(ref srcBlock, ref dequantMatrix, NormalizationValue, MaxOutputValue);
Block8x8F DEBUG_VARIABLE = Block8x8F.Load(expectedDest);
var comparer = new ApproximateFloatComparer(0.1f);
@ -287,12 +298,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// Dequantization using unit matrix - no values are upscaled
// as quant matrix is all 1's
// This step is needed to apply adjusting multipliers to the input block
FastFloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
ScaledFloatingPointDCT.AdjustToIDCT(ref dequantMatrix);
// testee
// IDCT implementation tranforms blocks after transposition
srcBlock.TransposeInplace();
float actual = DownScalingComponentProcessor8.TransformIDCT_1x1(
float actual = ScaledFloatingPointDCT.TransformIDCT_1x1(
srcBlock[0],
dequantMatrix[0],
NormalizationValue,
@ -328,14 +339,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
// testee
// Second transpose call is done by Quantize step
// Do this manually here just to be complient to the reference implementation
FastFloatingPointDCT.TransformFDCT(ref block);
FloatingPointDCT.TransformFDCT(ref block);
block.TransposeInplace();
// Part of the IDCT calculations is fused into the quantization step
// We must multiply input block with adjusted no-quantization matrix
// after applying FDCT
Block8x8F quantMatrix = CreateBlockFromScalar(1);
FastFloatingPointDCT.AdjustToFDCT(ref quantMatrix);
FloatingPointDCT.AdjustToFDCT(ref quantMatrix);
block.MultiplyInPlace(ref quantMatrix);
float[] actualDest = block.ToArray();

Loading…
Cancel
Save