public void IDCT2D8x4_RightPart() { float[] sourceArray = Create8x8FloatData(); var expectedDestArray = new float[64]; ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D8x4_32f(sourceArray.AsSpan(4), expectedDestArray.AsSpan(4)); var source = default(Block8x8F); source.LoadFrom(sourceArray); var dest = default(Block8x8F); FastFloatingPointDCT.IDCT8x4_RightPart(ref source, ref dest); var actualDestArray = new float[64]; dest.CopyTo(actualDestArray); this.Print8x8Data(expectedDestArray); this.Output.WriteLine("**************"); this.Print8x8Data(actualDestArray); Assert.Equal(expectedDestArray, actualDestArray); }
public void LLM_TransformIDCT_CompareToAccurate(int seed) { float[] sourceArray = Create8x8RoundedRandomFloatData(MinAllowedValue, MaxAllowedValue, seed); var srcBlock = Block8x8F.Load(sourceArray); // reference Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref srcBlock); // 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 // Dequantization using unit matrix - no values are upscaled Block8x8F dequantMatrix = CreateBlockFromScalar(1); // This step is needed to apply adjusting multipliers to the input block FastFloatingPointDCT.AdjustToIDCT(ref dequantMatrix); // IDCT implementation tranforms blocks after transposition srcBlock.TransposeInplace(); srcBlock.MultiplyInPlace(ref dequantMatrix); // IDCT calculation FastFloatingPointDCT.TransformIDCT(ref srcBlock); this.CompareBlocks(expected, srcBlock, 1f); }
static void RunTest(string serialized) { int seed = FeatureTestRunner.Deserialize <int>(serialized); Span <float> src = Create8x8RoundedRandomFloatData(-200, 200, seed); var srcBlock = default(Block8x8F); srcBlock.LoadFrom(src); var expectedDest = new float[64]; var temp1 = new float[64]; var temp2 = default(Block8x8F); // reference ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D_llm(src, expectedDest, temp1); // testee FastFloatingPointDCT.TransformIDCT(ref srcBlock, ref temp2); var actualDest = new float[64]; srcBlock.ScaledCopyTo(actualDest); Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); }
static void RunTest(string serialized) { int seed = FeatureTestRunner.Deserialize <int>(serialized); Span <float> src = Create8x8RoundedRandomFloatData(-200, 200, seed); var block = default(Block8x8F); block.LoadFrom(src); float[] expectedDest = new float[64]; float[] temp1 = new float[64]; // reference ReferenceImplementations.LLM_FloatingPoint_DCT.FDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true); // testee // Part of the FDCT calculations is fused into the quantization step // We must multiply transformed block with reciprocal values from FastFloatingPointDCT.ANN_DCT_reciprocalAdjustmen FastFloatingPointDCT.TransformFDCT(ref block); for (int i = 0; i < 64; i++) { block[i] = block[i] * FastFloatingPointDCT.DctReciprocalAdjustmentCoefficients[i]; } float[] actualDest = block.ToArray(); Assert.Equal(expectedDest, actualDest, new ApproximateFloatComparer(1f)); }
static void RunTest(string serialized) { int seed = FeatureTestRunner.Deserialize <int>(serialized); Span <float> src = Create8x8RoundedRandomFloatData(MinAllowedValue, MaxAllowedValue, seed); var block = default(Block8x8F); block.LoadFrom(src); float[] expectedDest = new float[64]; float[] temp1 = new float[64]; // reference ReferenceImplementations.LLM_FloatingPoint_DCT.FDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true); // 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); 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); block.MultiplyInPlace(ref quantMatrix); float[] actualDest = block.ToArray(); Assert.Equal(expectedDest, actualDest, new ApproximateFloatComparer(1f)); }
public void IDCT8x8_Avx(int seed) { #if SUPPORTS_RUNTIME_INTRINSICS if (!Avx.IsSupported) { this.Output.WriteLine("No AVX present, skipping test!"); } Span <float> src = Create8x8RoundedRandomFloatData(-200, 200, seed); Block8x8F srcBlock = default; srcBlock.LoadFrom(src); Block8x8F destBlock = default; float[] expectedDest = new float[64]; // reference, left part ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D8x4_32f(src, expectedDest); // reference, right part ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D8x4_32f(src.Slice(4), expectedDest.AsSpan(4)); // testee, whole 8x8 FastFloatingPointDCT.IDCT8x8_Avx(ref srcBlock, ref destBlock); float[] actualDest = new float[64]; destBlock.ScaledCopyTo(actualDest); Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); #endif }
/// <summary> /// Writes a block of pixel data using the given quantization table, /// returning the post-quantized DC value of the DCT-transformed block. /// The block is in natural (not zig-zag) order. /// </summary> /// <param name="index">The quantization table index.</param> /// <param name="prevDC">The previous DC value.</param> /// <param name="src">Source block</param> /// <param name="tempDest1">Temporal block to be used as FDCT Destination</param> /// <param name="tempDest2">Temporal block 2</param> /// <param name="quant">Quantization table</param> /// <param name="unzigPtr">The 8x8 Unzig block pointer</param> /// <returns> /// The <see cref="int"/> /// </returns> private int WriteBlock( QuantIndex index, int prevDC, Block8x8F *src, Block8x8F *tempDest1, Block8x8F *tempDest2, Block8x8F *quant, byte *unzigPtr) { FastFloatingPointDCT.TransformFDCT(ref *src, ref *tempDest1, ref *tempDest2); Block8x8F.Quantize(tempDest1, tempDest2, quant, unzigPtr); float *unziggedDestPtr = (float *)tempDest2; int dc = (int)unziggedDestPtr[0]; // Emit the DC delta. this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC); // Emit the AC components. var h = (HuffIndex)((2 * (int)index) + 1); int runLength = 0; for (int zig = 1; zig < Block8x8F.Size; zig++) { int ac = (int)unziggedDestPtr[zig]; if (ac == 0) { runLength++; } else { while (runLength > 15) { this.EmitHuff(h, 0xf0); runLength -= 16; } this.EmitHuffRLE(h, runLength, ac); runLength = 0; } } if (runLength > 0) { this.EmitHuff(h, 0x00); } return(dc); }
/// <summary> /// Writes a block of pixel data using the given quantization table, /// returning the post-quantized DC value of the DCT-transformed block. /// The block is in natural (not zig-zag) order. /// </summary> /// <param name="index">The quantization table index.</param> /// <param name="prevDC">The previous DC value.</param> /// <param name="src">Source block</param> /// <param name="tempDest1">Temporal block to be used as FDCT Destination</param> /// <param name="tempDest2">Temporal block 2</param> /// <param name="quant">Quantization table</param> /// <param name="unZig">The 8x8 Unzig block.</param> /// <param name="emitBufferBase">The reference to the emit buffer.</param> /// <returns>The <see cref="int"/>.</returns> private int WriteBlock( QuantIndex index, int prevDC, ref Block8x8F src, ref Block8x8F tempDest1, ref Block8x8F tempDest2, ref Block8x8F quant, ref ZigZag unZig, ref byte emitBufferBase) { FastFloatingPointDCT.TransformFDCT(ref src, ref tempDest1, ref tempDest2); Block8x8F.Quantize(ref tempDest1, ref tempDest2, ref quant, ref unZig); int dc = (int)tempDest2[0]; // Emit the DC delta. this.EmitHuffRLE((HuffIndex)((2 * (int)index) + 0), 0, dc - prevDC, ref emitBufferBase); // Emit the AC components. var h = (HuffIndex)((2 * (int)index) + 1); int runLength = 0; for (int zig = 1; zig < Block8x8F.Size; zig++) { int ac = (int)tempDest2[zig]; if (ac == 0) { runLength++; } else { while (runLength > 15) { this.EmitHuff(h, 0xf0, ref emitBufferBase); runLength -= 16; } this.EmitHuffRLE(h, runLength, ac, ref emitBufferBase); runLength = 0; } } if (runLength > 0) { this.EmitHuff(h, 0x00, ref emitBufferBase); } return(dc); }
public void LLM_TransformIDCT_CompareToAccurate(int seed) { float[] sourceArray = Create8x8RoundedRandomFloatData(-1000, 1000, seed); var srcBlock = Block8x8F.Load(sourceArray); Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref srcBlock); var temp = default(Block8x8F); FastFloatingPointDCT.TransformIDCT(ref srcBlock, ref temp); this.CompareBlocks(expected, srcBlock, 1f); }
public void LLM_TransformIDCT_CompareToNonOptimized(int seed) { float[] sourceArray = Create8x8RoundedRandomFloatData(-1000, 1000, seed); var source = Block8x8F.Load(sourceArray); Block8x8F expected = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformIDCT(ref source); var temp = default(Block8x8F); var actual = default(Block8x8F); FastFloatingPointDCT.TransformIDCT(ref source, ref actual, ref temp); this.CompareBlocks(expected, actual, 1f); }
/// <summary> /// Convert raw spectral DCT data to color data and copy it to the color buffer <see cref="ColorBuffer"/>. /// </summary> public void CopyBlocksToColorBuffer(int spectralStep) { Buffer2D <Block8x8> spectralBuffer = this.component.SpectralBlocks; float maximumValue = this.frame.MaxColorChannelValue; int destAreaStride = this.ColorBuffer.Width; int yBlockStart = spectralStep * this.blockRowsPerStep; Size subSamplingDivisors = this.component.SubSamplingDivisors; Block8x8F dequantTable = this.rawJpeg.QuantizationTables[this.component.QuantizationTableIndex]; Block8x8F workspaceBlock = default; for (int y = 0; y < this.blockRowsPerStep; y++) { int yBuffer = y * this.blockAreaSize.Height; Span <float> colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer); Span <Block8x8> blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y); for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++) { // Integer to float workspaceBlock.LoadFrom(ref blockRow[xBlock]); // Dequantize workspaceBlock.MultiplyInPlace(ref dequantTable); // Convert from spectral to color FastFloatingPointDCT.TransformIDCT(ref workspaceBlock); // To conform better to libjpeg we actually NEED TO loose precision here. // This is because they store blocks as Int16 between all the operations. // To be "more accurate", we need to emulate this by rounding! workspaceBlock.NormalizeColorsAndRoundInPlace(maximumValue); // Write to color buffer acording to sampling factors int xColorBufferStart = xBlock * this.blockAreaSize.Width; workspaceBlock.ScaledCopyTo( ref colorBufferRow[xColorBufferStart], destAreaStride, subSamplingDivisors.Width, subSamplingDivisors.Height); } } }
public void FDCT8x4_RightPart(int seed) { Span <float> src = Create8x8RoundedRandomFloatData(-200, 200, seed); var srcBlock = new Block8x8F(); srcBlock.LoadFrom(src); var destBlock = new Block8x8F(); float[] expectedDest = new float[64]; ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D8x4_32f(src.Slice(4), expectedDest.AsSpan(4)); FastFloatingPointDCT.FDCT8x4_RightPart(ref srcBlock, ref destBlock); float[] actualDest = new float[64]; destBlock.CopyTo(actualDest); Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); }
public void FDCT8x4_LeftPart(int seed) { Span <float> src = Create8x8RoundedRandomFloatData(-200, 200, seed); var srcBlock = default(Block8x8F); srcBlock.LoadFrom(src); var destBlock = default(Block8x8F); var expectedDest = new float[64]; ReferenceImplementations.LLM_FloatingPoint_DCT.FDCT2D8x4_32f(src, expectedDest); FastFloatingPointDCT.FDCT8x4_LeftPart(ref srcBlock, ref destBlock); var actualDest = new float[64]; destBlock.ScaledCopyTo(actualDest); Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); }
public void TransformFDCT(int seed) { Span <float> src = Create8x8RoundedRandomFloatData(-200, 200, seed); var srcBlock = new Block8x8F(); srcBlock.LoadFrom(src); var destBlock = new Block8x8F(); float[] expectedDest = new float[64]; float[] temp1 = new float[64]; var temp2 = new Block8x8F(); ReferenceImplementations.LLM_FloatingPoint_DCT.fDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true); FastFloatingPointDCT.TransformFDCT(ref srcBlock, ref destBlock, ref temp2, false); float[] actualDest = new float[64]; destBlock.CopyTo(actualDest); Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); }
static void RunTest(string serialized) { int seed = FeatureTestRunner.Deserialize <int>(serialized); Span <float> src = Create8x8RoundedRandomFloatData(MinAllowedValue, MaxAllowedValue, seed); 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); srcBlock.MultiplyInPlace(ref dequantMatrix); // IDCT implementation tranforms blocks after transposition srcBlock.TransposeInplace(); // IDCT calculation FastFloatingPointDCT.TransformIDCT(ref srcBlock); float[] actualDest = srcBlock.ToArray(); Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); }
public void iDCT2D8x4_LeftPart() { float[] sourceArray = JpegFixture.Create8x8FloatData(); float[] expectedDestArray = new float[64]; ReferenceImplementations.LLM_FloatingPoint_DCT.iDCT2D8x4_32f(sourceArray, expectedDestArray); var source = new Block8x8F(); source.LoadFrom(sourceArray); var dest = new Block8x8F(); FastFloatingPointDCT.IDCT8x4_LeftPart(ref source, ref dest); float[] actualDestArray = new float[64]; dest.CopyTo(actualDestArray); this.Print8x8Data(expectedDestArray); this.Output.WriteLine("**************"); this.Print8x8Data(actualDestArray); Assert.Equal(expectedDestArray, actualDestArray); }
public override void ProcessScan(ref JpegReader reader, JpegScanHeader scanHeader) { JpegFrameHeader frameHeader = _frameHeader; JpegBlockOutputWriter?outputWriter = Decoder.GetOutputWriter(); if (frameHeader.Components is null) { ThrowInvalidDataException(); } if (scanHeader.Components is null) { ThrowInvalidDataException(); } if (outputWriter is null) { ThrowInvalidDataException(); } // Resolve each component Span <JpegArithmeticDecodingComponent> components = _components.AsSpan(0, InitDecodeComponents(frameHeader, scanHeader, _components)); foreach (JpegArithmeticDecodingComponent component in _components) { component.DcPredictor = 0; component.DcContext = 0; component.DcStatistics?.Reset(); component.AcStatistics?.Reset(); } Reset(); // Prepare int maxHorizontalSampling = _maxHorizontalSampling; int maxVerticalSampling = _maxVerticalSampling; int mcusBeforeRestart = _restartInterval; int mcusPerLine = _mcusPerLine; int mcusPerColumn = _mcusPerColumn; int levelShift = _levelShift; JpegBitReader bitReader = new JpegBitReader(reader.RemainingBytes); // DCT Block JpegBlock8x8F blockFBuffer = default; JpegBlock8x8F outputFBuffer = default; JpegBlock8x8F tempFBuffer = default; JpegBlock8x8 outputBuffer; for (int rowMcu = 0; rowMcu < mcusPerColumn; rowMcu++) { int offsetY = rowMcu * maxVerticalSampling; for (int colMcu = 0; colMcu < mcusPerLine; colMcu++) { int offsetX = colMcu * maxHorizontalSampling; // Scan an interleaved mcu... process components in order foreach (JpegArithmeticDecodingComponent component in components) { int index = component.ComponentIndex; int h = component.HorizontalSamplingFactor; int v = component.VerticalSamplingFactor; int hs = component.HorizontalSubsamplingFactor; int vs = component.VerticalSubsamplingFactor; for (int y = 0; y < v; y++) { int blockOffsetY = (offsetY + y) * 8; for (int x = 0; x < h; x++) { // Read MCU outputBuffer = default; ReadBlock(ref bitReader, component, ref outputBuffer); // Dequantization DequantizeBlockAndUnZigZag(component.QuantizationTable, ref outputBuffer, ref blockFBuffer); // IDCT FastFloatingPointDCT.TransformIDCT(ref blockFBuffer, ref outputFBuffer, ref tempFBuffer); // Level shift ShiftDataLevel(ref outputFBuffer, ref outputBuffer, levelShift); // CopyToOutput WriteBlock(outputWriter, ref Unsafe.As <JpegBlock8x8, short>(ref outputBuffer), index, (offsetX + x) * 8, blockOffsetY, hs, vs); } } } // Handle restart if (_restartInterval > 0 && (--mcusBeforeRestart) == 0) { bitReader.AdvanceAlignByte(); JpegMarker marker = bitReader.TryReadMarker(); if (marker == JpegMarker.EndOfImage) { int bytesConsumedEoi = reader.RemainingByteCount - bitReader.RemainingBits / 8; reader.TryAdvance(bytesConsumedEoi - 2); return; } if (!marker.IsRestartMarker()) { throw new InvalidOperationException("Expect restart marker."); } mcusBeforeRestart = _restartInterval; foreach (JpegArithmeticDecodingComponent component in components) { component.DcPredictor = 0; component.DcContext = 0; component.DcStatistics?.Reset(); component.AcStatistics?.Reset(); } Reset(); } } } bitReader.AdvanceAlignByte(); int bytesConsumed = reader.RemainingByteCount - bitReader.RemainingBits / 8; if (bitReader.TryPeekMarker() != 0) { if (!bitReader.TryPeekMarker().IsRestartMarker()) { bytesConsumed -= 2; } } reader.TryAdvance(bytesConsumed); }