Browse Source

Implement HEVC quantization parameter derivation

pull/2633/head
James Jackson-South 1 week ago
parent
commit
d4e9b71dba
  1. 2
      HEIF_IMPLEMENTATION_PLAN.md
  2. 99
      src/ImageSharp/Formats/Heif/Hevc/HevcQuantizationParameters.cs
  3. 82
      tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcQuantizationParametersTests.cs

2
HEIF_IMPLEMENTATION_PLAN.md

@ -471,6 +471,8 @@ Implement and verify in dependency order:
- [x] Implement allocation-free SIMD-first inverse DCT for every 4/8/16/32 width and height combination, the four-by-four intra-luma inverse DST, normative intermediate and residual clipping, and saturated prediction addition for 8/10/12-bit samples. Verify normal and forced-scalar execution against fixed results and a dense scalar oracle.
- [x] Implement the SIMD-first inverse-quantization primitive for flat and scaling-list paths across every transform size, component matrix, prediction mode, transform-skip scaling rule, signed normalization direction, and range-extension precision path.
- [ ] Derive effective luma and chroma quantization parameters, including bit-depth offsets and chroma-format mapping, then connect scaling-list selection and inverse quantization to transform-unit traversal.
- [x] Implement the immutable effective-QP value used by reconstruction, including independent luma/chroma bit-depth offsets, the normative 4:2:0 mapping plateaus, the 4:2:2/4:4:4 saturation rule, and combined picture/slice/coding-unit chroma offsets.
- [ ] Select each transform unit's coding-unit luma QP and chroma-adjustment-list entry, then pass the derived component QP into inverse quantization.
- [ ] Implement transform skip, coefficient rotation, implicit and explicit residual DPCM, transquant bypass, and lossless reconstruction.
- [ ] Connect coefficient decoding, inverse quantization, transform selection, reusable scratch, and add/clip to transform-unit traversal.
- [ ] Deblocking and sample-adaptive offset for every signaled luma/chroma and bit-depth path.

99
src/ImageSharp/Formats/Heif/Hevc/HevcQuantizationParameters.cs

@ -0,0 +1,99 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Hevc;
/// <summary>
/// Contains the effective HEVC quantization parameters for one transform unit.
/// </summary>
internal readonly struct HevcQuantizationParameters
{
/// <summary>
/// Initializes a new instance of the <see cref="HevcQuantizationParameters"/> struct.
/// </summary>
/// <param name="lumaQuantizationParameter">The effective coding-unit luma quantization parameter before the luma bit-depth offset.</param>
/// <param name="lumaBitDepth">The reconstructed luma precision.</param>
/// <param name="chromaBitDepth">The reconstructed chroma precision.</param>
/// <param name="chromaFormat">The sequence chroma-format identifier.</param>
/// <param name="cbQuantizationParameterOffset">The combined picture, slice, and coding-unit Cb quantization-parameter offset.</param>
/// <param name="crQuantizationParameterOffset">The combined picture, slice, and coding-unit Cr quantization-parameter offset.</param>
public HevcQuantizationParameters(
int lumaQuantizationParameter,
int lumaBitDepth,
int chromaBitDepth,
byte chromaFormat,
int cbQuantizationParameterOffset,
int crQuantizationParameterOffset)
{
int lumaBitDepthOffset = 6 * (lumaBitDepth - 8);
int chromaBitDepthOffset = 6 * (chromaBitDepth - 8);
this.Luma = lumaQuantizationParameter + lumaBitDepthOffset;
this.Cb = GetChromaQuantizationParameter(lumaQuantizationParameter, cbQuantizationParameterOffset, chromaBitDepthOffset, chromaFormat);
this.Cr = GetChromaQuantizationParameter(lumaQuantizationParameter, crQuantizationParameterOffset, chromaBitDepthOffset, chromaFormat);
}
/// <summary>
/// Gets the effective nonnegative luma quantization parameter including its bit-depth offset.
/// </summary>
public int Luma { get; }
/// <summary>
/// Gets the effective nonnegative blue-difference chroma quantization parameter including its bit-depth offset.
/// </summary>
public int Cb { get; }
/// <summary>
/// Gets the effective nonnegative red-difference chroma quantization parameter including its bit-depth offset.
/// </summary>
public int Cr { get; }
/// <summary>
/// Gets the H.265 Table 8-10 chroma quantization-parameter mapping for 4:2:0 pictures.
/// </summary>
private static ReadOnlySpan<byte> Chroma420QuantizationParameterMap =>
[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
29, 30, 31, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
];
/// <summary>
/// Gets the effective quantization parameter for the selected reconstruction plane.
/// </summary>
/// <param name="plane">The reconstruction plane.</param>
/// <returns>The effective nonnegative quantization parameter including its bit-depth offset.</returns>
public int Get(HevcPlane plane) => plane switch
{
HevcPlane.Y => this.Luma,
HevcPlane.Cb => this.Cb,
_ => this.Cr,
};
/// <summary>
/// Derives an effective chroma quantization parameter from the luma value and combined component offset.
/// </summary>
/// <param name="lumaQuantizationParameter">The effective coding-unit luma quantization parameter before its bit-depth offset.</param>
/// <param name="componentOffset">The combined picture, slice, and coding-unit component offset.</param>
/// <param name="chromaBitDepthOffset">Six times the number of chroma bits above eight.</param>
/// <param name="chromaFormat">The sequence chroma-format identifier.</param>
/// <returns>The effective nonnegative chroma quantization parameter including its bit-depth offset.</returns>
private static int GetChromaQuantizationParameter(
int lumaQuantizationParameter,
int componentOffset,
int chromaBitDepthOffset,
byte chromaFormat)
{
int unscaled = Math.Clamp(lumaQuantizationParameter + componentOffset, -chromaBitDepthOffset, 57);
if (unscaled < 0)
{
return unscaled + chromaBitDepthOffset;
}
// H.265 section 8.6.1 maps nonnegative chroma QP before adding the bit-depth offset. The 4:2:0 table
// contains plateaus above QP 29, whereas 4:2:2 and 4:4:4 remain linear through 51 and then saturate.
int mapped = chromaFormat == 1
? Chroma420QuantizationParameterMap[unscaled]
: Math.Min(unscaled, 51);
return mapped + chromaBitDepthOffset;
}
}

82
tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcQuantizationParametersTests.cs

@ -0,0 +1,82 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Hevc;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc;
/// <summary>
/// Verifies HEVC luma and chroma quantization-parameter derivation.
/// </summary>
[Trait("Format", "Heic")]
public class HevcQuantizationParametersTests
{
/// <summary>
/// Verifies that luma quantization parameters include the precision-derived offset.
/// </summary>
/// <param name="quantizationParameter">The luma quantization parameter before the bit-depth offset.</param>
/// <param name="bitDepth">The reconstructed luma precision.</param>
/// <param name="expected">The expected effective luma quantization parameter.</param>
[Theory]
[InlineData(22, 8, 22)]
[InlineData(-12, 10, 0)]
[InlineData(-24, 12, 0)]
[InlineData(51, 12, 75)]
public void LumaIncludesBitDepthOffset(int quantizationParameter, int bitDepth, int expected)
{
HevcQuantizationParameters parameters = new(quantizationParameter, bitDepth, bitDepth, 1, 0, 0);
Assert.Equal(expected, parameters.Luma);
Assert.Equal(expected, parameters.Get(HevcPlane.Y));
}
/// <summary>
/// Verifies the 4:2:0 chroma mapping plateaus and the upper mapped value.
/// </summary>
/// <param name="quantizationParameter">The luma quantization parameter before component offsets.</param>
/// <param name="expected">The expected effective eight-bit chroma quantization parameter.</param>
[Theory]
[InlineData(29, 29)]
[InlineData(30, 29)]
[InlineData(35, 33)]
[InlineData(37, 34)]
[InlineData(43, 37)]
[InlineData(51, 45)]
public void Chroma420UsesNormativeMapping(int quantizationParameter, int expected)
{
HevcQuantizationParameters parameters = new(quantizationParameter, 8, 8, 1, 0, 0);
Assert.Equal(expected, parameters.Cb);
Assert.Equal(expected, parameters.Cr);
}
/// <summary>
/// Verifies that 4:2:2 and 4:4:4 chroma quantization parameters are linear through 51 and saturate above it.
/// </summary>
/// <param name="chromaFormat">The tested sequence chroma-format identifier.</param>
[Theory]
[InlineData((byte)2)]
[InlineData((byte)3)]
public void FullResolutionMappingsSaturateAboveFiftyOne(byte chromaFormat)
{
HevcQuantizationParameters parameters = new(51, 8, 8, chromaFormat, 6, 6);
Assert.Equal(51, parameters.Cb);
Assert.Equal(51, parameters.Cr);
}
/// <summary>
/// Verifies negative chroma values and independent combined component offsets at higher precision.
/// </summary>
[Fact]
public void ChromaAppliesCombinedOffsetsAndBitDepthOffset()
{
HevcQuantizationParameters parameters = new(-8, 10, 12, 1, -8, 20);
Assert.Equal(4, parameters.Luma);
Assert.Equal(8, parameters.Cb);
Assert.Equal(36, parameters.Cr);
Assert.Equal(parameters.Cb, parameters.Get(HevcPlane.Cb));
Assert.Equal(parameters.Cr, parameters.Get(HevcPlane.Cr));
}
}
Loading…
Cancel
Save