From 005a454b40c8f67c1e4c006ddfcbcd0dcc0ab094 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 17 Sep 2017 02:22:13 +0200 Subject: [PATCH] optimized quantization --- .../Common/Decoder/JpegBlockPostProcessor.cs | 28 ++++++++----------- .../Jpeg/Common/{UnzigData.cs => ZigZag.cs} | 23 ++++++++++++--- .../OrigJpegScanDecoder.ComputationData.cs | 4 +-- .../Jpeg/GolangPort/JpegEncoderCore.cs | 4 +-- .../Formats/Jpg/Block8x8FTests.cs | 26 +++++++++++++++-- .../Formats/Jpg/JpegProfilingBenchmarks.cs | 4 +-- .../Jpg/Utils/ReferenceImplementations.cs | 2 +- 7 files changed, 62 insertions(+), 29 deletions(-) rename src/ImageSharp/Formats/Jpeg/Common/{UnzigData.cs => ZigZag.cs} (70%) diff --git a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs index e679fddcf..7139260ec 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/Decoder/JpegBlockPostProcessor.cs @@ -34,26 +34,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder postProcessor->pointers = new DataPointers(&postProcessor->data); int qtIndex = component.QuantizationTableIndex; - postProcessor->data.QuantiazationTable = decoder.QuantizationTables[qtIndex]; + postProcessor->data.DequantiazationTable = ZigZag.CreateDequantizationTable(ref decoder.QuantizationTables[qtIndex]); postProcessor->subSamplingDivisors = component.SubSamplingDivisors; } - public void QuantizeAndTransform(ref Block8x8 sourceBlock) + public void ProcessBlockColorsInto( + ref Block8x8 sourceBlock, + BufferArea destArea) { this.data.SourceBlock = sourceBlock.AsFloatBlock(); Block8x8F* b = this.pointers.SourceBlock; - Block8x8F.DequantizeBlock(b, this.pointers.QuantiazationTable, this.pointers.Unzig); + // Dequantize: + b->MultiplyInplace(ref this.data.DequantiazationTable); FastFloatingPointDCT.TransformIDCT(ref *b, ref this.data.WorkspaceBlock1, ref this.data.WorkspaceBlock2); - } - - public void ProcessBlockColorsInto( - ref Block8x8 sourceBlock, - BufferArea destArea) - { - this.QuantizeAndTransform(ref sourceBlock); this.data.WorkspaceBlock1.NormalizeColorsInplace(); @@ -89,12 +85,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder /// /// The quantization table as /// - public Block8x8F QuantiazationTable; + public Block8x8F DequantiazationTable; /// /// The jpeg unzig data /// - public UnzigData Unzig; + public ZigZag Unzig; /// /// Creates and initializes a new instance @@ -103,7 +99,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder public static ComputationData Create() { var data = default(ComputationData); - data.Unzig = UnzigData.Create(); + data.Unzig = ZigZag.CreateUnzigTable(); return data; } } @@ -129,9 +125,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder public Block8x8F* WorkspaceBlock2; /// - /// Pointer to + /// Pointer to /// - public Block8x8F* QuantiazationTable; + public Block8x8F* DequantiazationTable; /// /// Pointer to as int* @@ -147,7 +143,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common.Decoder this.SourceBlock = &dataPtr->SourceBlock; this.WorkspaceBlock1 = &dataPtr->WorkspaceBlock1; this.WorkspaceBlock2 = &dataPtr->WorkspaceBlock2; - this.QuantiazationTable = &dataPtr->QuantiazationTable; + this.DequantiazationTable = &dataPtr->DequantiazationTable; this.Unzig = dataPtr->Unzig.Data; } } diff --git a/src/ImageSharp/Formats/Jpeg/Common/UnzigData.cs b/src/ImageSharp/Formats/Jpeg/Common/ZigZag.cs similarity index 70% rename from src/ImageSharp/Formats/Jpeg/Common/UnzigData.cs rename to src/ImageSharp/Formats/Jpeg/Common/ZigZag.cs index aaefbb3af..18270f5ba 100644 --- a/src/ImageSharp/Formats/Jpeg/Common/UnzigData.cs +++ b/src/ImageSharp/Formats/Jpeg/Common/ZigZag.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common /// unzig[3] is the column and row of the fourth element in zigzag order. The /// value is 16, which means first column (16%8 == 0) and third row (16/8 == 2). /// - internal unsafe struct UnzigData + internal unsafe struct ZigZag { /// /// Copy of in a value type @@ -32,15 +32,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common }; /// - /// Creates and fills an instance of with Jpeg unzig indices + /// Creates and fills an instance of with Jpeg unzig indices /// /// The new instance - public static UnzigData Create() + public static ZigZag CreateUnzigTable() { - UnzigData result = default(UnzigData); + ZigZag result = default(ZigZag); int* unzigPtr = result.Data; Marshal.Copy(Unzig, 0, (IntPtr)unzigPtr, 64); return result; } + + /// + /// Apply Zigging to the given quantization table, so it will be sufficient to multiply blocks for dequantizing them. + /// + public static Block8x8F CreateDequantizationTable(ref Block8x8F qt) + { + Block8x8F result = default(Block8x8F); + + for (int i = 0; i < 64; i++) + { + result[Unzig[i]] = qt[i]; + } + + return result; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.ComputationData.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.ComputationData.cs index 6252d8209..d426ed4f1 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.ComputationData.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OrigJpegScanDecoder.ComputationData.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder /// /// The jpeg unzig data /// - public UnzigData Unzig; + public ZigZag Unzig; /// /// The buffer storing the -s for each component @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder public static ComputationData Create() { ComputationData data = default(ComputationData); - data.Unzig = UnzigData.Create(); + data.Unzig = ZigZag.CreateUnzigTable(); return data; } } diff --git a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs index 984fb828c..3a24bded3 100644 --- a/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/GolangPort/JpegEncoderCore.cs @@ -455,7 +455,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; - UnzigData unzig = UnzigData.Create(); + ZigZag unzig = ZigZag.CreateUnzigTable(); // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; @@ -912,7 +912,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort Block8x8F onStackLuminanceQuantTable = this.luminanceQuantTable; Block8x8F onStackChrominanceQuantTable = this.chrominanceQuantTable; - UnzigData unzig = UnzigData.Create(); + ZigZag unzig = ZigZag.CreateUnzigTable(); // ReSharper disable once InconsistentNaming int prevDCY = 0, prevDCCb = 0, prevDCCr = 0; diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index 59fa19be4..66c9bb3e1 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -309,7 +309,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var qt = new Block8x8F(); qt.LoadFrom(Create8x8RoundedRandomFloatData(-2000, 2000, seed)); - var unzig = UnzigData.Create(); + var unzig = ZigZag.CreateUnzigTable(); int* expectedResults = stackalloc int[Block8x8F.Size]; ReferenceImplementations.UnZigDivRoundRational(&block, expectedResults, &qt, unzig.Data); @@ -396,7 +396,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Block8x8F original = CreateRandomFloatBlock(-500, 500, seed); Block8x8F qt = CreateRandomFloatBlock(0, 10, seed + 42); - var unzig = UnzigData.Create(); + var unzig = ZigZag.CreateUnzigTable(); Block8x8F expected = original; Block8x8F actual = original; @@ -406,5 +406,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg this.CompareBlocks(expected, actual, 0); } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + public unsafe void ZigZag_CreateDequantizationTable_MultiplicationShouldQuantize(int seed) + { + Block8x8F original = CreateRandomFloatBlock(-500, 500, seed); + Block8x8F qt = CreateRandomFloatBlock(0, 10, seed + 42); + + var unzig = ZigZag.CreateUnzigTable(); + Block8x8F zigQt = ZigZag.CreateDequantizationTable(ref qt); + + Block8x8F expected = original; + Block8x8F actual = original; + + ReferenceImplementations.DequantizeBlock(&expected, &qt, unzig.Data); + + actual.MultiplyInplace(ref zigQt); + + this.CompareBlocks(expected, actual, 0); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index 063f42c00..a1ff3550a 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -41,8 +41,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg this.DecodeJpegBenchmarkImpl(fileName, new OrigJpegDecoder()); } - [Theory] // Benchmark, enable manually - [MemberData(nameof(DecodeJpegData))] + //[Theory] // Benchmark, enable manually + //[MemberData(nameof(DecodeJpegData))] public void DecodeJpeg_PdfJs(string fileName) { this.DecodeJpegBenchmarkImpl(fileName, new PdfJsJpegDecoder()); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.cs index 38339e58f..f3722b5d6 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.cs @@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils /// The input block /// The destination block of integers /// The quantization table - /// Pointer to + /// Pointer to public static unsafe void UnZigDivRoundRational(Block8x8F* src, int* dest, Block8x8F* qt, int* unzigPtr) { float* s = (float*)src;