Exemple #1
0
def test_quantisation_index_bound():
    wavelet_index = WaveletFilters.haar_with_shift
    wavelet_index_ho = WaveletFilters.haar_with_shift
    dwt_depth = 1
    dwt_depth_ho = 0

    quantisation_matrix = {
        0: {
            "LL": 10
        },
        1: {
            "LH": 10,
            "HL": 10,
            "HH": 10
        },
    }

    picture_bit_width = 10

    (
        analysis_signal_bounds,
        synthesis_signal_bounds,
        analysis_test_patterns,
        synthesis_test_patterns,
    ) = static_filter_analysis(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
    )

    (
        concrete_analysis_signal_bounds,
        concrete_synthesis_signal_bounds,
    ) = evaluate_filter_bounds(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
        analysis_signal_bounds,
        synthesis_signal_bounds,
        picture_bit_width,
    )

    max_qi = quantisation_index_bound(
        concrete_analysis_signal_bounds,
        quantisation_matrix,
    )

    max_coeff_magnitude = max(
        max(abs(lower), abs(upper))
        for lower, upper in concrete_analysis_signal_bounds.values())

    assert forward_quant(max_coeff_magnitude, max_qi - 10) == 0
    assert forward_quant(max_coeff_magnitude, max_qi - 11) != 0
Exemple #2
0
def main(args=None):
    args = parse_args(args)
    
    # Load precomputed signal bounds
    static_filter_analysis = json.load(args.static_filter_analysis)
    
    quantisation_matrix = parse_quantisation_matrix_argument(
        args.custom_quantisation_matrix,
        static_filter_analysis["wavelet_index"],
        static_filter_analysis["wavelet_index_ho"],
        static_filter_analysis["dwt_depth"],
        static_filter_analysis["dwt_depth_ho"],
    )
    
    analysis_signal_bounds = deserialise_signal_bounds(
        static_filter_analysis["analysis_signal_bounds"]
    )
    synthesis_signal_bounds = deserialise_signal_bounds(
        static_filter_analysis["synthesis_signal_bounds"]
    )
    
    (
        concrete_analysis_signal_bounds,
        concrete_synthesis_signal_bounds,
    ) = evaluate_filter_bounds(
        static_filter_analysis["wavelet_index"],
        static_filter_analysis["wavelet_index_ho"],
        static_filter_analysis["dwt_depth"],
        static_filter_analysis["dwt_depth_ho"],
        analysis_signal_bounds,
        synthesis_signal_bounds,
        args.picture_bit_width,
    )
    
    print(quantisation_index_bound(
        concrete_analysis_signal_bounds,
        quantisation_matrix,
    ))
    
    return 0
#
# analysis_bounds_dicts = [{(level, array_name, x, y): (lower_bound, upper_bound), ...}, ...]
# synthesis_bounds_dicts = same as above
concrete_analysis_bounds, concrete_synthesis_bounds = evaluate_filter_bounds(
    static_filter_analysis["wavelet_index"],
    static_filter_analysis["wavelet_index_ho"],
    static_filter_analysis["dwt_depth"],
    static_filter_analysis["dwt_depth_ho"],
    analysis_signal_bounds,
    synthesis_signal_bounds,
    args.picture_bit_width,
)

# Find the maximum quantisation index for each bit width
max_quantisation_index = quantisation_index_bound(
    concrete_analysis_bounds,
    quantisation_matrix,
)

# Find test signal output values for each bit width
_, synthesis_test_signal_outputs = evaluate_test_signal_outputs(
    static_filter_analysis["wavelet_index"],
    static_filter_analysis["wavelet_index_ho"],
    static_filter_analysis["dwt_depth"],
    static_filter_analysis["dwt_depth_ho"],
    args.picture_bit_width,
    quantisation_matrix,
    max_quantisation_index,
    analysis_test_signals,
    synthesis_test_signals,
)
Exemple #4
0
def load_filter_analysis(
    static_filter_analysis_file,
    optimised_synthesis_patterns_file,
    quantisation_matrix_argument,
    picture_bit_width,
):
    """
    Load a static filter analysis and optionally a set of optimised synthesis
    test patterns, returning all of the loaded data.
    
    Parameters
    ==========
    static_filter_analysis_file : :py:class:`file`
        An open file ready to read the static filter analysis data from a JSON
        file.
    optimised_synthesis_patterns_file : :py:class:`file` or None
        An open file ready to read a set of optimised synthesis test patterns
        for a JSON file. If None, synthesis test patterns will be read from the
        ``static_filter_analysis_file`` instead.
    quantisation_matrix_argument : [str, ...] or None
        The --custom-quantisation-matrix argument which will be parsed (if
        optimised_synthesis_patterns_file is not provided)
    picture_bit_width : int or None
        The --picture-bit-width argument which will be used if no
        optimised_synthesis_test_patterns file is provided.
    
    Returns
    =======
    wavelet_index : int
    wavelet_index_ho : int
    dwt_depth : int
    dwt_depth_ho : int
    quantisation_matrix : {level: {orient: value, ...}, ...}
    picture_bit_width : int
    max_quantisation_index : int
    concrete_analysis_bounds : {(level, array_name, x, y): (lo, hi), ...}
    concrete_synthesis_bounds : {(level, array_name, x, y): (lo, hi), ...}
    analysis_test_patterns : {(level, array_name, x, y): :py:class:`~vc2_bit_widths.patterns.TestPatternSpecification`, ...}
    synthesis_test_patterns : {(level, array_name, x, y): :py:class:`~vc2_bit_widths.patterns.TestPatternSpecification`, ...}
    """
    # Load precomputed signal bounds
    static_filter_analysis = json.load(static_filter_analysis_file)
    analysis_signal_bounds = deserialise_signal_bounds(
        static_filter_analysis["analysis_signal_bounds"]
    )
    synthesis_signal_bounds = deserialise_signal_bounds(
        static_filter_analysis["synthesis_signal_bounds"]
    )
    
    # Load precomputed test patterns
    analysis_test_patterns = deserialise_test_pattern_specifications(
        TestPatternSpecification,
        static_filter_analysis["analysis_test_patterns"]
    )
    synthesis_test_patterns = deserialise_test_pattern_specifications(
        TestPatternSpecification,
        static_filter_analysis["synthesis_test_patterns"]
    )
    
    # Load optimised synthesis signal
    if optimised_synthesis_patterns_file is not None:
        optimised_json = json.load(optimised_synthesis_patterns_file)
        
        assert static_filter_analysis["wavelet_index"] == optimised_json["wavelet_index"]
        assert static_filter_analysis["wavelet_index_ho"] == optimised_json["wavelet_index_ho"]
        assert static_filter_analysis["dwt_depth"] == optimised_json["dwt_depth"]
        assert static_filter_analysis["dwt_depth_ho"] == optimised_json["dwt_depth_ho"]
        
        picture_bit_width = optimised_json["picture_bit_width"]
        
        quantisation_matrix = deserialise_quantisation_matrix(
            optimised_json["quantisation_matrix"]
        )
        
        synthesis_test_patterns = deserialise_test_pattern_specifications(
            OptimisedTestPatternSpecification,
            optimised_json["optimised_synthesis_test_patterns"]
        )
    else:
        quantisation_matrix = parse_quantisation_matrix_argument(
            quantisation_matrix_argument,
            static_filter_analysis["wavelet_index"],
            static_filter_analysis["wavelet_index_ho"],
            static_filter_analysis["dwt_depth"],
            static_filter_analysis["dwt_depth_ho"],
        )
    
    # Compute signal bounds for all specified bit widths
    #
    # analysis_bounds_dicts = [{(level, array_name, x, y): (lower_bound, upper_bound), ...}, ...]
    # synthesis_bounds_dicts = same as above
    concrete_analysis_bounds, concrete_synthesis_bounds = evaluate_filter_bounds(
        static_filter_analysis["wavelet_index"],
        static_filter_analysis["wavelet_index_ho"],
        static_filter_analysis["dwt_depth"],
        static_filter_analysis["dwt_depth_ho"],
        analysis_signal_bounds,
        synthesis_signal_bounds,
        picture_bit_width,
    )
    
    # Find the maximum quantisation index for each bit width
    max_quantisation_index = quantisation_index_bound(
        concrete_analysis_bounds,
        quantisation_matrix,
    )
    
    return (
        static_filter_analysis["wavelet_index"],
        static_filter_analysis["wavelet_index_ho"],
        static_filter_analysis["dwt_depth"],
        static_filter_analysis["dwt_depth_ho"],
        quantisation_matrix,
        picture_bit_width,
        max_quantisation_index,
        concrete_analysis_bounds,
        concrete_synthesis_bounds,
        analysis_test_patterns,
        synthesis_test_patterns,
    )
def get_test_pictures(codec_features, bundle_filename=get_bundle_filename()):
    """
    Gets a set of test pictures for a codec with the specified codec
    configuration.

    Raises a :py:exc:`MissingStaticAnalysisError` if no static filter analysis
    is available for the specified codec.

    Parameters
    ==========
    bundle_filename : str
        Filename of the VC-2 bit widths bundle file to read test patterns from.
    codec_features : :py:class:`~vc2_conformance.codec_features.CodecFeatures`

    Returns
    =======
    analysis_luma_pictures : [:py:class:`AnalysisPicture`, ...]
    synthesis_luma_pictures : [:py:class:`SynthesisPicture`, ...]
    analysis_color_diff_pictures : [:py:class:`AnalysisPicture`, ...]
    synthesis_color_diff_pictures : [:py:class:`SynthesisPicture`, ...]
        Test pictures for luma and color difference components respectively.
        (See :py:func:`vc2_bit_widths.helpers.generate_test_pictures`.)
    """
    if codec_features["quantization_matrix"] is not None:
        quantisation_matrix = codec_features["quantization_matrix"]
    else:
        quantisation_matrix = QUANTISATION_MATRICES[(
            codec_features["wavelet_index"],
            codec_features["wavelet_index_ho"],
            codec_features["dwt_depth"],
            codec_features["dwt_depth_ho"],
        )]

    dimensions_and_depths = compute_dimensions_and_depths(
        codec_features["video_parameters"],
        codec_features["picture_coding_mode"],
    )

    # Load the test pattern specifications
    try:
        (
            analysis_signal_bounds,
            synthesis_signal_bounds,
            analysis_test_patterns,
            synthesis_test_patterns,
        ) = bundle_get_static_filter_analysis(
            bundle_filename,
            codec_features["wavelet_index"],
            codec_features["wavelet_index_ho"],
            codec_features["dwt_depth"],
            codec_features["dwt_depth_ho"],
        )
    except KeyError:
        raise MissingStaticAnalysisError()

    all_analysis_pictures = []
    all_synthesis_pictures = []
    for component in ["Y", "C1"]:
        picture_bit_width = dimensions_and_depths[component].depth_bits
        picture_width = dimensions_and_depths[component].width
        picture_height = dimensions_and_depths[component].height

        # ...using optimised synthesis test patterns if available...
        try:
            synthesis_test_patterns = bundle_get_optimised_synthesis_test_patterns(
                bundle_filename,
                codec_features["wavelet_index"],
                codec_features["wavelet_index_ho"],
                codec_features["dwt_depth"],
                codec_features["dwt_depth_ho"],
                quantisation_matrix,
                picture_bit_width,
            )
        except KeyError:
            pass

        # Determine the maximum quantisation index which may be usefully used for
        # the codec configuration specified
        concrete_analysis_bounds, concrete_synthesis_bounds = evaluate_filter_bounds(
            codec_features["wavelet_index"],
            codec_features["wavelet_index_ho"],
            codec_features["dwt_depth"],
            codec_features["dwt_depth_ho"],
            analysis_signal_bounds,
            synthesis_signal_bounds,
            picture_bit_width,
        )
        max_quantisation_index = quantisation_index_bound(
            concrete_analysis_bounds,
            quantisation_matrix,
        )

        # Find the worst-case quantisation index for the synthesis test patterns
        _, synthesis_test_pattern_outputs = evaluate_test_pattern_outputs(
            codec_features["wavelet_index"],
            codec_features["wavelet_index_ho"],
            codec_features["dwt_depth"],
            codec_features["dwt_depth_ho"],
            picture_bit_width,
            quantisation_matrix,
            max_quantisation_index,
            analysis_test_patterns,
            synthesis_test_patterns,
        )

        # Generate packed test pictures
        analysis_pictures, synthesis_pictures = generate_test_pictures(
            picture_width,
            picture_height,
            picture_bit_width,
            analysis_test_patterns,
            synthesis_test_patterns,
            synthesis_test_pattern_outputs,
        )

        all_analysis_pictures.append(analysis_pictures)
        all_synthesis_pictures.append(synthesis_pictures)

    return (
        all_analysis_pictures[0],
        all_synthesis_pictures[0],
        all_analysis_pictures[1],
        all_synthesis_pictures[1],
    )
Exemple #6
0
def main(args=None):
    args = parse_args(args)

    if args.verbose:
        logging.basicConfig(level=logging.INFO)

    static_filter_analysis = json.load(args.static_filter_analysis)

    quantisation_matrix = parse_quantisation_matrix_argument(
        args.custom_quantisation_matrix,
        static_filter_analysis["wavelet_index"],
        static_filter_analysis["wavelet_index_ho"],
        static_filter_analysis["dwt_depth"],
        static_filter_analysis["dwt_depth_ho"],
    )

    analysis_signal_bounds = deserialise_signal_bounds(
        static_filter_analysis["analysis_signal_bounds"])
    synthesis_signal_bounds = deserialise_signal_bounds(
        static_filter_analysis["synthesis_signal_bounds"])
    synthesis_test_patterns = deserialise_test_pattern_specifications(
        TestPatternSpecification,
        static_filter_analysis["synthesis_test_patterns"])

    (
        concrete_analysis_signal_bounds,
        concrete_synthesis_signal_bounds,
    ) = evaluate_filter_bounds(
        static_filter_analysis["wavelet_index"],
        static_filter_analysis["wavelet_index_ho"],
        static_filter_analysis["dwt_depth"],
        static_filter_analysis["dwt_depth_ho"],
        analysis_signal_bounds,
        synthesis_signal_bounds,
        args.picture_bit_width,
    )

    max_quantisation_index = quantisation_index_bound(
        concrete_analysis_signal_bounds,
        quantisation_matrix,
    )

    random_state = np.random.RandomState(args.seed)

    optimised_synthesis_test_patterns = optimise_synthesis_test_patterns(
        wavelet_index=static_filter_analysis["wavelet_index"],
        wavelet_index_ho=static_filter_analysis["wavelet_index_ho"],
        dwt_depth=static_filter_analysis["dwt_depth"],
        dwt_depth_ho=static_filter_analysis["dwt_depth_ho"],
        quantisation_matrix=quantisation_matrix,
        picture_bit_width=args.picture_bit_width,
        synthesis_test_patterns=synthesis_test_patterns,
        max_quantisation_index=max_quantisation_index,
        random_state=random_state,
        number_of_searches=args.number_of_searches,
        terminate_early=args.terminate_early,
        added_corruption_rate=args.added_corruption_rate,
        removed_corruption_rate=args.removed_corruption_rate,
        base_iterations=args.base_iterations,
        added_iterations_per_improvement=args.added_iterations_per_improvement,
    )

    out = {
        "wavelet_index":
        static_filter_analysis["wavelet_index"],
        "wavelet_index_ho":
        static_filter_analysis["wavelet_index_ho"],
        "dwt_depth":
        static_filter_analysis["dwt_depth"],
        "dwt_depth_ho":
        static_filter_analysis["dwt_depth_ho"],
        "picture_bit_width":
        args.picture_bit_width,
        "quantisation_matrix":
        serialise_quantisation_matrix(quantisation_matrix, ),
        "optimised_synthesis_test_patterns":
        serialise_test_pattern_specifications(
            OptimisedTestPatternSpecification,
            optimised_synthesis_test_patterns,
        )
    }

    json.dump(out, args.output)
    args.output.write("\n")

    return 0
Exemple #7
0
def test_generate_test_pictures():
    wavelet_index = WaveletFilters.haar_with_shift
    wavelet_index_ho = WaveletFilters.le_gall_5_3
    dwt_depth = 1
    dwt_depth_ho = 0

    h_filter_params = LIFTING_FILTERS[wavelet_index_ho]
    v_filter_params = LIFTING_FILTERS[wavelet_index]

    quantisation_matrix = {
        0: {
            "LL": 0
        },
        1: {
            "LH": 1,
            "HL": 2,
            "HH": 3
        },
    }

    picture_width = 16
    picture_height = 8
    picture_bit_width = 10
    value_offset = 1 << (picture_bit_width - 1)

    (
        analysis_signal_bounds,
        synthesis_signal_bounds,
        analysis_test_patterns,
        synthesis_test_patterns,
    ) = static_filter_analysis(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
    )

    (
        concrete_analysis_signal_bounds,
        concrete_synthesis_signal_bounds,
    ) = evaluate_filter_bounds(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
        analysis_signal_bounds,
        synthesis_signal_bounds,
        picture_bit_width,
    )

    max_quantisation_index = quantisation_index_bound(
        concrete_analysis_signal_bounds,
        quantisation_matrix,
    )

    (
        analysis_test_pattern_outputs,
        synthesis_test_pattern_outputs,
    ) = evaluate_test_pattern_outputs(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
        picture_bit_width,
        quantisation_matrix,
        max_quantisation_index,
        analysis_test_patterns,
        synthesis_test_patterns,
    )

    (
        analysis_pictures,
        synthesis_pictures,
    ) = generate_test_pictures(
        picture_width,
        picture_height,
        picture_bit_width,
        analysis_test_patterns,
        synthesis_test_patterns,
        synthesis_test_pattern_outputs,
    )

    # Check all test patterns and maximise/minimise options were included
    assert set((tp.level, tp.array_name, tp.x, tp.y, tp.maximise)
               for p in analysis_pictures for tp in p.test_points) == set(
                   (level, array_name, x, y, maximise)
                   for (level, array_name, x, y) in analysis_test_patterns
                   for maximise in [True, False])
    assert set((tp.level, tp.array_name, tp.x, tp.y, tp.maximise)
               for p in synthesis_pictures for tp in p.test_points) == set(
                   (level, array_name, x, y, maximise)
                   for (level, array_name, x, y) in synthesis_test_patterns
                   for maximise in [True, False])

    # Test analysis pictures do what they claim
    for analysis_picture in analysis_pictures:
        for test_point in analysis_picture.test_points:
            # Perform analysis on the whole test picture, capturing the target
            # value along the way
            target_value = fast_partial_analysis_transform(
                h_filter_params,
                v_filter_params,
                dwt_depth,
                dwt_depth_ho,
                analysis_picture.picture.copy() -
                value_offset,  # NB: Argument is mutated
                (
                    test_point.level,
                    test_point.array_name,
                    test_point.tx,
                    test_point.ty,
                ),
            )

            # Compare with expected output level for that test pattern
            expected_outputs = analysis_test_pattern_outputs[(
                test_point.level,
                test_point.array_name,
                test_point.x,
                test_point.y,
            )]
            if test_point.maximise:
                expected_value = expected_outputs[1]
            else:
                expected_value = expected_outputs[0]

            assert target_value == expected_value

    # PyExps required for synthesis implementation
    _, synthesis_pyexps = synthesis_transform(
        h_filter_params,
        v_filter_params,
        dwt_depth,
        dwt_depth_ho,
        make_variable_coeff_arrays(dwt_depth, dwt_depth_ho),
    )

    # Test synthesis pictures do what they claim
    for synthesis_picture in synthesis_pictures:
        for test_point in synthesis_picture.test_points:
            # Perform analysis, quantisation and synthesis on the whole test
            # picture, capturing just the target synthesis value
            codec = FastPartialAnalyseQuantiseSynthesise(
                h_filter_params,
                v_filter_params,
                dwt_depth,
                dwt_depth_ho,
                quantisation_matrix,
                [synthesis_picture.quantisation_index],
                synthesis_pyexps[(
                    test_point.level,
                    test_point.array_name,
                )][test_point.tx, test_point.ty],
            )
            # NB: Argument is mutated
            target_value = codec.analyse_quantise_synthesise(
                synthesis_picture.picture.copy() - value_offset, )[0]

            # Compare with expected output level for that test pattern
            expected_outputs = synthesis_test_pattern_outputs[(
                test_point.level,
                test_point.array_name,
                test_point.x,
                test_point.y,
            )]
            if test_point.maximise:
                expected_value = expected_outputs[1][0]
            else:
                expected_value = expected_outputs[0][0]

            assert target_value == expected_value
Exemple #8
0
def test_evaluate_test_pattern_outputs():
    wavelet_index = WaveletFilters.haar_with_shift
    wavelet_index_ho = WaveletFilters.le_gall_5_3
    dwt_depth = 1
    dwt_depth_ho = 0

    quantisation_matrix = {
        0: {
            "LL": 0
        },
        1: {
            "LH": 1,
            "HL": 2,
            "HH": 3
        },
    }

    picture_bit_width = 10

    (
        analysis_signal_bounds,
        synthesis_signal_bounds,
        analysis_test_patterns,
        synthesis_test_patterns,
    ) = static_filter_analysis(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
    )

    (
        concrete_analysis_signal_bounds,
        concrete_synthesis_signal_bounds,
    ) = evaluate_filter_bounds(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
        analysis_signal_bounds,
        synthesis_signal_bounds,
        picture_bit_width,
    )

    max_quantisation_index = quantisation_index_bound(
        concrete_analysis_signal_bounds,
        quantisation_matrix,
    )

    # Run a null-search to determine the actual decoded output of the synthesis
    # test patterns generated... (bodge)
    random_state = np.random.RandomState(1)
    number_of_searches = 1
    terminate_early = None
    added_corruption_rate = 0
    removed_corruption_rate = 0.0
    base_iterations = 0
    added_iterations_per_improvement = 0
    optimised_synthesis_test_patterns = optimise_synthesis_test_patterns(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
        quantisation_matrix,
        picture_bit_width,
        synthesis_test_patterns,
        max_quantisation_index,
        random_state,
        number_of_searches,
        terminate_early,
        added_corruption_rate,
        removed_corruption_rate,
        base_iterations,
        added_iterations_per_improvement,
    )

    (
        analysis_test_pattern_outputs,
        synthesis_test_pattern_outputs,
    ) = evaluate_test_pattern_outputs(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
        picture_bit_width,
        quantisation_matrix,
        max_quantisation_index,
        analysis_test_patterns,
        optimised_synthesis_test_patterns,
    )

    # Analysis values should be similar to the bounds
    for (level, array_name, x,
         y), (minimum, maximum) in analysis_test_pattern_outputs.items():
        lower_bound, upper_bound = concrete_analysis_signal_bounds[(level,
                                                                    array_name,
                                                                    x, y)]

        assert np.isclose(minimum, lower_bound, 0.01)
        assert lower_bound <= minimum

        assert np.isclose(maximum, upper_bound, 0.01)
        assert maximum <= upper_bound

    # Synthesis values should be equal to the optimiser's values (where
    # present)
    for (
        (level, array_name, x, y),
        ((minimum, min_qi), (maximum, max_qi)),
    ) in synthesis_test_pattern_outputs.items():
        if (level, array_name, x, y) in optimised_synthesis_test_patterns:
            ts = optimised_synthesis_test_patterns[(level, array_name, x, y)]

            assert ts.decoded_value == maximum
            assert ts.quantisation_index == max_qi
Exemple #9
0
def test_optimise_synthesis_test_patterns():
    wavelet_index = WaveletFilters.haar_with_shift
    wavelet_index_ho = WaveletFilters.le_gall_5_3
    dwt_depth = 1
    dwt_depth_ho = 0

    quantisation_matrix = {
        0: {
            "LL": 0
        },
        1: {
            "LH": 0,
            "HL": 0,
            "HH": 0
        },
    }

    picture_bit_width = 10
    input_min = -512
    input_max = 511

    (
        analysis_signal_bounds,
        synthesis_signal_bounds,
        analysis_test_patterns,
        synthesis_test_patterns,
    ) = static_filter_analysis(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
    )

    (
        concrete_analysis_signal_bounds,
        concrete_synthesis_signal_bounds,
    ) = evaluate_filter_bounds(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
        analysis_signal_bounds,
        synthesis_signal_bounds,
        picture_bit_width,
    )

    max_quantisation_index = quantisation_index_bound(
        concrete_analysis_signal_bounds,
        quantisation_matrix,
    )

    random_state = np.random.RandomState(1)
    number_of_searches = 2
    terminate_early = None
    added_corruption_rate = 0.5
    removed_corruption_rate = 0.0
    base_iterations = 10
    added_iterations_per_improvement = 1

    optimised_synthesis_test_patterns = optimise_synthesis_test_patterns(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
        quantisation_matrix,
        picture_bit_width,
        synthesis_test_patterns,
        max_quantisation_index,
        random_state,
        number_of_searches,
        terminate_early,
        added_corruption_rate,
        removed_corruption_rate,
        base_iterations,
        added_iterations_per_improvement,
    )

    # As a sanity check, ensure that the test patterns provided do produce the
    # values they say they do at the quantisation indices claimed
    state = State(
        wavelet_index=wavelet_index,
        wavelet_index_ho=wavelet_index_ho,
        dwt_depth=dwt_depth,
        dwt_depth_ho=dwt_depth_ho,
    )
    for level, array_name, x, y in optimised_synthesis_test_patterns:
        # Only check the final decoder output (as the pseudocode doesn't
        # provide access to other arrays)
        if level != dwt_depth + dwt_depth_ho or array_name != "Output":
            continue

        ts = optimised_synthesis_test_patterns[(level, array_name, x, y)]

        picture, _ = convert_test_pattern_to_padded_picture_and_slice(
            ts.pattern,
            input_min,
            input_max,
            dwt_depth,
            dwt_depth_ho,
        )

        tx, ty = ts.target
        coeffs = dwt(state, picture.tolist())

        qi = ts.quantisation_index
        quantised_coeffs = {
            level: {
                orient: [[
                    inverse_quant(forward_quant(value, qi), qi)
                    for value in row
                ] for row in rows]
                for orient, rows in orients.items()
            }
            for level, orients in coeffs.items()
        }

        decoded_picture = idwt(state, quantised_coeffs)

        assert decoded_picture[ty][tx] == ts.decoded_value