Browse Source

Complete AV1 local warped prediction checkpoint

pull/2633/head
James Jackson-South 3 days ago
parent
commit
27a522424f
  1. 44
      HEIF_IMPLEMENTATION_PLAN.md
  2. 4
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReconstructionConformanceTests.cs
  3. 363
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1WarpedInterPredictorTests.cs
  4. 0
      tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifLocalWarpSequenceMatchesCurrentLibaomReferences_Rgba32_libavif-rotating-grid-local-warp.png
  5. 32
      tests/Images/Input/Heif/Av1/Conformance/README.md

44
HEIF_IMPLEMENTATION_PLAN.md

@ -30,8 +30,8 @@ Reference checkout evidence on 2026-08-31:
Reconciled with the worktree on 2026-08-31. Reconciled with the worktree on 2026-08-31.
- [~] The bounded container reader, still-image path, sequence parser, AV1 decoder, color pipeline, presentation pipeline, and broad AV1 test suite exist locally. - [~] The bounded container reader, still-image path, sequence parser, AV1 decoder, color pipeline, presentation pipeline, and broad AV1 test suite exist locally.
- [~] The inter-frame decoder has verified checkpoints through scaled-reference prediction. Local warped - [~] The inter-frame decoder has verified checkpoints through local warped prediction. Global motion exists
motion and global motion exist locally but remain open until their ordered checkpoints below are completed. locally but remains open until its ordered checkpoint below is completed.
- [~] Loop filtering, CDEF, super-resolution, restoration, film grain, layered presentation, alpha composition, and color conversion exist locally. Shared-source cleanup changed the current tree, so final production-path verification is open. - [~] Loop filtering, CDEF, super-resolution, restoration, film grain, layered presentation, alpha composition, and color conversion exist locally. Shared-source cleanup changed the current tree, so final production-path verification is open.
- [~] AV1 writer primitives, forward transforms, symbol encoding, and tile-writing source exist locally, but they are not connected to the public encoder. - [~] AV1 writer primitives, forward transforms, symbol encoding, and tile-writing source exist locally, but they are not connected to the public encoder.
- [ ] The public AV1 encoder is not implemented. HeifEncoderCore.Encode throws NotSupportedException when AV1 is selected. - [ ] The public AV1 encoder is not implemented. HeifEncoderCore.Encode throws NotSupportedException when AV1 is selected.
@ -184,8 +184,8 @@ The single-reference syntax, buffer, reconstruction, and ownership foundation is
- [x] Difference-weighted compound prediction. - [x] Difference-weighted compound prediction.
- [x] OBMC. - [x] OBMC.
- [x] Scaled-reference prediction. - [x] Scaled-reference prediction.
- [~] Local warped prediction. Next item. - [x] Local warped prediction.
- [~] Non-translational global prediction. - [~] Non-translational global prediction. Next item.
- [~] Inter deblocking decisions and reference/mode deltas. - [~] Inter deblocking decisions and reference/mode deltas.
Verified equal-average compound checkpoint evidence on 2026-08-31: Verified equal-average compound checkpoint evidence on 2026-08-31:
@ -429,6 +429,42 @@ Verified scaled-reference checkpoint evidence on 2026-08-31:
- [x] The focused Release checkpoint set passes 10/10 on net10.0 and 10/10 on net11.0, with zero failures - [x] The focused Release checkpoint set passes 10/10 on net10.0 and 10/10 on net11.0, with zero failures
or skips. Scoped analyzer and whitespace verification pass for every changed C# file. Roslynk reports or skips. Scoped analyzer and whitespace verification pass for every changed C# file. Roslynk reports
zero compiler errors, `git diff --check` passes, and `.gitattributes` is unchanged. zero compiler errors, `git diff --check` passes, and `.gitattributes` is unchanged.
- [x] The completed checkpoint was committed as `658a9cd1b6e22806decbae923da8800bca03a09e`
with author and committer `James Jackson-South <james_south@hotmail.com>`.
Verified local warped-prediction checkpoint evidence on 2026-08-31:
- [x] Refreshed the clean official libaom `main` checkout and audited the observed revision
`441c439b9916474cac15d2822af47a9ad70674a8`. Motion-mode eligibility and CDF selection match
`read_motion_mode` in `av1/decoder/decodemv.c`; above, left, top-left, and top-right spatial projection
samples and threshold selection match `findSamples` and `selectSamples` in
`av1/common/mvref_common.c`; affine fitting, shear reduction, phase derivation, filters, rounding,
clipping, and invalid-model fallback match `av1/common/warped_motion.c` and
`av1/common/reconinter.c`.
- [x] Mechanically compared all 1,544 ImageSharp and independent-test warped-filter coefficients against
current libaom's `av1_warped_filter`; both comparisons have zero differences. The separate scalar test
transcription covers 8-, 10-, and 12-bit luma and subsampled-chroma coordinates, tail widths, destination
stride preservation, libaom's 12-bit round adjustment, and AVX-512, AVX, 128-bit, and scalar dispatch
through `FeatureTestRunner`.
- [x] Extracted the fixture's exact 2,310-byte AV1 `mdat` payload at AVIF offset 997. Its SHA-256 is
`644D04FE1D1A32BB7A3856AD7EB49CF1EFDE0AC845E55BEAC4170F72353F2391`. Current official
libaom decoded both 256x256 YUV444 frames with one thread, row threading disabled, and all layers enabled.
The complete Y4M SHA-256 is
`8FDC5D46014F5E5A7455A83643AB6F0DA66FC5A984E72A43F8C75BAD8271C299`; the final
frame's 196,608 native samples have SHA-256
`47B2AB39BF3B9DA15C1EC59840F964DFDF227760947F6E1295FB38A84555F75C` and match the
retained native reference with zero differences.
- [x] The real two-frame fixture exercises `Av1BlockDecoder.DecodeBlock()`, requires decoded
`WARPED_CAUSAL` state and the expected multi-sample affine model, compares final native Y, U, and V
planes exactly, compares the retained final presentation through ImageSharp's established reference-output
API, and passes through intrinsic and scalar dispatch. The 1,024-byte constrained tracked-allocator path
passes with motion-field allocations present and balanced exactly-once returns.
- [x] Renamed the stale pinned-reference test and its contract-derived PNG together without changing the PNG
bytes. Its SHA-256 remains
`4490D62FB6679378E92CACA48427359091AD2106BE49FC1A3848F78BE03BEEB1`.
- [x] The focused Release checkpoint set passes 4/4 on net10.0 and 4/4 on net11.0, with zero failures or
skips. Scoped analyzer verification passes for both changed C# files. Roslynk reports zero compiler errors,
`git diff --check` passes, and `.gitattributes` is unchanged.
For every item: For every item:

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

@ -1079,11 +1079,11 @@ public class Av1ReconstructionConformanceTests
AverageCompoundFixtureFrameCount); AverageCompoundFixtureFrameCount);
/// <summary> /// <summary>
/// Verifies production local warped-motion reconstruction against pinned native and presentation references. /// Verifies production local warped-motion reconstruction against current-libaom native and retained presentation references.
/// </summary> /// </summary>
[Theory] [Theory]
[WithFile(TestImages.Heif.Av1LocalWarpSequenceAvif, PixelTypes.Rgba32)] [WithFile(TestImages.Heif.Av1LocalWarpSequenceAvif, PixelTypes.Rgba32)]
public void DecodeRealLibavifLocalWarpSequenceMatchesPinnedReferences(TestImageProvider<Rgba32> provider) public void DecodeRealLibavifLocalWarpSequenceMatchesCurrentLibaomReferences(TestImageProvider<Rgba32> provider)
=> FeatureTestRunner.RunWithHwIntrinsicsFeature( => FeatureTestRunner.RunWithHwIntrinsicsFeature(
ValidateLocalWarpSequenceWithDefaultConfiguration, ValidateLocalWarpSequenceWithDefaultConfiguration,
ReconstructionConfigurations, ReconstructionConfigurations,

363
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1WarpedInterPredictorTests.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 System.Numerics;
using SixLabors.ImageSharp.Formats.Heif.Av1.Motion; using SixLabors.ImageSharp.Formats.Heif.Av1.Motion;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.Inter; using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.Inter;
using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities;
@ -14,26 +15,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
public class Av1WarpedInterPredictorTests public class Av1WarpedInterPredictorTests
{ {
/// <summary> /// <summary>
/// The hardware configurations covering the portable vector operator and scalar fallback. /// The hardware configurations covering every descending SIMD width and the scalar fallback.
/// </summary> /// </summary>
private const HwIntrinsics PredictorConfigurations = HwIntrinsics.AllowAll | HwIntrinsics.DisableHWIntrinsic; private const HwIntrinsics PredictorConfigurations =
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic;
/// <summary> /// <summary>
/// Verifies exact 8-bit vector/scalar parity for luma and subsampled chroma coordinates. /// Verifies exact 8-bit prediction against the current libaom scalar equations.
/// </summary> /// </summary>
[Fact] [Fact]
public void BytePredictionMatchesScalarAcrossIntrinsicConfigurations() public void BytePredictionMatchesCurrentLibaomOracleAcrossIntrinsicConfigurations()
=> FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateBytePrediction, PredictorConfigurations); => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateBytePrediction, PredictorConfigurations);
/// <summary> /// <summary>
/// Verifies exact 8-, 10-, and 12-bit vector/scalar parity for luma and subsampled chroma coordinates. /// Verifies exact 8-, 10-, and 12-bit prediction against the current libaom scalar equations.
/// </summary> /// </summary>
[Fact] [Fact]
public void HighBitDepthPredictionMatchesScalarAcrossIntrinsicConfigurations() public void HighBitDepthPredictionMatchesCurrentLibaomOracleAcrossIntrinsicConfigurations()
=> FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateHighBitDepthPrediction, PredictorConfigurations); => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateHighBitDepthPrediction, PredictorConfigurations);
/// <summary> /// <summary>
/// Applies the pinned multi-sample affine model to deterministic byte storage. /// Applies the current-libaom multi-sample affine model to deterministic byte storage.
/// </summary> /// </summary>
private static void ValidateBytePrediction() private static void ValidateBytePrediction()
{ {
@ -52,18 +54,17 @@ public class Av1WarpedInterPredictorTests
} }
} }
Av1GlobalMotionParameters parameters = CreatePinnedParameters(); Av1GlobalMotionParameters parameters = CreateCurrentLibaomParameters();
for (int subsampling = 0; subsampling <= 1; subsampling++) for (int subsampling = 0; subsampling <= 1; subsampling++)
{ {
byte[] expected = new byte[destinationStride * height]; byte[] expected = new byte[destinationStride * height];
byte[] actual = new byte[destinationStride * height]; byte[] actual = new byte[destinationStride * height];
Array.Fill(expected, (byte)0xD3); Array.Fill(expected, (byte)0xD3);
Array.Fill(actual, (byte)0xD3); Array.Fill(actual, (byte)0xD3);
short[] expectedScratch = new short[Av1WarpedInterPredictor.WarpedScratchLength];
short[] actualScratch = new short[Av1WarpedInterPredictor.WarpedScratchLength]; short[] actualScratch = new short[Av1WarpedInterPredictor.WarpedScratchLength];
Point destinationPosition = subsampling == 0 ? new Point(32, 24) : new Point(16, 12); Point destinationPosition = subsampling == 0 ? new Point(32, 24) : new Point(16, 12);
Av1WarpedInterPredictor.PredictWarpedScalar( PredictCurrentLibaomReference(
source, source,
sourceStride, sourceStride,
new Point(padding, padding), new Point(padding, padding),
@ -76,8 +77,8 @@ public class Av1WarpedInterPredictorTests
height, height,
subsampling, subsampling,
subsampling, subsampling,
parameters, 8,
expectedScratch); parameters);
Av1WarpedInterPredictor.PredictWarped( Av1WarpedInterPredictor.PredictWarped(
source, source,
@ -100,7 +101,7 @@ public class Av1WarpedInterPredictorTests
} }
/// <summary> /// <summary>
/// Applies the pinned multi-sample affine model to every supported high-bit-depth precision. /// Applies the current-libaom multi-sample affine model to every supported high-bit-depth precision.
/// </summary> /// </summary>
private static void ValidateHighBitDepthPrediction() private static void ValidateHighBitDepthPrediction()
{ {
@ -110,7 +111,7 @@ public class Av1WarpedInterPredictorTests
const int width = 13; const int width = 13;
const int height = 11; const int height = 11;
const int destinationStride = width + 7; const int destinationStride = width + 7;
Av1GlobalMotionParameters parameters = CreatePinnedParameters(); Av1GlobalMotionParameters parameters = CreateCurrentLibaomParameters();
foreach (int bitDepth in new[] { 8, 10, 12 }) foreach (int bitDepth in new[] { 8, 10, 12 })
{ {
int maximum = (1 << bitDepth) - 1; int maximum = (1 << bitDepth) - 1;
@ -121,7 +122,6 @@ public class Av1WarpedInterPredictorTests
{ {
source[(row * sourceStride) + column] = source[(row * sourceStride) + column] =
(ushort)(((row * 269) + (column * 443) + (row * column * 31)) & maximum); (ushort)(((row * 269) + (column * 443) + (row * column * 31)) & maximum);
} }
} }
@ -131,11 +131,10 @@ public class Av1WarpedInterPredictorTests
ushort[] actual = new ushort[destinationStride * height]; ushort[] actual = new ushort[destinationStride * height];
Array.Fill(expected, (ushort)0xDEAD); Array.Fill(expected, (ushort)0xDEAD);
Array.Fill(actual, (ushort)0xDEAD); Array.Fill(actual, (ushort)0xDEAD);
short[] expectedScratch = new short[Av1WarpedInterPredictor.WarpedScratchLength];
short[] actualScratch = new short[Av1WarpedInterPredictor.WarpedScratchLength]; short[] actualScratch = new short[Av1WarpedInterPredictor.WarpedScratchLength];
Point destinationPosition = subsampling == 0 ? new Point(32, 24) : new Point(16, 12); Point destinationPosition = subsampling == 0 ? new Point(32, 24) : new Point(16, 12);
Av1WarpedInterPredictor.PredictWarpedScalar( PredictCurrentLibaomReference(
source, source,
sourceStride, sourceStride,
new Point(padding, padding), new Point(padding, padding),
@ -149,8 +148,7 @@ public class Av1WarpedInterPredictorTests
subsampling, subsampling,
subsampling, subsampling,
bitDepth, bitDepth,
parameters, parameters);
expectedScratch);
Av1WarpedInterPredictor.PredictWarped( Av1WarpedInterPredictor.PredictWarped(
source, source,
@ -175,9 +173,332 @@ public class Av1WarpedInterPredictorTests
} }
/// <summary> /// <summary>
/// Creates one nontrivial affine model traced from the pinned two-frame local-warp fixture. /// Reconstructs one warped block by directly transcribing current libaom's scalar affine loops.
/// </summary>
private static void PredictCurrentLibaomReference<TPixel>(
ReadOnlySpan<TPixel> source,
int sourceStride,
Point sourceOrigin,
int sourceWidth,
int sourceHeight,
Span<TPixel> destination,
int destinationStride,
Point destinationPosition,
int width,
int height,
int subsamplingX,
int subsamplingY,
int bitDepth,
Av1GlobalMotionParameters parameters)
where TPixel : unmanaged, IBinaryInteger<TPixel>
{
const int filterBits = 7;
const int filterTaps = 8;
const int tileSize = 8;
const int modelPrecisionBits = 16;
const int phasePrecisionBits = 10;
const int pixelPrecisionShifts = 64;
Span<int> intermediate = stackalloc int[15 * tileSize];
// Current libaom raises round0 by two for 12-bit sources so the biased horizontal intermediate
// remains representable in 16 bits, then removes those two bits from the vertical rounding.
int intermediateRange = bitDepth + filterBits - 3 + 2;
int round0 = 3 + Math.Max(intermediateRange - 16, 0);
int verticalRound = (2 * filterBits) - round0;
int horizontalBias = 1 << (bitDepth + filterBits - 1);
int verticalBias = 1 << (bitDepth + (2 * filterBits) - round0);
int maximum = (1 << bitDepth) - 1;
for (int tileRow = destinationPosition.Y; tileRow < destinationPosition.Y + height; tileRow += tileSize)
{
for (int tileColumn = destinationPosition.X; tileColumn < destinationPosition.X + width; tileColumn += tileSize)
{
int centerX = (tileColumn + 4) << subsamplingX;
int centerY = (tileRow + 4) << subsamplingY;
long projectedX =
((long)parameters[2] * centerX) + ((long)parameters[3] * centerY) + parameters[0];
long projectedY =
((long)parameters[4] * centerX) + ((long)parameters[5] * centerY) + parameters[1];
long planeX = projectedX >> subsamplingX;
long planeY = projectedY >> subsamplingY;
int integerX = (int)(planeX >> modelPrecisionBits);
int integerY = (int)(planeY >> modelPrecisionBits);
int phaseX = (int)planeX & ((1 << modelPrecisionBits) - 1);
int phaseY = (int)planeY & ((1 << modelPrecisionBits) - 1);
phaseX += (-4 * parameters.Alpha) + (-4 * parameters.Beta);
phaseY += (-4 * parameters.Gamma) + (-4 * parameters.Delta);
phaseX &= -1 << 6;
phaseY &= -1 << 6;
for (int row = -7; row < 8; row++)
{
int sourceY = Math.Clamp(integerY + row, 0, sourceHeight - 1);
int phase = phaseX + (parameters.Beta * (row + 4));
for (int column = -4; column < 4; column++)
{
int sourceX = integerX + column - 3;
int filterIndex = RoundPowerOfTwo(phase, phasePrecisionBits) + pixelPrecisionShifts;
int coefficientOffset = filterIndex * filterTaps;
int sum = horizontalBias;
for (int tap = 0; tap < filterTaps; tap++)
{
int sampleX = Math.Clamp(sourceX + tap, 0, sourceWidth - 1);
int sourceIndex =
((sourceOrigin.Y + sourceY) * sourceStride) + sourceOrigin.X + sampleX;
int sample = int.CreateChecked(source[sourceIndex]);
sum += sample * CurrentLibaomWarpedFilter[coefficientOffset + tap];
}
intermediate[((row + 7) * tileSize) + column + 4] = RoundPowerOfTwo(sum, round0);
phase += parameters.Alpha;
}
}
int lastRow = Math.Min(4, destinationPosition.Y + height - tileRow - 4);
for (int row = -4; row < lastRow; row++)
{
int phase = phaseY + (parameters.Delta * (row + 4));
int lastColumn = Math.Min(4, destinationPosition.X + width - tileColumn - 4);
for (int column = -4; column < lastColumn; column++)
{
int filterIndex = RoundPowerOfTwo(phase, phasePrecisionBits) + pixelPrecisionShifts;
int coefficientOffset = filterIndex * filterTaps;
int sum = verticalBias;
for (int tap = 0; tap < filterTaps; tap++)
{
int intermediateIndex = ((row + tap + 4) * tileSize) + column + 4;
sum += intermediate[intermediateIndex] * CurrentLibaomWarpedFilter[coefficientOffset + tap];
}
int value = RoundPowerOfTwo(sum, verticalRound) - (1 << (bitDepth - 1)) - (1 << bitDepth);
int destinationIndex =
((tileRow - destinationPosition.Y + row + 4) * destinationStride) +
tileColumn -
destinationPosition.X +
column +
4;
destination[destinationIndex] = TPixel.CreateChecked(Math.Clamp(value, 0, maximum));
phase += parameters.Gamma;
}
}
}
}
}
/// <summary>
/// Gets the 193 current-libaom eight-tap warped-filter phases used by the independent scalar oracle.
/// </summary>
private static ReadOnlySpan<short> CurrentLibaomWarpedFilter =>
[
0, 0, 127, 1, 0, 0, 0, 0,
0, -1, 127, 2, 0, 0, 0, 0,
1, -3, 127, 4, -1, 0, 0, 0,
1, -4, 126, 6, -2, 1, 0, 0,
1, -5, 126, 8, -3, 1, 0, 0,
1, -6, 125, 11, -4, 1, 0, 0,
1, -7, 124, 13, -4, 1, 0, 0,
2, -8, 123, 15, -5, 1, 0, 0,
2, -9, 122, 18, -6, 1, 0, 0,
2, -10, 121, 20, -6, 1, 0, 0,
2, -11, 120, 22, -7, 2, 0, 0,
2, -12, 119, 25, -8, 2, 0, 0,
3, -13, 117, 27, -8, 2, 0, 0,
3, -13, 116, 29, -9, 2, 0, 0,
3, -14, 114, 32, -10, 3, 0, 0,
3, -15, 113, 35, -10, 2, 0, 0,
3, -15, 111, 37, -11, 3, 0, 0,
3, -16, 109, 40, -11, 3, 0, 0,
3, -16, 108, 42, -12, 3, 0, 0,
4, -17, 106, 45, -13, 3, 0, 0,
4, -17, 104, 47, -13, 3, 0, 0,
4, -17, 102, 50, -14, 3, 0, 0,
4, -17, 100, 52, -14, 3, 0, 0,
4, -18, 98, 55, -15, 4, 0, 0,
4, -18, 96, 58, -15, 3, 0, 0,
4, -18, 94, 60, -16, 4, 0, 0,
4, -18, 91, 63, -16, 4, 0, 0,
4, -18, 89, 65, -16, 4, 0, 0,
4, -18, 87, 68, -17, 4, 0, 0,
4, -18, 85, 70, -17, 4, 0, 0,
4, -18, 82, 73, -17, 4, 0, 0,
4, -18, 80, 75, -17, 4, 0, 0,
4, -18, 78, 78, -18, 4, 0, 0,
4, -17, 75, 80, -18, 4, 0, 0,
4, -17, 73, 82, -18, 4, 0, 0,
4, -17, 70, 85, -18, 4, 0, 0,
4, -17, 68, 87, -18, 4, 0, 0,
4, -16, 65, 89, -18, 4, 0, 0,
4, -16, 63, 91, -18, 4, 0, 0,
4, -16, 60, 94, -18, 4, 0, 0,
3, -15, 58, 96, -18, 4, 0, 0,
4, -15, 55, 98, -18, 4, 0, 0,
3, -14, 52, 100, -17, 4, 0, 0,
3, -14, 50, 102, -17, 4, 0, 0,
3, -13, 47, 104, -17, 4, 0, 0,
3, -13, 45, 106, -17, 4, 0, 0,
3, -12, 42, 108, -16, 3, 0, 0,
3, -11, 40, 109, -16, 3, 0, 0,
3, -11, 37, 111, -15, 3, 0, 0,
2, -10, 35, 113, -15, 3, 0, 0,
3, -10, 32, 114, -14, 3, 0, 0,
2, -9, 29, 116, -13, 3, 0, 0,
2, -8, 27, 117, -13, 3, 0, 0,
2, -8, 25, 119, -12, 2, 0, 0,
2, -7, 22, 120, -11, 2, 0, 0,
1, -6, 20, 121, -10, 2, 0, 0,
1, -6, 18, 122, -9, 2, 0, 0,
1, -5, 15, 123, -8, 2, 0, 0,
1, -4, 13, 124, -7, 1, 0, 0,
1, -4, 11, 125, -6, 1, 0, 0,
1, -3, 8, 126, -5, 1, 0, 0,
1, -2, 6, 126, -4, 1, 0, 0,
0, -1, 4, 127, -3, 1, 0, 0,
0, 0, 2, 127, -1, 0, 0, 0,
0, 0, 0, 127, 1, 0, 0, 0,
0, 0, -1, 127, 2, 0, 0, 0,
0, 1, -3, 127, 4, -2, 1, 0,
0, 1, -5, 127, 6, -2, 1, 0,
0, 2, -6, 126, 8, -3, 1, 0,
-1, 2, -7, 126, 11, -4, 2, -1,
-1, 3, -8, 125, 13, -5, 2, -1,
-1, 3, -10, 124, 16, -6, 3, -1,
-1, 4, -11, 123, 18, -7, 3, -1,
-1, 4, -12, 122, 20, -7, 3, -1,
-1, 4, -13, 121, 23, -8, 3, -1,
-2, 5, -14, 120, 25, -9, 4, -1,
-1, 5, -15, 119, 27, -10, 4, -1,
-1, 5, -16, 118, 30, -11, 4, -1,
-2, 6, -17, 116, 33, -12, 5, -1,
-2, 6, -17, 114, 35, -12, 5, -1,
-2, 6, -18, 113, 38, -13, 5, -1,
-2, 7, -19, 111, 41, -14, 6, -2,
-2, 7, -19, 110, 43, -15, 6, -2,
-2, 7, -20, 108, 46, -15, 6, -2,
-2, 7, -20, 106, 49, -16, 6, -2,
-2, 7, -21, 104, 51, -16, 7, -2,
-2, 7, -21, 102, 54, -17, 7, -2,
-2, 8, -21, 100, 56, -18, 7, -2,
-2, 8, -22, 98, 59, -18, 7, -2,
-2, 8, -22, 96, 62, -19, 7, -2,
-2, 8, -22, 94, 64, -19, 7, -2,
-2, 8, -22, 91, 67, -20, 8, -2,
-2, 8, -22, 89, 69, -20, 8, -2,
-2, 8, -22, 87, 72, -21, 8, -2,
-2, 8, -21, 84, 74, -21, 8, -2,
-2, 8, -22, 82, 77, -21, 8, -2,
-2, 8, -21, 79, 79, -21, 8, -2,
-2, 8, -21, 77, 82, -22, 8, -2,
-2, 8, -21, 74, 84, -21, 8, -2,
-2, 8, -21, 72, 87, -22, 8, -2,
-2, 8, -20, 69, 89, -22, 8, -2,
-2, 8, -20, 67, 91, -22, 8, -2,
-2, 7, -19, 64, 94, -22, 8, -2,
-2, 7, -19, 62, 96, -22, 8, -2,
-2, 7, -18, 59, 98, -22, 8, -2,
-2, 7, -18, 56, 100, -21, 8, -2,
-2, 7, -17, 54, 102, -21, 7, -2,
-2, 7, -16, 51, 104, -21, 7, -2,
-2, 6, -16, 49, 106, -20, 7, -2,
-2, 6, -15, 46, 108, -20, 7, -2,
-2, 6, -15, 43, 110, -19, 7, -2,
-2, 6, -14, 41, 111, -19, 7, -2,
-1, 5, -13, 38, 113, -18, 6, -2,
-1, 5, -12, 35, 114, -17, 6, -2,
-1, 5, -12, 33, 116, -17, 6, -2,
-1, 4, -11, 30, 118, -16, 5, -1,
-1, 4, -10, 27, 119, -15, 5, -1,
-1, 4, -9, 25, 120, -14, 5, -2,
-1, 3, -8, 23, 121, -13, 4, -1,
-1, 3, -7, 20, 122, -12, 4, -1,
-1, 3, -7, 18, 123, -11, 4, -1,
-1, 3, -6, 16, 124, -10, 3, -1,
-1, 2, -5, 13, 125, -8, 3, -1,
-1, 2, -4, 11, 126, -7, 2, -1,
0, 1, -3, 8, 126, -6, 2, 0,
0, 1, -2, 6, 127, -5, 1, 0,
0, 1, -2, 4, 127, -3, 1, 0,
0, 0, 0, 2, 127, -1, 0, 0,
0, 0, 0, 1, 127, 0, 0, 0,
0, 0, 0, -1, 127, 2, 0, 0,
0, 0, 1, -3, 127, 4, -1, 0,
0, 0, 1, -4, 126, 6, -2, 1,
0, 0, 1, -5, 126, 8, -3, 1,
0, 0, 1, -6, 125, 11, -4, 1,
0, 0, 1, -7, 124, 13, -4, 1,
0, 0, 2, -8, 123, 15, -5, 1,
0, 0, 2, -9, 122, 18, -6, 1,
0, 0, 2, -10, 121, 20, -6, 1,
0, 0, 2, -11, 120, 22, -7, 2,
0, 0, 2, -12, 119, 25, -8, 2,
0, 0, 3, -13, 117, 27, -8, 2,
0, 0, 3, -13, 116, 29, -9, 2,
0, 0, 3, -14, 114, 32, -10, 3,
0, 0, 3, -15, 113, 35, -10, 2,
0, 0, 3, -15, 111, 37, -11, 3,
0, 0, 3, -16, 109, 40, -11, 3,
0, 0, 3, -16, 108, 42, -12, 3,
0, 0, 4, -17, 106, 45, -13, 3,
0, 0, 4, -17, 104, 47, -13, 3,
0, 0, 4, -17, 102, 50, -14, 3,
0, 0, 4, -17, 100, 52, -14, 3,
0, 0, 4, -18, 98, 55, -15, 4,
0, 0, 4, -18, 96, 58, -15, 3,
0, 0, 4, -18, 94, 60, -16, 4,
0, 0, 4, -18, 91, 63, -16, 4,
0, 0, 4, -18, 89, 65, -16, 4,
0, 0, 4, -18, 87, 68, -17, 4,
0, 0, 4, -18, 85, 70, -17, 4,
0, 0, 4, -18, 82, 73, -17, 4,
0, 0, 4, -18, 80, 75, -17, 4,
0, 0, 4, -18, 78, 78, -18, 4,
0, 0, 4, -17, 75, 80, -18, 4,
0, 0, 4, -17, 73, 82, -18, 4,
0, 0, 4, -17, 70, 85, -18, 4,
0, 0, 4, -17, 68, 87, -18, 4,
0, 0, 4, -16, 65, 89, -18, 4,
0, 0, 4, -16, 63, 91, -18, 4,
0, 0, 4, -16, 60, 94, -18, 4,
0, 0, 3, -15, 58, 96, -18, 4,
0, 0, 4, -15, 55, 98, -18, 4,
0, 0, 3, -14, 52, 100, -17, 4,
0, 0, 3, -14, 50, 102, -17, 4,
0, 0, 3, -13, 47, 104, -17, 4,
0, 0, 3, -13, 45, 106, -17, 4,
0, 0, 3, -12, 42, 108, -16, 3,
0, 0, 3, -11, 40, 109, -16, 3,
0, 0, 3, -11, 37, 111, -15, 3,
0, 0, 2, -10, 35, 113, -15, 3,
0, 0, 3, -10, 32, 114, -14, 3,
0, 0, 2, -9, 29, 116, -13, 3,
0, 0, 2, -8, 27, 117, -13, 3,
0, 0, 2, -8, 25, 119, -12, 2,
0, 0, 2, -7, 22, 120, -11, 2,
0, 0, 1, -6, 20, 121, -10, 2,
0, 0, 1, -6, 18, 122, -9, 2,
0, 0, 1, -5, 15, 123, -8, 2,
0, 0, 1, -4, 13, 124, -7, 1,
0, 0, 1, -4, 11, 125, -6, 1,
0, 0, 1, -3, 8, 126, -5, 1,
0, 0, 1, -2, 6, 126, -4, 1,
0, 0, 0, -1, 4, 127, -3, 1,
0, 0, 0, 0, 2, 127, -1, 0,
0, 0, 0, 0, 2, 127, -1, 0,
];
/// <summary>
/// Divides a signed fixed-point value by a power of two with current libaom's rounding.
/// </summary>
private static int RoundPowerOfTwo(int value, int bitCount)
=> (value + (1 << (bitCount - 1))) >> bitCount;
/// <summary>
/// Creates one nontrivial affine model traced from the current-libaom-verified two-frame local-warp fixture.
/// </summary> /// </summary>
private static Av1GlobalMotionParameters CreatePinnedParameters() private static Av1GlobalMotionParameters CreateCurrentLibaomParameters()
{ {
Av1GlobalMotionParameters parameters = Av1GlobalMotionParameters.Identity; Av1GlobalMotionParameters parameters = Av1GlobalMotionParameters.Identity;
parameters.Type = Av1GlobalMotionType.Affine; parameters.Type = Av1GlobalMotionType.Affine;

0
tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifLocalWarpSequenceMatchesPinnedReferences_Rgba32_libavif-rotating-grid-local-warp.png → tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifLocalWarpSequenceMatchesCurrentLibaomReferences_Rgba32_libavif-rotating-grid-local-warp.png

32
tests/Images/Input/Heif/Av1/Conformance/README.md

@ -229,15 +229,39 @@ bytes. Their SHA-256 values remain
## Local warped-motion fixture ## Local warped-motion fixture
The `libavif-rotating-grid-local-warp.avif` fixture was encoded from a deterministic two-frame 256x256 limited-range YUV444 source. The source combines checkerboard, ring, and chroma-gradient detail; its second frame rotates the first by 2.5 degrees with nearest-neighbor sampling and edge clamping. The two-frame source Y4M SHA-256 is `82C1468C95C996B05165590417184F59373D67896F8C7B0EB398582C29C9C7A7`. Pinned scalar libavif and libaom generated the fixture and references with: The `libavif-rotating-grid-local-warp.avif` file is retained solely as interoperability input. It was
originally encoded from a deterministic two-frame 256x256 limited-range YUV444 source whose second frame
rotates the first by 2.5 degrees. The original packaging command records fixture provenance only; libavif
is not used as an AV1 implementation or reconstruction reference:
```text ```text
./avifenc -j 1 -s 0 -q 60 -a color:enable-warped-motion=1 -a color:enable-global-motion=0 -a color:enable-obmc=0 rotating-grid-256-two-frame.y4m libavif-rotating-grid-local-warp.avif ./avifenc -j 1 -s 0 -q 60 -a color:enable-warped-motion=1 -a color:enable-global-motion=0 -a color:enable-obmc=0 rotating-grid-256-two-frame.y4m libavif-rotating-grid-local-warp.avif
./avifdec -j 1 --index 1 libavif-rotating-grid-local-warp.avif libavif-rotating-grid-local-warp-libaom.y4m
./avifdec -j 1 --index 1 libavif-rotating-grid-local-warp.avif libavif-rotating-grid-local-warp-libavif.png
``` ```
The AVIF SHA-256 is `990BAC4AD443005C217B0DA4FCCFA9ADFB3AA147AD06C85F9A655A4433E9E8A7`. The retained frame-1 Y4M SHA-256 is `984B2815CEE0C05FDE26430F150A21B5C993141E25BA1ED4FDE09372DB64AC13`, and the frame-1 PNG SHA-256 is `4490D62FB6679378E92CACA48427359091AD2106BE49FC1A3848F78BE03BEEB1`. Pinned libaom tracing records many actual `WARPED_CAUSAL` blocks. The multi-sample model at mode-information row 6, column 8 derives matrix `[-191565, 599107, 61755, -140, -6909, 62012]` and reduced shear `[-3776, -128, -7360, -3520]` from four retained neighbor samples. The production test requires decoded warped mode state, compares every final native Y, U, and V sample and the final RGBA presentation exactly, runs normal and scalar dispatch through `FeatureTestRunner`, and repeats reconstruction with constrained tracked allocation. On 2026-08-31 the clean official libaom `main` checkout was refreshed from its upstream remote. At the
observed revision `441c439b9916474cac15d2822af47a9ad70674a8`, the fixture's 2,310-byte AV1 `mdat`
payload at AVIF offset 997 has SHA-256
`644D04FE1D1A32BB7A3856AD7EB49CF1EFDE0AC845E55BEAC4170F72353F2391` and was decoded with:
```text
aomdec --codec=av1 --threads=1 --row-mt=0 --all-layers --output-bit-depth=8 -o local-warp-current-main.y4m local-warp-current-main.obu
```
Current libaom produced two 256x256 YUV444 frames. The complete Y4M has SHA-256
`8FDC5D46014F5E5A7455A83643AB6F0DA66FC5A984E72A43F8C75BAD8271C299`. Its final
frame's 196,608 native samples have SHA-256
`47B2AB39BF3B9DA15C1EC59840F964DFDF227760947F6E1295FB38A84555F75C` and match the
retained `libavif-rotating-grid-local-warp-libaom.y4m` reference with zero differing samples.
The multi-sample model at mode-information row 6, column 8 derives matrix
`[-191565, 599107, 61755, -140, -6909, 62012]` and reduced shear
`[-3776, -128, -7360, -3520]` from four retained spatial neighbor samples. The production test requires
decoded `WARPED_CAUSAL` state through `Av1BlockDecoder.DecodeBlock()`, compares every final native Y,
U, and V sample exactly, compares the retained final presentation through the established image-reference
API, runs intrinsic and scalar dispatch through `FeatureTestRunner`, and repeats reconstruction with a
constrained tracked allocator. Separate 8-, 10-, and 12-bit operator tests use an independent scalar
transcription of current libaom's affine loops and all 1,544 current filter coefficients across AVX-512,
AVX, 128-bit, and scalar execution.
## Global warped-motion fixture ## Global warped-motion fixture

Loading…
Cancel
Save