[Libva] [PATCH intel-driver 4/8] vpp: add support for high-quality scaling.

Gwenole Beauchesne gb.devel at gmail.com
Mon Oct 13 23:39:50 PDT 2014


2014-10-13 19:27 GMT+02:00 Gwenole Beauchesne <gb.devel at gmail.com>:
> Add support for high-quality scaling during video processing. This is
> enabled with the VA_FILTER_SCALING_HQ filter flag. The algorithm used
> for that is based on a Lanczos convolution kernel: 3 lobes on either
> side for luma samples, and 2 lobes on either side for chroma samples.
>
> Signed-off-by: Gwenole Beauchesne <gwenole.beauchesne at intel.com>
> ---
>  src/i965_vpp_avs.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 86 insertions(+)
>
> diff --git a/src/i965_vpp_avs.c b/src/i965_vpp_avs.c
> index 39407ac..33d6eba 100644
> --- a/src/i965_vpp_avs.c
> +++ b/src/i965_vpp_avs.c
> @@ -52,6 +52,72 @@ avs_kernel_linear(float x)
>      return abs_x < 1.0f ? 1 - abs_x : 0.0f;
>  }
>
> +/* Convolution kernel for Lanczos-based interpolation */
> +static float
> +avs_kernel_lanczos(float x, float a)
> +{
> +    const float abs_x = fabsf(x);
> +
> +    if (abs_x == 0.0f)
> +        return 1.0;
> +    if (abs_x < a)
> +        return a * sin(x * M_PI) * sin((x * M_PI) / a) / (M_PI * M_PI * x * x);
> +    return 0.0f;
> +}

(possible comment to add while I think about it)
Note: the first multiplication by a could be omitted since we
normalize the outputs afterwards, but I'd prefer keep it as is.

> +
> +/* Truncates floating-point value towards an epsilon factor */
> +static inline float
> +avs_trunc_coeff(float x, float epsilon)
> +{
> +    return floorf(x / epsilon) * epsilon;
> +}
> +
> +/* Normalize coefficients for one sample/direction */
> +static void
> +avs_normalize_coeffs_1(float *coeffs, int num_coeffs, float epsilon)
> +{
> +    float s, sum = 0.0;
> +    int i, c, r, r1;
> +
> +    for (i = 0; i < num_coeffs; i++)
> +        sum += coeffs[i];
> +
> +    if (sum < epsilon)
> +        return;
> +
> +    s = 0.0;
> +    for (i = 0; i < num_coeffs; i++)
> +        s += (coeffs[i] = avs_trunc_coeff(coeffs[i] / sum, epsilon));
> +
> +    /* Distribute the remaining bits, while allocating more to the center */
> +    c = num_coeffs/2;
> +    c = c - (coeffs[c - 1] > coeffs[c]);
> +
> +    r = (1.0f - s) / epsilon;
> +    r1 = r / 4;
> +    if (coeffs[c + 1] != 0.0f)

This condition is reversed. I will fix it. :)
Initial testing showed no distortion since the remaining bits were
always distributed. The idea here was to fine tune the distribution
with 2x more to the center than the immediate neighbours.

> +        coeffs[c] += r * epsilon;
> +    else {
> +        coeffs[c] += (r - 2*r1) * epsilon;
> +        coeffs[c - 1] += r1 * epsilon;
> +        coeffs[c + 1] += r1 * epsilon;
> +    }
> +}
> +
> +/* Normalize all coefficients so that their sum yields 1.0f */
> +static void
> +avs_normalize_coeffs(AVSCoeffs *coeffs, const AVSConfig *config)
> +{
> +    avs_normalize_coeffs_1(coeffs->y_k_h, config->num_luma_coeffs,
> +        config->coeff_epsilon);
> +    avs_normalize_coeffs_1(coeffs->y_k_v, config->num_luma_coeffs,
> +        config->coeff_epsilon);
> +    avs_normalize_coeffs_1(coeffs->uv_k_h, config->num_chroma_coeffs,
> +        config->coeff_epsilon);
> +    avs_normalize_coeffs_1(coeffs->uv_k_v, config->num_chroma_coeffs,
> +        config->coeff_epsilon);
> +}
> +
>  /* Generate coefficients for default quality (bilinear) */
>  static void
>  avs_gen_coeffs_linear(float *coeffs, int num_coeffs, int phase, int num_phases,
> @@ -65,6 +131,21 @@ avs_gen_coeffs_linear(float *coeffs, int num_coeffs, int phase, int num_phases,
>      coeffs[c + 1] = avs_kernel_linear(p - 1);
>  }
>
> +/* Generate coefficients for high quality (lanczos) */
> +static void
> +avs_gen_coeffs_lanczos(float *coeffs, int num_coeffs, int phase, int num_phases,
> +    float f)
> +{
> +    const int c = num_coeffs/2 - 1;
> +    const float p = (float)phase / (num_phases*2);
> +    int i, l = 2;
> +
> +    l = num_coeffs > 4 ? 3 : 2;
> +    f = 1.0f / ceilf(1.0f/f);
> +    for (i = 0; i < num_coeffs; i++)
> +        coeffs[i] = avs_kernel_lanczos((i - (c + p)) * f, l);
> +}
> +
>  /* Generate coefficients with the supplied scaler */
>  static void
>  avs_gen_coeffs(AVSState *avs, float sx, float sy, AVSGenCoeffsFunc gen_coeffs)
> @@ -83,6 +164,8 @@ avs_gen_coeffs(AVSState *avs, float sx, float sy, AVSGenCoeffsFunc gen_coeffs)
>              i, config->num_phases, sy);
>          gen_coeffs(coeffs->uv_k_v, config->num_chroma_coeffs,
>              i, config->num_phases, sy);
> +
> +        avs_normalize_coeffs(coeffs, config);
>      }
>  }
>
> @@ -100,6 +183,9 @@ avs_update_coefficients(AVSState *avs, float sx, float sy, uint32_t flags)
>      AVSGenCoeffsFunc gen_coeffs;
>
>      switch (flags & VA_FILTER_SCALING_MASK) {
> +    case VA_FILTER_SCALING_HQ:
> +        gen_coeffs = avs_gen_coeffs_lanczos;
> +        break;
>      default:
>          gen_coeffs = avs_gen_coeffs_linear;
>          break;
> --
> 1.9.1
>


More information about the Libva mailing list