def test_consistent_for_same_feed(self):
        vp = VideoParameters(
            frame_width=10,
            frame_height=5,
            color_diff_format_index=ColorDifferenceSamplingFormats.color_4_4_4,
            luma_offset=0,
            luma_excursion=1023,
            color_diff_offset=0,
            color_diff_excursion=511,
        )

        p1 = list(white_noise(vp, PictureCodingModes.pictures_are_frames))
        p2 = list(white_noise(vp, PictureCodingModes.pictures_are_frames))
        p3 = list(
            white_noise(vp, PictureCodingModes.pictures_are_frames, seed=1337))
        p4 = list(
            white_noise(vp, PictureCodingModes.pictures_are_frames, seed=1337))

        assert p1 == p2
        assert p1 != p3
        assert p3 == p4
def test_progressive_to_split_fields():
    pictures = [
        np.arange(4 * 6 * 3).reshape(4, 6, 3) * 1,
        np.arange(4 * 6 * 3).reshape(4, 6, 3) * 10,
        np.arange(4 * 6 * 3).reshape(4, 6, 3) * 100,
        np.arange(4 * 6 * 3).reshape(4, 6, 3) * 1000,
    ]

    vp = VideoParameters(top_field_first=True)
    pcm = PictureCodingModes.pictures_are_fields
    expected = [
        pictures[0][0::2, :, :],
        pictures[0][1::2, :, :],
        pictures[1][0::2, :, :],
        pictures[1][1::2, :, :],
        pictures[2][0::2, :, :],
        pictures[2][1::2, :, :],
        pictures[3][0::2, :, :],
        pictures[3][1::2, :, :],
    ]
    actual = iter(progressive_to_split_fields(vp, pcm, pictures))
    for e in expected:
        assert np.array_equal(next(actual), e)
    assert list(actual) == []

    vp["top_field_first"] = False
    expected = [
        pictures[0][1::2, :, :],
        pictures[0][0::2, :, :],
        pictures[1][1::2, :, :],
        pictures[1][0::2, :, :],
        pictures[2][1::2, :, :],
        pictures[2][0::2, :, :],
        pictures[3][1::2, :, :],
        pictures[3][0::2, :, :],
    ]
    actual = iter(progressive_to_split_fields(vp, pcm, pictures))
    for e in expected:
        assert np.array_equal(next(actual), e)
    assert list(actual) == []
    def test_num_frames(self):
        vp = VideoParameters(
            frame_width=10,
            frame_height=5,
            color_diff_format_index=ColorDifferenceSamplingFormats.color_4_4_4,
            luma_offset=0,
            luma_excursion=1023,
            color_diff_offset=0,
            color_diff_excursion=511,
        )

        p1 = list(
            white_noise(vp,
                        PictureCodingModes.pictures_are_frames,
                        num_frames=3))
        p2 = list(
            white_noise(vp,
                        PictureCodingModes.pictures_are_fields,
                        num_frames=3))

        assert len(p1) == 3
        assert len(p2) == 6
예제 #4
0
def test_to_xyz_and_from_xyz_roundtrip(
    luma_offset,
    luma_excursion,
    color_diff_offset,
    color_diff_excursion,
    primaries,
    matrix,
    transfer_function,
):
    video_parameters = VideoParameters(
        color_diff_format_index=ColorDifferenceSamplingFormats.color_4_4_4,
        luma_offset=luma_offset,
        luma_excursion=luma_excursion,
        color_diff_offset=color_diff_offset,
        color_diff_excursion=color_diff_excursion,
        color_primaries_index=primaries,
        color_matrix_index=matrix,
        transfer_function_index=transfer_function,
    )

    luma_bits = intlog2(luma_excursion + 1)
    color_diff_bits = intlog2(color_diff_excursion + 1)

    w, h = 6, 4

    rand = np.random.RandomState(0)
    y = rand.randint(0, 2 ** luma_bits, (h, w))
    c1 = rand.randint(0, 2 ** color_diff_bits, (h, w))
    c2 = rand.randint(0, 2 ** color_diff_bits, (h, w))

    xyz = to_xyz(y, c1, c2, video_parameters)

    assert xyz.shape == (h, w, 3)

    new_y, new_c1, new_c2 = from_xyz(xyz, video_parameters)

    assert np.array_equal(y, new_y)
    assert np.array_equal(c1, new_c1)
    assert np.array_equal(c2, new_c2)
def test_explain_ffmpeg_and_imagemagick_commands(
    explain,
    matrix,
    expect_supported,
):
    video_parameters = VideoParameters(
        frame_width=8,
        frame_height=8,
        color_diff_format_index=ColorDifferenceSamplingFormats.color_4_4_4,
        source_sampling=SourceSamplingModes.progressive,
        top_field_first=True,
        frame_rate_numer=1,
        frame_rate_denom=1,
        pixel_aspect_ratio_numer=1,
        pixel_aspect_ratio_denom=1,
        clean_width=1,
        clean_height=1,
        top_offset=0,
        left_offset=0,
        luma_offset=0,
        luma_excursion=255,
        color_diff_offset=128,
        color_diff_excursion=255,
        color_primaries_index=PresetColorPrimaries.hdtv,
        color_matrix_index=matrix,
        transfer_function_index=PresetTransferFunctions.tv_gamma,
    )

    picture_coding_mode = PictureCodingModes.pictures_are_frames

    explanation = explain("foo.raw", video_parameters, picture_coding_mode)

    if expect_supported:
        assert "$ " in explanation
        assert not explanation.startswith("No")
    else:
        assert "$ " not in explanation
        assert explanation.startswith("No")
 def video_parameters(self):
     return VideoParameters(
         frame_width=4,
         frame_height=4,
         color_diff_format_index=ColorDifferenceSamplingFormats.color_4_4_4,
         source_sampling=SourceSamplingModes.progressive,
         top_field_first=True,
         frame_rate_numer=1,
         frame_rate_denom=1,
         pixel_aspect_ratio_numer=1,
         pixel_aspect_ratio_denom=1,
         clean_width=4,
         clean_height=4,
         left_offset=0,
         top_offset=0,
         luma_offset=0,
         luma_excursion=1023,
         color_diff_offset=512,
         color_diff_excursion=1023,
         color_primaries_index=PresetColorPrimaries.hdtv,
         color_matrix_index=PresetColorMatrices.hdtv,
         transfer_function_index=PresetTransferFunctions.tv_gamma,
     )
def test_mid_gray(primaries, transfer_function):
    vp = VideoParameters(
        frame_width=10,
        frame_height=5,
        frame_rate_numer=1,
        frame_rate_denom=1,
        pixel_aspect_ratio_numer=1,
        pixel_aspect_ratio_denom=1,
        source_sampling=SourceSamplingModes.progressive,
        top_field_first=True,
        color_diff_format_index=ColorDifferenceSamplingFormats.color_4_4_4,
        # The choice of primaries and transfer function should have no effect:
        # the color should be chosen at the code level, not the color model
        # level
        color_primaries_index=primaries,
        color_matrix_index=PresetColorMatrices.hdtv,
        transfer_function_index=transfer_function,
        # Set two wonky off-center ranges; the offsets should be ignored with the
        # 'mid gray' being the middle of the code range
        luma_offset=20,
        luma_excursion=150,
        color_diff_offset=100,
        color_diff_excursion=900,
    )

    pictures = list(mid_gray(vp, PictureCodingModes.pictures_are_frames))
    assert len(pictures) == 1

    picture = pictures[0]
    y = np.array(picture["Y"])
    c1 = np.array(picture["C1"])
    c2 = np.array(picture["C2"])

    assert np.array_equal(y, np.full(y.shape, 128))

    assert np.array_equal(c1, np.full(c1.shape, 512))
    assert np.array_equal(c2, np.full(c2.shape, 512))
def test_pipe():
    vp = VideoParameters(
        source_sampling=SourceSamplingModes.interlaced,
        top_field_first=True,
    )
    pcm = PictureCodingModes.pictures_are_frames

    def repeat_pictures(video_parameters, picture_coding_mode, iterable):
        assert video_parameters == vp
        assert picture_coding_mode == pcm
        for picture in iterable:
            yield picture
            yield picture

    @pipe(repeat_pictures)
    def picture_generator(video_parameters, picture_coding_mode, values):
        assert video_parameters == vp
        assert picture_coding_mode == pcm

        for value in values:
            yield value * 10

    assert list(picture_generator(vp, pcm,
                                  [1, 2, 3])) == [10, 10, 20, 20, 30, 30]
HD_1440X1080I60_OVER_SD_SDI_CODEC_FEATURES = CodecFeatures(
    level=Levels.hd_over_sd_sdi,
    profile=Profiles.low_delay,
    picture_coding_mode=PictureCodingModes.pictures_are_fields,
    video_parameters=VideoParameters(
        frame_width=1440,
        frame_height=1080,
        color_diff_format_index=ColorDifferenceSamplingFormats.color_4_2_2,
        source_sampling=SourceSamplingModes.interlaced,
        top_field_first=True,
        frame_rate_numer=30000,
        frame_rate_denom=1001,
        pixel_aspect_ratio_numer=4,
        pixel_aspect_ratio_denom=3,
        clean_width=1440,
        clean_height=1080,
        left_offset=0,
        top_offset=0,
        luma_offset=64,
        luma_excursion=876,
        color_diff_offset=512,
        color_diff_excursion=896,
        color_primaries_index=PresetColorPrimaries.hdtv,
        color_matrix_index=PresetColorMatrices.hdtv,
        transfer_function_index=PresetTransferFunctions.tv_gamma,
    ),
    wavelet_index=WaveletFilters.le_gall_5_3,
    wavelet_index_ho=WaveletFilters.le_gall_5_3,
    dwt_depth=3,
    dwt_depth_ho=0,
    slices_x=90,
        PictureCodingModes.pictures_are_frames,
    ) == expected)


@pytest.mark.parametrize(
    "video_parameters,expected",
    [
        # Sizes match, components all the same and match the bit width used in the
        # file format.
        (
            VideoParameters(
                frame_width=200,
                frame_height=100,
                color_matrix_index=PresetColorMatrices.rgb,
                color_diff_format_index=ColorDifferenceSamplingFormats.
                color_4_4_4,
                luma_offset=0,
                luma_excursion=255,
                color_diff_offset=0,
                color_diff_excursion=255,
            ),
            ("Each component consists of 200x50 8 bit values. "
             "Expressible values run from 0 (video level 0.00) "
             "to 255 (video level 1.00)."),
        ),
        # Components differ in size
        (
            VideoParameters(
                frame_width=200,
                frame_height=100,
                color_matrix_index=PresetColorMatrices.rgb,
예제 #11
0
def test_to_xyz():
    # A crude samity check which verifies that the conversion steps appear to
    # work correctly

    # An esoteric video format which is easy to hand-evaluate and also test
    # subsampling
    video_parameters = VideoParameters(
        color_diff_format_index=ColorDifferenceSamplingFormats.color_4_2_2,
        luma_offset=0,
        luma_excursion=255,
        color_diff_offset=0,
        color_diff_excursion=255,
        color_primaries_index=PresetColorPrimaries.hdtv,
        color_matrix_index=PresetColorMatrices.rgb,
        transfer_function_index=PresetTransferFunctions.tv_gamma,
    )

    # Find XYZ coordinates for White, black, red green and blue
    test_colors_rgb = np.array(
        [
            [1.0, 0.0, 1.0, 0.0, 0.0],
            [1.0, 0.0, 0.0, 1.0, 0.0],
            [1.0, 0.0, 0.0, 0.0, 1.0],
        ]
    )
    test_colors_xyz = np.matmul(
        LINEAR_RGB_TO_XYZ[video_parameters["color_primaries_index"]],
        test_colors_rgb,
    )

    # The following test image is defined in the XYZ domain
    #
    #      +---+---+---+---+---+---+---+---+---+---+
    #      |Wht|Wht|Blk|Blk|Red|Red|Grn|Grn|Blu|Blu|
    #      +---+---+---+---+---+---+---+---+---+---+
    #      |Wht|Blk|Blk|Blk|Red|Blk|Grn|Blk|Blu|Blk|
    #      +---+---+---+---+---+---+---+---+---+---+
    #      |Wht|Wht|Wht|Wht|Wht|Wht|Wht|Wht|Wht|Wht|
    #      +---+---+---+---+---+---+---+---+---+---+
    #      |Blk|Blk|Blk|Blk|Blk|Blk|Blk|Blk|Blk|Blk|
    #      +---+---+---+---+---+---+---+---+---+---+
    #
    # This has the following features:
    #
    # * Row 0: Fully saturated colors in pairs of pixels
    # * Row 1: Colors paired with black (which after 4:2:2 subsampling will
    #   result in darker colors (at least with the low-pass filter used here)
    # * Row 2: All white
    # * Row 3: All black

    test_colors_xyz_row = test_colors_xyz.T.reshape(-1, 3)

    row_0 = np.repeat(test_colors_xyz_row, 2, axis=0)

    row_1 = row_0.copy()
    row_1[1::2, :] = 0

    row_2 = np.empty_like(row_0)
    row_2[:, 0] = row_0[0, 0]
    row_2[:, 1] = row_0[0, 1]
    row_2[:, 2] = row_0[0, 2]

    row_3 = np.empty_like(row_0)
    row_3[:, 0] = row_0[2, 0]
    row_3[:, 1] = row_0[2, 1]
    row_3[:, 2] = row_0[2, 2]

    test_picture_xyz = np.stack([row_0, row_1, row_2, row_3], axis=0)

    # Colors should come out as expected RGB values in GBR order
    g, b, r = from_xyz(
        test_picture_xyz,
        video_parameters,
    )

    # First row: solid colors
    #                            Wht      Blk    Red     Grn       Blu
    assert np.allclose(r[0], [255, 0, 255, 0, 0], atol=1)
    assert np.allclose(g[0], [255, 255, 0, 0, 0, 0, 255, 255, 0, 0], atol=1)
    assert np.allclose(b[0], [255, 0, 0, 0, 255], atol=1)

    # Second row, dimmed R and B channels
    #                            Wht    Blk    Red     Grn     Blu
    assert np.allclose(r[1], [128, 0, 128, 0, 0], atol=1)
    assert np.allclose(g[1], [255, 0, 0, 0, 0, 0, 255, 0, 0, 0], atol=1)
    assert np.allclose(b[1], [128, 0, 0, 0, 128], atol=1)

    # Third row: all-white
    assert np.allclose(r[2], [255, 255, 255, 255, 255], atol=1)
    assert np.allclose(g[2], [255, 255, 255, 255, 255, 255, 255, 255, 255, 255], atol=1)
    assert np.allclose(b[2], [255, 255, 255, 255, 255], atol=1)

    # Fourth row: all-black
    assert np.allclose(r[3], [0, 0, 0, 0, 0], atol=1)
    assert np.allclose(g[3], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], atol=1)
    assert np.allclose(b[3], [0, 0, 0, 0, 0], atol=1)
            CodecFeatures(
                MINIMAL_CODEC_FEATURES,
                picture_coding_mode=PictureCodingModes.pictures_are_fields,
            ),
            # Test with very high, asymmetric bit depths.
            #
            # Here 'high' means 16 bits and 14 bits (for luma and color
            # difference). In practice no real codec is likely to use more than
            # 16 bits since any video format requiring greater dynamic range is
            # likely to need to turn to floating point anyway.
            CodecFeatures(
                MINIMAL_CODEC_FEATURES,
                video_parameters=VideoParameters(
                    MINIMAL_CODEC_FEATURES["video_parameters"],
                    luma_offset=0,
                    luma_excursion=(1 << 16) - 1,
                    color_diff_offset=(1 << 14) // 2,
                    color_diff_excursion=(1 << 14) - 1,
                ),
            ),
        ]
    ]


def test_names_unique():
    for codec_features, test_cases in ALL_TEST_CASES:
        names = [tc.name for tc in test_cases]
        assert len(set(names)) == len(names)


@pytest.mark.parametrize(
예제 #13
0
class TestCustomQuantizationMatrix(object):
    @pytest.mark.parametrize(
        "codec_features",
        [
            # Lossless mode with not enough bits for test pattern
            CodecFeatures(
                MINIMAL_CODEC_FEATURES_WITH_CUSTOM_QUANT_MATRIX,
                video_parameters=VideoParameters(
                    MINIMAL_CODEC_FEATURES_WITH_CUSTOM_QUANT_MATRIX[
                        "video_parameters"],
                    luma_excursion=4,
                    color_diff_excursion=4,
                ),
                lossless=True,
                picture_bytes=None,
            ),
            # Level prohibits using custom quantisation matrices
            CodecFeatures(
                MINIMAL_CODEC_FEATURES_WITH_CUSTOM_QUANT_MATRIX,
                level=Levels.uhd_over_hd_sdi,
            ),
        ],
    )
    def test_skipped_cases(self, codec_features):
        assert len(list(custom_quantization_matrix(codec_features))) == 0

    @pytest.mark.parametrize("lossless", [False, True])
    @pytest.mark.parametrize(
        "asymmetric,constrain_level_zero,initial_quant_matrix,exp_matrices",
        [
            # By default should produce all three examples
            (
                False,
                False,
                {
                    0: {
                        "LL": 0
                    },
                    1: {
                        "HL": 1,
                        "LH": 1,
                        "HH": 1
                    },
                    2: {
                        "HL": 1,
                        "LH": 1,
                        "HH": 1
                    },
                },
                {
                    "zeros": [0, 0, 0, 0, 0, 0, 0],
                    "arbitrary": [0, 1, 2, 3, 4, 5, 6],
                    "default": [4, 2, 2, 0, 4, 4, 2],
                },
            ),
            # If provided matrix matches a test case, that case should be omitted
            (
                False,
                False,
                {
                    0: {
                        "LL": 0
                    },
                    1: {
                        "HL": 0,
                        "LH": 0,
                        "HH": 0
                    },
                    2: {
                        "HL": 0,
                        "LH": 0,
                        "HH": 0
                    },
                },
                {
                    "arbitrary": [0, 1, 2, 3, 4, 5, 6],
                    "default": [4, 2, 2, 0, 4, 4, 2]
                },
            ),
            # When no default matrix is available, should omit the default case
            (
                True,
                False,
                {
                    0: {
                        "LL": 0
                    },
                    1: {
                        "HL": 1,
                        "LH": 1,
                        "HH": 1
                    },
                    2: {
                        "HL": 1,
                        "LH": 1,
                        "HH": 1
                    },
                },
                {
                    "zeros": [0, 0, 0, 0, 0, 0, 0],
                    "arbitrary": [0, 1, 2, 3, 4, 5, 6]
                },
            ),
            # When a level restricts the custom quant index values, this limit should be
            # obeyed
            (
                False,
                True,
                {
                    0: {
                        "LL": 0
                    },
                    1: {
                        "HL": 1,
                        "LH": 1,
                        "HH": 1
                    },
                    2: {
                        "HL": 1,
                        "LH": 1,
                        "HH": 1
                    },
                },
                {
                    "arbitrary": [3, 4, 8, 3, 4, 8, 3]
                },
            ),
        ],
    )
    def test_generates_custom_quant_matrices(
        self,
        lossless,
        asymmetric,
        constrain_level_zero,
        initial_quant_matrix,
        exp_matrices,
    ):
        codec_features = CodecFeatures(
            MINIMAL_CODEC_FEATURES_WITH_CUSTOM_QUANT_MATRIX,
            wavelet_index_ho=(WaveletFilters.le_gall_5_3
                              if not asymmetric else WaveletFilters.fidelity),
            quantization_matrix=initial_quant_matrix,
            lossless=lossless,
            picture_bytes=(None if lossless else
                           MINIMAL_CODEC_FEATURES_WITH_CUSTOM_QUANT_MATRIX[
                               "picture_bytes"]),
        )

        with temporary_level_override():
            if constrain_level_zero:
                # Sanity check
                assert LEVEL_CONSTRAINTS[0]["level"] == ValueSet(0)
                LEVEL_CONSTRAINTS[0]["quant_matrix_values"] = ValueSet(3, 4, 8)

            test_cases = list(custom_quantization_matrix(codec_features))

        matrices = {}

        for test_case in test_cases:
            stream = test_case.value

            to_visit = list(stream["sequences"][0]["data_units"])
            while to_visit:
                d = to_visit.pop(0)
                if isinstance(d, dict):
                    for key, value in d.items():
                        if key == "quant_matrix":
                            assert value["custom_quant_matrix"]
                            matrix = value["quant_matrix"]
                            if test_case.subcase_name in matrices:
                                # All pictures should have same custom
                                # quantization matrix
                                assert matrix == matrices[
                                    test_case.subcase_name]
                            else:
                                matrices[test_case.subcase_name] = matrix
                        else:
                            to_visit.append(value)

        assert matrices == exp_matrices
    def test_top_field_first_always_matches(self, top_field_first):
        video_parameters = VideoParameters(top_field_first=top_field_first)

        for index in rank_base_video_format_similarity(video_parameters):
            assert set_source_defaults(index)["top_field_first"] == top_field_first
예제 #15
0
def test_progressive_to_pictures():
    pictures = [
        np.arange(4 * 6 * 3).reshape(4, 6, 3) * 1,
        np.arange(4 * 6 * 3).reshape(4, 6, 3) * 10,
        np.arange(4 * 6 * 3).reshape(4, 6, 3) * 100,
        np.arange(4 * 6 * 3).reshape(4, 6, 3) * 1000,
    ]

    # Progressive, pictures are frames
    vp = VideoParameters(
        source_sampling=SourceSamplingModes.progressive,
        top_field_first=True,
    )
    pcm = PictureCodingModes.pictures_are_frames
    expected = pictures
    actual = iter(progressive_to_pictures(vp, pcm, pictures))
    for e in expected:
        assert np.array_equal(next(actual), e)
    assert list(actual) == []

    # Progressive, pictures are fields
    vp = VideoParameters(
        source_sampling=SourceSamplingModes.progressive,
        top_field_first=True,
    )
    pcm = PictureCodingModes.pictures_are_fields
    expected = [
        pictures[0][0::2, :, :],
        pictures[0][1::2, :, :],
        pictures[1][0::2, :, :],
        pictures[1][1::2, :, :],
        pictures[2][0::2, :, :],
        pictures[2][1::2, :, :],
        pictures[3][0::2, :, :],
        pictures[3][1::2, :, :],
    ]
    actual = iter(progressive_to_pictures(vp, pcm, pictures))
    for e in expected:
        assert np.array_equal(next(actual), e)
    assert list(actual) == []

    # Interlaced, pictures are frames
    vp = VideoParameters(
        source_sampling=SourceSamplingModes.interlaced,
        top_field_first=True,
    )
    pcm = PictureCodingModes.pictures_are_frames
    expected = [
        np.stack(list(zip(pictures[0][0::2, :, :], pictures[1][1::2, :, :])),
                 axis=0).reshape(4, 6, 3),
        np.stack(list(zip(pictures[2][0::2, :, :], pictures[3][1::2, :, :])),
                 axis=0).reshape(4, 6, 3),
    ]
    actual = iter(progressive_to_pictures(vp, pcm, pictures))
    for e in expected:
        assert np.array_equal(next(actual), e)
    assert list(actual) == []

    # Interlaced, pictures are fields
    vp = VideoParameters(
        source_sampling=SourceSamplingModes.interlaced,
        top_field_first=True,
    )
    pcm = PictureCodingModes.pictures_are_fields
    expected = [
        pictures[0][0::2, :, :],
        pictures[1][1::2, :, :],
        pictures[2][0::2, :, :],
        pictures[3][1::2, :, :],
    ]
    actual = iter(progressive_to_pictures(vp, pcm, pictures))
    for e in expected:
        assert np.array_equal(next(actual), e)
    assert list(actual) == []
def test_is_rgb_color():
    for matrix_index in PresetColorMatrices:
        expected = matrix_index == PresetColorMatrices.rgb
        vp = VideoParameters(color_matrix_index=matrix_index)
        assert is_rgb_color(vp) is expected
def generate_picture(
    filename,
    pixel_values=0,
    picture_number=0,
    bit_width=10,
    luma_bit_width=None,
    color_diff_bit_width=None,
    picture_coding_mode=PictureCodingModes.pictures_are_fields,
    width=8,
    height=4,
):
    """
    Generate a 4:2:0 color subsampled raw picture with the specified file name.

    Parameters
    ==========
    filename : str
        Filename to write the raw picture to.
    pixel_values : int or (2D-array, 2D-array, 2D-array)
        If an integer, a ``width`` x ``height`` picture with all pixel values
        set to that value will be generated.

        If a set of three 2D arrays, uses these for the picture. The second and
        third arrays must be exactly half the width and height of the first.
    picture_number: int
        The VC-2 picture number.
    bit_width : int
        Bit depth for pixel values.
    luma_bit_width : int
        Bit depth for luma pixel values (overrides bit_width).
    color_diff_bit_width : int
        Bit depth for color difference pixel values (overrides bit_width).
    picture_coding_mode : :py:class:`~vc2_data_tables.PictureCodingModes`
        The picture coding mode to use.
    width, height : int
        The dimensions of the picture to generate (not the frame dimensions).
        Ignored if pixel_values is given.
    """
    if isinstance(pixel_values, int):
        pixel_values = (
            np.full((height, width), pixel_values, dtype=np.int64),
            np.full((height // 2, width // 2), pixel_values, dtype=np.int64),
            np.full((height // 2, width // 2), pixel_values, dtype=np.int64),
        )
    else:
        pixel_values = (
            np.array(pixel_values[0]),
            np.array(pixel_values[1]),
            np.array(pixel_values[2]),
        )
        height, width = pixel_values[0].shape
        assert pixel_values[1].shape == (height // 2, width // 2)
        assert pixel_values[2].shape == (height // 2, width // 2)

    if luma_bit_width is None:
        luma_bit_width = bit_width
    if color_diff_bit_width is None:
        color_diff_bit_width = bit_width

    if picture_coding_mode == PictureCodingModes.pictures_are_fields:
        height *= 2

    video_parameters = VideoParameters(
        frame_width=width,
        frame_height=height,
        color_diff_format_index=ColorDifferenceSamplingFormats.color_4_2_0,
        source_sampling=SourceSamplingModes.progressive,
        top_field_first=True,
        frame_rate_numer=1,
        frame_rate_denom=1,
        pixel_aspect_ratio_numer=1,
        pixel_aspect_ratio_denom=1,
        clean_width=width,
        clean_height=height,
        left_offset=0,
        top_offset=0,
        luma_offset=0,
        luma_excursion=(1 << luma_bit_width) - 1,
        color_diff_offset=1 << (color_diff_bit_width - 1),
        color_diff_excursion=(1 << color_diff_bit_width) - 1,
        color_primaries_index=PresetColorPrimaries.hdtv,
        color_matrix_index=PresetColorMatrices.hdtv,
        transfer_function_index=PresetTransferFunctions.tv_gamma,
    )

    picture = {
        "Y": pixel_values[0],
        "C1": pixel_values[1],
        "C2": pixel_values[2],
        "pic_num": picture_number,
    }

    write(picture, video_parameters, picture_coding_mode, filename)
예제 #18
0
def test_linear_ramps(primaries, transfer_function):
    vp = VideoParameters(
        frame_width=32,
        frame_height=16,
        frame_rate_numer=1,
        frame_rate_denom=1,
        pixel_aspect_ratio_numer=1,
        pixel_aspect_ratio_denom=1,
        source_sampling=SourceSamplingModes.progressive,
        top_field_first=True,
        color_diff_format_index=ColorDifferenceSamplingFormats.color_4_4_4,
        color_primaries_index=primaries,
        color_matrix_index=PresetColorMatrices.rgb,
        transfer_function_index=transfer_function,
        luma_offset=0,
        luma_excursion=255,
        color_diff_offset=0,
        color_diff_excursion=255,
    )

    pictures = list(linear_ramps(vp, PictureCodingModes.pictures_are_frames))
    assert len(pictures) == 1
    g = np.array(pictures[0]["Y"])
    b = np.array(pictures[0]["C1"])
    r = np.array(pictures[0]["C2"])

    rgb = np.stack([r, g, b], axis=-1) / 255.0

    # NB: The d_cinema_transfer_function does not map 1.0 to 1.0 so in that
    # special case we must add some slack here.
    if transfer_function == PresetTransferFunctions.d_cinema:
        atol = 0.05
    else:
        atol = 0.0

    assert np.all(np.isclose(rgb[0, 0, :], [0, 0, 0], atol=atol))
    assert np.all(np.isclose(rgb[0, 31, :], [1, 1, 1], atol=atol))

    assert np.all(np.isclose(rgb[4, 0, :], [0, 0, 0], atol=atol))
    assert np.all(np.isclose(rgb[4, 31, :], [1, 0, 0], atol=atol))

    assert np.all(np.isclose(rgb[8, 0, :], [0, 0, 0], atol=atol))
    assert np.all(np.isclose(rgb[8, 31, :], [0, 1, 0], atol=atol))

    assert np.all(np.isclose(rgb[12, 0, :], [0, 0, 0], atol=atol))
    assert np.all(np.isclose(rgb[12, 31, :], [0, 0, 1], atol=atol))

    # Whites should be pure whites
    for c1 in range(3):
        for c2 in range(3):
            assert np.all(np.isclose(rgb[0, :, c1], rgb[0, :, c2], atol=0.05))

    # Colors should be pure primaries
    assert np.all(rgb[4:8, :, [1, 2]] == 0)
    assert np.all(rgb[8:12, :, [0, 2]] == 0)
    assert np.all(rgb[12:16, :, [0, 1]] == 0)

    # Bands should contain same values throughout
    assert np.all(rgb[0::4, :, :] == rgb[1::4, :, :])
    assert np.all(rgb[0::4, :, :] == rgb[2::4, :, :])
    assert np.all(rgb[0::4, :, :] == rgb[3::4, :, :])

    if transfer_function == PresetTransferFunctions.linear:
        # Should be perfectly linear
        assert np.all(
            np.isclose(rgb[0, :, 0], np.linspace(0, 1, 32), atol=0.05))
        assert np.all(
            np.isclose(rgb[4, :, 0], np.linspace(0, 1, 32), atol=0.05))
        assert np.all(
            np.isclose(rgb[8, :, 1], np.linspace(0, 1, 32), atol=0.05))
        assert np.all(
            np.isclose(rgb[12, :, 2], np.linspace(0, 1, 32), atol=0.05))
    else:
        # Should be monotonic for other transfer functions, at least
        assert np.all((rgb[0, 1:, 0] - rgb[0, :-1, 0]) >= 0)
        assert np.all((rgb[4, 1:, 0] - rgb[0, :-1, 0]) >= 0)
        assert np.all((rgb[8, 1:, 1] - rgb[0, :-1, 0]) >= 0)
        assert np.all((rgb[12, 1:, 2] - rgb[0, :-1, 0]) >= 0)
예제 #19
0
 def test_basic_valid(self):
     assert read_codec_features_csv(
         [
             "name,                      hd",
             "level,                     unconstrained,        0",
             "profile,                   high_quality,         3",
             "base_video_format,         hd1080p_50,           custom_format",
             "picture_coding_mode,       pictures_are_frames,  pictures_are_frames",
             "frame_width,               default,              8",
             "frame_height,              default,              4",
             "color_diff_format_index,   default,              color_4_4_4",
             "source_sampling,           default,              progressive",
             "top_field_first,           default,              TRUE",
             "frame_rate_numer,          default,              1",
             "frame_rate_denom,          default,              1",
             "pixel_aspect_ratio_numer,  default,              1",
             "pixel_aspect_ratio_denom,  default,              1",
             "clean_width,               default,              8",
             "clean_height,              default,              4",
             "left_offset,               default,              0",
             "top_offset,                default,              0",
             "luma_offset,               default,              0",
             "luma_excursion,            default,              255",
             "color_diff_offset,         default,              128",
             "color_diff_excursion,      default,              255",
             "color_primaries_index,     default,              hdtv",
             "color_matrix_index,        default,              hdtv",
             "transfer_function_index,   default,              tv_gamma",
             "wavelet_index,             haar_with_shift,      haar_with_shift",
             "wavelet_index_ho,          haar_with_shift,      haar_no_shift",
             "dwt_depth,                 2,                    2",
             "dwt_depth_ho,              0,                    1",
             "slices_x,                  120,                  2",
             "slices_y,                  108,                  1",
             "fragment_slice_count,      0,                    1",
             "lossless,                  FALSE,                FALSE",
             "picture_bytes,             1036800,              24",
             "quantization_matrix,       default,              0 0 0 0 0 0 0 0",
         ]
     ) == OrderedDict(
         [
             (
                 "hd",
                 CodecFeatures(
                     name="hd",
                     level=Levels.unconstrained,
                     profile=Profiles.high_quality,
                     picture_coding_mode=PictureCodingModes.pictures_are_frames,
                     video_parameters=set_source_defaults(
                         BaseVideoFormats.hd1080p_50
                     ),
                     wavelet_index=WaveletFilters.haar_with_shift,
                     wavelet_index_ho=WaveletFilters.haar_with_shift,
                     dwt_depth=2,
                     dwt_depth_ho=0,
                     slices_x=120,
                     slices_y=108,
                     fragment_slice_count=0,
                     lossless=False,
                     picture_bytes=1036800,
                     quantization_matrix=None,
                 ),
             ),
             (
                 "column_C",
                 CodecFeatures(
                     name="column_C",
                     level=Levels.unconstrained,
                     profile=Profiles.high_quality,
                     picture_coding_mode=PictureCodingModes.pictures_are_frames,
                     video_parameters=VideoParameters(
                         frame_width=8,
                         frame_height=4,
                         color_diff_format_index=ColorDifferenceSamplingFormats.color_4_4_4,  # noqa: E501
                         source_sampling=SourceSamplingModes.progressive,
                         top_field_first=True,
                         frame_rate_numer=1,
                         frame_rate_denom=1,
                         pixel_aspect_ratio_numer=1,
                         pixel_aspect_ratio_denom=1,
                         clean_width=8,
                         clean_height=4,
                         left_offset=0,
                         top_offset=0,
                         luma_offset=0,
                         luma_excursion=255,
                         color_diff_offset=128,
                         color_diff_excursion=255,
                         color_primaries_index=PresetColorPrimaries.hdtv,
                         color_matrix_index=PresetColorMatrices.hdtv,
                         transfer_function_index=PresetTransferFunctions.tv_gamma,
                     ),
                     wavelet_index=WaveletFilters.haar_with_shift,
                     wavelet_index_ho=WaveletFilters.haar_no_shift,
                     dwt_depth=2,
                     dwt_depth_ho=1,
                     slices_x=2,
                     slices_y=1,
                     fragment_slice_count=1,
                     lossless=False,
                     picture_bytes=24,
                     quantization_matrix={
                         0: {"L": 0},
                         1: {"H": 0},
                         2: {"HL": 0, "LH": 0, "HH": 0},
                         3: {"HL": 0, "LH": 0, "HH": 0},
                     },
                 ),
             ),
         ]
     )
class TestIterCustomOptionsDicts(object):
    @pytest.mark.parametrize(
        "bvp,vp,lcd,exp",
        [
            # Override required
            (
                VideoParameters(
                    frame_width=640,
                    frame_height=480,
                ),
                VideoParameters(
                    frame_width=1920,
                    frame_height=1080,
                ),
                defaultdict(AnyValue),
                [
                    FrameSize(
                        custom_dimensions_flag=True,
                        frame_width=1920,
                        frame_height=1080,
                    ),
                ],
            ),
            # Override optional
            (
                VideoParameters(
                    frame_width=1920,
                    frame_height=1080,
                ),
                VideoParameters(
                    frame_width=1920,
                    frame_height=1080,
                ),
                defaultdict(AnyValue),
                [
                    FrameSize(
                        custom_dimensions_flag=False,
                    ),
                    FrameSize(
                        custom_dimensions_flag=True,
                        frame_width=1920,
                        frame_height=1080,
                    ),
                ],
            ),
            # Force no-custom by disallowing the custom flag
            (
                VideoParameters(
                    frame_width=1920,
                    frame_height=1080,
                ),
                VideoParameters(
                    frame_width=1920,
                    frame_height=1080,
                ),
                defaultdict(AnyValue, custom_dimensions_flag=ValueSet(False)),
                [
                    FrameSize(
                        custom_dimensions_flag=False,
                    )
                ],
            ),
            # Force no-custom by disallowing the required dimensions
            (
                VideoParameters(
                    frame_width=1920,
                    frame_height=1080,
                ),
                VideoParameters(
                    frame_width=1920,
                    frame_height=1080,
                ),
                defaultdict(AnyValue, frame_height=ValueSet(720)),
                [
                    FrameSize(
                        custom_dimensions_flag=False,
                    )
                ],
            ),
            # Force custom flag
            (
                VideoParameters(
                    frame_width=1920,
                    frame_height=1080,
                ),
                VideoParameters(
                    frame_width=1920,
                    frame_height=1080,
                ),
                defaultdict(AnyValue, custom_dimensions_flag=ValueSet(True)),
                [
                    FrameSize(
                        custom_dimensions_flag=True,
                        frame_width=1920,
                        frame_height=1080,
                    ),
                ],
            ),
            # Make impossible
            (
                VideoParameters(
                    frame_width=1920,
                    frame_height=720,
                ),
                VideoParameters(
                    frame_width=1920,
                    frame_height=1080,
                ),
                defaultdict(AnyValue, frame_height=ValueSet(720)),
                [],
            ),
        ],
    )
    def test_no_presets(self, bvp, vp, lcd, exp):
        dicts = list(
            iter_custom_options_dicts(
                bvp,
                vp,
                lcd,
                dict_type=FrameSize,
                flag_key="custom_dimensions_flag",
                parameters=["frame_width", "frame_height"],
            )
        )

        assert dicts == exp

    @pytest.mark.parametrize(
        "bvp,vp,lcd,exp",
        [
            # Custom required (no preset available)
            (
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                VideoParameters(
                    pixel_aspect_ratio_numer=2,
                    pixel_aspect_ratio_denom=1,
                ),
                defaultdict(AnyValue),
                [
                    PixelAspectRatio(
                        custom_pixel_aspect_ratio_flag=True,
                        index=0,
                        pixel_aspect_ratio_numer=2,
                        pixel_aspect_ratio_denom=1,
                    ),
                ],
            ),
            # Custom required (preset available)
            (
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                VideoParameters(
                    pixel_aspect_ratio_numer=4,
                    pixel_aspect_ratio_denom=3,
                ),
                defaultdict(AnyValue),
                [
                    PixelAspectRatio(
                        custom_pixel_aspect_ratio_flag=True,
                        index=PresetPixelAspectRatios.reduced_horizontal_resolution,
                    ),
                    PixelAspectRatio(
                        custom_pixel_aspect_ratio_flag=True,
                        index=0,
                        pixel_aspect_ratio_numer=4,
                        pixel_aspect_ratio_denom=3,
                    ),
                ],
            ),
            # Custom optional
            (
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                defaultdict(AnyValue),
                [
                    PixelAspectRatio(
                        custom_pixel_aspect_ratio_flag=False,
                    ),
                    PixelAspectRatio(
                        custom_pixel_aspect_ratio_flag=True,
                        index=PresetPixelAspectRatios.ratio_1_1,
                    ),
                    PixelAspectRatio(
                        custom_pixel_aspect_ratio_flag=True,
                        index=0,
                        pixel_aspect_ratio_numer=1,
                        pixel_aspect_ratio_denom=1,
                    ),
                ],
            ),
            # Force no-custom by disallowing the flag
            (
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                defaultdict(AnyValue, custom_pixel_aspect_ratio_flag=ValueSet(False)),
                [
                    PixelAspectRatio(
                        custom_pixel_aspect_ratio_flag=False,
                    )
                ],
            ),
            # Force no-custom by disallowing required indices
            (
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                defaultdict(AnyValue, pixel_aspect_ratio_index=ValueSet(99)),
                [
                    PixelAspectRatio(
                        custom_pixel_aspect_ratio_flag=False,
                    )
                ],
            ),
            # Force no full custom by disallowing 0 index
            (
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                defaultdict(
                    AnyValue,
                    pixel_aspect_ratio_index=ValueSet(
                        PresetPixelAspectRatios.ratio_1_1
                    ),
                ),
                [
                    PixelAspectRatio(
                        custom_pixel_aspect_ratio_flag=False,
                    ),
                    PixelAspectRatio(
                        custom_pixel_aspect_ratio_flag=True,
                        index=PresetPixelAspectRatios.ratio_1_1,
                    ),
                ],
            ),
            # Force full custom
            (
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                defaultdict(
                    AnyValue,
                    custom_pixel_aspect_ratio_flag=ValueSet(True),
                    pixel_aspect_ratio_index=ValueSet(0),
                ),
                [
                    PixelAspectRatio(
                        custom_pixel_aspect_ratio_flag=True,
                        index=0,
                        pixel_aspect_ratio_numer=1,
                        pixel_aspect_ratio_denom=1,
                    ),
                ],
            ),
            # Make impossible
            (
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                VideoParameters(
                    pixel_aspect_ratio_numer=1,
                    pixel_aspect_ratio_denom=1,
                ),
                defaultdict(
                    AnyValue,
                    custom_pixel_aspect_ratio_flag=ValueSet(True),
                    pixel_aspect_ratio_index=ValueSet(0),
                    pixel_aspect_ratio_numer=ValueSet(1),
                    pixel_aspect_ratio_denom=ValueSet(2),
                ),
                [],
            ),
        ],
    )
    def test_with_presets(self, bvp, vp, lcd, exp):
        dicts = list(
            iter_custom_options_dicts(
                bvp,
                vp,
                lcd,
                dict_type=PixelAspectRatio,
                flag_key="custom_pixel_aspect_ratio_flag",
                parameters=["pixel_aspect_ratio_numer", "pixel_aspect_ratio_denom"],
                presets=PRESET_PIXEL_ASPECT_RATIOS,
                preset_index_constraint_key="pixel_aspect_ratio_index",
            )
        )

        assert dicts == exp

    @pytest.mark.parametrize(
        "bvp,vp,exp",
        [
            # Values match
            (
                VideoParameters(
                    color_primaries_index=PresetColorPrimaries.hdtv,
                ),
                VideoParameters(
                    color_primaries_index=PresetColorPrimaries.hdtv,
                ),
                [
                    ColorPrimaries(
                        custom_color_primaries_flag=False,
                    ),
                    ColorPrimaries(
                        custom_color_primaries_flag=True,
                        index=PresetColorPrimaries.hdtv,
                    ),
                ],
            ),
            # Values don't match
            (
                VideoParameters(
                    color_primaries_index=PresetColorPrimaries.uhdtv,
                ),
                VideoParameters(
                    color_primaries_index=PresetColorPrimaries.hdtv,
                ),
                [
                    ColorPrimaries(
                        custom_color_primaries_flag=True,
                        index=PresetColorPrimaries.hdtv,
                    ),
                ],
            ),
        ],
    )
    def test_with_differing_key_names(self, bvp, vp, exp):
        dicts = list(
            iter_custom_options_dicts(
                bvp,
                vp,
                defaultdict(AnyValue),
                dict_type=ColorPrimaries,
                flag_key="custom_color_primaries_flag",
                parameters=[("color_primaries_index", "index")],
                preset_index_constraint_key="color_primaries_index",
            )
        )

        assert dicts == exp