Пример #1
0
    def test_pictures(self, parse_code):
        stream = Stream(
            sequences=[
                Sequence(
                    data_units=[
                        # First in sequence should be auto-numbered to expected start
                        # offset
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(picture_number=AUTO)
                            ),
                        ),
                        # If picture number not mentioned, it should be autofilled
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            picture_parse=PictureParse(picture_header=PictureHeader()),
                        ),
                        # If explicit picture number given, should be used
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(picture_number=0xFFFFFFFE)
                            ),
                        ),
                        # Should continue from last explicit number if given
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(picture_number=AUTO)
                            ),
                        ),
                        # Should wrap-around
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(picture_number=AUTO)
                            ),
                        ),
                    ]
                )
            ]
        )

        autofill_picture_number(stream, 1234)

        picture_numbers = [
            data_unit["picture_parse"]["picture_header"]["picture_number"]
            for seq in stream["sequences"]
            for data_unit in seq["data_units"]
        ]
        assert picture_numbers == [
            1234,
            1235,
            0xFFFFFFFE,
            0xFFFFFFFF,
            0x0,
        ]
Пример #2
0
 def test_picture_tp_already_exists(self, parse_code, existing_tp):
     data_unit = DataUnit(
         parse_info=ParseInfo(parse_code=parse_code),
         picture_parse=PictureParse(
             wavelet_transform=WaveletTransform(
                 transform_parameters=existing_tp,
             ),
         ),
     )
     assert get_transform_parameters(data_unit) is existing_tp
Пример #3
0
    def test_picture_tp_created(self, parse_code):
        data_unit = DataUnit(
            parse_info=ParseInfo(parse_code=parse_code),
        )
        tp = get_transform_parameters(data_unit)
        assert tp == TransformParameters()
        tp["dwt_depth"] = 4

        # Transform parameters (and parent structures) created
        assert data_unit == DataUnit(
            parse_info=ParseInfo(parse_code=parse_code),
            picture_parse=PictureParse(
                wavelet_transform=WaveletTransform(
                    transform_parameters=TransformParameters(
                        dwt_depth=4,
                    ),
                ),
            ),
        )
Пример #4
0
    def test_finalizer_works(self):
        f = BytesIO()
        w = BitstreamWriter(f)

        # Sequence with every data unit type and fully automatic numbers
        stream = Stream(
            sequences=[
                Sequence(
                    data_units=[
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.sequence_header
                            ),
                            sequence_header=SequenceHeader(
                                parse_parameters=ParseParameters(major_version=3),
                                video_parameters=SourceParameters(
                                    # Tiny custom frame-size used to reduce test suite
                                    # runtime
                                    frame_size=FrameSize(
                                        custom_dimensions_flag=True,
                                        frame_width=4,
                                        frame_height=4,
                                    )
                                ),
                            ),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.high_quality_picture
                            ),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(picture_number=0)
                            ),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.low_delay_picture
                            ),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(picture_number=0)
                            ),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.high_quality_picture_fragment
                            ),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(picture_number=0)
                            ),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.high_quality_picture_fragment
                            ),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(picture_number=0)
                            ),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.padding_data
                            ),
                            padding=Padding(bytes=b"123"),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.auxiliary_data
                            ),
                            auxiliary_data=AuxiliaryData(bytes=b"123"),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.end_of_sequence
                            ),
                        ),
                    ]
                )
            ]
        )

        (
            next_parse_offsets_to_autofill,
            previous_parse_offsets_to_autofill,
        ) = autofill_parse_offsets(stream)

        with Serialiser(w, stream, vc2_default_values_with_auto) as serdes:
            vc2.parse_stream(serdes, State())
        w.flush()

        offset_before = w.tell()
        autofill_parse_offsets_finalize(
            w,
            serdes.context,
            next_parse_offsets_to_autofill,
            previous_parse_offsets_to_autofill,
        )
        assert w.tell() == offset_before

        f.seek(0)
        r = BitstreamReader(f)
        with Deserialiser(r) as serdes:
            vc2.parse_stream(serdes, State())

        parse_infos = [
            data_unit["parse_info"]
            for sequence in serdes.context["sequences"]
            for data_unit in sequence["data_units"]
        ]

        # Check for start/end offsets being zero
        assert parse_infos[0]["previous_parse_offset"] == 0
        assert parse_infos[-1]["next_parse_offset"] == 0

        # Check for consistency and plusibility of offsets
        for pi1, pi2 in zip(parse_infos, parse_infos[1:]):
            assert pi1["next_parse_offset"] > 13
            assert pi2["previous_parse_offset"] > 13

            assert pi1["next_parse_offset"] == pi2["previous_parse_offset"]
Пример #5
0
 def test_removal_of_extended_transform_parameters(self):
     # NB: The stream specified below is actually compatible with version 2
     # so the extended transform parameters field should be removed if and
     # only if the major version was set to AUTO in the proceeding sequence
     # header.
     stream = Stream(
         sequences=[
             Sequence(
                 data_units=[
                     DataUnit(
                         parse_info=ParseInfo(
                             parse_code=tables.ParseCodes.sequence_header,
                         ),
                     ),
                     DataUnit(
                         parse_info=ParseInfo(
                             parse_code=tables.ParseCodes.high_quality_picture,
                         ),
                         picture_parse=PictureParse(
                             wavelet_transform=WaveletTransform(
                                 transform_parameters=TransformParameters(
                                     wavelet_index=tables.WaveletFilters.haar_no_shift,
                                     dwt_depth=2,
                                     extended_transform_parameters=ExtendedTransformParameters(
                                         asym_transform_index_flag=True,
                                         wavelet_index_ho=tables.WaveletFilters.haar_no_shift,
                                         asym_transform_flag=True,
                                         dwt_depth_ho=0,
                                     ),
                                 ),
                             ),
                         ),
                     ),
                 ]
             )
         ]
     )
     autofill_major_version(stream)
     assert stream == Stream(
         sequences=[
             Sequence(
                 data_units=[
                     DataUnit(
                         parse_info=ParseInfo(
                             parse_code=tables.ParseCodes.sequence_header,
                         ),
                         sequence_header=SequenceHeader(
                             parse_parameters=ParseParameters(
                                 major_version=2,
                             ),
                         ),
                     ),
                     DataUnit(
                         parse_info=ParseInfo(
                             parse_code=tables.ParseCodes.high_quality_picture,
                         ),
                         picture_parse=PictureParse(
                             wavelet_transform=WaveletTransform(
                                 transform_parameters=TransformParameters(
                                     wavelet_index=tables.WaveletFilters.haar_no_shift,
                                     dwt_depth=2,
                                 ),
                             ),
                         ),
                     ),
                 ]
             )
         ]
     )
Пример #6
0
    def test_version_selection(self, parameters, exp_version):
        profile = parameters.get("profile", tables.Profiles.low_delay)
        frame_rate_index = parameters.get("frame_rate_index")
        signal_range_index = parameters.get("signal_range_index")
        color_spec_index = parameters.get("color_spec_index", 0)
        color_primaries_index = parameters.get("color_primaries_index", 0)
        color_matrix_index = parameters.get("color_matrix_index", 0)
        transfer_function_index = parameters.get("transfer_function_index", 0)
        wavelet_index = parameters.get(
            "wavelet_index", tables.WaveletFilters.haar_no_shift
        )
        wavelet_index_ho = parameters.get("wavelet_index_ho")
        dwt_depth_ho = parameters.get("dwt_depth_ho", None)
        parse_code = parameters.get("parse_code", tables.ParseCodes.low_delay_picture)

        # Kept separate to allow later checking of the version chosen
        pp = ParseParameters(major_version=AUTO, profile=profile)

        # Repeated in the appropriate place for fragments and pictures
        tp = TransformParameters(
            wavelet_index=wavelet_index,
            dwt_depth=2,
            extended_transform_parameters=ExtendedTransformParameters(
                asym_transform_index_flag=wavelet_index_ho is not None,
                wavelet_index_ho=wavelet_index_ho,
                asym_transform_flag=dwt_depth_ho is not None,
                dwt_depth_ho=dwt_depth_ho,
            ),
        )

        stream = Stream(
            sequences=[
                Sequence(
                    data_units=[
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=tables.ParseCodes.sequence_header
                            ),
                            sequence_header=SequenceHeader(
                                parse_parameters=pp,
                                video_parameters=SourceParameters(
                                    frame_rate=FrameRate(
                                        custom_frame_rate_flag=frame_rate_index
                                        is not None,
                                        index=frame_rate_index,
                                    ),
                                    signal_range=SignalRange(
                                        custom_signal_range_flag=signal_range_index
                                        is not None,
                                        index=signal_range_index,
                                    ),
                                    color_spec=ColorSpec(
                                        custom_color_spec_flag=True,
                                        index=color_spec_index,
                                        color_primaries=ColorPrimaries(
                                            custom_color_primaries_flag=color_primaries_index
                                            is not None,
                                            index=color_primaries_index,
                                        ),
                                        color_matrix=ColorMatrix(
                                            custom_color_matrix_flag=color_matrix_index
                                            is not None,
                                            index=color_matrix_index,
                                        ),
                                        transfer_function=TransferFunction(
                                            custom_transfer_function_flag=transfer_function_index
                                            is not None,
                                            index=transfer_function_index,
                                        ),
                                    ),
                                ),
                            ),
                        ),
                        DataUnit(
                            parse_info=ParseInfo(
                                parse_code=parse_code,
                            ),
                            picture_parse=PictureParse(
                                wavelet_transform=WaveletTransform(
                                    transform_parameters=tp,
                                )
                            ),
                            fragment_parse=FragmentParse(
                                transform_parameters=tp,
                            ),
                        ),
                    ]
                )
            ]
        )
        autofill_major_version(stream)
        assert pp["major_version"] == exp_version
        if pp["major_version"] == 3:
            assert "extended_transform_parameters" in tp
        else:
            assert "extended_transform_parameters" not in tp
Пример #7
0
class TestAutofillPictureNumber(object):
    @pytest.mark.parametrize(
        "seq",
        [
            # Empty dictionary
            Sequence(),
            # Empty sequence
            Sequence(data_units=[]),
            # Sequence with immediate end-of-sequence
            Sequence(
                data_units=[
                    DataUnit(
                        parse_info=ParseInfo(
                            parse_code=tables.ParseCodes.end_of_sequence
                        ),
                    )
                ]
            ),
            # Sequence with no pictures
            Sequence(
                data_units=[
                    DataUnit(
                        parse_info=ParseInfo(
                            parse_code=tables.ParseCodes.sequence_header
                        )
                    ),
                    DataUnit(
                        parse_info=ParseInfo(parse_code=tables.ParseCodes.padding_data)
                    ),
                    DataUnit(
                        parse_info=ParseInfo(
                            parse_code=tables.ParseCodes.auxiliary_data
                        )
                    ),
                    DataUnit(
                        parse_info=ParseInfo(
                            parse_code=tables.ParseCodes.end_of_sequence
                        )
                    ),
                ]
            ),
        ],
    )
    def test_non_picture_sequence(self, seq):
        # Shouldn't crash or make any changes
        stream = Stream(sequences=[seq])
        stream_orig = deepcopy(stream)
        autofill_picture_number(stream)
        assert stream == stream_orig

    @pytest.mark.parametrize(
        "seq",
        (
            [
                # Pictures
                Sequence(
                    data_units=[
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(
                                    picture_number=1234,
                                )
                            ),
                        )
                    ]
                )
                for parse_code in [
                    tables.ParseCodes.high_quality_picture,
                    tables.ParseCodes.low_delay_picture,
                ]
            ]
            + [
                # Fragments
                Sequence(
                    data_units=[
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(
                                    picture_number=1234,
                                    fragment_slice_count=fragment_slice_count,
                                )
                            ),
                        )
                    ]
                )
                for parse_code in [
                    tables.ParseCodes.high_quality_picture_fragment,
                    tables.ParseCodes.low_delay_picture_fragment,
                ]
                for fragment_slice_count in [0, 1]
            ]
        ),
    )
    def test_dont_change_non_auto_picture_numbers(self, seq):
        # Shouldn't crash or make any changes
        stream = Stream(sequences=[seq])
        stream_orig = deepcopy(stream)
        autofill_picture_number(stream)
        assert stream == stream_orig

    @pytest.mark.parametrize(
        "parse_code",
        [tables.ParseCodes.high_quality_picture, tables.ParseCodes.low_delay_picture],
    )
    def test_pictures(self, parse_code):
        stream = Stream(
            sequences=[
                Sequence(
                    data_units=[
                        # First in sequence should be auto-numbered to expected start
                        # offset
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(picture_number=AUTO)
                            ),
                        ),
                        # If picture number not mentioned, it should be autofilled
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            picture_parse=PictureParse(picture_header=PictureHeader()),
                        ),
                        # If explicit picture number given, should be used
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(picture_number=0xFFFFFFFE)
                            ),
                        ),
                        # Should continue from last explicit number if given
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(picture_number=AUTO)
                            ),
                        ),
                        # Should wrap-around
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            picture_parse=PictureParse(
                                picture_header=PictureHeader(picture_number=AUTO)
                            ),
                        ),
                    ]
                )
            ]
        )

        autofill_picture_number(stream, 1234)

        picture_numbers = [
            data_unit["picture_parse"]["picture_header"]["picture_number"]
            for seq in stream["sequences"]
            for data_unit in seq["data_units"]
        ]
        assert picture_numbers == [
            1234,
            1235,
            0xFFFFFFFE,
            0xFFFFFFFF,
            0x0,
        ]

    @pytest.mark.parametrize(
        "parse_code",
        [
            tables.ParseCodes.high_quality_picture_fragment,
            tables.ParseCodes.low_delay_picture_fragment,
        ],
    )
    def test_fragments(self, parse_code):
        stream = Stream(
            sequences=[
                Sequence(
                    data_units=[
                        # First in sequence should be auto-numbered to expected start
                        # offset
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(
                                    picture_number=AUTO,
                                    fragment_slice_count=0,
                                )
                            ),
                        ),
                        # If not the first fragment in the picture, the picture number
                        # should not be incremented
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(
                                    picture_number=AUTO,
                                    fragment_slice_count=1,
                                )
                            ),
                        ),
                        # If picture number not mentioned, it should still be autofilled
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(
                                    fragment_slice_count=1,
                                )
                            ),
                        ),
                        # Should auto increment on new picture started
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(
                                    picture_number=AUTO,
                                    fragment_slice_count=0,
                                )
                            ),
                        ),
                        # If explicit picture number when given, should be used
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(
                                    picture_number=4321,
                                    fragment_slice_count=0,
                                )
                            ),
                        ),
                        # ...even if that changes the picture number mid picture
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(
                                    picture_number=0xFFFFFFFE,
                                    fragment_slice_count=1,
                                )
                            ),
                        ),
                        # Should continue on from last explicit number
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(
                                    picture_number=AUTO,
                                    fragment_slice_count=0,
                                )
                            ),
                        ),
                        # Should wrap-around
                        DataUnit(
                            parse_info=ParseInfo(parse_code=parse_code),
                            fragment_parse=FragmentParse(
                                fragment_header=FragmentHeader(
                                    picture_number=AUTO,
                                    fragment_slice_count=0,
                                )
                            ),
                        ),
                    ]
                )
            ]
        )

        autofill_picture_number(stream, 1234)

        picture_numbers = [
            data_unit["fragment_parse"]["fragment_header"]["picture_number"]
            for seq in stream["sequences"]
            for data_unit in seq["data_units"]
        ]
        assert picture_numbers == [
            1234,
            1234,
            1234,
            1235,
            4321,
            0xFFFFFFFE,
            0xFFFFFFFF,
            0x0,
        ]

    @pytest.mark.parametrize(
        "parse_code",
        [tables.ParseCodes.high_quality_picture, tables.ParseCodes.low_delay_picture],
    )
    def test_multiple_sequences(self, parse_code):
        stream = Stream(
            sequences=[
                Sequence(
                    data_units=[
                        DataUnit(parse_info=ParseInfo(parse_code=parse_code)),
                        DataUnit(parse_info=ParseInfo(parse_code=parse_code)),
                        DataUnit(parse_info=ParseInfo(parse_code=parse_code)),
                    ]
                ),
                Sequence(
                    data_units=[
                        DataUnit(parse_info=ParseInfo(parse_code=parse_code)),
                        DataUnit(parse_info=ParseInfo(parse_code=parse_code)),
                        DataUnit(parse_info=ParseInfo(parse_code=parse_code)),
                    ]
                ),
            ]
        )

        autofill_picture_number(stream, 1234)

        picture_numbers = [
            data_unit["picture_parse"]["picture_header"]["picture_number"]
            for seq in stream["sequences"]
            for data_unit in seq["data_units"]
        ]
        assert picture_numbers == [
            1234,
            1235,
            1236,
            # Restarts in second sequence
            1234,
            1235,
            1236,
        ]
Пример #8
0
def make_picture_parse(codec_features,
                       picture,
                       minimum_qindex=0,
                       minimum_slice_size_scaler=1):
    """
    Compress a picture.

    Raises :py:exc:`PictureBytesSpecifiedForLosslessModeError` if
    ``picture_bytes`` is specifiied for a lossless coding mode.

    Raises :py:exc:`InsufficientLDPictureBytesError`
    :py:exc:`InsufficientHQPictureBytesError` if ``picture_bytes`` is too small
    for the coding mode used.

    Parameters
    ==========
    codec_features : :py:class:`~vc2_conformance.codec_features.CodecFeatures`
    picture : {"Y": [[s, ...], ...], "C1": ..., "C2": ..., "pic_num": int}
        The picture to be encoded. This picture will be compressed using a
        simple VC-2 encoder implementation. It does not necessarily produce the
        most high-quality encodings. If ``pic_num`` is omitted,
        ``picture_number`` fields will be omitted in the output.
    minimum_qindex : int
        Specifies the minimum quantization index to be used. Must be 0 for
        lossless codecs.
    minimum_slice_size_scaler : int
        Specifies the minimum slice_size_scaler to be used for high quality
        pictures. Ignored in low delay mode.

    Returns
    =======
    transform_data : :py:class:`vc2_conformance.bitstream.TransformData`
        The transform data, ready for serialization.
    """
    # Apply transform and split into slices
    transform_coeffs = transform_and_slice_picture(codec_features, picture)

    picture_header = PictureHeader()
    if "pic_num" in picture:
        picture_header["picture_number"] = picture["pic_num"]

    slice_parameters = SliceParameters(
        slices_x=codec_features["slices_x"],
        slices_y=codec_features["slices_y"],
    )

    # Quantize coefficients as required and set slice_size_scaler (HQ Only) and
    # slice_bytes (LD only)
    if codec_features["profile"] == Profiles.high_quality:
        if codec_features["lossless"]:
            assert minimum_qindex == 0
            if codec_features["picture_bytes"] is not None:
                raise PictureBytesSpecifiedForLosslessModeError(codec_features)
            slice_size_scaler, transform_data = make_transform_data_hq_lossless(
                transform_coeffs,
                minimum_slice_size_scaler,
            )
        else:
            try:
                slice_size_scaler, transform_data = make_transform_data_hq_lossy(
                    codec_features["picture_bytes"],
                    transform_coeffs,
                    minimum_qindex,
                    minimum_slice_size_scaler,
                )
            except InsufficientHQPictureBytesError:
                # Re-raise with codec features dict
                raise InsufficientHQPictureBytesError(codec_features)

        # NB: For simplicity, this implementation currently does not support
        # setting the slice prefix bytes to anything except zero since this is
        # not required by any existing VC-2 level. The assumption that this is
        # OK is verified in
        # ``tests/encoder/test_level_constraints_assumptions.py``.
        #
        # In addition, the `codec_features_to_trivial_level_constraints`
        # function assumes that this value is always 0 too.
        slice_parameters["slice_prefix_bytes"] = 0
        slice_parameters["slice_size_scaler"] = slice_size_scaler
    elif codec_features["profile"] == Profiles.low_delay:
        if codec_features["lossless"]:
            raise LosslessUnsupportedByLowDelayError(codec_features)
        try:
            transform_data = make_transform_data_ld_lossy(
                codec_features["picture_bytes"],
                transform_coeffs,
                minimum_qindex,
            )
        except InsufficientLDPictureBytesError:
            # Re-raise with codec features dict
            raise InsufficientLDPictureBytesError(codec_features)

        slice_bytes_fraction = Fraction(
            codec_features["picture_bytes"],
            codec_features["slices_x"] * codec_features["slices_y"],
        )
        slice_parameters[
            "slice_bytes_numerator"] = slice_bytes_fraction.numerator
        slice_parameters[
            "slice_bytes_denominator"] = slice_bytes_fraction.denominator

    transform_parameters = TransformParameters(
        wavelet_index=codec_features["wavelet_index"],
        dwt_depth=codec_features["dwt_depth"],
        # NB: We *always* include an ExtendedTransformParameters field. This
        # field will later be removed (if not supported by the major_version
        # chosen for the stream) using the
        # :py:func:`vc2_conformance.bitstream.vc2_autofill.autofill_major_version`
        # function.
        extended_transform_parameters=make_extended_transform_parameters(
            codec_features),
        slice_parameters=slice_parameters,
        quant_matrix=make_quant_matrix(codec_features),
    )

    wavelet_transform = WaveletTransform(
        transform_parameters=transform_parameters,
        transform_data=transform_data,
    )

    return PictureParse(
        picture_header=picture_header,
        wavelet_transform=wavelet_transform,
    )