def synthesis_filter_bounds(expression): """ Find the lower- and upper-bound reachable in a :py:class:`~vc2_bit_widths.linexp.LinExp` describing a synthesis filter. The filter expression must contain only affine error symbols (:py:class:`~vc2_bit_widths.linexp.AAError`) and symbols of the form ``((_, level, orient), x, y)`` representing transform coefficients. Parameters ========== expression : :py:class:`~vc2_bit_widths.linexp.LinExp` Returns ======= (lower_bound, upper_bound) : :py:class:`~vc2_bit_widths.linexp.LinExp` Lower and upper bounds for the signal level in terms of the symbols of the form ``LinExp("signal_LEVEL_ORIENT_min")`` and ``LinExp("signal_LEVEL_ORIENT_max")``, representing the minimum and maximum signal levels for transform coefficients in level ``LEVEL`` and orientation ``ORIENT`` respectively. """ # Replace all transform coefficients with affine error terms scaled to # appropriate ranges expression = LinExp(expression) lower_bound = affine_lower_bound( expression.subs({ sym: ("coeff_{}_{}_min" if coeff > 0 else "coeff_{}_{}_max").format( sym[0][1], sym[0][2], ) for sym, coeff in expression if sym is not None and not isinstance(sym, AAError) })) upper_bound = affine_upper_bound( expression.subs({ sym: ("coeff_{}_{}_min" if coeff < 0 else "coeff_{}_{}_max").format( sym[0][1], sym[0][2], ) for sym, coeff in expression if sym is not None and not isinstance(sym, AAError) })) return (lower_bound, upper_bound)
def analysis_filter_bounds(expression): """ Find the lower- and upper-bound reachable in a :py:class:`~vc2_bit_widths.linexp.LinExp` describing an analysis filter. The filter expression must consist of only affine error symbols (:py:class:`~vc2_bit_widths.linexp.AAError`) and symbols of the form ``(_, x, y)`` representing pixel values in an input picture. Parameters ========== expression : :py:class:`~vc2_bit_widths.linexp.LinExp` Returns ======= lower_bound : :py:class:`~vc2_bit_widths.linexp.LinExp` upper_bound : :py:class:`~vc2_bit_widths.linexp.LinExp` Algebraic expressions for the lower and upper bounds for the signal level. These expressions are given in terms of the symbols ``LinExp("signal_min")`` and ``LinExp("signal_max")``, which represent the minimum and maximum picture signal levels respectively. """ signal_min = LinExp("signal_min") signal_max = LinExp("signal_max") expression = LinExp(expression) lower_bound = affine_lower_bound( expression.subs({ sym: signal_min if coeff > 0 else signal_max for sym, coeff in expression if sym is not None and not isinstance(sym, AAError) })) upper_bound = affine_upper_bound( expression.subs({ sym: signal_min if coeff < 0 else signal_max for sym, coeff in expression if sym is not None and not isinstance(sym, AAError) })) return (lower_bound, upper_bound)
def test_integration(): # A simple integration test which computes signal bounds for a small # transform operation filter_params = LIFTING_FILTERS[WaveletFilters.haar_with_shift] dwt_depth = 1 dwt_depth_ho = 1 input_picture_array = SymbolArray(2) analysis_coeff_arrays, analysis_intermediate_values = analysis_transform( filter_params, filter_params, dwt_depth, dwt_depth_ho, input_picture_array, ) input_coeff_arrays = make_symbol_coeff_arrays(dwt_depth, dwt_depth_ho) synthesis_output, synthesis_intermediate_values = synthesis_transform( filter_params, filter_params, dwt_depth, dwt_depth_ho, input_coeff_arrays, ) signal_min = LinExp("signal_min") signal_max = LinExp("signal_max") example_range = {signal_min: -512, signal_max: 511} # Input signal bounds should be as specified assert analysis_filter_bounds( analysis_intermediate_values[(2, "Input")][0, 0], ) == (signal_min, signal_max) # Output of final analysis filter should require a greater depth (NB: for # the Haar transform it is the high-pass bands which gain the largest # signal range) analysis_output_lower, analysis_output_upper = analysis_filter_bounds( analysis_intermediate_values[(1, "H")][0, 0], ) assert analysis_output_lower.subs(example_range) < signal_min.subs( example_range) assert analysis_output_upper.subs(example_range) > signal_max.subs( example_range) example_coeff_range = { "coeff_{}_{}_{}".format(level, orient, minmax): maximum_dequantised_magnitude( int(round(value.subs(example_range).constant))) for level, orients in analysis_coeff_arrays.items() for orient, expr in orients.items() for minmax, value in zip(["min", "max"], analysis_filter_bounds(expr)) } # Signal range should shrink down by end of synthesis process but should # still be larger than the original signal final_output_lower, final_output_upper = synthesis_filter_bounds( synthesis_output[0, 0]) assert final_output_upper.subs( example_coeff_range) < analysis_output_upper.subs(example_range) assert final_output_lower.subs( example_coeff_range) > analysis_output_lower.subs(example_range) assert final_output_upper.subs(example_coeff_range) > signal_max.subs( example_range) assert final_output_lower.subs(example_coeff_range) < signal_min.subs( example_range)