예제 #1
0
    def test_plausible_maximisation(self, input_array, transform_coeffs,
                                    wavelet_index, wavelet_index_ho, dwt_depth,
                                    dwt_depth_ho):
        # Check that the test pattern does in fact appear to maximise the filter
        # output value within the limits of the affine arithmetic bounds
        value_min = -512
        value_max = 255
        signal_range = {"signal_min": value_min, "signal_max": value_max}

        for level, orients in transform_coeffs.items():
            for orient, target_array in orients.items():
                for tx in range(target_array.period[0]):
                    for ty in range(target_array.period[1]):
                        # Produce a test pattern
                        ts = make_analysis_maximising_pattern(
                            input_array,
                            target_array,
                            tx,
                            ty,
                        )

                        # Find the expected bounds for values in the targeted
                        # transform coefficient
                        new_tx, new_ty = ts.target
                        target_filter = target_array[new_tx, new_ty]
                        lower_bound, upper_bound = analysis_filter_bounds(
                            target_filter)
                        lower_bound = lower_bound.subs(signal_range).constant
                        upper_bound = upper_bound.subs(signal_range).constant

                        # Create a test picture and encode it with the VC-2
                        # pseudocode
                        test_pattern_picture, _ = ts.pattern.as_picture_and_slice(
                            value_min, value_max)
                        height, width = test_pattern_picture.shape
                        test_pattern_picture = test_pattern_picture.tolist()

                        value_maximised = encode_with_vc2(
                            test_pattern_picture,
                            width,
                            height,
                            wavelet_index,
                            wavelet_index_ho,
                            dwt_depth,
                            dwt_depth_ho,
                        )[level][orient][new_ty][new_tx]

                        # Check we get within 1% of the upper bound
                        assert upper_bound * 0.99 <= value_maximised <= upper_bound
예제 #2
0
    def test_plausible_maximisation_no_quantisation(
            self, analysis_input_array, analysis_transform_coeff_arrays,
            synthesis_output_array, synthesis_intermediate_arrays,
            wavelet_index, wavelet_index_ho, dwt_depth, dwt_depth_ho):
        # Check that the test patterns do, in fact, appear to maximise the
        # filter output for quantisation-free filtering
        value_min = -512
        value_max = 255

        for tx in range(synthesis_output_array.period[0]):
            for ty in range(synthesis_output_array.period[1]):
                # Produce a test pattern
                ts = make_synthesis_maximising_pattern(
                    analysis_input_array,
                    analysis_transform_coeff_arrays,
                    synthesis_output_array,
                    synthesis_output_array,
                    tx,
                    ty,
                )

                # Create a test picture and encode/decode it with the VC-2
                # pseudocode (with no quantisaton)
                test_pattern_picture, _ = ts.pattern.as_picture_and_slice(
                    value_min, value_max)
                height, width = test_pattern_picture.shape
                test_pattern_picture = test_pattern_picture.tolist()

                new_tx, new_ty = ts.target
                kwargs = {
                    "width": width,
                    "height": height,
                    "wavelet_index": wavelet_index,
                    "wavelet_index_ho": wavelet_index_ho,
                    "dwt_depth": dwt_depth,
                    "dwt_depth_ho": dwt_depth_ho,
                }
                value_maximised = decode_with_vc2(
                    encode_with_vc2(test_pattern_picture, **kwargs),
                    **kwargs)[new_ty][new_tx]

                # Check the value was in fact maximised
                assert value_maximised == value_max
def test_correctness(tmpdir):
    # This integration test runs the generated test pictures through a VC-2
    # encoder/quantiser/decoder and verifies that the (observable) signal
    # levels match those predicted by the bit-widths table tool (and in the
    # locations indicated by the provided metadata).

    static_analysis_filename = str(tmpdir.join("static_analysis.json"))
    bit_widths_table_filename = str(tmpdir.join("bit_widths_table.csv"))
    test_pictures_dir = str(tmpdir.join("test_pictures"))
    os.mkdir(test_pictures_dir)

    wavelet_index = WaveletFilters.le_gall_5_3
    wavelet_index_ho = WaveletFilters.le_gall_5_3
    dwt_depth = 1
    dwt_depth_ho = 0
    quantisation_matrix = QUANTISATION_MATRICES[(
        wavelet_index,
        wavelet_index_ho,
        dwt_depth,
        dwt_depth_ho,
    )]
    picture_bit_width = 8  # Assumed to be 8 in rest of test...

    vc2_static_filter_analysis([
        "--wavelet-index",
        wavelet_index.name,
        "--wavelet-index-ho",
        wavelet_index_ho.name,
        "--dwt-depth",
        str(dwt_depth),
        "--dwt-depth-ho",
        str(dwt_depth_ho),
        "--output",
        static_analysis_filename,
    ])

    vc2_bit_widths_table([
        static_analysis_filename,
        "--picture-bit-width",
        str(picture_bit_width),
        "--show-all-filter-phases",
        "--output",
        bit_widths_table_filename,
    ])

    # Read bit widths table (to get model answers)
    # {(type, level, array_name, x, y, maximise): value, ...}
    expected_signal_values = {}
    for row in csv.DictReader(open(bit_widths_table_filename)):
        typename = row.pop("type")
        level = int(row.pop("level"))
        array_name = row.pop("array_name")
        x = int(row.pop("x"))
        y = int(row.pop("y"))
        minimise = int(row.pop("test_pattern_min"))
        maximise = int(row.pop("test_pattern_max"))

        expected_signal_values[(
            typename,
            level,
            array_name,
            x,
            y,
            False,
        )] = minimise

        expected_signal_values[(
            typename,
            level,
            array_name,
            x,
            y,
            True,
        )] = maximise

    vc2_bit_width_test_pictures([
        static_analysis_filename,
        "64",
        "32",  # Chosen to be multiple of required power of 2
        "--picture-bit-width",
        str(picture_bit_width),
        "--output-directory",
        test_pictures_dir,
    ])

    test_picture_filenames = os.listdir(test_pictures_dir)

    # Encode the analysis test pictures
    # {filename: encoded_data, ...}
    encoded_analysis_pictures = {}
    for filename in test_picture_filenames:
        if filename.startswith("analysis_") and filename.endswith(".png"):
            picture = np.asarray(
                PIL.Image.open(os.path.join(test_pictures_dir, filename)))
            picture = picture.astype(int) - 128

            encoded_analysis_pictures[filename] = encode_with_vc2(
                picture.tolist(),
                picture.shape[1],
                picture.shape[0],
                wavelet_index,
                wavelet_index_ho,
                dwt_depth,
                dwt_depth_ho,
            )

    # Encode/quantise/decode the synthesis test pictures
    # {filename: encoded_data, ...}
    decoded_synthesis_pictures = {}
    for filename in test_picture_filenames:
        if filename.startswith("synthesis_") and filename.endswith(".png"):
            picture = np.asarray(
                PIL.Image.open(os.path.join(test_pictures_dir, filename)))
            picture = picture.astype(int) - 128

            qi = int(filename.partition("qi")[2].partition(".")[0])

            transform_coeffs = encode_with_vc2(
                picture.tolist(),
                picture.shape[1],
                picture.shape[0],
                wavelet_index,
                wavelet_index_ho,
                dwt_depth,
                dwt_depth_ho,
            )

            quantised_coeffs = quantise_coeffs(transform_coeffs, qi,
                                               quantisation_matrix)

            decoded_synthesis_pictures[filename] = decode_with_vc2(
                quantised_coeffs,
                picture.shape[1],
                picture.shape[0],
                wavelet_index,
                wavelet_index_ho,
                dwt_depth,
                dwt_depth_ho,
            )

    # Extract the target values for the outputs of the encoded/decoded pictures
    # {(type, level, array_name, x, y, maximise): value, ...}
    actual_signal_values = {}
    for filename in test_picture_filenames:
        if filename.startswith("analysis_") and filename.endswith(".png"):
            json_filename = filename.replace(".png", ".json")
            test_points = json.load(
                open(os.path.join(test_pictures_dir, json_filename)))
            for test_point in test_points:
                # Convert L'' and H'' coordinates into LL, LH, HL, HH
                # coordinates (omitted since they're just an interleaving).
                # This assumes we've chosen a 2D transform with 2 lifting
                # stages in this test...
                if test_point["array_name"] not in ("L''", "H''"):
                    continue

                level = test_point["level"]

                tx = test_point["tx"]
                ty = test_point["ty"] // 2

                if test_point["y"] % 2 == 0:
                    subband = "LL" if test_point[
                        "array_name"] == "L''" else "HL"
                else:
                    subband = "LH" if test_point[
                        "array_name"] == "L''" else "HH"

                if subband == "LL":
                    level -= 1

                if subband == "LL" and level != 0:
                    continue

                value = encoded_analysis_pictures[filename][level][subband][
                    ty][tx]

                actual_signal_values[(
                    "analysis",
                    test_point["level"],
                    test_point["array_name"],
                    test_point["x"],
                    test_point["y"],
                    test_point["maximise"],
                )] = value

    for filename in test_picture_filenames:
        if filename.startswith("synthesis_") and filename.endswith(".png"):
            json_filename = filename.replace(".png", ".json")
            test_points = json.load(
                open(os.path.join(test_pictures_dir, json_filename)))
            for test_point in test_points:
                # Only consider the output picture
                if test_point["array_name"] != "Output" or test_point[
                        "level"] != dwt_depth + dwt_depth_ho:
                    continue

                tx = test_point["tx"]
                ty = test_point["ty"]
                value = decoded_synthesis_pictures[filename][ty][tx]

                actual_signal_values[(
                    "synthesis",
                    test_point["level"],
                    test_point["array_name"],
                    test_point["x"],
                    test_point["y"],
                    test_point["maximise"],
                )] = value

    # Check the actual signal levels observed match the expectations
    for key, value in actual_signal_values.items():
        assert value == expected_signal_values[key]
예제 #4
0
    def test_plausible_maximisation_with_quantisation(
            self, analysis_input_array, analysis_transform_coeff_arrays,
            synthesis_output_array, synthesis_intermediate_arrays,
            wavelet_index, wavelet_index_ho, dwt_depth, dwt_depth_ho):
        # Check that the test patterns do, in fact, appear to make larger values
        # post-quantisation than would appear with a more straight-forward
        # scheme
        value_min = -512
        value_max = 511

        num_periods_made_larger_by_test_pattern = 0
        for tx in range(synthesis_output_array.period[0]):
            for ty in range(synthesis_output_array.period[1]):
                # Produce a test pattern
                ts = make_synthesis_maximising_pattern(
                    analysis_input_array,
                    analysis_transform_coeff_arrays,
                    synthesis_output_array,
                    synthesis_output_array,
                    tx,
                    ty,
                )

                test_pattern_picture, _ = ts.pattern.as_picture_and_slice(
                    value_min, value_max)
                height, width = test_pattern_picture.shape
                test_pattern_picture = test_pattern_picture.tolist()

                # Create picture where just the target pixel is set
                new_tx, new_ty = ts.target
                just_target_picture = [[
                    int((x, y) == (new_tx, new_ty)) * value_max
                    for x in range(width)
                ] for y in range(height)]

                kwargs = {
                    "width": width,
                    "height": height,
                    "wavelet_index": wavelet_index,
                    "wavelet_index_ho": wavelet_index_ho,
                    "dwt_depth": dwt_depth,
                    "dwt_depth_ho": dwt_depth_ho,
                }

                # Encode with VC-2 pseudocode
                test_pattern_coeffs = encode_with_vc2(test_pattern_picture,
                                                      **kwargs)
                just_target_coeffs = encode_with_vc2(just_target_picture,
                                                     **kwargs)

                # Try different quantisation levels and record the worst-case
                # effect on the target pixel
                test_pattern_value_worst_case = 0
                just_target_value_worst_case = 0
                for qi in range(64):
                    test_pattern_value = decode_with_vc2(
                        quantise_coeffs(test_pattern_coeffs, qi),
                        **kwargs)[new_ty][new_tx]
                    if abs(test_pattern_value) > abs(
                            test_pattern_value_worst_case):
                        test_pattern_value_worst_case = test_pattern_value

                    just_target_value = decode_with_vc2(
                        quantise_coeffs(just_target_coeffs, qi),
                        **kwargs)[new_ty][new_tx]
                    if abs(just_target_value) > abs(
                            just_target_value_worst_case):
                        just_target_value_worst_case = just_target_value

                # Check the value was in fact made bigger in the worst case by
                # the new test pattern
                if abs(test_pattern_value_worst_case) > abs(
                        just_target_value_worst_case):
                    num_periods_made_larger_by_test_pattern += 1

        # Check that, in the common case, make the worst-case values worse with
        # the test pattern than a single hot pixel would.
        num_periods = synthesis_output_array.period[
            0] * synthesis_output_array.period[1]
        assert num_periods_made_larger_by_test_pattern > (num_periods / 2.0)
    def test_correctness(
        self,
        wavelet_index,
        filter_params,
        dwt_depth,
        dwt_depth_ho,
        quantisation_matrix,
        analysis_input_linexp_array,
        analysis_coeff_linexp_arrays,
        synthesis_output_linexp_array,
        synthesis_output_pyexp_array,
    ):
        # This test will simply attempt to maximise a real test pattern and verify
        # that the procedure appears to produce an improved result.

        max_quantisation_index = 63

        input_min = -512
        input_max = 511

        # Make an arbitrary choice of target to maximise
        synthesis_target_linexp_array = synthesis_output_linexp_array
        synthesis_target_pyexp_array = synthesis_output_pyexp_array

        # Run against all filter phases as some phases may happen to be maximised
        # by the test pattern anyway
        num_improved_phases = 0
        for tx in range(synthesis_target_linexp_array.period[0]):
            for ty in range(synthesis_target_linexp_array.period[1]):
                # Produce test pattern
                ts = make_synthesis_maximising_pattern(
                    analysis_input_linexp_array,
                    analysis_coeff_linexp_arrays,
                    synthesis_target_linexp_array,
                    synthesis_output_linexp_array,
                    tx,
                    ty,
                )

                new_tx, new_ty = ts.target
                synthesis_pyexp = synthesis_target_pyexp_array[new_tx, new_ty]

                kwargs = {
                    "h_filter_params": filter_params,
                    "v_filter_params": filter_params,
                    "dwt_depth": dwt_depth,
                    "dwt_depth_ho": dwt_depth_ho,
                    "quantisation_matrix": quantisation_matrix,
                    "synthesis_pyexp": synthesis_pyexp,
                    "test_pattern_specification": ts,
                    "input_min": input_min,
                    "input_max": input_max,
                    "max_quantisation_index": max_quantisation_index,
                    "random_state": np.random.RandomState(1),
                    "added_corruptions_per_iteration":
                    (len(ts.pattern) + 19) // 20,  # 5%
                    "removed_corruptions_per_iteration":
                    (len(ts.pattern) + 8) // 5,  # 20%
                    "added_iterations_per_improvement": 50,
                    "terminate_early": None,
                }

                # Run without any greedy searches to get 'baseline' figure
                base_ts = optimise_synthesis_maximising_test_pattern(
                    number_of_searches=1, base_iterations=0, **kwargs)

                # Verify unrelated test pattern parameters passed through
                assert base_ts.target == ts.target
                assert base_ts.pattern_translation_multiple == ts.pattern_translation_multiple
                assert base_ts.target_translation_multiple == ts.target_translation_multiple

                # Run with greedy search to verify better result
                imp_ts = optimise_synthesis_maximising_test_pattern(
                    number_of_searches=3, base_iterations=100, **kwargs)
                assert imp_ts.num_search_iterations >= 3 * 100

                # Ensure new test pattern is normalised to polarities
                assert np.all(np.isin(imp_ts.pattern.polarities, (-1, +1)))

                # Should have improved over the test pattern alone
                if abs(imp_ts.decoded_value) > abs(base_ts.decoded_value):
                    num_improved_phases += 1

                # Check to see if decoded value matches what the pseudocode decoder
                # would produce
                imp_test_pattern_picture, _ = imp_ts.pattern.as_picture_and_slice(
                    input_min, input_max)
                height, width = imp_test_pattern_picture.shape
                imp_test_pattern_picture = imp_test_pattern_picture.tolist()
                kwargs = {
                    "width": width,
                    "height": height,
                    "wavelet_index": wavelet_index,
                    "wavelet_index_ho": wavelet_index,
                    "dwt_depth": dwt_depth,
                    "dwt_depth_ho": dwt_depth_ho,
                }
                actual_decoded_value = decode_with_vc2(
                    quantise_coeffs(
                        encode_with_vc2(imp_test_pattern_picture, **kwargs),
                        imp_ts.quantisation_index,
                        quantisation_matrix,
                    ), **kwargs)[new_ty][new_tx]

                assert imp_ts.decoded_value == actual_decoded_value

        # Consider the procedure to work if some of the phases are improved
        assert num_improved_phases >= 1