Browse Source

Clip AV1 loop-filter adjustments after combining reference and mode deltas

pull/2633/head
James Jackson-South 4 weeks ago
parent
commit
5f7bad3a64
  1. 27
      HEIF_IMPLEMENTATION_PLAN.md
  2. 14
      src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1LoopFilterDecoder.cs
  3. 46
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1DeblockingFilterTests.cs

27
HEIF_IMPLEMENTATION_PLAN.md

@ -338,10 +338,29 @@ Independent loop-filter delta entropy correction, verified on 2026-09-05:
`av1/encoder/encodeframe.c:2357`). Existing native output must not be assumed to exercise multi-delta syntax. `av1/encoder/encodeframe.c:2357`). Existing native output must not be assumed to exercise multi-delta syntax.
A complete independently authored multi-delta bitstream remains a verification gap. This correction does not A complete independently authored multi-delta bitstream remains a verification gap. This correction does not
establish encoder parity or complete decoder correctness. No benchmark was run. establish encoder parity or complete decoder correctness. No benchmark was run.
- Following the decoded values into deblocking found a further clipping-order discrepancy:
`Av1LoopFilterDecoder.cs:373-390` clips the reference adjustment before adding the mode adjustment; Loop-filter level clipping correction after checkpoint `77f535828`, verified on 2026-09-05:
native `av1/common/av1_loopfilter.c:95-101,195-201` clips their combined result once. A production-frame
regression and correction are still pending for that separately identified numerical defect. - Following decoded delta values into deblocking found another numerical defect. At the preceding checkpoint,
`Av1LoopFilterDecoder.cs:373-390` clipped the reference adjustment before adding the mode adjustment.
Native `av1/common/av1_loopfilter.c:95-101,182-187` clips their combined result once, both for per-block
delta-LF and for the precomputed frame-level table. Opposite adjustments must be allowed to cancel before clipping.
- The production-frame regression failed before correction: base level 1, reference delta -63, and mode delta +63
should retain level 1, but premature clipping produced level 63. An expected sample of 100 became 104 at the first
differing position (`loop-level-before.trx`); later edge samples also differed. VSTest stopped on that first failure.
The local runner then failed to print xUnit's Unicode arrows under cp1252. The saved report was inspected and the
runner's stdout encoding corrected; the failed test was not rerun before the implementation change.
- `Av1LoopFilterDecoder.GetFilterLevel` now adds both adjustments using the scale derived from the original level,
then clips once. Existing base/delta-LF and segmentation clipping remain in their normative order.
No allocation, ownership, or syntax policy changed.
- The existing production-frame test and its three expected outputs are retained. Four new cases exercise cancelling
deltas at both limits, with delta-LF present and absent, through `DecodeFrame` and an independent scalar filter.
Final Release net11.0 incremental build reported zero errors and zero warnings; the earlier test compilation
reported 1,009 existing warnings. Roslynk reports zero compiler errors.
- Serialized Visual Studio VSTest passed **11/11** in `loop-level-final.trx` (5.7519 seconds): deblocking scalar/SIMD
definitions, production-frame delta cases, and native deblocking, CDF-update, profile, and all-intra fixtures.
The earlier 2,095-case report applies to the preceding checkpoint. No benchmark or separate-encoder parity
measurement was run, and the complete multi-delta bitstream verification gap remains open.
Motion-controller investigation continued after correction checkpoint `578ec34d9`: Motion-controller investigation continued after correction checkpoint `578ec34d9`:

14
src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1LoopFilterDecoder.cs

@ -372,10 +372,7 @@ internal sealed class Av1LoopFilterDecoder
{ {
int referenceScale = 1 << (level >> 5); int referenceScale = 1 << (level >> 5);
Av1ReferenceFrameType referenceFrame = modeInfo.ReferenceFrames[0]; Av1ReferenceFrameType referenceFrame = modeInfo.ReferenceFrames[0];
level = Av1Math.Clip3( level += parameters.ReferenceDeltas[(int)referenceFrame] * referenceScale;
0,
Av1Constants.MaxLoopFilter,
level + (parameters.ReferenceDeltas[(int)referenceFrame] * referenceScale));
if (referenceFrame > Av1ReferenceFrameType.Intra) if (referenceFrame > Av1ReferenceFrameType.Intra)
{ {
@ -384,11 +381,12 @@ internal sealed class Av1LoopFilterDecoder
int modeDeltaIndex = modeInfo.YMode is Av1PredictionMode.GlobalMotionVector or int modeDeltaIndex = modeInfo.YMode is Av1PredictionMode.GlobalMotionVector or
Av1PredictionMode.GlobalGlobalMotionVector ? 0 : 1; Av1PredictionMode.GlobalGlobalMotionVector ? 0 : 1;
level = Av1Math.Clip3( level += parameters.ModeDeltas[modeDeltaIndex] * referenceScale;
0,
Av1Constants.MaxLoopFilter,
level + (parameters.ModeDeltas[modeDeltaIndex] * referenceScale));
} }
// Reference and mode adjustments use the same scale and may cancel beyond either limit.
// Clipping the intermediate reference sum would discard part of that cancellation.
level = Av1Math.Clip3(0, Av1Constants.MaxLoopFilter, level);
} }
return level; return level;

46
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1DeblockingFilterTests.cs

@ -59,19 +59,53 @@ public class Av1DeblockingFilterTests
ValidateInterEdgeAndDeltaDecisions( ValidateInterEdgeAndDeltaDecisions(
Av1PredictionMode.GlobalMotionVector, Av1PredictionMode.GlobalMotionVector,
Av1ReferenceFrameType.Last, Av1ReferenceFrameType.Last,
20,
-3,
4,
false,
17); 17);
ValidateInterEdgeAndDeltaDecisions( ValidateInterEdgeAndDeltaDecisions(
Av1PredictionMode.NewMotionVector, Av1PredictionMode.NewMotionVector,
Av1ReferenceFrameType.Last, Av1ReferenceFrameType.Last,
20,
-3,
4,
false,
21); 21);
ValidateInterEdgeAndDeltaDecisions( ValidateInterEdgeAndDeltaDecisions(
Av1PredictionMode.GlobalMotionVector, Av1PredictionMode.GlobalMotionVector,
Av1ReferenceFrameType.Golden, Av1ReferenceFrameType.Golden,
20,
2,
4,
false,
22); 22);
} }
/// <summary>
/// Verifies that opposite reference and mode adjustments cancel before the final filter-level clamp.
/// </summary>
[Theory]
[InlineData(1, -63, 63, false)]
[InlineData(63, 63, -63, false)]
[InlineData(1, -63, 63, true)]
[InlineData(63, 63, -63, true)]
public void DecodeFrameCombinesDeltasBeforeClipping(int baseLevel, int referenceDelta, int modeDelta, bool deltaLoopFilterPresent)
{
// Both native paths, per-block delta-LF and the precomputed frame table, clamp only after adding
// reference and mode adjustments. These equal and opposite deltas leave the base level unchanged.
ValidateInterEdgeAndDeltaDecisions(
Av1PredictionMode.NewMotionVector,
Av1ReferenceFrameType.Last,
baseLevel,
referenceDelta,
modeDelta,
deltaLoopFilterPresent,
baseLevel);
}
/// <summary> /// <summary>
/// Exercises mixed flatness, high-edge-variance, disabled-mask, direction, and bit-depth cases. /// Exercises mixed flatness, high-edge-variance, disabled-mask, direction, and bit-depth cases.
/// </summary> /// </summary>
@ -109,12 +143,15 @@ public class Av1DeblockingFilterTests
private static void ValidateInterEdgeAndDeltaDecisions( private static void ValidateInterEdgeAndDeltaDecisions(
Av1PredictionMode mode, Av1PredictionMode mode,
Av1ReferenceFrameType referenceFrame, Av1ReferenceFrameType referenceFrame,
int baseLevel,
int referenceDelta,
int modeDelta,
bool deltaLoopFilterPresent,
int expectedLevel) int expectedLevel)
{ {
const int width = Stride; const int width = Stride;
const int height = 8; const int height = 8;
const int edge = 16; const int edge = 16;
const int baseLevel = 20;
ObuSequenceHeader sequenceHeader = new() ObuSequenceHeader sequenceHeader = new()
{ {
MaxFrameWidth = width, MaxFrameWidth = width,
@ -140,11 +177,12 @@ public class Av1DeblockingFilterTests
}; };
ObuLoopFilterParameters filterParameters = frameHeader.LoopFilterParameters; ObuLoopFilterParameters filterParameters = frameHeader.LoopFilterParameters;
frameHeader.DeltaQParameters.IsPresent = deltaLoopFilterPresent;
frameHeader.DeltaLoopFilterParameters.IsPresent = deltaLoopFilterPresent;
filterParameters.FilterLevel[0] = baseLevel; filterParameters.FilterLevel[0] = baseLevel;
filterParameters.ReferenceDeltaModeEnabled = true; filterParameters.ReferenceDeltaModeEnabled = true;
filterParameters.ReferenceDeltas[(int)Av1ReferenceFrameType.Last] = -3; filterParameters.ReferenceDeltas[(int)referenceFrame] = referenceDelta;
filterParameters.ReferenceDeltas[(int)Av1ReferenceFrameType.Golden] = 2; filterParameters.ModeDeltas[1] = modeDelta;
filterParameters.ModeDeltas[1] = 4;
using Av1FrameBuffer<byte> frameBuffer = new( using Av1FrameBuffer<byte> frameBuffer = new(
Configuration.Default, Configuration.Default,

Loading…
Cancel
Save