Browse Source

Unit tests for high level symbols

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
1d1c382206
  1. 11
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1DefaultDistributions.cs
  2. 71
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
  3. 22
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
  4. 58
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
  5. 8
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs
  6. 278
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EntropyTests.cs
  7. 4
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1SymbolContextTests.cs

11
src/ImageSharp/Formats/Heif/Av1/Entropy/Av1DefaultDistributions.cs

@ -135,7 +135,16 @@ internal static class Av1DefaultDistributions
public static Av1Distribution DeltaQuantizerAbsolute => new(28160, 32120, 32677);
public static Av1Distribution[] SegmentId => [new(128 * 128), new(128 * 128), new(128 * 128)];
/// <summary>
/// Gets the Segment identifier <see cref="Av1Distribution"/>.
/// </summary>
/// <remarks>SVT: default_spatial_pred_seg_tree_cdf</remarks>
public static Av1Distribution[] SegmentId =>
[
new(5622, 7893, 16093, 18233, 27809, 28373, 32533),
new(14274, 18230, 22557, 24935, 29980, 30851, 32344),
new(27527, 28487, 28723, 28890, 32397, 32647, 32679),
];
public static Av1Distribution[][] KeyFrameYMode =>
[

71
src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs

@ -53,7 +53,7 @@ internal ref struct Av1SymbolDecoder
this.endOfBlockExtra = Av1DefaultDistributions.GetEndOfBlockExtra(qIndex);
}
public int ReadLiteral(int bitCount)
public int ReadCdfStrength(int bitCount)
{
ref Av1SymbolReader r = ref this.reader;
return r.ReadLiteral(bitCount);
@ -130,22 +130,53 @@ internal ref struct Av1SymbolDecoder
return r.ReadSymbol(this.skipMode[(int)blockSize]) > 0;
}
public int ReadDeltaLoopFilterAbsolute()
public int ReadDeltaLoopFilter()
{
ref Av1SymbolReader r = ref this.reader;
return r.ReadSymbol(this.deltaLoopFilterAbsolute);
int deltaLoopFilterAbsolute = r.ReadSymbol(this.deltaLoopFilterAbsolute);
if (deltaLoopFilterAbsolute == Av1Constants.DeltaLoopFilterSmall)
{
int deltaLoopFilterRemainingBits = r.ReadLiteral(3) + 1;
int deltaLoopFilterAbsoluteBitCount = r.ReadLiteral(deltaLoopFilterRemainingBits);
deltaLoopFilterAbsolute = deltaLoopFilterAbsoluteBitCount + (1 << deltaLoopFilterRemainingBits) + 1;
}
bool deltaLoopFilterSign = true;
if (deltaLoopFilterAbsolute != 0)
{
deltaLoopFilterSign = r.ReadLiteral(1) > 0;
}
return deltaLoopFilterSign ? -deltaLoopFilterAbsolute : deltaLoopFilterAbsolute;
}
public int ReadDeltaQuantizerAbsolute()
/// <summary>
/// SVT: read_delta_qindex
/// </summary>
public int ReadDeltaQuantizerIndex()
{
ref Av1SymbolReader r = ref this.reader;
return r.ReadSymbol(this.deltaQuantizerAbsolute);
int deltaQuantizerAbsolute = r.ReadSymbol(this.deltaQuantizerAbsolute);
if (deltaQuantizerAbsolute == Av1Constants.DeltaQuantizerSmall)
{
int deltaQuantizerRemainingBits = r.ReadLiteral(3) + 1;
int deltaQuantizerAbsoluteBase = r.ReadLiteral(deltaQuantizerRemainingBits);
deltaQuantizerAbsolute = deltaQuantizerAbsoluteBase + (1 << deltaQuantizerRemainingBits) + 1;
}
bool deltaQuantizerSignBit = true;
if (deltaQuantizerAbsolute != 0)
{
deltaQuantizerSignBit = r.ReadLiteral(1) > 0;
}
return deltaQuantizerSignBit ? -deltaQuantizerAbsolute : deltaQuantizerAbsolute;
}
public int ReadSegmentId(int ctx)
public int ReadSegmentId(int context)
{
ref Av1SymbolReader r = ref this.reader;
return r.ReadSymbol(this.segmentId[ctx]);
return r.ReadSymbol(this.segmentId[context]);
}
public int ReadAngleDelta(Av1PredictionMode mode)
@ -154,16 +185,17 @@ internal ref struct Av1SymbolDecoder
return r.ReadSymbol(this.angleDelta[(int)mode - 1]);
}
public bool ReadUseFilterUltra(Av1BlockSize blockSize)
public Av1FilterIntraMode ReadFilterUltraMode(Av1BlockSize blockSize)
{
ref Av1SymbolReader r = ref this.reader;
return r.ReadSymbol(this.filterIntra[(int)blockSize]) > 0;
}
Av1FilterIntraMode filterIntraMode = Av1FilterIntraMode.AllFilterIntraModes;
bool useFilterIntra = r.ReadSymbol(this.filterIntra[(int)blockSize]) > 0;
if (useFilterIntra)
{
filterIntraMode = (Av1FilterIntraMode)r.ReadSymbol(this.filterIntraMode);
}
public Av1FilterIntraMode ReadFilterUltraMode()
{
ref Av1SymbolReader r = ref this.reader;
return (Av1FilterIntraMode)r.ReadSymbol(this.filterIntraMode);
return filterIntraMode;
}
public Av1TransformSize ReadTransformSize(Av1BlockSize blockSize, int context)
@ -348,6 +380,7 @@ internal ref struct Av1SymbolDecoder
public int ReadEndOfBlockPosition(Av1TransformSize transformSize, Av1TransformClass transformClass, Av1TransformSize transformSizeContext, Av1PlaneType planeType)
{
ref Av1SymbolReader r = ref this.reader;
int endOfBlockExtra = 0;
int endOfBlockPoint = this.ReadEndOfBlockFlag(planeType, transformClass, transformSize);
int endOfBlockShift = Av1SymbolContextHelper.EndOfBlockOffsetBits[endOfBlockPoint];
@ -362,7 +395,7 @@ internal ref struct Av1SymbolDecoder
for (int j = 1; j < endOfBlockShift; j++)
{
if (this.ReadLiteral(1) != 0)
if (r.ReadLiteral(1) != 0)
{
Av1Math.SetBit(ref endOfBlockExtra, endOfBlockShift - 1 - j);
}
@ -451,6 +484,7 @@ internal ref struct Av1SymbolDecoder
public int ReadCoefficientsSign(Span<int> coefficientBuffer, int endOfBlock, ReadOnlySpan<short> scan, Av1LevelBuffer levels, int dcSignContext, Av1PlaneType planeType)
{
ref Av1SymbolReader r = ref this.reader;
int maxScanLine = 0;
int culLevel = 0;
int dcValue = 0;
@ -469,7 +503,7 @@ internal ref struct Av1SymbolDecoder
}
else
{
sign = this.ReadLiteral(1);
sign = r.ReadLiteral(1);
}
if (level >= Av1Constants.CoefficientBaseRange + Av1Constants.BaseLevelsCount + 1)
@ -535,13 +569,14 @@ internal ref struct Av1SymbolDecoder
internal int ReadGolomb()
{
ref Av1SymbolReader r = ref this.reader;
int x = 1;
int length = 0;
int i = 0;
while (i == 0)
{
i = this.ReadLiteral(1);
i = r.ReadLiteral(1);
++length;
if (length > 20)
{
@ -553,7 +588,7 @@ internal ref struct Av1SymbolDecoder
for (i = 0; i < length - 1; ++i)
{
x <<= 1;
x += this.ReadLiteral(1);
x += r.ReadLiteral(1);
}
return x - 1;

22
src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs

@ -330,19 +330,21 @@ internal class Av1SymbolEncoder : IDisposable
w.WriteSymbol(skip, this.skipMode[context]);
}
internal void WriteFilterIntra(Av1FilterIntraMode filterIntraMode, Av1BlockSize blockSize)
internal void WriteFilterIntraMode(Av1FilterIntraMode filterIntraMode, Av1BlockSize blockSize)
{
ref Av1SymbolWriter w = ref this.writer;
w.WriteSymbol(filterIntraMode != Av1FilterIntraMode.AllFilterIntraModes, this.filterIntra[(int)blockSize]);
}
internal void WriteFilterIntraMode(Av1FilterIntraMode filterIntraMode)
{
ref Av1SymbolWriter w = ref this.writer;
w.WriteSymbol((int)filterIntraMode, this.filterIntraMode);
bool useFilter = filterIntraMode != Av1FilterIntraMode.AllFilterIntraModes;
w.WriteSymbol(useFilter, this.filterIntra[(int)blockSize]);
if (useFilter)
{
w.WriteSymbol((int)filterIntraMode, this.filterIntraMode);
}
}
internal void WriteDeltaQIndex(int deltaQindex)
/// <summary>
/// SVT: av1_write_delta_q_index
/// </summary>
internal void WriteDeltaQuantizerIndex(int deltaQindex)
{
ref Av1SymbolWriter w = ref this.writer;
bool sign = deltaQindex < 0;
@ -353,7 +355,7 @@ internal class Av1SymbolEncoder : IDisposable
if (!smallval)
{
int rem_bits = Av1Math.MostSignificantBit((uint)(abs - 1)) - 1;
int rem_bits = Av1Math.MostSignificantBit((uint)(abs - 1));
int threshold = (1 << rem_bits) + 1;
w.WriteLiteral((uint)(rem_bits - 1), 3);
w.WriteLiteral((uint)(abs - threshold), rem_bits);

58
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs

@ -1051,21 +1051,19 @@ internal class Av1TileReader : IAv1TileReader
private void FilterIntraModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = false;
if (this.SequenceHeader.EnableFilterIntra &&
partitionInfo.ModeInfo.YMode == Av1PredictionMode.DC &&
partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) == 0 &&
Math.Max(partitionInfo.ModeInfo.BlockSize.GetWidth(), partitionInfo.ModeInfo.BlockSize.GetHeight()) <= 32)
{
partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = reader.ReadUseFilterUltra(partitionInfo.ModeInfo.BlockSize);
if (partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra)
Av1FilterIntraMode filterIntraMode = reader.ReadFilterUltraMode(partitionInfo.ModeInfo.BlockSize);
if (filterIntraMode != Av1FilterIntraMode.AllFilterIntraModes)
{
partitionInfo.ModeInfo.FilterIntraModeInfo.Mode = reader.ReadFilterUltraMode();
partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = true;
partitionInfo.ModeInfo.FilterIntraModeInfo.Mode = filterIntraMode;
}
}
else
{
partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = false;
}
}
/// <summary>
@ -1209,7 +1207,8 @@ internal class Av1TileReader : IAv1TileReader
int c = partitionInfo.ColumnIndex & cdefMask4;
if (partitionInfo.CdefStrength[r][c] == -1)
{
partitionInfo.CdefStrength[r][c] = reader.ReadLiteral(this.FrameHeader.CdefParameters.BitCount);
int cdfStrength = reader.ReadCdfStrength(this.FrameHeader.CdefParameters.BitCount);
partitionInfo.CdefStrength[r][c] = cdfStrength;
if (this.SequenceHeader.SuperblockSize == Av1BlockSize.Block128x128)
{
int w4 = partitionInfo.ModeInfo.BlockSize.Get4x4WideCount();
@ -1218,7 +1217,7 @@ internal class Av1TileReader : IAv1TileReader
{
for (int j = c; j < c + w4; j += cdefSize4)
{
partitionInfo.CdefStrength[i & cdefMask4][j & cdefMask4] = partitionInfo.CdefStrength[r][c];
partitionInfo.CdefStrength[i & cdefMask4][j & cdefMask4] = cdfStrength;
}
}
}
@ -1245,21 +1244,9 @@ internal class Av1TileReader : IAv1TileReader
Span<int> currentDeltaLoopFilter = partitionInfo.SuperblockInfo.SuperblockDeltaLoopFilter;
for (int i = 0; i < frameLoopFilterCount; i++)
{
int deltaLoopFilterAbsolute = reader.ReadDeltaLoopFilterAbsolute();
if (deltaLoopFilterAbsolute == Av1Constants.DeltaLoopFilterSmall)
{
int deltaLoopFilterRemainingBits = reader.ReadLiteral(3) + 1;
int deltaLoopFilterAbsoluteBitCount = reader.ReadLiteral(deltaLoopFilterRemainingBits);
deltaLoopFilterAbsolute = deltaLoopFilterAbsoluteBitCount + (1 << deltaLoopFilterRemainingBits) + 1;
}
if (deltaLoopFilterAbsolute != 0)
{
bool deltaLoopFilterSign = reader.ReadLiteral(1) > 0;
int reducedDeltaLoopFilterLevel = deltaLoopFilterSign ? -deltaLoopFilterAbsolute : deltaLoopFilterAbsolute;
int deltaLoopFilterResolution = this.FrameHeader.DeltaLoopFilterParameters.Resolution;
currentDeltaLoopFilter[i] = Av1Math.Clip3(-Av1Constants.MaxLoopFilter, Av1Constants.MaxLoopFilter, currentDeltaLoopFilter[i] + (reducedDeltaLoopFilterLevel << deltaLoopFilterResolution));
}
int reducedDeltaLoopFilterLevel = reader.ReadDeltaLoopFilter();
int deltaLoopFilterResolution = this.FrameHeader.DeltaLoopFilterParameters.Resolution;
currentDeltaLoopFilter[i] = Av1Math.Clip3(-Av1Constants.MaxLoopFilter, Av1Constants.MaxLoopFilter, currentDeltaLoopFilter[i] + (reducedDeltaLoopFilterLevel << deltaLoopFilterResolution));
}
}
}
@ -1280,6 +1267,9 @@ internal class Av1TileReader : IAv1TileReader
}
}
/// <summary>
/// SVT: read_delta_qindex
/// </summary>
private void ReadDeltaQuantizerIndex(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128Superblock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64;
@ -1291,22 +1281,10 @@ internal class Av1TileReader : IAv1TileReader
if (partitionInfo.ModeInfo.BlockSize != this.SequenceHeader.SuperblockSize || !partitionInfo.ModeInfo.Skip)
{
int deltaQuantizerAbsolute = reader.ReadDeltaQuantizerAbsolute();
if (deltaQuantizerAbsolute == Av1Constants.DeltaQuantizerSmall)
{
int deltaQuantizerRemainingBits = reader.ReadLiteral(3) + 1;
int deltaQuantizerAbsoluteBitCount = reader.ReadLiteral(deltaQuantizerRemainingBits);
deltaQuantizerAbsolute = deltaQuantizerRemainingBits + (1 << deltaQuantizerRemainingBits) + 1;
}
if (deltaQuantizerAbsolute != 0)
{
bool deltaQuantizerSignBit = reader.ReadLiteral(1) > 0;
int reducedDeltaQuantizerIndex = deltaQuantizerSignBit ? -deltaQuantizerAbsolute : deltaQuantizerAbsolute;
int deltaQuantizerResolution = this.FrameHeader.DeltaQParameters.Resolution;
this.currentQuantizerIndex = Av1Math.Clip3(1, 255, this.currentQuantizerIndex + (reducedDeltaQuantizerIndex << deltaQuantizerResolution));
partitionInfo.SuperblockInfo.SuperblockDeltaQ = this.currentQuantizerIndex;
}
int reducedDeltaQuantizerIndex = reader.ReadDeltaQuantizerIndex();
int deltaQuantizerResolution = this.FrameHeader.DeltaQParameters.Resolution;
this.currentQuantizerIndex = Av1Math.Clip3(1, 255, this.currentQuantizerIndex + (reducedDeltaQuantizerIndex << deltaQuantizerResolution));
partitionInfo.SuperblockInfo.SuperblockDeltaQ = this.currentQuantizerIndex;
}
}

8
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs

@ -449,7 +449,7 @@ internal partial class Av1TileWriter
int reduced_delta_qindex = (current_q_index - pcs.Parent.PreviousQIndex[tile_idx]) /
frm_hdr.DeltaQParameters.Resolution;
writer.WriteDeltaQIndex(reduced_delta_qindex);
writer.WriteDeltaQuantizerIndex(reduced_delta_qindex);
pcs.Parent.PreviousQIndex[tile_idx] = current_q_index;
}
}
@ -495,11 +495,7 @@ internal partial class Av1TileWriter
if (!macroBlockModeInfo.Block.UseIntraBlockCopy &&
IsFilterIntraAllowed(scs.SequenceHeader.FilterIntraLevel > 0, blockSize, blk_ptr.PaletteSize[0], intra_luma_mode))
{
writer.WriteFilterIntra(blk_ptr.FilterIntraMode, blockSize);
if (blk_ptr.FilterIntraMode != Av1FilterIntraMode.AllFilterIntraModes)
{
writer.WriteFilterIntraMode(blk_ptr.FilterIntraMode);
}
writer.WriteFilterIntraMode(blk_ptr.FilterIntraMode, blockSize);
}
if (!macroBlockModeInfo.Block.UseIntraBlockCopy)

278
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EntropyTests.cs

@ -191,11 +191,11 @@ public class Av1EntropyTests
Assert.Equal(expectedValues, values);
}
[Fact]
public void RoundTripPartitionType()
[Theory]
[MemberData(nameof(GetRangeData), 20)]
public void RoundTripPartitionType(int context)
{
// Assign
int ctx = 7;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
Av1PartitionType[] values = [
@ -206,7 +206,7 @@ public class Av1EntropyTests
// Act
foreach (Av1PartitionType value in values)
{
encoder.WritePartitionType(value, 7);
encoder.WritePartitionType(value, context);
}
using IMemoryOwner<byte> encoded = encoder.Exit();
@ -214,7 +214,7 @@ public class Av1EntropyTests
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
for (int i = 0; i < values.Length; i++)
{
actuals[i] = decoder.ReadPartitionType(ctx);
actuals[i] = decoder.ReadPartitionType(context);
}
// Assert
@ -222,16 +222,11 @@ public class Av1EntropyTests
}
[Theory]
[InlineData((int)Av1BlockSize.Block4x4, 7)]
[InlineData((int)Av1BlockSize.Block4x4, 5)]
[InlineData((int)Av1BlockSize.Block8x4, 7)]
[InlineData((int)Av1BlockSize.Block4x8, 7)]
[InlineData((int)Av1BlockSize.Block32x64, 7)]
[InlineData((int)Av1BlockSize.Block64x32, 7)]
[InlineData((int)Av1BlockSize.Block64x64, 7)]
public void RoundTripSplitOrHorizontalPartitionType(int blockSize, int context)
[MemberData(nameof(GetSplitPartitionTypeData))]
public void RoundTripSplitOrHorizontalPartitionType(int size, int context)
{
// Assign
Av1BlockSize blockSize = (Av1BlockSize)size;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
Av1PartitionType[] values = [
@ -242,7 +237,7 @@ public class Av1EntropyTests
// Act
foreach (Av1PartitionType value in values)
{
encoder.WriteSplitOrHorizontal(value, (Av1BlockSize)blockSize, context);
encoder.WriteSplitOrHorizontal(value, blockSize, context);
}
using IMemoryOwner<byte> encoded = encoder.Exit();
@ -250,7 +245,7 @@ public class Av1EntropyTests
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
for (int i = 0; i < values.Length; i++)
{
actuals[i] = decoder.ReadSplitOrHorizontal((Av1BlockSize)blockSize, context);
actuals[i] = decoder.ReadSplitOrHorizontal(blockSize, context);
}
// Assert
@ -258,16 +253,11 @@ public class Av1EntropyTests
}
[Theory]
[InlineData((int)Av1BlockSize.Block4x4, 7)]
[InlineData((int)Av1BlockSize.Block4x4, 5)]
[InlineData((int)Av1BlockSize.Block8x4, 7)]
[InlineData((int)Av1BlockSize.Block4x8, 7)]
[InlineData((int)Av1BlockSize.Block32x64, 7)]
[InlineData((int)Av1BlockSize.Block64x32, 7)]
[InlineData((int)Av1BlockSize.Block64x64, 7)]
public void RoundTripSplitOrVerticalPartitionType(int blockSize, int context)
[MemberData(nameof(GetSplitPartitionTypeData))]
public void RoundTripSplitOrVerticalPartitionType(int size, int context)
{
// Assign
Av1BlockSize blockSize = (Av1BlockSize)size;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
Av1PartitionType[] values = [
@ -278,7 +268,7 @@ public class Av1EntropyTests
// Act
foreach (Av1PartitionType value in values)
{
encoder.WriteSplitOrVertical(value, (Av1BlockSize)blockSize, context);
encoder.WriteSplitOrVertical(value, blockSize, context);
}
using IMemoryOwner<byte> encoded = encoder.Exit();
@ -286,19 +276,49 @@ public class Av1EntropyTests
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
for (int i = 0; i < values.Length; i++)
{
actuals[i] = decoder.ReadSplitOrVertical((Av1BlockSize)blockSize, context);
actuals[i] = decoder.ReadSplitOrVertical(blockSize, context);
}
// Assert
Assert.Equal(values, actuals);
}
[Fact]
public void RoundTripTransformBlockSkip()
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
public void RoundTripSkip(int context)
{
// Assign
const Av1TransformSize transformSizeContext = Av1TransformSize.Size4x4;
const int skipContext = 0;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
bool[] values = [true, true, false, false, false, false, false, false, true];
bool[] actuals = new bool[values.Length];
// Act
foreach (bool value in values)
{
encoder.WriteSkip(value, context);
}
using IMemoryOwner<byte> encoded = encoder.Exit();
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
for (int i = 0; i < values.Length; i++)
{
actuals[i] = decoder.ReadSkip(context);
}
// Assert
Assert.Equal(values, actuals);
}
[Theory]
[MemberData(nameof(GetTransformBlockSkipData))]
internal void RoundTripTransformBlockSkip(int transformContext, int skipContext)
{
// Assign
Av1TransformSize transformSizeContext = (Av1TransformSize)transformContext;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
bool[] values = [true, true, false, false, false, false, false, false, true];
@ -322,13 +342,14 @@ public class Av1EntropyTests
Assert.Equal(values, actuals);
}
[Fact]
public void RoundTripTransformType()
// [Theory]
[MemberData(nameof(GetTransformTypeData))]
public void RoundTripTransformType(int txSizeContext, int intraMode, int intraDir)
{
// Assign
const Av1TransformSize transformSizeContext = Av1TransformSize.Size4x4;
const Av1FilterIntraMode filterIntraMode = Av1FilterIntraMode.DC;
const Av1PredictionMode intraDirection = Av1PredictionMode.DC;
Av1TransformSize transformSizeContext = (Av1TransformSize)txSizeContext;
Av1FilterIntraMode filterIntraMode = (Av1FilterIntraMode)intraMode;
Av1PredictionMode intraDirection = (Av1PredictionMode)intraDir;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
@ -336,7 +357,7 @@ public class Av1EntropyTests
Av1TransformType[] values = [
Av1TransformType.DctDct, Av1TransformType.DctDct, Av1TransformType.Identity, Av1TransformType.AdstDct,
Av1TransformType.DctDct, Av1TransformType.AdstAdst, Av1TransformType.Identity, Av1TransformType.DctAdst
];
];
Av1TransformType[] actuals = new Av1TransformType[values.Length];
// Act
@ -357,25 +378,26 @@ public class Av1EntropyTests
Assert.Equal(values, actuals);
}
[Fact]
public void RoundTripEndOfBlockPosition()
[Theory]
[MemberData(nameof(GetEndOfBlockPositionData))]
public void RoundTripEndOfBlockPosition(int txSize, int txSizeContext, int plane, int txClass)
{
// Assign
const Av1TransformSize transformSize = Av1TransformSize.Size4x4;
const Av1TransformSize transformSizeContext = Av1TransformSize.Size4x4;
const Av1ComponentType componentType = Av1ComponentType.Luminance;
const Av1PlaneType planeType = Av1PlaneType.Y;
const Av1TransformClass transformClass = Av1TransformClass.Class2D;
Av1TransformSize transformSize = (Av1TransformSize)txSize;
Av1TransformSize transformSizeContext = (Av1TransformSize)txSizeContext;
Av1ComponentType componentType = (Av1ComponentType)plane;
Av1PlaneType planeType = (Av1PlaneType)plane;
Av1TransformClass transformClass = (Av1TransformClass)txClass;
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
ushort[] values = [1, 2, 3, 4, 5];
int[] values = [1, 2, 3, 4, 5];
int[] actuals = new int[values.Length];
// Act
foreach (ushort value in values)
foreach (int value in values)
{
encoder.WriteEndOfBlockPosition(value, componentType, transformClass, transformSize, transformSizeContext);
encoder.WriteEndOfBlockPosition((ushort)value, componentType, transformClass, transformSize, transformSizeContext);
}
using IMemoryOwner<byte> encoded = encoder.Exit();
@ -387,7 +409,7 @@ public class Av1EntropyTests
}
// Assert
Assert.Equal(values.Select(x => (int)x).ToArray(), actuals);
Assert.Equal(values, actuals);
}
[Fact]
@ -418,6 +440,94 @@ public class Av1EntropyTests
Assert.Equal(values, actuals);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
public void RoundTripSegmentId(int context)
{
// Assign
int[] values = [3, 6, 7, 0, 2, 0, 2, 1, 1];
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
int[] actuals = new int[values.Length];
// Act
foreach (int value in values)
{
encoder.WriteSegmentId(value, context);
}
using IMemoryOwner<byte> encoded = encoder.Exit();
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
for (int i = 0; i < values.Length; i++)
{
actuals[i] = decoder.ReadSegmentId(context);
}
// Assert
Assert.Equal(values, actuals);
}
[Fact]
public void RoundTripDeltaQuantizerIndex()
{
// Assign
int[] values = [3, 6, -7, -8, -2, 0, 2, 1, -1];
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
int[] actuals = new int[values.Length];
// Act
foreach (int value in values)
{
encoder.WriteDeltaQuantizerIndex(value);
}
using IMemoryOwner<byte> encoded = encoder.Exit();
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
for (int i = 0; i < values.Length; i++)
{
actuals[i] = decoder.ReadDeltaQuantizerIndex();
}
// Assert
Assert.Equal(values, actuals);
}
[Theory]
[MemberData(nameof(GetRangeData), (int)Av1BlockSize.AllSizes)]
public void RoundTripFilterIntraMode(int bSize)
{
// Assign
Av1BlockSize blockSize = (Av1BlockSize)bSize;
Av1FilterIntraMode[] values = [
Av1FilterIntraMode.DC, Av1FilterIntraMode.Vertical, Av1FilterIntraMode.DC, Av1FilterIntraMode.Paeth,
Av1FilterIntraMode.AllFilterIntraModes, Av1FilterIntraMode.Directional157, Av1FilterIntraMode.DC, Av1FilterIntraMode.Directional157];
Configuration configuration = Configuration.Default;
Av1SymbolEncoder encoder = new(configuration, 100 / 8, BaseQIndex);
Av1FilterIntraMode[] actuals = new Av1FilterIntraMode[values.Length];
// Act
foreach (Av1FilterIntraMode value in values)
{
encoder.WriteFilterIntraMode(value, blockSize);
}
using IMemoryOwner<byte> encoded = encoder.Exit();
Av1SymbolDecoder decoder = new(Configuration.Default, encoded.GetSpan(), BaseQIndex);
for (int i = 0; i < values.Length; i++)
{
actuals[i] = decoder.ReadFilterUltraMode(blockSize);
}
// Assert
Assert.Equal(values, actuals);
}
[Fact]
public void RoundTripUseIntraBlockCopy()
{
@ -444,4 +554,80 @@ public class Av1EntropyTests
// Assert
Assert.Equal(values, actuals);
}
public static TheoryData<int> GetRangeData(int count)
{
TheoryData<int> result = [];
for (int i = 0; i < count; i++)
{
result.Add(i);
}
return result;
}
public static TheoryData<int, int> GetTransformBlockSkipData()
{
TheoryData<int, int> result = [];
for (Av1TransformSize transformSizeContext = Av1TransformSize.Size4x4; transformSizeContext <= Av1TransformSize.Size64x64; transformSizeContext++)
{
for (int skipContext = 0; skipContext < 5; skipContext++)
{
result.Add((int)transformSizeContext, skipContext);
}
}
return result;
}
public static TheoryData<int, int> GetSplitPartitionTypeData()
{
TheoryData<int, int> result = [];
for (Av1BlockSize blockSize = Av1BlockSize.Block4x4; blockSize < Av1BlockSize.AllSizes; blockSize++)
{
for (int context = 4; context < 16; context++)
{
result.Add((int)blockSize, context);
}
}
return result;
}
public static TheoryData<int, int, int> GetTransformTypeData()
{
TheoryData<int, int, int> result = [];
for (Av1TransformSize transformSize = Av1TransformSize.Size4x4; transformSize < Av1TransformSize.AllSizes; transformSize++)
{
for (Av1FilterIntraMode filterIntraMode = Av1FilterIntraMode.DC; filterIntraMode <= Av1FilterIntraMode.AllFilterIntraModes; filterIntraMode++)
{
for (Av1PredictionMode intraDirection = Av1PredictionMode.IntraModeStart; intraDirection < Av1PredictionMode.IntraModeEnd; intraDirection++)
{
result.Add((int)transformSize, (int)filterIntraMode, (int)intraDirection);
}
}
}
return result;
}
public static TheoryData<int, int, int, int> GetEndOfBlockPositionData()
{
TheoryData<int, int, int, int> result = [];
for (Av1TransformSize transformSize = Av1TransformSize.Size4x4; transformSize < Av1TransformSize.AllSizes; transformSize++)
{
for (Av1TransformSize transformSizeContext = Av1TransformSize.Size4x4; transformSizeContext <= Av1TransformSize.Size64x64; transformSizeContext++)
{
for (int componentType = 0; componentType < 2; componentType++)
{
for (Av1TransformClass transformClass = Av1TransformClass.Class2D; transformClass <= Av1TransformClass.ClassVertical; transformClass++)
{
result.Add((int)transformSize, (int)transformSizeContext, componentType, (int)transformClass);
}
}
}
}
return result;
}
}

4
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1SymbolContextTests.cs

@ -69,9 +69,9 @@ public class Av1SymbolContextTests
for (Av1TransformSetType setType = Av1TransformSetType.DctOnly; setType <= Av1TransformSetType.All16; setType++)
{
int count = Av1SymbolContextHelper.GetExtendedTransformTypeCount(setType);
for (int type = 1; type < count; type++)
for (int index = 1; index < count; index++)
{
result.Add((int)setType, type);
result.Add((int)setType, index);
}
}

Loading…
Cancel
Save