Browse Source

Implementation of some inverse transformers

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
796ea84006
  1. 5
      src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs
  2. 1
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dFlipConfiguration.cs
  3. 78
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst4Inverse1dTransformer.cs
  4. 86
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct4Inverse1dTransformer.cs
  5. 43
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity16Inverse1dTransformer.cs
  6. 36
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity32Inverse1dTransformer.cs
  7. 34
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity4Inverse1dTransformer.cs
  8. 44
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity64Inverse1dTransformer.cs
  9. 31
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity8Inverse1dTransformer.cs
  10. 196
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1InverseTransformTests.cs

5
src/ImageSharp/Formats/Heif/Av1/Av1Constants.cs

@ -175,4 +175,9 @@ internal static class Av1Constants
public const int QuantificationMatrixLevelCount = 4;
public const int AngleStep = 3;
/// <summary>
/// Maximum number of stages in a 1-dimensioanl transform function.
/// </summary>
public const int MaxTransformStageNumber = 12;
}

1
src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dFlipConfiguration.cs

@ -179,6 +179,7 @@ internal class Av1Transform2dFlipConfiguration
/// <summary>
/// SVT: svt_av1_gen_fwd_stage_range
/// SVT: svt_av1_gen_inv_stage_range
/// </summary>
public void GenerateStageRange(int bitDepth)
{

78
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst4Inverse1dTransformer.cs

@ -0,0 +1,78 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
internal class Av1Adst4Inverse1dTransformer : IAv1Forward1dTransformer
{
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 4, nameof(input));
Guard.MustBeSizedAtLeast(output, 4, nameof(output));
TransformScalar(ref input[0], ref output[0], cosBit, stageRange);
}
/// <summary>
/// SVT: svt_av1_iadst4_new
/// </summary>
private static void TransformScalar(ref int input, ref int output, int cosBit, Span<byte> stageRange)
{
int bit = cosBit;
Span<int> sinpi = Av1SinusConstants.SinusPi(bit);
int s0, s1, s2, s3, s4, s5, s6, s7;
int x0 = input;
int x1 = Unsafe.Add(ref input, 1);
int x2 = Unsafe.Add(ref input, 2);
int x3 = Unsafe.Add(ref input, 3);
if (!(x0 != 0 | x1 != 0 | x2 != 0 | x3 != 0))
{
output = 0;
Unsafe.Add(ref output, 1) = 0;
Unsafe.Add(ref output, 2) = 0;
Unsafe.Add(ref output, 3) = 0;
return;
}
Guard.IsTrue(sinpi[1] + sinpi[2] == sinpi[4], nameof(sinpi), "Sinus Pi check failed.");
s0 = sinpi[1] * x0;
s1 = sinpi[2] * x0;
s2 = sinpi[3] * x1;
s3 = sinpi[4] * x2;
s4 = sinpi[1] * x2;
s5 = sinpi[2] * x3;
s6 = sinpi[4] * x3;
s7 = (x0 - x2) + x3;
// stage 3
s0 = s0 + s3;
s1 = s1 - s4;
s3 = s2;
s2 = sinpi[3] * s7;
// stage 4
s0 = s0 + s5;
s1 = s1 - s6;
// stage 5
x0 = s0 + s3;
x1 = s1 + s3;
x2 = s2;
x3 = s0 + s1;
// stage 6
x3 = x3 - s3;
output = Av1Math.RoundShift(x0, bit);
Unsafe.Add(ref output, 1) = Av1Math.RoundShift(x1, bit);
Unsafe.Add(ref output, 2) = Av1Math.RoundShift(x2, bit);
Unsafe.Add(ref output, 3) = Av1Math.RoundShift(x3, bit);
// range_check_buf(6, input, output, 4, stage_range[6]);
}
}

86
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct4Inverse1dTransformer.cs

@ -0,0 +1,86 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
internal class Av1Dct4Inverse1dTransformer : IAv1Forward1dTransformer
{
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 4, nameof(input));
Guard.MustBeSizedAtLeast(output, 4, nameof(output));
TransformScalar(ref input[0], ref output[0], cosBit, stageRange);
}
/// <summary>
/// SVT: svt_av1_idct4_new
/// </summary>
private static void TransformScalar(ref int input, ref int output, int cosBit, Span<byte> stageRange)
{
Span<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
Span<int> temp0 = stackalloc int[4];
Span<int> temp1 = stackalloc int[4];
// stage 0;
// stage 1;
stage++;
temp0[0] = input;
temp0[1] = Unsafe.Add(ref input, 2);
temp0[2] = Unsafe.Add(ref input, 1);
temp0[3] = Unsafe.Add(ref input, 3);
// range_check_buf(stage, input, bf1, size, stage_range[stage]);
// stage 2
stage++;
temp1[0] = HalfButterfly(cospi[32], temp0[0], cospi[32], temp0[1], cosBit);
temp1[1] = HalfButterfly(cospi[32], temp0[0], -cospi[32], temp0[1], cosBit);
temp1[2] = HalfButterfly(cospi[48], temp0[2], -cospi[16], temp0[3], cosBit);
temp1[3] = HalfButterfly(cospi[16], temp0[2], cospi[48], temp0[3], cosBit);
// range_check_buf(stage, input, bf1, size, stage_range[stage]);
// stage 3
stage++;
Unsafe.Add(ref output, 0) = ClampValue(temp1[0] + temp1[3], stageRange[stage]);
Unsafe.Add(ref output, 1) = ClampValue(temp1[1] + temp1[2], stageRange[stage]);
Unsafe.Add(ref output, 2) = ClampValue(temp1[1] - temp1[2], stageRange[stage]);
Unsafe.Add(ref output, 3) = ClampValue(temp1[0] - temp1[3], stageRange[stage]);
}
internal static int ClampValue(int value, byte bit)
{
if (bit <= 0)
{
return value; // Do nothing for invalid clamp bit.
}
long max_value = (1L << (bit - 1)) - 1;
long min_value = -(1L << (bit - 1));
return (int)Av1Math.Clamp(value, min_value, max_value);
}
internal static int HalfButterfly(int w0, int in0, int w1, int in1, int bit)
{
long result64 = (long)(w0 * in0) + (w1 * in1);
long intermediate = result64 + (1L << (bit - 1));
// NOTE(david.barker): The value 'result_64' may not necessarily fit
// into 32 bits. However, the result of this function is nominally
// ROUND_POWER_OF_TWO_64(result_64, bit)
// and that is required to fit into stage_range[stage] many bits
// (checked by range_check_buf()).
//
// Here we've unpacked that rounding operation, and it can be shown
// that the value of 'intermediate' here *does* fit into 32 bits
// for any conformant bitstream.
// The upshot is that, if you do all this calculation using
// wrapping 32-bit arithmetic instead of (non-wrapping) 64-bit arithmetic,
// then you'll still get the correct result.
return (int)(intermediate >> bit);
}
}

43
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity16Inverse1dTransformer.cs

@ -0,0 +1,43 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
internal class Av1Identity16Inverse1dTransformer : IAv1Forward1dTransformer
{
private const long Sqrt2Times2 = Av1Identity4Inverse1dTransformer.Sqrt2 >> 1;
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 16, nameof(input));
Guard.MustBeSizedAtLeast(output, 16, nameof(output));
TransformScalar(ref input[0], ref output[0]);
}
/// <summary>
/// SVT: svt_av1_iidentity16_c
/// </summary>
private static void TransformScalar(ref int input, ref int output)
{
// Normal input should fit into 32-bit. Cast to 64-bit here to avoid
// overflow with corrupted/fuzzed input. The same for av1_iidentity/16/64_c.
output = Av1Math.RoundShift(Sqrt2Times2 * input, Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 1) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 1), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 2) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 2), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 3) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 3), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 4) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 4), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 5) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 5), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 6) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 6), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 7) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 7), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 8) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 8), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 9) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 9), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 10) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 10), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 11) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 11), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 12) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 12), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 13) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 13), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 14) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 14), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 15) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 15), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
}
}

36
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity32Inverse1dTransformer.cs

@ -0,0 +1,36 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
internal class Av1Identity32Inverse1dTransformer : IAv1Forward1dTransformer
{
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 32, nameof(input));
Guard.MustBeSizedAtLeast(output, 32, nameof(output));
ref int inputRef = ref input[0];
ref int outputRef = ref output[0];
TransformScalar(ref inputRef, ref outputRef);
TransformScalar(ref Unsafe.Add(ref inputRef, 8), ref Unsafe.Add(ref outputRef, 8));
TransformScalar(ref Unsafe.Add(ref inputRef, 16), ref Unsafe.Add(ref outputRef, 16));
TransformScalar(ref Unsafe.Add(ref inputRef, 24), ref Unsafe.Add(ref outputRef, 24));
}
/// <summary>
/// SVT: svt_av1_iidentity32_c
/// </summary>
private static void TransformScalar(ref int input, ref int output)
{
output = input << 2;
Unsafe.Add(ref output, 1) = Unsafe.Add(ref input, 1) << 2;
Unsafe.Add(ref output, 2) = Unsafe.Add(ref input, 2) << 2;
Unsafe.Add(ref output, 3) = Unsafe.Add(ref input, 3) << 2;
Unsafe.Add(ref output, 4) = Unsafe.Add(ref input, 4) << 2;
Unsafe.Add(ref output, 5) = Unsafe.Add(ref input, 5) << 2;
Unsafe.Add(ref output, 6) = Unsafe.Add(ref input, 6) << 2;
Unsafe.Add(ref output, 7) = Unsafe.Add(ref input, 7) << 2;
}
}

34
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity4Inverse1dTransformer.cs

@ -0,0 +1,34 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
internal class Av1Identity4Inverse1dTransformer : IAv1Forward1dTransformer
{
internal const int Sqrt2Bits = 12;
// 2^12 * sqrt(2)
internal const long Sqrt2 = 5793;
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 4, nameof(input));
Guard.MustBeSizedAtLeast(output, 4, nameof(output));
TransformScalar(ref input[0], ref output[0]);
}
/// <summary>
/// SVT: svt_av1_iidentity4_c
/// </summary>
private static void TransformScalar(ref int input, ref int output)
{
// Normal input should fit into 32-bit. Cast to 64-bit here to avoid
// overflow with corrupted/fuzzed input. The same for av1_iidentity/16/64_c.
output = Av1Math.RoundShift(Sqrt2 * input, Sqrt2Bits);
Unsafe.Add(ref output, 1) = Av1Math.RoundShift(Sqrt2 * Unsafe.Add(ref input, 1), Sqrt2Bits);
Unsafe.Add(ref output, 2) = Av1Math.RoundShift(Sqrt2 * Unsafe.Add(ref input, 2), Sqrt2Bits);
Unsafe.Add(ref output, 3) = Av1Math.RoundShift(Sqrt2 * Unsafe.Add(ref input, 3), Sqrt2Bits);
}
}

44
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity64Inverse1dTransformer.cs

@ -0,0 +1,44 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
internal class Av1Identity64Inverse1dTransformer : IAv1Forward1dTransformer
{
private const long Sqrt2Times4 = Av1Identity4Inverse1dTransformer.Sqrt2 >> 2;
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 64, nameof(input));
Guard.MustBeSizedAtLeast(output, 64, nameof(output));
ref int inputRef = ref input[0];
ref int outputRef = ref output[0];
TransformScalar(ref inputRef, ref outputRef);
TransformScalar(ref Unsafe.Add(ref inputRef, 8), ref Unsafe.Add(ref outputRef, 8));
TransformScalar(ref Unsafe.Add(ref inputRef, 16), ref Unsafe.Add(ref outputRef, 16));
TransformScalar(ref Unsafe.Add(ref inputRef, 24), ref Unsafe.Add(ref outputRef, 24));
TransformScalar(ref Unsafe.Add(ref inputRef, 32), ref Unsafe.Add(ref outputRef, 32));
TransformScalar(ref Unsafe.Add(ref inputRef, 40), ref Unsafe.Add(ref outputRef, 40));
TransformScalar(ref Unsafe.Add(ref inputRef, 48), ref Unsafe.Add(ref outputRef, 48));
TransformScalar(ref Unsafe.Add(ref inputRef, 56), ref Unsafe.Add(ref outputRef, 56));
}
/// <summary>
/// SVT: svt_av1_iidentity64_c
/// </summary>
private static void TransformScalar(ref int input, ref int output)
{
// Normal input should fit into 32-bit. Cast to 64-bit here to avoid
// overflow with corrupted/fuzzed input. The same for av1_iidentity/16/64_c.
output = Av1Math.RoundShift(Sqrt2Times4 * input, Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 1) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 1), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 2) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 2), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 3) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 3), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 4) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 4), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 5) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 5), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 6) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 6), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
Unsafe.Add(ref output, 7) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 7), Av1Identity4Inverse1dTransformer.Sqrt2Bits);
}
}

31
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity8Inverse1dTransformer.cs

@ -0,0 +1,31 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
internal class Av1Identity8Inverse1dTransformer : IAv1Forward1dTransformer
{
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 8, nameof(input));
Guard.MustBeSizedAtLeast(output, 8, nameof(output));
TransformScalar(ref input[0], ref output[0]);
}
/// <summary>
/// SVT: svt_av1_iidentity8_c
/// </summary>
private static void TransformScalar(ref int input, ref int output)
{
output = input << 1;
Unsafe.Add(ref output, 1) = Unsafe.Add(ref input, 1) << 1;
Unsafe.Add(ref output, 2) = Unsafe.Add(ref input, 2) << 1;
Unsafe.Add(ref output, 3) = Unsafe.Add(ref input, 3) << 1;
Unsafe.Add(ref output, 4) = Unsafe.Add(ref input, 4) << 1;
Unsafe.Add(ref output, 5) = Unsafe.Add(ref input, 5) << 1;
Unsafe.Add(ref output, 6) = Unsafe.Add(ref input, 6) << 1;
Unsafe.Add(ref output, 7) = Unsafe.Add(ref input, 7) << 1;
}
}

196
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1InverseTransformTests.cs

@ -0,0 +1,196 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
/// <summary>
/// SVT: test/InvTxfm1dTest.cc
/// SVT: test/InvTxfm2dAsmTest.cc
/// </summary>
[Trait("Format", "Avif")]
public class Av1InverseTransformTests
{
[Fact]
public void AccuracyOfDct1dTransformSize4Test()
=> AssertAccuracy1d(Av1TransformType.DctDct, Av1TransformSize.Size4x4, 1);
// [Fact]
public void AccuracyOfDct1dTransformSize8Test()
=> AssertAccuracy1d(Av1TransformType.DctDct, Av1TransformSize.Size8x8, 1, 2);
// [Fact]
public void AccuracyOfDct1dTransformSize16Test()
=> AssertAccuracy1d(Av1TransformType.DctDct, Av1TransformSize.Size16x16, 1, 3);
// [Fact]
public void AccuracyOfDct1dTransformSize32Test()
=> AssertAccuracy1d(Av1TransformType.DctDct, Av1TransformSize.Size32x32, 1, 4);
// [Fact]
public void AccuracyOfDct1dTransformSize64Test()
=> AssertAccuracy1d(Av1TransformType.DctDct, Av1TransformSize.Size64x64, 1, 5);
[Fact]
public void AccuracyOfAdst1dTransformSize4Test()
=> AssertAccuracy1d(Av1TransformType.AdstAdst, Av1TransformSize.Size4x4, 1);
// [Fact]
public void AccuracyOfAdst1dTransformSize8Test()
=> AssertAccuracy1d(Av1TransformType.AdstAdst, Av1TransformSize.Size8x8, 1, 2);
// [Fact]
public void AccuracyOfAdst1dTransformSize16Test()
=> AssertAccuracy1d(Av1TransformType.AdstAdst, Av1TransformSize.Size16x16, 1, 3);
// [Fact]
public void AccuracyOfAdst1dTransformSize32Test()
=> AssertAccuracy1d(Av1TransformType.AdstAdst, Av1TransformSize.Size32x32, 1, 3);
[Fact]
public void AccuracyOfIdentity1dTransformSize4Test()
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size4x4, 1);
[Fact]
public void AccuracyOfIdentity1dTransformSize8Test()
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size8x8, 2);
[Fact]
public void AccuracyOfIdentity1dTransformSize16Test()
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size16x16, 1);
[Fact]
public void AccuracyOfIdentity1dTransformSize32Test()
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size32x32, 4);
[Fact]
public void AccuracyOfIdentity1dTransformSize64Test()
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size64x64, 4);
[Fact]
public void AccuracyOfEchoTransformSize4Test()
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size4x4, 0, new EchoTestTransformer(), new EchoTestTransformer());
private static void AssertAccuracy1d(
Av1TransformType transformType,
Av1TransformSize transformSize,
int scaleLog2,
int allowedError = 1)
{
Av1Transform2dFlipConfiguration config = new(transformType, transformSize);
IAv1Forward1dTransformer forward = GetForwardTransformer(config.TransformFunctionTypeColumn);
IAv1Forward1dTransformer inverse = GetInverseTransformer(config.TransformFunctionTypeColumn);
AssertAccuracy1d(transformType, transformSize, scaleLog2, forward, inverse, allowedError);
}
private static void AssertAccuracy1d(
Av1TransformType transformType,
Av1TransformSize transformSize,
int scaleLog2,
IAv1Forward1dTransformer forwardTransformer,
IAv1Forward1dTransformer inverseTransformer,
int allowedError = 1)
{
const int bitDepth = 10;
Random rnd = new(0);
const int testBlockCount = 100; // Originally set to: 5000
Av1Transform2dFlipConfiguration config = new(transformType, transformSize);
config.GenerateStageRange(bitDepth);
int width = config.TransformSize.GetWidth();
int[] inputOfTest = new int[width];
int[] outputOfTest = new int[width];
int[] outputReference = new int[width];
for (int ti = 0; ti < testBlockCount; ++ti)
{
// prepare random test data
for (int ni = 0; ni < width; ++ni)
{
inputOfTest[ni] = (short)rnd.Next((1 << bitDepth) - 1);
outputReference[ni] = 0;
outputOfTest[ni] = 255;
}
// calculate in forward transform functions
forwardTransformer.Transform(
inputOfTest,
outputReference,
config.CosBitColumn,
config.StageRangeColumn);
// calculate in inverse transform functions
inverseTransformer.Transform(
outputReference,
outputOfTest,
config.CosBitColumn,
config.StageRangeColumn);
// Assert
Assert.True(CompareWithError(inputOfTest, outputOfTest.Select(x => x >> scaleLog2).ToArray(), allowedError), $"Error: {GetMaximumError(inputOfTest, outputOfTest)}");
}
}
private static IAv1Forward1dTransformer GetForwardTransformer(Av1TransformFunctionType func) =>
func switch
{
Av1TransformFunctionType.Dct4 => new Av1Dct4Forward1dTransformer(),
Av1TransformFunctionType.Dct8 => new Av1Dct8Forward1dTransformer(),
Av1TransformFunctionType.Dct16 => new Av1Dct16Forward1dTransformer(),
Av1TransformFunctionType.Dct32 => new Av1Dct32Forward1dTransformer(),
Av1TransformFunctionType.Dct64 => new Av1Dct64Forward1dTransformer(),
Av1TransformFunctionType.Adst4 => new Av1Adst4Forward1dTransformer(),
Av1TransformFunctionType.Adst8 => new Av1Adst8Forward1dTransformer(),
Av1TransformFunctionType.Adst16 => new Av1Adst16Forward1dTransformer(),
Av1TransformFunctionType.Adst32 => new Av1Adst32Forward1dTransformer(),
Av1TransformFunctionType.Identity4 => new Av1Identity4Forward1dTransformer(),
Av1TransformFunctionType.Identity8 => new Av1Identity8Forward1dTransformer(),
Av1TransformFunctionType.Identity16 => new Av1Identity16Forward1dTransformer(),
Av1TransformFunctionType.Identity32 => new Av1Identity32Forward1dTransformer(),
Av1TransformFunctionType.Identity64 => new Av1Identity64Forward1dTransformer(),
Av1TransformFunctionType.Invalid => null,
_ => null,
};
private static IAv1Forward1dTransformer GetInverseTransformer(Av1TransformFunctionType func) =>
func switch
{
Av1TransformFunctionType.Dct4 => new Av1Dct4Inverse1dTransformer(),
Av1TransformFunctionType.Dct8 => null, // new Av1Dct8Inverse1dTransformer(),
Av1TransformFunctionType.Dct16 => null, // new Av1Dct16Inverse1dTransformer(),
Av1TransformFunctionType.Dct32 => null, // new Av1Dct32Inverse1dTransformer(),
Av1TransformFunctionType.Dct64 => null, // new Av1Dct64Inverse1dTransformer(),
Av1TransformFunctionType.Adst4 => new Av1Adst4Inverse1dTransformer(),
Av1TransformFunctionType.Adst8 => null, // new Av1Adst8Inverse1dTransformer(),
Av1TransformFunctionType.Adst16 => null, // new Av1Adst16Inverse1dTransformer(),
Av1TransformFunctionType.Adst32 => null, // new Av1Adst32Inverse1dTransformer(),
Av1TransformFunctionType.Identity4 => new Av1Identity4Inverse1dTransformer(),
Av1TransformFunctionType.Identity8 => new Av1Identity8Inverse1dTransformer(),
Av1TransformFunctionType.Identity16 => new Av1Identity16Inverse1dTransformer(),
Av1TransformFunctionType.Identity32 => new Av1Identity32Inverse1dTransformer(),
Av1TransformFunctionType.Identity64 => new Av1Identity64Inverse1dTransformer(),
Av1TransformFunctionType.Invalid => null,
_ => null,
};
private static bool CompareWithError(Span<int> expected, Span<int> actual, int allowedError)
{
// compare for the result is within accuracy
int maximumErrorInTest = GetMaximumError(expected, actual);
return maximumErrorInTest <= allowedError;
}
private static int GetMaximumError(Span<int> expected, Span<int> actual)
{
int maximumErrorInTest = 0;
int count = Math.Min(expected.Length, 32);
for (int ni = 0; ni < count; ++ni)
{
maximumErrorInTest = Math.Max(maximumErrorInTest, Math.Abs(actual[ni] - expected[ni]));
}
return maximumErrorInTest;
}
}
Loading…
Cancel
Save