diff options
author | Gwenole Beauchesne <gwenole.beauchesne@intel.com> | 2014-10-13 16:19:19 +0200 |
---|---|---|
committer | Xiang, Haihao <haihao.xiang@intel.com> | 2014-12-24 08:46:34 +0800 |
commit | b21a650d58cdbe128a6e96b9a7b63faae9c53432 (patch) | |
tree | 92e418216772cee9d512c426b31a3f73f9c5c5ec | |
parent | b83ce01349720bdaa712cec67d63fc426ef2331f (diff) | |
download | libva-intel-driver-b21a650d58cdbe128a6e96b9a7b63faae9c53432.tar.gz |
vpp: add support for high-quality scaling.
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.
v2: fixed rounding precision for normalized coefficients.
Signed-off-by: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
-rw-r--r-- | src/i965_vpp_avs.c | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/src/i965_vpp_avs.c b/src/i965_vpp_avs.c index 12f1dfe4..eae141ea 100644 --- a/src/i965_vpp_avs.c +++ b/src/i965_vpp_avs.c @@ -43,6 +43,15 @@ avs_init_coeffs(float *coeffs, int num_coeffs) #endif } +/* Computes the sinc(x) function */ +static float +avs_sinc(float x) +{ + if (x == 0.0f) + return 1.0f; + return sin(x * M_PI) / (x * M_PI); +} + /* Convolution kernel for linear interpolation */ static float avs_kernel_linear(float x) @@ -52,6 +61,68 @@ 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); + + return abs_x < a ? avs_sinc(x) * avs_sinc(x / a) : 0.0f; +} + +/* Truncates floating-point value towards an epsilon factor */ +static inline float +avs_trunc_coeff(float x, float epsilon) +{ + return rintf(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) + 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 +136,22 @@ 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 int l = num_coeffs > 4 ? 3 : 2; + const float p = (float)phase / (num_phases*2); + int i; + + if (f > 1.0f) + f = 1.0f; + 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 +170,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); } } @@ -101,6 +190,9 @@ avs_update_coefficients(AVSState *avs, float sx, float sy, uint32_t flags) flags &= VA_FILTER_SCALING_MASK; switch (flags) { + case VA_FILTER_SCALING_HQ: + gen_coeffs = avs_gen_coeffs_lanczos; + break; default: gen_coeffs = avs_gen_coeffs_linear; break; |