From: Debargha Mukherjee Date: Fri, 29 Jan 2016 23:30:19 +0000 (-0800) Subject: Refactor to separate restoration from loop filter X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f0a4485e540fc3bef66e87feb37d8bb5c2f7409d;p=libvpx Refactor to separate restoration from loop filter Change-Id: Iab517862d957f3aa2a664e9349d57bbf424febb3 --- diff --git a/vp10/common/loopfilter.c b/vp10/common/loopfilter.c index d4381f825..125b5bf0f 100644 --- a/vp10/common/loopfilter.c +++ b/vp10/common/loopfilter.c @@ -15,6 +15,7 @@ #include "vp10/common/loopfilter.h" #include "vp10/common/onyxc_int.h" #include "vp10/common/reconinter.h" +#include "vp10/common/restoration.h" #include "vpx_dsp/vpx_dsp_common.h" #include "vpx_mem/vpx_mem.h" #include "vpx_ports/mem.h" @@ -215,318 +216,6 @@ static const int mode_lf_lut[MB_MODE_COUNT] = { #endif // CONFIG_EXT_INTER }; -#if CONFIG_LOOP_RESTORATION -#define RESTORATION_RANGE 256 -#define RESTORATION_RANGE_SYM (2 * RESTORATION_RANGE + 1) -static double restoration_filters_r_kf[RESTORATION_LEVELS_KF + 1] - [RESTORATION_RANGE_SYM]; -static double restoration_filters_r[RESTORATION_LEVELS + 1] - [RESTORATION_RANGE_SYM]; -static double restoration_filters_s_kf[RESTORATION_LEVELS_KF + 1] - [RESTORATION_WIN][RESTORATION_WIN]; -static double restoration_filters_s[RESTORATION_LEVELS + 1] - [RESTORATION_WIN][RESTORATION_WIN]; - -void vp10_loop_restoration_precal() { - int i; - for (i = 1; i < RESTORATION_LEVELS_KF + 1; i ++) { - const restoration_params_t param = vp10_restoration_level_to_params(i, 1); - const int sigma_x = param.sigma_x; - const int sigma_y = param.sigma_y; - const int sigma_r = param.sigma_r; - const double sigma_r_d = (double)sigma_r / RESTORATION_PRECISION; - const double sigma_x_d = (double)sigma_x / RESTORATION_PRECISION; - const double sigma_y_d = (double)sigma_y / RESTORATION_PRECISION; - - double *fr = restoration_filters_r_kf[i] + RESTORATION_RANGE; - int j, x, y; - for (j = 0; j <= RESTORATION_RANGE; j++) { - fr[j] = exp(-(j * j) / (2 * sigma_r_d * sigma_r_d)); - fr[-j] = fr[j]; - } - for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; y++) { - for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; x++) { - restoration_filters_s_kf[i][y + RESTORATION_HALFWIN] - [x + RESTORATION_HALFWIN] = - exp(-(x * x) / (2 * sigma_x_d * sigma_x_d) - -(y * y) / (2 * sigma_y_d * sigma_y_d)); - } - } - } - for (i = 1; i < RESTORATION_LEVELS + 1; i ++) { - const restoration_params_t param = vp10_restoration_level_to_params(i, 0); - const int sigma_x = param.sigma_x; - const int sigma_y = param.sigma_y; - const int sigma_r = param.sigma_r; - const double sigma_r_d = (double)sigma_r / RESTORATION_PRECISION; - const double sigma_x_d = (double)sigma_x / RESTORATION_PRECISION; - const double sigma_y_d = (double)sigma_y / RESTORATION_PRECISION; - - double *fr = restoration_filters_r[i] + RESTORATION_RANGE; - int j, x, y; - for (j = 0; j <= RESTORATION_RANGE; j++) { - fr[j] = exp(-(j * j) / (2 * sigma_r_d * sigma_r_d)); - fr[-j] = fr[j]; - } - for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; y++) { - for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; x++) { - restoration_filters_s - [i][y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] = - exp(-(x * x) / (2 * sigma_x_d * sigma_x_d) - -(y * y) / (2 * sigma_y_d * sigma_y_d)); - } - } - } -} - -int vp10_restoration_level_bits(const VP10_COMMON *const cm) { - return cm->frame_type == KEY_FRAME ? - RESTORATION_LEVEL_BITS_KF : RESTORATION_LEVEL_BITS; -} - -int vp10_loop_restoration_used(int level, int kf) { - const restoration_params_t param = - vp10_restoration_level_to_params(level, kf); - return (param.sigma_x && param.sigma_y && param.sigma_r); -} - -void vp10_loop_restoration_init(loop_filter_info_n *lfi, int level, int kf) { - lfi->restoration_used = vp10_loop_restoration_used(level, kf); - - if (lfi->restoration_used) { - int i; - lfi->wr_lut = kf ? restoration_filters_r_kf[level] : - restoration_filters_r[level]; - for (i = 0; i < RESTORATION_WIN; i++) - lfi->wx_lut[i] = kf ? restoration_filters_s_kf[level][i] : - restoration_filters_s[level][i]; - } -} - -static int is_in_image(int x, int y, int width, int height) { - return (x >= 0 && x < width && y >= 0 && y < height); -} - -void loop_restoration_filter(uint8_t *data, int width, int height, - int stride, loop_filter_info_n *lfi, - uint8_t *tmpdata, int tmpstride) { - int i, j; - const double *wr_lut_ = lfi->wr_lut + RESTORATION_RANGE; - - uint8_t *data_p = data; - uint8_t *tmpdata_p = tmpdata; - for (i = 0; i < height; ++i) { - for (j = 0; j < width; ++j) { - int x, y; - double flsum = 0, wtsum = 0, wt; - uint8_t *data_p2 = data_p + j - RESTORATION_HALFWIN * stride; - for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; ++y) { - for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; ++x) { - if (!is_in_image(j + x, i + y, width, height)) - continue; - wt = lfi->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] * - wr_lut_[data_p2[x] - data_p[j]]; - wtsum += wt; - flsum += wt * data_p2[x]; - } - data_p2 += stride; - } - assert(wtsum > 0); - tmpdata_p[j] = clip_pixel((int)(flsum / wtsum + 0.5)); - } - tmpdata_p += tmpstride; - data_p += stride; - } - - for (i = 0; i < height; ++i) { - memcpy(data + i * stride, tmpdata + i * tmpstride, - width * sizeof(*data)); - } -} - -// Normalized non-separable filter where weights all sum to 1 -void loop_restoration_filter_norm(uint8_t *data, int width, int height, - int stride, loop_filter_info_n *lfi, - uint8_t *tmpdata, int tmpstride) { - int i, j; - uint8_t *data_p = data; - uint8_t *tmpdata_p = tmpdata; - for (i = RESTORATION_HALFWIN; i < height - RESTORATION_HALFWIN; ++i) { - for (j = RESTORATION_HALFWIN; j < width - RESTORATION_HALFWIN; ++j) { - int x, y; - double flsum = 0; - uint8_t *data_p2 = data_p + j - RESTORATION_HALFWIN * stride; - for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; ++y) { - for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; ++x) { - flsum += data_p2[x] * - lfi->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN]; - } - data_p2 += stride; - } - tmpdata_p[j] = clip_pixel((int)(flsum + 0.5)); - } - tmpdata_p += tmpstride; - data_p += stride; - } - for (i = 0; i < height; ++i) { - memcpy(data + i * stride, tmpdata + i * tmpstride, - width * sizeof(*data)); - } -} - -#if CONFIG_VP9_HIGHBITDEPTH -void loop_restoration_filter_highbd(uint8_t *data8, int width, int height, - int stride, loop_filter_info_n *lfi, - uint8_t *tmpdata8, int tmpstride, - int bit_depth) { - int i, j; - const double *wr_lut_ = lfi->wr_lut + RESTORATION_RANGE; - - uint16_t *data = CONVERT_TO_SHORTPTR(data8); - uint16_t *tmpdata = CONVERT_TO_SHORTPTR(tmpdata8); - uint16_t *data_p = data; - uint16_t *tmpdata_p = tmpdata; - for (i = 0; i < height; ++i) { - for (j = 0; j < width; ++j) { - int x, y, diff_r; - double flsum = 0, wtsum = 0, wt; - uint16_t *data_p2 = data_p + j - RESTORATION_HALFWIN * stride; - - for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; ++y) { - for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; ++x) { - if (!is_in_image(j + x, i + y, width, height)) - continue; - - diff_r = (data_p2[x] - data_p[j]) >> (bit_depth - 8); - assert(diff_r >= -RESTORATION_RANGE && diff_r <= RESTORATION_RANGE); - - wt = lfi->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] * - wr_lut_[diff_r]; - wtsum += wt; - flsum += wt * data_p2[x]; - } - data_p2 += stride; - } - - assert(wtsum > 0); - tmpdata_p[j] = (int)(flsum / wtsum + 0.5); - } - tmpdata_p += tmpstride; - data_p += stride; - } - for (i = 0; i < height; ++i) { - memcpy(data + i * stride, tmpdata + i * tmpstride, - width * sizeof(*data)); - } -} - -// Normalized non-separable filter where weights all sum to 1 -void loop_restoration_filter_norm(uint8_t *data8, int width, int height, - int stride, loop_filter_info_n *lfi, - uint8_t *tmpdata8, int tmpstride) { - int i, j; - uint16_t *data = CONVERT_TO_SHORTPTR(data8); - uint16_t *tmpdata = CONVERT_TO_SHORTPTR(tmpdata8); - uint16_t *data_p = data; - uint16_t *tmpdata_p = tmpdata; - for (i = RESTORATION_HALFWIN; i < height - RESTORATION_HALFWIN; ++i) { - for (j = RESTORATION_HALFWIN; j < width - RESTORATION_HALFWIN; ++j) { - int x, y; - double flsum = 0; - uint16_t *data_p2 = data_p + j - RESTORATION_HALFWIN * stride; - for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; ++y) { - for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; ++x) { - flsum += data_p2[x] * - lfi->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN]; - } - data_p2 += stride; - } - tmpdata_p[j] = (int)(flsum + 0.5); - } - tmpdata_p += tmpstride; - data_p += stride; - } - for (i = 0; i < height; ++i) { - memcpy(data + i * stride, tmpdata + i * tmpstride, - width * sizeof(*data)); - } -} -#endif // CONFIG_VP9_HIGHBITDEPTH - -void vp10_loop_restoration_rows(YV12_BUFFER_CONFIG *frame, - VP10_COMMON *cm, - int start_mi_row, int end_mi_row, - int y_only) { - const int ywidth = frame->y_crop_width; - const int ystride = frame->y_stride; - const int uvwidth = frame->uv_crop_width; - const int uvstride = frame->uv_stride; - const int ystart = start_mi_row << MI_SIZE_LOG2; - const int uvstart = ystart >> cm->subsampling_y; - int yend = end_mi_row << MI_SIZE_LOG2; - int uvend = yend >> cm->subsampling_y; - YV12_BUFFER_CONFIG *tmp_buf; - yend = VPXMIN(yend, cm->height); - uvend = VPXMIN(uvend, cm->subsampling_y ? (cm->height + 1) >> 1 : cm->height); - - if (vpx_realloc_frame_buffer(&cm->tmp_loop_buf, cm->width, cm->height, - cm->subsampling_x, cm->subsampling_y, -#if CONFIG_VP9_HIGHBITDEPTH - cm->use_highbitdepth, -#endif - VP9_DEC_BORDER_IN_PIXELS, cm->byte_alignment, - NULL, NULL, NULL) < 0) - vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR, - "Failed to allocate tmp restoration buffer"); - - tmp_buf = &cm->tmp_loop_buf; - -#if CONFIG_VP9_HIGHBITDEPTH - if (cm->use_highbitdepth) - loop_restoration_filter_highbd( - frame->y_buffer + ystart * ystride, - ywidth, yend - ystart, ystride, &cm->lf_info, - tmp_buf->y_buffer + ystart * tmp_buf->y_stride, - tmp_buf->y_stride, cm->bit_depth); - else -#endif // CONFIG_VP9_HIGHBITDEPTH - loop_restoration_filter( - frame->y_buffer + ystart * ystride, - ywidth, yend - ystart, ystride, &cm->lf_info, - tmp_buf->y_buffer + ystart * tmp_buf->y_stride, - tmp_buf->y_stride); - if (!y_only) { -#if CONFIG_VP9_HIGHBITDEPTH - if (cm->use_highbitdepth) { - loop_restoration_filter_highbd( - frame->u_buffer + uvstart * uvstride, - uvwidth, uvend - uvstart, uvstride, &cm->lf_info, - tmp_buf->u_buffer + uvstart * tmp_buf->uv_stride, - tmp_buf->uv_stride, cm->bit_depth); - loop_restoration_filter_highbd( - frame->v_buffer + uvstart * uvstride, - uvwidth, uvend - uvstart, uvstride, &cm->lf_info, - tmp_buf->v_buffer + uvstart * tmp_buf->uv_stride, - tmp_buf->uv_stride, cm->bit_depth); - } else { -#endif // CONFIG_VP9_HIGHBITDEPTH - loop_restoration_filter( - frame->u_buffer + uvstart * uvstride, - uvwidth, uvend - uvstart, uvstride, &cm->lf_info, - tmp_buf->u_buffer + uvstart * tmp_buf->uv_stride, - tmp_buf->uv_stride); - loop_restoration_filter( - frame->v_buffer + uvstart * uvstride, - uvwidth, uvend - uvstart, uvstride, &cm->lf_info, - tmp_buf->v_buffer + uvstart * tmp_buf->uv_stride, - tmp_buf->uv_stride); -#if CONFIG_VP9_HIGHBITDEPTH - } -#endif // CONFIG_VP9_HIGHBITDEPTH - } -} -#endif // CONFIG_LOOP_RESTORATION - static void update_sharpness(loop_filter_info_n *lfi, int sharpness_lvl) { int lvl; @@ -567,10 +256,6 @@ void vp10_loop_filter_init(VP10_COMMON *cm) { // init hev threshold const vectors for (lvl = 0; lvl <= MAX_LOOP_FILTER; lvl++) memset(lfi->lfthr[lvl].hev_thr, (lvl >> 4), SIMD_WIDTH); - -#if CONFIG_LOOP_RESTORATION - vp10_loop_restoration_precal(); -#endif // CONFIG_LOOP_RESTORATION } void vp10_loop_filter_frame_init(VP10_COMMON *cm, int default_filt_lvl) { @@ -2040,30 +1725,6 @@ void vp10_loop_filter_data_reset( memcpy(lf_data->planes, planes, sizeof(lf_data->planes)); } -#if CONFIG_LOOP_RESTORATION -void vp10_loop_restoration_frame(YV12_BUFFER_CONFIG *frame, - VP10_COMMON *cm, - int restoration_level, - int y_only, int partial_frame) { - int start_mi_row, end_mi_row, mi_rows_to_filter; - // const int loop_restoration_used = vp10_loop_restoration_used( - // restoration_level, cm->frame_type == KEY_FRAME); - vp10_loop_restoration_init(&cm->lf_info, restoration_level, - cm->frame_type == KEY_FRAME); - if (!cm->lf_info.restoration_used) - return; - start_mi_row = 0; - mi_rows_to_filter = cm->mi_rows; - if (partial_frame && cm->mi_rows > 8) { - start_mi_row = cm->mi_rows >> 1; - start_mi_row &= 0xfffffff8; - mi_rows_to_filter = VPXMAX(cm->mi_rows / 8, 8); - } - end_mi_row = start_mi_row + mi_rows_to_filter; - vp10_loop_restoration_rows(frame, cm, start_mi_row, end_mi_row, y_only); -} -#endif // CONFIG_LOOP_RESTORATION - int vp10_loop_filter_worker(LFWorkerData *const lf_data, void *unused) { (void)unused; vp10_loop_filter_rows(lf_data->frame_buffer, lf_data->cm, lf_data->planes, diff --git a/vp10/common/loopfilter.h b/vp10/common/loopfilter.h index 953a6b842..5a16baa25 100644 --- a/vp10/common/loopfilter.h +++ b/vp10/common/loopfilter.h @@ -15,6 +15,7 @@ #include "./vpx_config.h" #include "vp10/common/blockd.h" +#include "vp10/common/restoration.h" #include "vp10/common/seg_common.h" #ifdef __cplusplus @@ -28,69 +29,6 @@ extern "C" { #define MAX_MODE_LF_DELTAS 2 -#if CONFIG_LOOP_RESTORATION -#define RESTORATION_LEVEL_BITS_KF 4 -#define RESTORATION_LEVELS_KF (1 << RESTORATION_LEVEL_BITS_KF) -#define RESTORATION_LEVEL_BITS 3 -#define RESTORATION_LEVELS (1 << RESTORATION_LEVEL_BITS) -#define DEF_RESTORATION_LEVEL 2 - -#define RESTORATION_PRECISION 16 -#define RESTORATION_HALFWIN 3 -#define RESTORATION_WIN (2 * RESTORATION_HALFWIN + 1) - -typedef struct restoration_params { - int sigma_x; // spatial variance x - int sigma_y; // spatial variance y - int sigma_r; // range variance -} restoration_params_t; - -static restoration_params_t - restoration_level_to_params_arr[RESTORATION_LEVELS + 1] = { - // Values are rounded to 1/16 th precision - {0, 0, 0}, // 0 - default - {8, 9, 30}, - {9, 8, 30}, - {9, 11, 32}, - {11, 9, 32}, - {14, 14, 32}, - {18, 18, 36}, - {24, 24, 40}, - {32, 32, 40}, -}; - -static restoration_params_t - restoration_level_to_params_arr_kf[RESTORATION_LEVELS_KF + 1] = { - // Values are rounded to 1/16 th precision - {0, 0, 0}, // 0 - default - {8, 8, 30}, - {9, 9, 32}, - {10, 10, 32}, - {12, 12, 32}, - {14, 14, 32}, - {18, 18, 36}, - {24, 24, 40}, - {30, 30, 44}, - {36, 36, 48}, - {42, 42, 48}, - {48, 48, 48}, - {48, 48, 56}, - {56, 56, 48}, - {56, 56, 56}, - {56, 56, 64}, - {64, 64, 48}, -}; - -int vp10_restoration_level_bits(const struct VP10Common *const cm); -int vp10_loop_restoration_used(int level, int kf); - -static INLINE restoration_params_t vp10_restoration_level_to_params( - int index, int kf) { - return kf ? restoration_level_to_params_arr_kf[index] : - restoration_level_to_params_arr[index]; -} -#endif // CONFIG_LOOP_RESTORATION - enum lf_path { LF_PATH_420, LF_PATH_444, @@ -132,14 +70,6 @@ typedef struct { typedef struct { loop_filter_thresh lfthr[MAX_LOOP_FILTER + 1]; uint8_t lvl[MAX_SEGMENTS][MAX_REF_FRAMES][MAX_MODE_LF_DELTAS]; -#if CONFIG_LOOP_RESTORATION - double *wx_lut[RESTORATION_WIN]; - double *wr_lut; - int restoration_sigma_x_set; - int restoration_sigma_y_set; - int restoration_sigma_r_set; - int restoration_used; -#endif // CONFIG_LOOP_RESTORATION } loop_filter_info_n; // This structure holds bit masks for all 8x8 blocks in a 64x64 region. @@ -209,24 +139,6 @@ void vp10_loop_filter_rows(YV12_BUFFER_CONFIG *frame_buffer, struct macroblockd_plane planes[MAX_MB_PLANE], int start, int stop, int y_only); -#if CONFIG_LOOP_RESTORATION -void vp10_loop_restoration_frame(YV12_BUFFER_CONFIG *frame, - struct VP10Common *cm, - int restoration_level, - int y_only, int partial_frame); -void vp10_loop_filter_restoration_frame(YV12_BUFFER_CONFIG *frame, - struct VP10Common *cm, - struct macroblockd *mbd, - int frame_filter_level, - int restoration_level, - int y_only, int partial_frame); -void vp10_loop_restoration_init(loop_filter_info_n *lfi, int T, int kf); -void vp10_loop_restoration_rows(YV12_BUFFER_CONFIG *frame, - struct VP10Common *cm, - int start_mi_row, int end_mi_row, - int y_only); -#endif // CONFIG_LOOP_RESTORATION - typedef struct LoopFilterWorkerData { YV12_BUFFER_CONFIG *frame_buffer; struct VP10Common *cm; diff --git a/vp10/common/onyxc_int.h b/vp10/common/onyxc_int.h index b6509cb48..53de852d1 100644 --- a/vp10/common/onyxc_int.h +++ b/vp10/common/onyxc_int.h @@ -24,6 +24,7 @@ #include "vp10/common/frame_buffers.h" #include "vp10/common/quant_common.h" #include "vp10/common/tile_common.h" +#include "vp10/common/restoration.h" #if CONFIG_VP9_POSTPROC #include "vp10/common/postproc.h" @@ -255,6 +256,9 @@ typedef struct VP10Common { INTERP_FILTER interp_filter; loop_filter_info_n lf_info; +#if CONFIG_LOOP_RESTORATION + restoration_info_n rst_info; +#endif // CONFIG_LOOP_RESTORATION // Flag signaling how frame contexts should be updated at the end of // a frame decode diff --git a/vp10/common/restoration.c b/vp10/common/restoration.c new file mode 100644 index 000000000..ee720c484 --- /dev/null +++ b/vp10/common/restoration.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2016 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +#include "./vpx_config.h" +#include "./vpx_dsp_rtcd.h" +#include "vp10/common/onyxc_int.h" +#include "vp10/common/restoration.h" +#include "vpx_dsp/vpx_dsp_common.h" +#include "vpx_mem/vpx_mem.h" +#include "vpx_ports/mem.h" + +#define RESTORATION_RANGE 256 +#define RESTORATION_RANGE_SYM (2 * RESTORATION_RANGE + 1) +static double restoration_filters_r_kf[RESTORATION_LEVELS_KF + 1] + [RESTORATION_RANGE_SYM]; +static double restoration_filters_r[RESTORATION_LEVELS + 1] + [RESTORATION_RANGE_SYM]; +static double restoration_filters_s_kf[RESTORATION_LEVELS_KF + 1] + [RESTORATION_WIN][RESTORATION_WIN]; +static double restoration_filters_s[RESTORATION_LEVELS + 1] + [RESTORATION_WIN][RESTORATION_WIN]; + +void vp10_loop_restoration_precal() { + int i; + for (i = 1; i < RESTORATION_LEVELS_KF + 1; i ++) { + const restoration_params_t param = vp10_restoration_level_to_params(i, 1); + const int sigma_x = param.sigma_x; + const int sigma_y = param.sigma_y; + const int sigma_r = param.sigma_r; + const double sigma_r_d = (double)sigma_r / RESTORATION_PRECISION; + const double sigma_x_d = (double)sigma_x / RESTORATION_PRECISION; + const double sigma_y_d = (double)sigma_y / RESTORATION_PRECISION; + + double *fr = restoration_filters_r_kf[i] + RESTORATION_RANGE; + int j, x, y; + for (j = 0; j <= RESTORATION_RANGE; j++) { + fr[j] = exp(-(j * j) / (2 * sigma_r_d * sigma_r_d)); + fr[-j] = fr[j]; + } + for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; y++) { + for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; x++) { + restoration_filters_s_kf[i][y + RESTORATION_HALFWIN] + [x + RESTORATION_HALFWIN] = + exp(-(x * x) / (2 * sigma_x_d * sigma_x_d) + -(y * y) / (2 * sigma_y_d * sigma_y_d)); + } + } + } + for (i = 1; i < RESTORATION_LEVELS + 1; i ++) { + const restoration_params_t param = vp10_restoration_level_to_params(i, 0); + const int sigma_x = param.sigma_x; + const int sigma_y = param.sigma_y; + const int sigma_r = param.sigma_r; + const double sigma_r_d = (double)sigma_r / RESTORATION_PRECISION; + const double sigma_x_d = (double)sigma_x / RESTORATION_PRECISION; + const double sigma_y_d = (double)sigma_y / RESTORATION_PRECISION; + + double *fr = restoration_filters_r[i] + RESTORATION_RANGE; + int j, x, y; + for (j = 0; j <= RESTORATION_RANGE; j++) { + fr[j] = exp(-(j * j) / (2 * sigma_r_d * sigma_r_d)); + fr[-j] = fr[j]; + } + for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; y++) { + for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; x++) { + restoration_filters_s + [i][y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] = + exp(-(x * x) / (2 * sigma_x_d * sigma_x_d) + -(y * y) / (2 * sigma_y_d * sigma_y_d)); + } + } + } +} + +int vp10_restoration_level_bits(const VP10_COMMON *const cm) { + return cm->frame_type == KEY_FRAME ? + RESTORATION_LEVEL_BITS_KF : RESTORATION_LEVEL_BITS; +} + +int vp10_loop_restoration_used(int level, int kf) { + const restoration_params_t param = + vp10_restoration_level_to_params(level, kf); + return (param.sigma_x && param.sigma_y && param.sigma_r); +} + +void vp10_loop_restoration_init(restoration_info_n *rst, + int level, int kf) { + rst->restoration_used = vp10_loop_restoration_used(level, kf); + + if (rst->restoration_used) { + int i; + rst->wr_lut = kf ? restoration_filters_r_kf[level] : + restoration_filters_r[level]; + for (i = 0; i < RESTORATION_WIN; i++) + rst->wx_lut[i] = kf ? restoration_filters_s_kf[level][i] : + restoration_filters_s[level][i]; + } +} + +static int is_in_image(int x, int y, int width, int height) { + return (x >= 0 && x < width && y >= 0 && y < height); +} + +static void loop_restoration_filter(uint8_t *data, int width, int height, + int stride, restoration_info_n *rst, + uint8_t *tmpdata, int tmpstride) { + int i, j; + const double *wr_lut_ = rst->wr_lut + RESTORATION_RANGE; + + uint8_t *data_p = data; + uint8_t *tmpdata_p = tmpdata; + for (i = 0; i < height; ++i) { + for (j = 0; j < width; ++j) { + int x, y; + double flsum = 0, wtsum = 0, wt; + uint8_t *data_p2 = data_p + j - RESTORATION_HALFWIN * stride; + for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; ++y) { + for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; ++x) { + if (!is_in_image(j + x, i + y, width, height)) + continue; + wt = rst->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] * + wr_lut_[data_p2[x] - data_p[j]]; + wtsum += wt; + flsum += wt * data_p2[x]; + } + data_p2 += stride; + } + assert(wtsum > 0); + tmpdata_p[j] = clip_pixel((int)(flsum / wtsum + 0.5)); + } + tmpdata_p += tmpstride; + data_p += stride; + } + + for (i = 0; i < height; ++i) { + memcpy(data + i * stride, tmpdata + i * tmpstride, + width * sizeof(*data)); + } +} + +// Normalized non-separable filter where weights all sum to 1 +static void loop_restoration_filter_norm(uint8_t *data, int width, int height, + int stride, restoration_info_n *rst, + uint8_t *tmpdata, int tmpstride) { + int i, j; + uint8_t *data_p = data; + uint8_t *tmpdata_p = tmpdata; + for (i = RESTORATION_HALFWIN; i < height - RESTORATION_HALFWIN; ++i) { + for (j = RESTORATION_HALFWIN; j < width - RESTORATION_HALFWIN; ++j) { + int x, y; + double flsum = 0; + uint8_t *data_p2 = data_p + j - RESTORATION_HALFWIN * stride; + for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; ++y) { + for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; ++x) { + flsum += data_p2[x] * + rst->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN]; + } + data_p2 += stride; + } + tmpdata_p[j] = clip_pixel((int)(flsum + 0.5)); + } + tmpdata_p += tmpstride; + data_p += stride; + } + for (i = 0; i < height; ++i) { + memcpy(data + i * stride, tmpdata + i * tmpstride, + width * sizeof(*data)); + } +} + +#if CONFIG_VP9_HIGHBITDEPTH +static void loop_restoration_filter_highbd( + uint8_t *data8, int width, int height, + int stride, restoration_info_n *rst, + uint8_t *tmpdata8, int tmpstride, int bit_depth) { + int i, j; + const double *wr_lut_ = rst->wr_lut + RESTORATION_RANGE; + + uint16_t *data = CONVERT_TO_SHORTPTR(data8); + uint16_t *tmpdata = CONVERT_TO_SHORTPTR(tmpdata8); + uint16_t *data_p = data; + uint16_t *tmpdata_p = tmpdata; + for (i = 0; i < height; ++i) { + for (j = 0; j < width; ++j) { + int x, y, diff_r; + double flsum = 0, wtsum = 0, wt; + uint16_t *data_p2 = data_p + j - RESTORATION_HALFWIN * stride; + + for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; ++y) { + for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; ++x) { + if (!is_in_image(j + x, i + y, width, height)) + continue; + + diff_r = (data_p2[x] - data_p[j]) >> (bit_depth - 8); + assert(diff_r >= -RESTORATION_RANGE && diff_r <= RESTORATION_RANGE); + + wt = rst->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN] * + wr_lut_[diff_r]; + wtsum += wt; + flsum += wt * data_p2[x]; + } + data_p2 += stride; + } + + assert(wtsum > 0); + tmpdata_p[j] = (int)(flsum / wtsum + 0.5); + } + tmpdata_p += tmpstride; + data_p += stride; + } + for (i = 0; i < height; ++i) { + memcpy(data + i * stride, tmpdata + i * tmpstride, + width * sizeof(*data)); + } +} + +// Normalized non-separable filter where weights all sum to 1 +static void loop_restoration_filter_norm_highbd( + uint8_t *data8, int width, int height, + int stride, restoration_info_n *rst, + uint8_t *tmpdata8, int tmpstride) { + int i, j; + uint16_t *data = CONVERT_TO_SHORTPTR(data8); + uint16_t *tmpdata = CONVERT_TO_SHORTPTR(tmpdata8); + uint16_t *data_p = data; + uint16_t *tmpdata_p = tmpdata; + for (i = RESTORATION_HALFWIN; i < height - RESTORATION_HALFWIN; ++i) { + for (j = RESTORATION_HALFWIN; j < width - RESTORATION_HALFWIN; ++j) { + int x, y; + double flsum = 0; + uint16_t *data_p2 = data_p + j - RESTORATION_HALFWIN * stride; + for (y = -RESTORATION_HALFWIN; y <= RESTORATION_HALFWIN; ++y) { + for (x = -RESTORATION_HALFWIN; x <= RESTORATION_HALFWIN; ++x) { + flsum += data_p2[x] * + rst->wx_lut[y + RESTORATION_HALFWIN][x + RESTORATION_HALFWIN]; + } + data_p2 += stride; + } + tmpdata_p[j] = (int)(flsum + 0.5); + } + tmpdata_p += tmpstride; + data_p += stride; + } + for (i = 0; i < height; ++i) { + memcpy(data + i * stride, tmpdata + i * tmpstride, + width * sizeof(*data)); + } +} +#endif // CONFIG_VP9_HIGHBITDEPTH + +void vp10_loop_restoration_rows(YV12_BUFFER_CONFIG *frame, + VP10_COMMON *cm, + int start_mi_row, int end_mi_row, + int y_only) { + const int ywidth = frame->y_crop_width; + const int ystride = frame->y_stride; + const int uvwidth = frame->uv_crop_width; + const int uvstride = frame->uv_stride; + const int ystart = start_mi_row << MI_SIZE_LOG2; + const int uvstart = ystart >> cm->subsampling_y; + int yend = end_mi_row << MI_SIZE_LOG2; + int uvend = yend >> cm->subsampling_y; + YV12_BUFFER_CONFIG *tmp_buf; + yend = VPXMIN(yend, cm->height); + uvend = VPXMIN(uvend, cm->subsampling_y ? (cm->height + 1) >> 1 : cm->height); + + if (vpx_realloc_frame_buffer(&cm->tmp_loop_buf, cm->width, cm->height, + cm->subsampling_x, cm->subsampling_y, +#if CONFIG_VP9_HIGHBITDEPTH + cm->use_highbitdepth, +#endif + VP9_DEC_BORDER_IN_PIXELS, cm->byte_alignment, + NULL, NULL, NULL) < 0) + vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR, + "Failed to allocate tmp restoration buffer"); + + tmp_buf = &cm->tmp_loop_buf; + +#if CONFIG_VP9_HIGHBITDEPTH + if (cm->use_highbitdepth) + loop_restoration_filter_highbd( + frame->y_buffer + ystart * ystride, + ywidth, yend - ystart, ystride, &cm->rst_info, + tmp_buf->y_buffer + ystart * tmp_buf->y_stride, + tmp_buf->y_stride, cm->bit_depth); + else +#endif // CONFIG_VP9_HIGHBITDEPTH + loop_restoration_filter( + frame->y_buffer + ystart * ystride, + ywidth, yend - ystart, ystride, &cm->rst_info, + tmp_buf->y_buffer + ystart * tmp_buf->y_stride, + tmp_buf->y_stride); + if (!y_only) { +#if CONFIG_VP9_HIGHBITDEPTH + if (cm->use_highbitdepth) { + loop_restoration_filter_highbd( + frame->u_buffer + uvstart * uvstride, + uvwidth, uvend - uvstart, uvstride, &cm->rst_info, + tmp_buf->u_buffer + uvstart * tmp_buf->uv_stride, + tmp_buf->uv_stride, cm->bit_depth); + loop_restoration_filter_highbd( + frame->v_buffer + uvstart * uvstride, + uvwidth, uvend - uvstart, uvstride, &cm->rst_info, + tmp_buf->v_buffer + uvstart * tmp_buf->uv_stride, + tmp_buf->uv_stride, cm->bit_depth); + } else { +#endif // CONFIG_VP9_HIGHBITDEPTH + loop_restoration_filter( + frame->u_buffer + uvstart * uvstride, + uvwidth, uvend - uvstart, uvstride, &cm->rst_info, + tmp_buf->u_buffer + uvstart * tmp_buf->uv_stride, + tmp_buf->uv_stride); + loop_restoration_filter( + frame->v_buffer + uvstart * uvstride, + uvwidth, uvend - uvstart, uvstride, &cm->rst_info, + tmp_buf->v_buffer + uvstart * tmp_buf->uv_stride, + tmp_buf->uv_stride); +#if CONFIG_VP9_HIGHBITDEPTH + } +#endif // CONFIG_VP9_HIGHBITDEPTH + } +} + +void vp10_loop_restoration_frame(YV12_BUFFER_CONFIG *frame, + VP10_COMMON *cm, + int restoration_level, + int y_only, int partial_frame) { + int start_mi_row, end_mi_row, mi_rows_to_filter; + vp10_loop_restoration_init(&cm->rst_info, restoration_level, + cm->frame_type == KEY_FRAME); + if (!cm->rst_info.restoration_used) + return; + start_mi_row = 0; + mi_rows_to_filter = cm->mi_rows; + if (partial_frame && cm->mi_rows > 8) { + start_mi_row = cm->mi_rows >> 1; + start_mi_row &= 0xfffffff8; + mi_rows_to_filter = VPXMAX(cm->mi_rows / 8, 8); + } + end_mi_row = start_mi_row + mi_rows_to_filter; + vp10_loop_restoration_rows(frame, cm, start_mi_row, end_mi_row, y_only); +} diff --git a/vp10/common/restoration.h b/vp10/common/restoration.h new file mode 100644 index 000000000..3859191f1 --- /dev/null +++ b/vp10/common/restoration.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef VP10_COMMON_RESTORATION_H_ +#define VP10_COMMON_RESTORATION_H_ + +#include "vpx_ports/mem.h" +#include "./vpx_config.h" + +#include "vp10/common/blockd.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define RESTORATION_LEVEL_BITS_KF 4 +#define RESTORATION_LEVELS_KF (1 << RESTORATION_LEVEL_BITS_KF) +#define RESTORATION_LEVEL_BITS 3 +#define RESTORATION_LEVELS (1 << RESTORATION_LEVEL_BITS) +#define DEF_RESTORATION_LEVEL 2 + +#define RESTORATION_PRECISION 16 +#define RESTORATION_HALFWIN 3 +#define RESTORATION_WIN (2 * RESTORATION_HALFWIN + 1) + +typedef struct restoration_params { + int sigma_x; // spatial variance x + int sigma_y; // spatial variance y + int sigma_r; // range variance +} restoration_params_t; + +static restoration_params_t + restoration_level_to_params_arr[RESTORATION_LEVELS + 1] = { + // Values are rounded to 1/16 th precision + {0, 0, 0}, // 0 - default + {8, 9, 30}, + {9, 8, 30}, + {9, 11, 32}, + {11, 9, 32}, + {14, 14, 32}, + {18, 18, 36}, + {24, 24, 40}, + {32, 32, 40}, +}; + +static restoration_params_t + restoration_level_to_params_arr_kf[RESTORATION_LEVELS_KF + 1] = { + // Values are rounded to 1/16 th precision + {0, 0, 0}, // 0 - default + {8, 8, 30}, + {9, 9, 32}, + {10, 10, 32}, + {12, 12, 32}, + {14, 14, 32}, + {18, 18, 36}, + {24, 24, 40}, + {30, 30, 44}, + {36, 36, 48}, + {42, 42, 48}, + {48, 48, 48}, + {48, 48, 56}, + {56, 56, 48}, + {56, 56, 56}, + {56, 56, 64}, + {64, 64, 48}, +}; + +typedef struct { + double *wx_lut[RESTORATION_WIN]; + double *wr_lut; + int restoration_sigma_x_set; + int restoration_sigma_y_set; + int restoration_sigma_r_set; + int restoration_used; +} restoration_info_n; + +int vp10_restoration_level_bits(const struct VP10Common *const cm); +int vp10_loop_restoration_used(int level, int kf); + +static INLINE restoration_params_t vp10_restoration_level_to_params( + int index, int kf) { + return kf ? restoration_level_to_params_arr_kf[index] : + restoration_level_to_params_arr[index]; +} + +void vp10_loop_restoration_init(restoration_info_n *rst, int T, int kf); +void vp10_loop_restoration_frame(YV12_BUFFER_CONFIG *frame, + struct VP10Common *cm, + int restoration_level, + int y_only, int partial_frame); +void vp10_loop_restoration_rows(YV12_BUFFER_CONFIG *frame, + struct VP10Common *cm, + int start_mi_row, int end_mi_row, + int y_only); +void vp10_loop_restoration_precal(); +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // VP10_COMMON_RESTORATION_H_ diff --git a/vp10/decoder/decodeframe.c b/vp10/decoder/decodeframe.c index d0c9ffbc2..c8c8d8ae7 100644 --- a/vp10/decoder/decodeframe.c +++ b/vp10/decoder/decodeframe.c @@ -3481,9 +3481,9 @@ void vp10_decode_frame(VP10Decoder *pbi, *p_data_end = decode_tiles(pbi, data + first_partition_size, data_end); } #if CONFIG_LOOP_RESTORATION - vp10_loop_restoration_init(&cm->lf_info, cm->lf.restoration_level, + vp10_loop_restoration_init(&cm->rst_info, cm->lf.restoration_level, cm->frame_type == KEY_FRAME); - if (cm->lf_info.restoration_used) { + if (cm->rst_info.restoration_used) { vp10_loop_restoration_rows(new_fb, cm, 0, cm->mi_rows, 0); } #endif // CONFIG_LOOP_RESTORATION diff --git a/vp10/decoder/decoder.c b/vp10/decoder/decoder.c index 329e54cc1..90365a819 100644 --- a/vp10/decoder/decoder.c +++ b/vp10/decoder/decoder.c @@ -115,6 +115,9 @@ VP10Decoder *vp10_decoder_create(BufferPool *const pool) { cm->setup_mi = vp10_dec_setup_mi; vp10_loop_filter_init(cm); +#if CONFIG_LOOP_RESTORATION + vp10_loop_restoration_precal(); +#endif // CONFIG_LOOP_RESTORATION #if CONFIG_ANS vp10_build_pareto8_dec_tab(vp10_pareto8_token_probs, pbi->token_tab); #endif // CONFIG_ANS diff --git a/vp10/encoder/encoder.c b/vp10/encoder/encoder.c index c3e2fcd72..374a62e7f 100644 --- a/vp10/encoder/encoder.c +++ b/vp10/encoder/encoder.c @@ -1871,6 +1871,9 @@ VP10_COMP *vp10_create_compressor(VP10EncoderConfig *oxcf, vp10_init_quantizer(cpi); vp10_loop_filter_init(cm); +#if CONFIG_LOOP_RESTORATION + vp10_loop_restoration_precal(); +#endif // CONFIG_LOOP_RESTORATION cm->error.setjmp = 0; @@ -2779,9 +2782,9 @@ static void loopfilter_frame(VP10_COMP *cpi, VP10_COMMON *cm) { #endif } #if CONFIG_LOOP_RESTORATION - vp10_loop_restoration_init(&cm->lf_info, cm->lf.restoration_level, - cm->frame_type == KEY_FRAME); - if (cm->lf_info.restoration_used) + vp10_loop_restoration_init(&cm->rst_info, cm->lf.restoration_level, + cm->frame_type == KEY_FRAME); + if (cm->rst_info.restoration_used) vp10_loop_restoration_rows(cm->frame_to_show, cm, 0, cm->mi_rows, 0); #endif // CONFIG_LOOP_RESTORATION diff --git a/vp10/vp10_common.mk b/vp10/vp10_common.mk index bc3d84aa1..fab97eae3 100644 --- a/vp10/vp10_common.mk +++ b/vp10/vp10_common.mk @@ -80,6 +80,8 @@ VP10_COMMON_SRCS-$(CONFIG_ANS) += common/divide.c VP10_COMMON_SRCS-$(CONFIG_VP9_POSTPROC) += common/postproc.h VP10_COMMON_SRCS-$(CONFIG_VP9_POSTPROC) += common/postproc.c +VP10_COMMON_SRCS-$(CONFIG_LOOP_RESTORATION) += common/restoration.h +VP10_COMMON_SRCS-$(CONFIG_LOOP_RESTORATION) += common/restoration.c VP10_COMMON_SRCS-$(CONFIG_VP9_POSTPROC) += common/mfqe.h VP10_COMMON_SRCS-$(CONFIG_VP9_POSTPROC) += common/mfqe.c ifeq ($(CONFIG_VP9_POSTPROC),yes)