diff --git a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs index f556aa169..3205c6712 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcPictureDecoderTests.cs @@ -80,6 +80,57 @@ public class HevcPictureDecoderTests } } + /// + /// Verifies coding-tree and transform-tree conformance streams against native-plane digests produced by the + /// pinned HM decoder from output that matches each archive's published checksum. + /// + /// The official Annex B conformance stream. + /// The expected coding-tree-block size logarithm. + /// The expected minimum coding-block size logarithm. + /// The expected minimum transform-block size logarithm. + /// The expected internal intra transform-depth limit. + /// The reference luma-plane MD5 digest. + /// The reference blue-difference-plane MD5 digest. + /// The reference red-difference-plane MD5 digest. + [Theory] + [InlineData(TestImages.Heif.RqtA, 6, 3, 2, 1, "adf2bfab6de808840c82f48eee31a0a5", "4b29b1d2699b77e42a4e22fb0d70993e", "8ada6d784647329a4f0da232176914f4")] + [InlineData(TestImages.Heif.RqtB, 6, 3, 2, 2, "797f41be9a4d53b640332f9d03b1f304", "ef67e5ceff912f7aeb14f2e00ddd5cbf", "e2af5fa6f3c4bcc96dfa95cb8947a7db")] + [InlineData(TestImages.Heif.RqtC, 6, 3, 2, 3, "5d431346cd0b3f52846fc20fc0afdcc4", "ff9355b8cc72d77edbad6f5bd4938df1", "24f8aae8c00f418af487be29d2a5a126")] + [InlineData(TestImages.Heif.RqtD, 6, 3, 2, 4, "30138fa13664590d16355be8f7362eb2", "6198b3d1e990baadcaa533b4c44a5be2", "45f5427aec28b4f4655240812387607b")] + [InlineData(TestImages.Heif.RqtE, 6, 3, 2, 5, "e9e182380f3209ef877b75199b546c00", "f555fd054855dc5a1cb82cb8f3393b5a", "baed53765a5424fbc38aa541917d0625")] + [InlineData(TestImages.Heif.StructA, 4, 3, 2, 2, "bf47fb8ff96a225c2646c0539744ac93", "c105b60fd0e8740574fb76972bbc3b1a", "f9e2d8327da50734b54771df29b343bb")] + [InlineData(TestImages.Heif.StructB, 5, 4, 2, 2, "61e13729a4e3d6fd96f5002a501d1d60", "fee8312ceaccb1f254ef21449f104e4e", "dbe684d5fffbbc2f5ca90b205018060c")] + [InlineData(TestImages.Heif.TuSizeA, 6, 5, 4, 3, "17a84e6f516dbcb8810c7548bf6e216c", "edd3085f7a152d7ffaabae816f4942ac", "e8b47494064c1a730aca3c6ee23572c8")] + public void DecodeOfficialTraversalPictureMatchesPinnedHmDigest( + string path, + int expectedCodingTreeBlockLog2, + int expectedMinCodingBlockLog2, + int expectedMinTransformBlockLog2, + int expectedMaxTransformHierarchyDepthIntra, + string lumaDigest, + string chromaBlueDigest, + string chromaRedDigest) + { + byte[] annexB = TestFile.Create(path).Bytes; + ConvertAnnexBStillPicture(annexB, 8, 1, out byte[] configurationData, out byte[] itemData); + HevcCodecConfiguration configuration = new(configurationData); + HevcImageItemBitstream bitstream = new(itemData, configuration); + HevcSequenceParameterSet sequenceParameterSet = bitstream.SliceSegments[0].PictureParameterSet.SequenceParameterSet; + using HevcPictureDecoder decoder = new(Configuration.Default, bitstream.SliceSegments[0].PictureParameterSet); + + decoder.Decode(bitstream); + + Assert.Equal(8, decoder.Picture.BitDepthLuma); + Assert.Equal(1, decoder.Picture.ChromaFormat); + Assert.Equal(expectedCodingTreeBlockLog2, sequenceParameterSet.CodingTreeBlockLog2); + Assert.Equal(expectedMinCodingBlockLog2, sequenceParameterSet.MinCodingBlockLog2); + Assert.Equal(expectedMinTransformBlockLog2, sequenceParameterSet.MinTransformBlockLog2); + Assert.Equal(expectedMaxTransformHierarchyDepthIntra, sequenceParameterSet.MaxTransformHierarchyDepthIntra); + Assert.Equal(lumaDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Y)); + Assert.Equal(chromaBlueDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cb)); + Assert.Equal(chromaRedDigest, GetPlaneDigest(decoder.Picture, HevcPlane.Cr)); + } + /// /// Verifies all reconstructed samples from a real HEIC grid tile against the HM reference decoder. /// @@ -290,11 +341,12 @@ public class HevcPictureDecoderTests } } - Assert.True( - mismatchCount == 0, + string message = $"{plane} contained {mismatchCount} differing samples. The maximum difference was {maximumDifference}; " + $"the mismatches span ({minimumMismatchX}, {minimumMismatchY}) through ({maximumMismatchX}, {maximumMismatchY}), " + - $"and the first mismatch at ({firstMismatchX}, {firstMismatchY}) was {firstActual}, expected {firstExpected}."); + $"and the first mismatch at ({firstMismatchX}, {firstMismatchY}) was {firstActual}, expected {firstExpected}."; + + Assert.True(mismatchCount == 0, message); } /// @@ -593,7 +645,6 @@ public class HevcPictureDecoderTests Assert.Single( allocator.ReturnLog, returned => returned.AllocationId == allocation.AllocationId); - } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 297fc0921..97a5a9329 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -1304,6 +1304,14 @@ public static class TestImages public const string General12Bit420 = "Heif/Hevc/Conformance/GENERAL_12b_420_RExt_Sony_1.bit"; public const string General12Bit422 = "Heif/Hevc/Conformance/GENERAL_12b_422_RExt_Sony_1.bit"; public const string General12Bit444 = "Heif/Hevc/Conformance/GENERAL_12b_444_RExt_Sony_2.bit"; + public const string RqtA = "Heif/Hevc/Conformance/RQT_A_HHI_4.bit"; + public const string RqtB = "Heif/Hevc/Conformance/RQT_B_HHI_4.bit"; + public const string RqtC = "Heif/Hevc/Conformance/RQT_C_HHI_4.bit"; + public const string RqtD = "Heif/Hevc/Conformance/RQT_D_HHI_4.bit"; + public const string RqtE = "Heif/Hevc/Conformance/RQT_E_HHI_4.bit"; + public const string StructA = "Heif/Hevc/Conformance/STRUCT_A_Samsung_7.bit"; + public const string StructB = "Heif/Hevc/Conformance/STRUCT_B_Samsung_7.bit"; + public const string TuSizeA = "Heif/Hevc/Conformance/TUSIZE_A_Samsung_1.bit"; public const string Image1 = "Heif/image1.heic"; public const string Image2 = "Heif/image2.heic"; public const string Image3 = "Heif/image3.heic"; diff --git a/tests/Images/Input/Heif/Hevc/Conformance/README.md b/tests/Images/Input/Heif/Hevc/Conformance/README.md index 098a490c3..0d2cde298 100644 --- a/tests/Images/Input/Heif/Hevc/Conformance/README.md +++ b/tests/Images/Input/Heif/Hevc/Conformance/README.md @@ -1,9 +1,17 @@ -# HEVC Range Extensions conformance fixtures +# HEVC conformance fixtures These fixtures come from the electronic attachment to [ITU-T H.265.1 (10/2018)](https://www.itu.int/rec/T-REC-H.265.1-201810-I/en), the conformance specification aligned with ISO/IEC 23008-8. Table 4 identifies the Sony `GENERAL_*_RExt` streams and their required profiles. -The `.bit` files are the original Annex B conformance streams. Each `_frame0.yuv` file contains the first decoded picture extracted byte-for-byte from the corresponding published planar reference output; it was not generated by ImageSharp. +The `.bit` files are the original Annex B conformance streams. The Samsung archives name their payloads `.bin`; the repository stores those unchanged bytes with the common `.bit` extension. Each `GENERAL_*_frame0.yuv` file contains the first decoded picture extracted byte-for-byte from the corresponding published planar reference output; it was not generated by ImageSharp. `HevcPictureDecoderTests.DecodeOfficialRangeExtensionsPictureMatchesPublishedDigest` limits each stream to its first independently coded picture, adapts that picture to the bounded `hvc1` item contract, compares every reconstructed sample with `_frame0.yuv`, and verifies the published decoded-picture MD5 value for every present plane. The retained matrix covers the ImageSharp 8, 10, and 12-bit still-image precision boundary across monochrome, 4:2:0, 4:2:2, and 4:4:4. Tool-specific H.265.1 streams remain necessary before every exposed Range Extensions tool can be marked complete. + +The Main-profile traversal matrix comes from the official `RQT_A_HHI_4` through `RQT_E_HHI_4`, `STRUCT_A_Samsung_7`, `STRUCT_B_Samsung_7`, and `TUSIZE_A_Samsung_1` archives. Their published descriptions target residual-quadtree intra hierarchy depths zero through four, coding-tree and minimum-coding-unit structure, and a 64x64 coding-tree block with 32x32 minimum coding units and 16x16 minimum luma transforms, respectively. + +`HevcPictureDecoderTests.DecodeOfficialTraversalPictureMatchesPinnedHmDigest` decodes each first independently coded picture through the production still-item path and compares all three native planes with fixed MD5 digests. The RQT values are the streams' decoded-picture-hash SEI values. For STRUCT and TUSIZE, the plane values were calculated from the first picture written by the [HM reference decoder at commit `9c1f298659ab0cee9dc13d23d0304221575410b9`](https://vcgit.hhi.fraunhofer.de/jvet/HM/-/commit/9c1f298659ab0cee9dc13d23d0304221575410b9); the complete HM output also matched each archive's published file MD5: + +- `STRUCT_A_Samsung_7`: `02f02cc9a67222004ae18ee6bebd475a` +- `STRUCT_B_Samsung_7`: `1f472e014aff120681e8482c8a04ef93` +- `TUSIZE_A_Samsung_1`: `5e6c198c852e92cdcca0e137fc2610c4` diff --git a/tests/Images/Input/Heif/Hevc/Conformance/RQT_A_HHI_4.bit b/tests/Images/Input/Heif/Hevc/Conformance/RQT_A_HHI_4.bit new file mode 100644 index 000000000..8466c1c00 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/RQT_A_HHI_4.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c07a976943200d97ba9eb0a16a858d2c77fb3fe37410c6c6f956e5960b2f4621 +size 66244 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/RQT_B_HHI_4.bit b/tests/Images/Input/Heif/Hevc/Conformance/RQT_B_HHI_4.bit new file mode 100644 index 000000000..6a9a096ac --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/RQT_B_HHI_4.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49a8037bbbed4efa8de285c522d215c3c5a117dd58d17822db0ffb9808161e29 +size 66149 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/RQT_C_HHI_4.bit b/tests/Images/Input/Heif/Hevc/Conformance/RQT_C_HHI_4.bit new file mode 100644 index 000000000..bc5295c2c --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/RQT_C_HHI_4.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a7b157215595b68ddbb70d952fb786923826c26ecc448bbfe36f4df47f9abc5c +size 66094 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/RQT_D_HHI_4.bit b/tests/Images/Input/Heif/Hevc/Conformance/RQT_D_HHI_4.bit new file mode 100644 index 000000000..052db04bb --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/RQT_D_HHI_4.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b37468d7e4acf7d1fb97bf0062e0124785d3bda1284e1f698a1e8eb6441d76b +size 66078 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/RQT_E_HHI_4.bit b/tests/Images/Input/Heif/Hevc/Conformance/RQT_E_HHI_4.bit new file mode 100644 index 000000000..111213496 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/RQT_E_HHI_4.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d792e442c19d9ffc45f43a8842c7a6ad57d0a51d62364c43ef97810689c2eea8 +size 84820 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/STRUCT_A_Samsung_7.bit b/tests/Images/Input/Heif/Hevc/Conformance/STRUCT_A_Samsung_7.bit new file mode 100644 index 000000000..144dcaa04 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/STRUCT_A_Samsung_7.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e511007d79d66f6988654a7f89a934cd68329be6492143cda9570e3741f5a551 +size 319053 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/STRUCT_B_Samsung_7.bit b/tests/Images/Input/Heif/Hevc/Conformance/STRUCT_B_Samsung_7.bit new file mode 100644 index 000000000..654dd6ea6 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/STRUCT_B_Samsung_7.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e58b04f1434449721957857ca38adab3865b71cb93ae9705a4361b7cb4400d8e +size 324322 diff --git a/tests/Images/Input/Heif/Hevc/Conformance/TUSIZE_A_Samsung_1.bit b/tests/Images/Input/Heif/Hevc/Conformance/TUSIZE_A_Samsung_1.bit new file mode 100644 index 000000000..7475185e8 --- /dev/null +++ b/tests/Images/Input/Heif/Hevc/Conformance/TUSIZE_A_Samsung_1.bit @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23c9959a0dd09d9a0b9c64b980794ca18214d7a3cb0fd091ca87f98dda75acf4 +size 1238476