Browse Source

Normalize high-bit-depth AV1 intra-copy search SAD

Scale each completed absolute-difference sum into the eight-bit motion-rate domain before comparing candidates, including batched mesh candidates.

Verified in the current worktree with Release .NET 11 and serialized Visual Studio VSTest: 138 focused cases passed, including 10-bit and 12-bit candidate-ranking regressions. Decoder comparison of 12 freshly encoded streams was sample exact across 21,348 samples. The regression test additions remain with the pending explicit-border constructor changes. This checkpoint does not establish encoder parity or complete motion search.
pull/2633/head
James Jackson-South 4 weeks ago
parent
commit
d78734dc76
  1. 23
      src/ImageSharp/Formats/Heif/Av1/Motion/Av1IntraBlockCopySearchIndex.cs

23
src/ImageSharp/Formats/Heif/Av1/Motion/Av1IntraBlockCopySearchIndex.cs

@ -686,6 +686,9 @@ internal readonly struct Av1IntraBlockCopySearchIndex
where TSample : unmanaged where TSample : unmanaged
where TOperation : struct, ISearchOperation<TSample> where TOperation : struct, ISearchOperation<TSample>
{ {
// Sample operators retain native precision. Truncate the complete SAD into the eight-bit domain before
// adding motion rate; scaling only the rate would change integer rounding and candidate ties.
int sadShift = bitDepth.GetBitCount() - 8;
SearchDiamond<TSample, TOperation>( SearchDiamond<TSample, TOperation>(
source, source,
reconstruction, reconstruction,
@ -693,6 +696,7 @@ internal readonly struct Av1IntraBlockCopySearchIndex
writer, writer,
reference, reference,
sadPerBit, sadPerBit,
sadShift,
searchStepParameter, searchStepParameter,
minimumColumnOffset, minimumColumnOffset,
minimumRowOffset, minimumRowOffset,
@ -724,6 +728,7 @@ internal readonly struct Av1IntraBlockCopySearchIndex
writer, writer,
reference, reference,
sadPerBit, sadPerBit,
sadShift,
searchStepParameter + shortenedBy, searchStepParameter + shortenedBy,
minimumColumnOffset, minimumColumnOffset,
minimumRowOffset, minimumRowOffset,
@ -763,6 +768,7 @@ internal readonly struct Av1IntraBlockCopySearchIndex
writer, writer,
reference, reference,
sadPerBit, sadPerBit,
sadShift,
minimumColumnOffset, minimumColumnOffset,
minimumRowOffset, minimumRowOffset,
maximumColumnOffset, maximumColumnOffset,
@ -795,6 +801,7 @@ internal readonly struct Av1IntraBlockCopySearchIndex
Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1MotionVector reference, Av1MotionVector reference,
int sadPerBit, int sadPerBit,
int sadShift,
int searchStepParameter, int searchStepParameter,
int minimumColumnOffset, int minimumColumnOffset,
int minimumRowOffset, int minimumRowOffset,
@ -816,6 +823,7 @@ internal readonly struct Av1IntraBlockCopySearchIndex
writer, writer,
reference, reference,
sadPerBit, sadPerBit,
sadShift,
best); best);
for (int stage = SearchRadii.Length - 1 - searchStepParameter; stage >= 0; stage--) for (int stage = SearchRadii.Length - 1 - searchStepParameter; stage >= 0; stage--)
@ -839,7 +847,7 @@ internal readonly struct Av1IntraBlockCopySearchIndex
source, source,
blockOrigin, blockOrigin,
reconstruction, reconstruction,
predictionOrigin); predictionOrigin) >> sadShift;
// Motion-vector cost is nonnegative, so a raw absolute difference that already reaches the // Motion-vector cost is nonnegative, so a raw absolute difference that already reaches the
// best combined cost cannot win and does not need an entropy-rate lookup. // best combined cost cannot win and does not need an entropy-rate lookup.
@ -891,6 +899,7 @@ internal readonly struct Av1IntraBlockCopySearchIndex
Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1MotionVector reference, Av1MotionVector reference,
int sadPerBit, int sadPerBit,
int sadShift,
int minimumColumnOffset, int minimumColumnOffset,
int minimumRowOffset, int minimumRowOffset,
int maximumColumnOffset, int maximumColumnOffset,
@ -907,6 +916,7 @@ internal readonly struct Av1IntraBlockCopySearchIndex
writer, writer,
reference, reference,
sadPerBit, sadPerBit,
sadShift,
start); start);
int startColumn = Math.Max(-ExhaustiveSearchRange, minimumColumnOffset - start.X); int startColumn = Math.Max(-ExhaustiveSearchRange, minimumColumnOffset - start.X);
@ -924,8 +934,8 @@ internal readonly struct Av1IntraBlockCopySearchIndex
blockOrigin.X + firstCandidate.X, blockOrigin.X + firstCandidate.X,
blockOrigin.Y + firstCandidate.Y); blockOrigin.Y + firstCandidate.Y);
// Four adjacent candidates share the source load and row traversal, matching the batch width // Four adjacent candidates share the source load and row traversal. Normalize each complete sum
// used by the native full-resolution pass without allocating temporary candidate buffers. // independently so batching preserves individual candidate costs and their tie order.
TOperation.GetFourSumsOfAbsoluteDifferences( TOperation.GetFourSumsOfAbsoluteDifferences(
source, source,
blockOrigin, blockOrigin,
@ -935,7 +945,7 @@ internal readonly struct Av1IntraBlockCopySearchIndex
for (int i = 0; i < ExhaustiveSearchBatchSize; i++) for (int i = 0; i < ExhaustiveSearchBatchSize; i++)
{ {
int sumOfAbsoluteDifferences = sumsOfAbsoluteDifferences[i]; int sumOfAbsoluteDifferences = sumsOfAbsoluteDifferences[i] >> sadShift;
if (sumOfAbsoluteDifferences >= bestCost) if (sumOfAbsoluteDifferences >= bestCost)
{ {
continue; continue;
@ -967,7 +977,7 @@ internal readonly struct Av1IntraBlockCopySearchIndex
source, source,
blockOrigin, blockOrigin,
reconstruction, reconstruction,
predictionOrigin); predictionOrigin) >> sadShift;
if (sumOfAbsoluteDifferences >= bestCost) if (sumOfAbsoluteDifferences >= bestCost)
{ {
@ -999,6 +1009,7 @@ internal readonly struct Av1IntraBlockCopySearchIndex
Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1MotionVector reference, Av1MotionVector reference,
int sadPerBit, int sadPerBit,
int sadShift,
Point candidate) Point candidate)
where TSample : unmanaged where TSample : unmanaged
where TOperation : struct, ISearchOperation<TSample> where TOperation : struct, ISearchOperation<TSample>
@ -1008,7 +1019,7 @@ internal readonly struct Av1IntraBlockCopySearchIndex
source, source,
blockOrigin, blockOrigin,
reconstruction, reconstruction,
predictionOrigin); predictionOrigin) >> sadShift;
Av1MotionVector vector = new(candidate.Y * 8, candidate.X * 8); Av1MotionVector vector = new(candidate.Y * 8, candidate.X * 8);
int rate = writer.GetDisplacementVectorSearchCost(vector, reference); int rate = writer.GetDisplacementVectorSearchCost(vector, reference);

Loading…
Cancel
Save