📷 A modern, cross-platform, 2D Graphics library for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

196 lines
8.5 KiB

// 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;
}
}