Browse Source

Tests for inverse transform

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
2fa5ca11a1
  1. 48
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs
  2. 25
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformMath.cs
  3. 6
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformerFactory.cs
  4. 305
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1InverseTransformTests.cs

48
src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs

@ -1,7 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices; using System.ComponentModel;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
@ -12,11 +12,11 @@ internal class Av1Inverse2dTransformer
/// <summary> /// <summary>
/// SVT: inv_txfm2d_add_c /// SVT: inv_txfm2d_add_c
/// </summary> /// </summary>
internal static void InverseTransform2dAdd( internal static void Transform2dAdd(
Span<int> input, Span<int> input,
Span<ushort> outputForRead, Span<short> outputForRead,
int strideForRead, int strideForRead,
Span<ushort> outputForWrite, Span<short> outputForWrite,
int strideForWrite, int strideForWrite,
Av1Transform2dFlipConfiguration config, Av1Transform2dFlipConfiguration config,
Span<int> transformFunctionBuffer, Span<int> transformFunctionBuffer,
@ -78,8 +78,8 @@ internal class Av1Inverse2dTransformer
} }
Av1InverseTransformMath.RoundShiftArray(bufPtr, transformWidth, -shift[0]); Av1InverseTransformMath.RoundShiftArray(bufPtr, transformWidth, -shift[0]);
input.Slice(transformWidth); input = input[transformWidth..];
bufPtr.Slice(transformWidth); bufPtr = bufPtr.Slice(transformWidth);
} }
// Columns // Columns
@ -87,17 +87,21 @@ internal class Av1Inverse2dTransformer
{ {
if (!config.FlipLeftToRight) if (!config.FlipLeftToRight)
{ {
int t = c;
for (r = 0; r < transformHeight; ++r) for (r = 0; r < transformHeight; ++r)
{ {
tempIn[r] = buf[(r * transformWidth) + c]; tempIn[r] = buf[t];
t += transformWidth;
} }
} }
else else
{ {
// flip left right // flip left right
int t = transformWidth - c - 1;
for (r = 0; r < transformHeight; ++r) for (r = 0; r < transformHeight; ++r)
{ {
tempIn[r] = buf[(r * transformWidth) + (transformWidth - c - 1)]; tempIn[r] = buf[t];
t += transformWidth;
} }
} }
@ -106,19 +110,29 @@ internal class Av1Inverse2dTransformer
Av1InverseTransformMath.RoundShiftArray(tempOut, transformHeight, -shift[1]); Av1InverseTransformMath.RoundShiftArray(tempOut, transformHeight, -shift[1]);
if (!config.FlipUpsideDown) if (!config.FlipUpsideDown)
{ {
int indexForWrite = c;
int indexForRead = c;
for (r = 0; r < transformHeight; ++r) for (r = 0; r < transformHeight; ++r)
{ {
outputForWrite[(r * strideForWrite) + c] = outputForWrite[indexForWrite] =
Av1InverseTransformMath.ClipPixelAdd(outputForRead[(r * strideForRead) + c], tempOut[r], bitDepth); Av1InverseTransformMath.ClipPixelAdd(outputForRead[indexForRead], tempOut[r], bitDepth);
indexForWrite += strideForWrite;
indexForRead += strideForRead;
} }
} }
else else
{ {
// flip upside down // flip upside down
int indexForWrite = c;
int indexForRead = c;
int indexTemp = transformHeight - 1;
for (r = 0; r < transformHeight; ++r) for (r = 0; r < transformHeight; ++r)
{ {
outputForWrite[(r * strideForWrite) + c] = Av1InverseTransformMath.ClipPixelAdd( outputForWrite[indexForWrite] = Av1InverseTransformMath.ClipPixelAdd(
outputForRead[(r * strideForRead) + c], tempOut[transformHeight - r - 1], bitDepth); outputForRead[indexForRead], tempOut[indexTemp], bitDepth);
indexForWrite += strideForWrite;
indexForRead += strideForRead;
indexTemp--;
} }
} }
} }
@ -127,7 +141,7 @@ internal class Av1Inverse2dTransformer
/// <summary> /// <summary>
/// SVT: inv_txfm2d_add_c /// SVT: inv_txfm2d_add_c
/// </summary> /// </summary>
internal static void InverseTransform2dAdd( internal static void Transform2dAdd(
Span<int> input, Span<int> input,
Span<byte> outputForRead, Span<byte> outputForRead,
int strideForRead, int strideForRead,
@ -240,6 +254,7 @@ internal class Av1Inverse2dTransformer
} }
} }
/*
/// <summary> /// <summary>
/// SVT: highbd_iwht4x4_add /// SVT: highbd_iwht4x4_add
/// </summary> /// </summary>
@ -260,8 +275,8 @@ internal class Av1Inverse2dTransformer
/// </summary> /// </summary>
private static void InverseWhalshHadamard4x4Add16(ref int input, ref byte destinationForRead, int strideForRead, ref byte destinationForWrite, int strideForWrite, int bitDepth) private static void InverseWhalshHadamard4x4Add16(ref int input, ref byte destinationForRead, int strideForRead, ref byte destinationForWrite, int strideForWrite, int bitDepth)
{ {
/* 4-point reversible, orthonormal inverse Walsh-Hadamard in 3.5 adds, // 4-point reversible, orthonormal inverse Walsh-Hadamard in 3.5 adds,
0.5 shifts per pixel. */ // 0.5 shifts per pixel.
int i; int i;
Span<ushort> output = stackalloc ushort[16]; Span<ushort> output = stackalloc ushort[16];
ushort a1, b1, c1, d1, e1; ushort a1, b1, c1, d1, e1;
@ -311,7 +326,7 @@ internal class Av1Inverse2dTransformer
range_check_value(b1, (int8_t)(bd + 1)); range_check_value(b1, (int8_t)(bd + 1));
range_check_value(c1, (int8_t)(bd + 1)); range_check_value(c1, (int8_t)(bd + 1));
range_check_value(d1, (int8_t)(bd + 1)); range_check_value(d1, (int8_t)(bd + 1));
*/ //
destForWrite = Av1InverseTransformMath.ClipPixelAdd(destForRead, a1, bitDepth); destForWrite = Av1InverseTransformMath.ClipPixelAdd(destForRead, a1, bitDepth);
Unsafe.Add(ref destForWrite, strideForWrite) = Av1InverseTransformMath.ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead), b1, bitDepth); Unsafe.Add(ref destForWrite, strideForWrite) = Av1InverseTransformMath.ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead), b1, bitDepth);
@ -360,4 +375,5 @@ internal class Av1Inverse2dTransformer
destForWrite = ref Unsafe.Add(ref destForWrite, 1); destForWrite = ref Unsafe.Add(ref destForWrite, 1);
} }
} }
*/
} }

25
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformMath.cs

@ -157,23 +157,23 @@ internal static class Av1InverseTransformMath
return (byte)ClipPixelHighBitDepth(dest + trans, 8); return (byte)ClipPixelHighBitDepth(dest + trans, 8);
} }
public static ushort ClipPixelAdd(ushort dest, long trans, int bitDepth) public static short ClipPixelAdd(short dest, long trans, int bitDepth)
{ {
trans = CheckRange(trans, bitDepth); trans = CheckRange(trans, bitDepth);
return ClipPixelHighBitDepth(dest + trans, bitDepth); return ClipPixelHighBitDepth(dest + trans, bitDepth);
} }
private static ushort ClipPixelHighBitDepth(long val, int bd) private static short ClipPixelHighBitDepth(long val, int bd)
{ {
switch (bd) switch (bd)
{ {
case 8: case 8:
default: default:
return (ushort)Av1Math.Clamp(val, 0, 255); return (short)Av1Math.Clamp(val, 0, 255);
case 10: case 10:
return (ushort)Av1Math.Clamp(val, 0, 1023); return (short)Av1Math.Clamp(val, 0, 1023);
case 12: case 12:
return (ushort)Av1Math.Clamp(val, 0, 4095); return (short)Av1Math.Clamp(val, 0, 4095);
} }
} }
@ -234,4 +234,19 @@ internal static class Av1InverseTransformMath
int int_min = -int_max - 1; int int_min = -int_max - 1;
return Av1Math.Clamp(input, int_min, int_max); return Av1Math.Clamp(input, int_min, int_max);
} }
internal static int GetMaxEndOfBuffer(Av1TransformSize transformSize)
{
if (transformSize is Av1TransformSize.Size64x64 or Av1TransformSize.Size64x32 or Av1TransformSize.Size32x64)
{
return 1024;
}
if (transformSize is Av1TransformSize.Size16x64 or Av1TransformSize.Size64x16)
{
return 512;
}
return transformSize.GetSize2d();
}
} }

6
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformerFactory.cs

@ -15,17 +15,17 @@ internal static class Av1InverseTransformerFactory
int height = transformFunctionParameters.TransformSize.GetHeight(); int height = transformFunctionParameters.TransformSize.GetHeight();
Span<int> buffer = new int[(width * height) + (2 * Math.Max(width, height))]; Span<int> buffer = new int[(width * height) + (2 * Math.Max(width, height))];
Av1Transform2dFlipConfiguration config = new(transformFunctionParameters.TransformType, transformFunctionParameters.TransformSize); Av1Transform2dFlipConfiguration config = new(transformFunctionParameters.TransformType, transformFunctionParameters.TransformSize);
Av1Inverse2dTransformer.InverseTransform2dAdd(coefficients, readBuffer, readStride, writeBuffer, writeStride, config, buffer); Av1Inverse2dTransformer.Transform2dAdd(coefficients, readBuffer, readStride, writeBuffer, writeStride, config, buffer);
} }
public static unsafe void InverseTransformAdd(Span<int> coefficients, Span<ushort> readBuffer, int readStride, Span<ushort> writeBuffer, int writeStride, Av1TransformFunctionParameters transformFunctionParameters) public static unsafe void InverseTransformAdd(Span<int> coefficients, Span<short> readBuffer, int readStride, Span<short> writeBuffer, int writeStride, Av1TransformFunctionParameters transformFunctionParameters)
{ {
Guard.IsTrue(transformFunctionParameters.Is16BitPipeline, nameof(transformFunctionParameters), "Calling 16-bit pipeline while 8-bit is requested."); Guard.IsTrue(transformFunctionParameters.Is16BitPipeline, nameof(transformFunctionParameters), "Calling 16-bit pipeline while 8-bit is requested.");
int width = transformFunctionParameters.TransformSize.GetWidth(); int width = transformFunctionParameters.TransformSize.GetWidth();
int height = transformFunctionParameters.TransformSize.GetHeight(); int height = transformFunctionParameters.TransformSize.GetHeight();
Span<int> buffer = new int[(width * height) + (2 * Math.Max(width, height))]; Span<int> buffer = new int[(width * height) + (2 * Math.Max(width, height))];
Av1Transform2dFlipConfiguration config = new(transformFunctionParameters.TransformType, transformFunctionParameters.TransformSize); Av1Transform2dFlipConfiguration config = new(transformFunctionParameters.TransformType, transformFunctionParameters.TransformSize);
Av1Inverse2dTransformer.InverseTransform2dAdd(coefficients, readBuffer, readStride, writeBuffer, writeStride, config, buffer, transformFunctionParameters.BitDepth); Av1Inverse2dTransformer.Transform2dAdd(coefficients, readBuffer, readStride, writeBuffer, writeStride, config, buffer, transformFunctionParameters.BitDepth);
} }
internal static IAv1Forward1dTransformer? GetTransformer(Av1TransformFunctionType type) => type switch internal static IAv1Forward1dTransformer? GetTransformer(Av1TransformFunctionType type) => type switch

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

@ -1,6 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
@ -74,6 +75,151 @@ public class Av1InverseTransformTests
public void AccuracyOfEchoTransformSize4Test() public void AccuracyOfEchoTransformSize4Test()
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size4x4, 0, new EchoTestTransformer(), new EchoTestTransformer()); => AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size4x4, 0, new EchoTestTransformer(), new EchoTestTransformer());
[Fact]
public void FlipNothingTest()
{
// Arrange
int[] input = [
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16];
short[] expected = [
2, 4, 6, 8,
10, 12, 14, 16,
18, 20, 22, 24,
26, 28, 30, 32];
int[] temp = new int[16 + 8];
short[] actual = new short[16];
Av1Transform2dFlipConfiguration config = new(Av1TransformType.Identity, Av1TransformSize.Size4x4);
config.GenerateStageRange(8);
config.SetFlip(false, false);
config.SetShift(0, 0, 0);
IAv1Forward1dTransformer transformer = new EchoTestTransformer();
// Act
Av1Inverse2dTransformer.Transform2dAdd(
input,
actual,
4,
actual,
4,
config,
temp,
8);
// Assert
Assert.True(CompareWithError<short>(expected, actual, 1));
}
[Fact]
public void FlipHorizontalTest()
{
// Arrange
short[] expected = [
8, 6, 4, 2,
16, 14, 12, 10,
24, 22, 20, 18,
32, 30, 28, 26];
int[] input = [
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16];
int[] temp = new int[16 + 8];
short[] actual = new short[16];
Av1Transform2dFlipConfiguration config = new(Av1TransformType.Identity, Av1TransformSize.Size4x4);
config.SetFlip(false, true);
config.SetShift(0, 0, 0);
IAv1Forward1dTransformer transformer = new EchoTestTransformer();
// Act
Av1Inverse2dTransformer.Transform2dAdd(
input,
actual,
4,
actual,
4,
config,
temp,
8);
// Assert
Assert.True(CompareWithError<short>(expected, actual, 1));
}
[Fact]
public void FlipVerticalTest()
{
// Arrange
short[] expected = [
26, 28, 30, 32,
18, 20, 22, 24,
10, 12, 14, 16,
2, 4, 6, 8];
int[] input = [
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16];
int[] temp = new int[16 + 8];
short[] actual = new short[16];
Av1Transform2dFlipConfiguration config = new(Av1TransformType.Identity, Av1TransformSize.Size4x4);
config.SetFlip(true, false);
config.SetShift(0, 0, 0);
IAv1Forward1dTransformer transformer = new EchoTestTransformer();
// Act
Av1Inverse2dTransformer.Transform2dAdd(
input,
actual,
4,
actual,
4,
config,
temp,
8);
// Assert
Assert.True(CompareWithError<short>(expected, actual, 1));
}
[Fact]
public void FlipHorizontalAndVerticalTest()
{
// Arrange
short[] expected = [
32, 30, 28, 26,
24, 22, 20, 18,
16, 14, 12, 10,
8, 6, 4, 2];
int[] input = [
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16];
int[] temp = new int[16 + 8];
short[] actual = new short[16];
Av1Transform2dFlipConfiguration config = new(Av1TransformType.Identity, Av1TransformSize.Size4x4);
config.SetFlip(true, true);
config.SetShift(0, 0, 0);
IAv1Forward1dTransformer transformer = new EchoTestTransformer();
// Act
Av1Inverse2dTransformer.Transform2dAdd(
input,
actual,
4,
actual,
4,
config,
temp,
8);
// Assert
Assert.True(CompareWithError<short>(expected, actual, 1));
}
private static void AssertAccuracy1d( private static void AssertAccuracy1d(
Av1TransformType transformType, Av1TransformType transformType,
Av1TransformSize transformSize, Av1TransformSize transformSize,
@ -129,10 +275,160 @@ public class Av1InverseTransformTests
config.StageRangeColumn); config.StageRangeColumn);
// Assert // Assert
Assert.True(CompareWithError(inputOfTest, outputOfTest.Select(x => x >> scaleLog2).ToArray(), allowedError), $"Error: {GetMaximumError(inputOfTest, outputOfTest)}"); Assert.True(CompareWithError<int>(inputOfTest, outputOfTest.Select(x => x >> scaleLog2).ToArray(), allowedError), $"Error: {GetMaximumError<int>(inputOfTest, outputOfTest)}");
}
}
// [Theory]
// [MemberData(nameof(Generate2dCombinations))]
public void Test2dTransformAdd(int txSize, int txType, bool isLossless)
{
const int bitDepth = 8;
Av1TransformType transformType = (Av1TransformType)txType;
Av1TransformSize transformSize = (Av1TransformSize)txSize;
Av1TransformFunctionParameters transformFunctionParams = new()
{
BitDepth = bitDepth,
IsLossless = isLossless,
TransformSize = transformSize,
EndOfBuffer = Av1InverseTransformMath.GetMaxEndOfBuffer(transformSize)
};
if (bitDepth > 8 && !isLossless)
{
// Not support 10 bit with not lossless
return;
}
int width = transformSize.GetWidth();
int height = transformSize.GetHeight();
uint stride = (uint)width;
short[] input = new short[width * height];
int[] referenceOutput = new int[width * height];
short[] outputOfTest = new short[width * height];
int[] transformActual = new int[width * height];
int[] tempBuffer = new int[(width * height) + 128];
transformFunctionParams.TransformType = transformType;
Av1Transform2dFlipConfiguration config = new(transformType, transformSize);
config.GenerateStageRange(bitDepth);
const int loops = 1; // Initially: 10;
for (int k = 0; k < loops; k++)
{
PopulateWithRandomValues(input, bitDepth);
Av1ForwardTransformer.Transform2d(
input,
referenceOutput,
stride,
transformType,
transformSize,
bitDepth);
Av1Inverse2dTransformer.Transform2dAdd(
referenceOutput.Select(x => x >> 3).ToArray(),
outputOfTest,
width,
outputOfTest,
width,
config,
tempBuffer,
bitDepth);
Av1ForwardTransformer.Transform2d(
outputOfTest.Select(x => (short)(x >> 1)).ToArray(),
transformActual,
stride,
transformType,
transformSize,
bitDepth);
Assert.True(CompareWithError<int>(referenceOutput, transformActual, 1), $"Error: {GetMaximumError<int>(referenceOutput, transformActual)}");
}
}
private static void DivideArray(Span<int> list, int factor)
{
for (int i = 0; i < list.Length; i++)
{
list[i] = list[i] / factor;
}
}
private static void DivideArray(Span<short> list, int factor)
{
for (int i = 0; i < list.Length; i++)
{
list[i] = (short)(list[i] / factor);
} }
} }
public static TheoryData<int, int, bool> Generate2dCombinations()
{
int[][] transformFunctionSupportMatrix = [
// [Size][type]" // O - No; 1 - lossless; 2 - !lossless; 3 - any
/*0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15*/
[3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], // 0 TX_4X4,
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], // 1 TX_8X8,
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], // 2 TX_16X16,
[3, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0], // 3 TX_32X32,
[3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // 4 TX_64X64,
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], // 5 TX_4X8,
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], // 6 TX_8X4,
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], // 7 TX_8X16,
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], // 8 TX_16X8,
[3, 1, 3, 1, 1, 3, 1, 1, 1, 3, 3, 3, 1, 3, 1, 3], // 9 TX_16X32,
[3, 3, 1, 1, 3, 1, 1, 1, 1, 3, 3, 3, 3, 1, 3, 1], // 10 TX_32X16,
[3, 0, 1, 0, 0, 1, 0, 0, 0, 3, 3, 3, 0, 1, 0, 1], // 11 TX_32X64,
[3, 1, 0, 0, 1, 0, 0, 0, 0, 3, 3, 3, 1, 0, 1, 0], // 12 TX_64X32,
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], // 13 TX_4X16,
[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3], // 14 TX_16X4,
[3, 1, 3, 1, 1, 3, 1, 1, 1, 3, 3, 3, 1, 3, 1, 3], // 15 TX_8X32,
[3, 3, 1, 1, 3, 1, 1, 1, 1, 3, 3, 3, 3, 1, 3, 1], // 16 TX_32X8,
[3, 0, 3, 0, 0, 3, 0, 0, 0, 3, 3, 3, 0, 3, 0, 3], // 17 TX_16X64,
[3, 3, 0, 0, 3, 0, 0, 0, 0, 3, 3, 3, 3, 0, 3, 0], // 18 TX_64X16,
/*0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15*/
];
TheoryData<int, int, bool> data = [];
for (int size = 0; size < (int)Av1TransformSize.AllSizes; size++)
{
for (int type = 0; type < (int)Av1TransformType.AllTransformTypes; type++)
{
for (int i = 0; i < 2; i++)
{
bool isLossless = i == 1;
if ((isLossless && ((transformFunctionSupportMatrix[size][type] & 1) == 0)) ||
(!isLossless && ((transformFunctionSupportMatrix[size][type] & 2) == 0)))
{
continue;
}
if (IsTransformTypeImplemented((Av1TransformType)type, (Av1TransformSize)size))
{
data.Add(size, type, isLossless);
}
}
}
}
return data;
}
private static void PopulateWithRandomValues(Span<short> input, int bitDepth)
{
Random rnd = new(42);
int maxValue = (1 << (bitDepth - 1)) - 1;
int minValue = -maxValue;
for (int i = 0; i < input.Length; i++)
{
input[i] = (short)rnd.Next(minValue, maxValue);
}
}
private static bool IsTransformTypeImplemented(Av1TransformType transformType, Av1TransformSize transformSize)
=> transformSize == Av1TransformSize.Size4x4;
private static IAv1Forward1dTransformer GetForwardTransformer(Av1TransformFunctionType func) => private static IAv1Forward1dTransformer GetForwardTransformer(Av1TransformFunctionType func) =>
func switch func switch
{ {
@ -175,20 +471,21 @@ public class Av1InverseTransformTests
_ => null, _ => null,
}; };
private static bool CompareWithError(Span<int> expected, Span<int> actual, int allowedError) private static bool CompareWithError<T>(Span<T> expected, Span<T> actual, int allowedError)
where T : unmanaged
{ {
// compare for the result is within accuracy // compare for the result is within accuracy
int maximumErrorInTest = GetMaximumError(expected, actual); int maximumErrorInTest = GetMaximumError(expected, actual);
return maximumErrorInTest <= allowedError; return maximumErrorInTest <= allowedError;
} }
private static int GetMaximumError(Span<int> expected, Span<int> actual) private static int GetMaximumError<T>(Span<T> expected, Span<T> actual)
{ {
int maximumErrorInTest = 0; int maximumErrorInTest = 0;
int count = Math.Min(expected.Length, 32); int count = Math.Min(expected.Length, 32);
for (int ni = 0; ni < count; ++ni) for (int ni = 0; ni < count; ++ni)
{ {
maximumErrorInTest = Math.Max(maximumErrorInTest, Math.Abs(actual[ni] - expected[ni])); maximumErrorInTest = Math.Max(maximumErrorInTest, Math.Abs(Convert.ToInt32(actual[ni]) - Convert.ToInt32(expected[ni])));
} }
return maximumErrorInTest; return maximumErrorInTest;

Loading…
Cancel
Save